Skip to content

Commit

Permalink
feat: Everything + Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
Hypfer committed Apr 7, 2022
1 parent b54f7af commit 242d50a
Show file tree
Hide file tree
Showing 15 changed files with 5,345 additions and 0 deletions.
63 changes: 63 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
'name': 'Release',
'on': { 'release': { 'types': ['published'] } },
'jobs':
{
'publish-release':
{
'runs-on': 'ubuntu-latest',
'strategy': { 'matrix': { 'node-version': ['16.x'] } },
'steps':
[
{ 'uses': 'actions/checkout@v2' },
{
'name': 'Use Node.js ${{ matrix.node-version }}',
'uses': 'actions/setup-node@v1',
'with': { 'node-version': '${{ matrix.node-version }}' },
},
{ 'name': 'Install', 'run': 'npm ci' },
{
'name': 'Build',
'run': 'npm run build',
},

{
'name': 'Upload win x64',
'uses': 'actions/upload-release-asset@v1.0.2',
'env': { 'GITHUB_TOKEN': '${{ secrets.GITHUB_TOKEN }}' },
'with':
{
'upload_url': '${{ github.event.release.upload_url }}',
'asset_path': './build/valetudo-helper-miioota.exe',
'asset_name': 'valetudo-helper-miioota.exe',
'asset_content_type': 'binary/octet-stream',
},
},
{
'name': 'Upload lin x64',
'uses': 'actions/upload-release-asset@v1.0.2',
'env': { 'GITHUB_TOKEN': '${{ secrets.GITHUB_TOKEN }}' },
'with':
{
'upload_url': '${{ github.event.release.upload_url }}',
'asset_path': './build/valetudo-helper-miioota-amd64',
'asset_name': 'valetudo-helper-miioota-amd64',
'asset_content_type': 'binary/octet-stream',
},
},
{
'name': 'Upload lin armv7',
'uses': 'actions/upload-release-asset@v1.0.2',
'env': { 'GITHUB_TOKEN': '${{ secrets.GITHUB_TOKEN }}' },
'with':
{
'upload_url': '${{ github.event.release.upload_url }}',
'asset_path': './build/valetudo-helper-miioota-armv7',
'asset_name': 'valetudo-helper-miioota-armv7',
'asset_content_type': 'binary/octet-stream',
},
},
],
},
},
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build_dependencies/
/build/
248 changes: 248 additions & 0 deletions Logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
const EventEmitter = require("events").EventEmitter;
const fs = require("fs");
const os = require("os");
const Tools = require("./utils/Tools");
const util = require("util");

class Logger {
constructor() {
this._logEventEmitter = new EventEmitter();
this._logFileMaxSizeCheckLineCounter = 1;


this.logFileMaxSize = 4 * 1024 * 1024; //4MiB
this.logLevel = Logger.LogLevels["info"];

this.logFilePath = os.type() === "Windows_NT" ? Logger.DEFAULT_LOGFILE_PATHS.WINNT : Logger.DEFAULT_LOGFILE_PATHS.POSIX;
this.logFileWriteStream = fs.createWriteStream(this.logFilePath, Logger.LogFileOptions);
}

/**
* @public
* @return {string}
*/
getLogLevel() {
return Object.keys(Logger.LogLevels).find(key => {
return Logger.LogLevels[key] === this.logLevel;
});
}

/**
* @public
* @param {string} value
*/
setLogLevel(value) {
if (Logger.LogLevels[value] === undefined) {
throw new Error(`Invalid log level '${value}', valid are '${Object.keys(Logger.LogLevels).join("','")}'`);
} else {
this.logLevel = Logger.LogLevels[value];
}
}

/**
* @public
* @return {string}
*/
getLogFilePath() {
return this.logFilePath;
}

/**
* @public
* @param {string} filePath
*/
setLogFilePath(filePath) {
if (Tools.ARE_SAME_FILES(this.logFilePath, filePath)) {
// We are already writing to that file
return;
}

if (this.logFileWriteStream) {
this.logFileWriteStream.close();
this.logFileWriteStream = null;
}

this.logFilePath = filePath;
// Check if output is already redirected to that same file. If
// it is, we do not need to write to that same file, because that
// would lead to duplicate log entries.
// Setting the LogFilename anyway ensures that the UI Log still works.
if (!Tools.ARE_SAME_FILES(filePath, "/proc/self/fd/1")) {
this.logFileWriteStream = fs.createWriteStream(this.logFilePath, Logger.LogFileOptions);
}

this.log("info", "Set Logfile to " + filePath);
}


/**
* @private
* @param {string} logLevel
* @return {string}
*/
buildLogLinePrefix(logLevel) {
return `[${new Date().toISOString()}] [${logLevel}]`;
}

/**
* @param {string} level
* @param {...any} args
* @private
*/
log(level, ...args) {
if (this.logLevel["level"] <= Logger.LogLevels[level]["level"]) {
const logLinePrefix = this.buildLogLinePrefix(level.toUpperCase());
const logLine = [logLinePrefix, ...args].map(arg => {
if (typeof arg === "string") {
return arg;
}

return util.inspect(
arg,
{
depth: Infinity
}
);
}).join(" ");

Logger.LogLevels[level]["callback"](logLine);
this.logLineToFile(logLine);
this._logEventEmitter.emit("LogMessage", logLine);
}
}

/**
* @private
* @param {string} line
*/
logLineToFile(line) {
if (this.logFileWriteStream) {
/*
As the default limit is rather large, we can avoid checking the logfile size on every single
log line without running into any OOM issues
*/
this._logFileMaxSizeCheckLineCounter = (this._logFileMaxSizeCheckLineCounter + 1) % 100;

if (this._logFileMaxSizeCheckLineCounter === 0) {
if (
this.logFilePath !== Logger.DEFAULT_LOGFILE_PATHS.WINNT &&
this.logFilePath !== Logger.DEFAULT_LOGFILE_PATHS.POSIX
) {
let fileSize = 0;

try {
const stat = fs.statSync(this.logFilePath);

fileSize = stat.size;
} catch (e) {
this.error("Error while checking Logfile size:", e);
}

if (fileSize > this.logFileMaxSize) {
this.logFileWriteStream.close();
fs.writeFileSync(this.logFilePath, "");
this.logFileWriteStream = fs.createWriteStream(this.logFilePath, Logger.LogFileOptions);

this.warn(`Logfile ${this.logFilePath} was cleared after reaching a size of ${fileSize} bytes.`);
}
}
}


this.logFileWriteStream.write(line);
this.logFileWriteStream.write("\n");
}
}

/**
* @public
* @param {any} listener
*/
onLogMessage(listener) {
this._logEventEmitter.on("LogMessage", listener);
}

/**
* @public
* @see console.trace
* @param {...any} args
*/
trace(...args) {
this.log("trace", ...args);
}

/**
* @public
* @see console.debug
* @param {...any} args
*/
debug(...args) {
this.log("debug", ...args);
}

/**
* @public
* @see console.info
* @param {...any} args
*/
info(...args) {
this.log("info", ...args);
}

/**
* @public
* @see console.warn
* @param {...any} args
*/
warn(...args) {
this.log("warn", ...args);
}

/**
* @public
* @see console.error
* @param {...any} args
*/
error( ...args) {
this.log("error", ...args);
}

/**
* @public
*/
getProperties() {
return {
EVENTS: Logger.EVENTS,
LogLevels: Logger.LogLevels
};
}
}

Logger.EVENTS = {
LogMessage: "LogMessage",
};

Logger.LogLevels = Object.freeze({
// eslint-disable-next-line no-console
"trace": {"level": -2, "callback": console.debug},
// eslint-disable-next-line no-console
"debug": {"level": -1, "callback": console.debug},
// eslint-disable-next-line no-console
"info": {"level": 0, "callback": console.info},
// eslint-disable-next-line no-console
"warn": {"level": 1, "callback": console.warn},
// eslint-disable-next-line no-console
"error": {"level": 2, "callback": console.error},
});

Logger.LogFileOptions = Object.freeze({
flags: "as"
});

Logger.DEFAULT_LOGFILE_PATHS = Object.freeze({
POSIX: "/dev/null",
WINNT: "\\\\.\\NUL"
});


module.exports = new Logger();
26 changes: 26 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# valetudo-helper-miioota

![example](https://user-images.githubusercontent.com/974410/162240051-e27cb05b-0fda-4ce0-a6b6-4809a5935f63.png)


valetudo-helper-miioota is a small utility that can be used to install rooted firmwares onto some older robots.

Those being:
- Roborock S5 (non Max!!)
- Roborock V1 also known as the Xiaomi Mi Robot Vacuum (made before 2020-03)

It comes as a single binary with no additional dependencies and requires only experience with a terminal.

Simply download the latest binary [from the releases section](https://github.com/Hypfer/valetudo-helper-miioota/releases)
and execute it in a terminal/powershell window.



## Valetudo helpers

Valetudo helpers are a series of small standalone self-contained single-purpose single-file tools built to make
usage and/or installation of Valetudo a bit easier.

As with everything Valetudo, some intermediate computer skills are required. You should know what a network is,
what HTTP is, how a terminal works and that kind of stuff.
Please understand that it's not feasible for this or any open source project to provide basic computer lessons.
23 changes: 23 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { Command } = require('commander');
const aioInstallCommand = require("./commands/aioinstall");


const program = new Command();

program
.name('valetudo-helper-miioota')
.description('CLI tool to install firmwares via miio local OTA')
.version('2022.04.0');


program.command('install-firmware')
.description('Install a rooted firmware on an unprovisioned robot')
.argument('<filename>', 'path to the rooted firmware image .pkg')
.action((filePath) => {
aioInstallCommand(filePath).catch(err => {
console.error("Error during execution of install command", err);
process.exit(-1);
})
});

program.parse();
Loading

0 comments on commit 242d50a

Please sign in to comment.