diff --git a/README.md b/README.md
index 5c01d42430..0a361f3e8d 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,12 @@ This feature only works when map creation is enabled in the adapter options!
Placeholder for the next version (at the beginning of the line):
### **WORK IN PROGRESS**
-->
+
+### **WORK IN PROGRESS**
+ * (copystring) Add some missing attributes
+ * (copystring) Change min of update interval to 60s to prevent issues
+ * (copystring) Add web interface to sidebar
+
### 0.6.16 (2024-10-02)
* (copystring) Bugfixes
* (copystring) update test-and-release.yml
diff --git a/admin/jsonConfig.json b/admin/jsonConfig.json
index 460de054d3..e8bac6db22 100644
--- a/admin/jsonConfig.json
+++ b/admin/jsonConfig.json
@@ -30,7 +30,7 @@
"type": "number",
"label": "Update interval",
"newLine": true,
- "min": 1,
+ "min": 60,
"max": 240,
"style": {
"width": "198px"
diff --git a/io-package.json b/io-package.json
index 236de3cc7f..bd71a01bde 100644
--- a/io-package.json
+++ b/io-package.json
@@ -147,8 +147,26 @@
"connectionType": "cloud",
"dataSource": "poll",
"adminUI": {
- "config": "json"
+ "config": "json",
+ "tab": "html"
},
+ "adminTab": {
+ "name": {
+ "en": "Roborock",
+ "de": "Roborock",
+ "ru": "Roborock",
+ "pt": "Roborock",
+ "nl": "Roborock",
+ "fr": "Roborock",
+ "it": "Roborock",
+ "es": "Roborock",
+ "pl": "Roborock",
+ "zh-cn": "Roborock"
+ },
+ "link": "%web_protocol%://%ip%:%webserverPort%/map.html",
+ "fa-icon": ""
+ },
+ "localLink": "%web_protocol%://%ip%:%webserverPort%/map.html",
"dependencies": [
{
"js-controller": ">=5.0.19"
diff --git a/lib/RRMapParser.js b/lib/RRMapParser.js
index 463a2f9130..f82b780138 100644
--- a/lib/RRMapParser.js
+++ b/lib/RRMapParser.js
@@ -67,13 +67,7 @@ class RRMapParser {
}
BytesToInt(buffer, offset, len) {
- let result = 0;
-
- for (let i = 0; i < len; i++) {
- result |= (0x000000FF & parseInt(buffer[i + offset])) << 8 * i;
- }
-
- return result;
+ return buffer.slice(offset, offset + len).reduce((acc, byte, i) => acc | (byte << (8 * i)), 0);
}
async parsedata(buf) {
@@ -90,8 +84,7 @@ class RRMapParser {
let dataPosition = 0x14; // Skip header
- const result = {};
- result.metaData = metaData;
+ const result = { metaData };
while (dataPosition < metaData.data_length) {
const type = buf.readUInt16LE(dataPosition);
@@ -104,7 +97,6 @@ class RRMapParser {
// this.adapter.log.debug("Known values: type=" + type + ", hlength=" + hlength + ", length=" + length);
if (TYPES_REVERSE[type]) {
-
// this.adapter.log.debug("Test length: " + TYPES_REVERSE[type] + " " + length);
// if (length < 100) this.adapter.log.debug("Test data type: " + TYPES_REVERSE[type] + " " + buf.toString("hex", dataPosition, dataPosition + length));
@@ -294,9 +286,12 @@ class RRMapParser {
major: mapBuf.readUInt16LE(0x08),
minor: mapBuf.readUInt16LE(0x0a),
},
- map_index: mapBuf.readUInt32LE(0x0C),
+ map_index: mapBuf.readUInt32LE(0x0c),
map_sequence: mapBuf.readUInt32LE(0x10),
- SHA1: crypto.createHash("sha1").update(Uint8Array.prototype.slice.call(mapBuf, 0, mapBuf.length - 20)).digest("hex"),
+ SHA1: crypto
+ .createHash("sha1")
+ .update(Uint8Array.prototype.slice.call(mapBuf, 0, mapBuf.length - 20))
+ .digest("hex"),
expectedSHA1: Buffer.from(Uint8Array.prototype.slice.call(mapBuf, mapBuf.length - 20)).toString("hex"),
};
} else {
diff --git a/lib/deviceFeatures.js b/lib/deviceFeatures.js
index 7e34e64178..b3a7e607a3 100644
--- a/lib/deviceFeatures.js
+++ b/lib/deviceFeatures.js
@@ -609,7 +609,6 @@ class deviceFeatures {
"roborock.vacuum.a62", // S7 Pro Ultra
"roborock.vacuum.a51", // S8
"roborock.vacuum.a15", // S7
- "roborock.vacuum.a72", // Q5 Pro
"roborock.vacuum.a27", // S7 MaxV (Ultra)
"roborock.vacuum.a19", // S4 Max
"roborock.vacuum.a40", // Q7
@@ -766,6 +765,10 @@ class deviceFeatures {
"set_back_type",
"set_charge_status",
"set_clean_percent",
+ "set_cleaned_area",
+ "set_switch_status",
+ "set_common_status",
+ "set_in_warmup",
],
// Q8 Max
"roborock.vacuum.a73": [
@@ -796,6 +799,7 @@ class deviceFeatures {
"set_clean_percent",
"set_rdt",
"set_switch_status",
+ "set_cleaned_area",
],
// S4
"roborock.vacuum.s4": ["setCleaningRecordsInt", "setConsumablesString"],
@@ -855,6 +859,7 @@ class deviceFeatures {
"set_in_warmup",
"set_map_flag",
"set_task_id",
+ "set_dss",
],
// Roborock Qrevo S
"roborock.vacuum.a104": [
@@ -905,7 +910,7 @@ class deviceFeatures {
}
}
} else {
- this.adapter.catchError(`This robot ${robotModel} is not fully supported just yet. Contact the dev to get this robot fully supported!`);
+ this.adapter.catchError(`This robot is not fully supported just yet. Contact the dev to get this robot fully supported!`, null, null, robotModel);
}
this.adapter.createBaseRobotObjects(this.duid);
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000000..0937ed196b
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,9 @@
+module.exports = {
+ localConnector: require("./localConnector").localConnector,
+ roborock_mqtt_connector: require("./roborock_mqtt_connector").roborock_mqtt_connector,
+ message: require("./message").message,
+ vacuum: require("./vacuum").vacuum,
+ roborockPackageHelper: require("./roborockPackageHelper").roborockPackageHelper,
+ deviceFeatures: require("./deviceFeatures").deviceFeatures,
+ messageQueueHandler: require("./messageQueueHandler").messageQueueHandler,
+};
\ No newline at end of file
diff --git a/lib/mapCreator.js b/lib/mapCreator.js
index bcffee404c..aafb05ee7e 100644
--- a/lib/mapCreator.js
+++ b/lib/mapCreator.js
@@ -102,11 +102,12 @@ class MapCreator {
const sy = y1 < y2 ? 1 : -1;
let err = dx - dy;
- for(;;) {
+ for (;;) {
// Setze Pixel im ImageData
- if (x1 >= 0 && x1 < imageData.width && y1 >= 0 && y1 < imageData.height) { // handle out of bounds. lineto would already do this but we need to set pixels directly
+ if (x1 >= 0 && x1 < imageData.width && y1 >= 0 && y1 < imageData.height) {
+ // handle out of bounds. lineto would already do this but we need to set pixels directly
const index = (x1 + y1 * imageData.width) * 4;
- pixels[index] = 128; // r
+ pixels[index] = 128; // r
pixels[index + 1] = 128; // g
pixels[index + 2] = 128; // b
pixels[index + 3] = 128; // a
@@ -157,8 +158,7 @@ class MapCreator {
} else if (options.ROBOT === "originalRobot") {
img = await loadImage(originalRobot);
}
- }
- else {
+ } else {
img = await loadImage(originalRobot);
}
img_charger = await loadImage(charger);
@@ -210,7 +210,7 @@ class MapCreator {
if (mapdata.IMAGE.pixels.segments && !mapdata.CURRENTLY_CLEANED_BLOCKS && colors.newmap) {
mapdata.IMAGE.pixels.segments.forEach((px) => {
- const segnum = (px >> 21);
+ const segnum = px >> 21;
const x = this.getX(mapdata.IMAGE.dimensions, px & 0xfffff);
const y = this.getY(mapdata.IMAGE.dimensions, px & 0xfffff);
@@ -226,25 +226,25 @@ class MapCreator {
}
});
- Object.keys(segmentsData).forEach(segnum => {
+ Object.keys(segmentsData).forEach((segnum) => {
const segment = segmentsData[segnum];
segmentsBounds[segnum] = {
minX: segment.minX,
maxX: segment.maxX,
minY: segment.minY,
- maxY: segment.maxY
+ maxY: segment.maxY,
};
});
- Object.keys(segmentsBounds).forEach(segnum => {
+ Object.keys(segmentsBounds).forEach((segnum) => {
const currentBounds = segmentsBounds[segnum];
- const adjacentSegments = Object.keys(segmentsBounds).filter(otherSegnum => {
+ const adjacentSegments = Object.keys(segmentsBounds).filter((otherSegnum) => {
const otherBounds = segmentsBounds[otherSegnum];
return segnum !== otherSegnum && this.areRoomsAdjacent(currentBounds, otherBounds);
});
- const usedColors = adjacentSegments.map(adjSegnum => assignedColors[adjSegnum]);
- const availableColor = availableColors.find(color => !usedColors.includes(color));
+ const usedColors = adjacentSegments.map((adjSegnum) => assignedColors[adjSegnum]);
+ const availableColor = availableColors.find((color) => !usedColors.includes(color));
if (availableColor) {
assignedColors[segnum] = availableColor;
@@ -253,11 +253,11 @@ class MapCreator {
}
});
- Object.keys(segmentsData).forEach(segnum => {
+ Object.keys(segmentsData).forEach((segnum) => {
const segment = segmentsData[segnum];
ctx.fillStyle = assignedColors[segnum] || availableColors[0];
ctx.beginPath();
- segment.points.forEach(point => {
+ segment.points.forEach((point) => {
ctx.rect(point.x, point.y, this.scaleFactor, this.scaleFactor);
});
ctx.fill();
@@ -367,31 +367,24 @@ class MapCreator {
}
// Male den Pfad
- if (mapdata.PATH) {
- if (mapdata.PATH.points && mapdata.PATH.points.length !== 0) {
- ctx.fillStyle = colors.path;
- let first = true;
- let cold1, cold2;
+ if (mapdata.PATH?.points?.length) {
+ ctx.fillStyle = colors.path;
+ ctx.lineWidth = this.scaleFactor / 2;
+ ctx.strokeStyle = colors.path;
- ctx.beginPath();
- mapdata.PATH.points.forEach((coord) => {
- if (first) {
- (cold1 = this.robotXtoPixelX(mapdata.IMAGE, coord[0] / 50)),
- (cold2 = this.robotYtoPixelY(mapdata.IMAGE, coord[1] / 50)),
- ctx.fillRect(cold1, cold2, (1 * this.scaleFactor) / 2, (1 * this.scaleFactor) / 2);
- first = false;
- } else {
- ctx.lineWidth = this.scaleFactor / 2;
- ctx.strokeStyle = colors.path;
+ ctx.beginPath();
+ let [cold1, cold2] = [this.robotXtoPixelX(mapdata.IMAGE, mapdata.PATH.points[0][0] / 50), this.robotYtoPixelY(mapdata.IMAGE, mapdata.PATH.points[0][1] / 50)];
+ ctx.fillRect(cold1, cold2, (1 * this.scaleFactor) / 2, (1 * this.scaleFactor) / 2);
+
+ mapdata.PATH.points.slice(1).forEach((coord) => {
+ ctx.moveTo(cold1, cold2);
+ cold1 = this.robotXtoPixelX(mapdata.IMAGE, coord[0] / 50);
+ cold2 = this.robotYtoPixelY(mapdata.IMAGE, coord[1] / 50);
+ ctx.lineTo(cold1, cold2);
+ });
- ctx.moveTo(cold1, cold2);
- (cold1 = this.robotXtoPixelX(mapdata.IMAGE, coord[0] / 50)), (cold2 = this.robotYtoPixelY(mapdata.IMAGE, coord[1] / 50)), ctx.lineTo(cold1, cold2);
- // ctx.stroke();
- }
- });
- ctx.stroke();
- ctx.closePath();
- }
+ ctx.stroke();
+ ctx.closePath();
}
// Male geplanten Pfad
@@ -692,7 +685,6 @@ class MapCreator {
} else {
return [createCanvas(1, 1).toDataURL(), createCanvas(1, 1).toDataURL()]; // return empty canvas
}
-
}
}
diff --git a/lib/vacuum.js b/lib/vacuum.js
index 5c70edf5d4..c4383f5257 100644
--- a/lib/vacuum.js
+++ b/lib/vacuum.js
@@ -64,7 +64,7 @@ class vacuum {
// const deviceStatus = await this.adapter.messageQueueHandler.sendRequest(duid, "get_status", []);
const deviceStatus = await this.adapter.messageQueueHandler.sendRequest(duid, "get_prop", ["get_status"]);
- const selectedMap = deviceStatus[0].map_status >> 2 ?? -1; // to get the currently selected map perform bitwise right shift
+ const selectedMap = this.adapter.getSelectedMap(deviceStatus);
// This is for testing and debugging maps. This can't be stored in a state.
zlib.gzip(map, (error, buffer) => {
@@ -101,7 +101,7 @@ class vacuum {
}
}
} catch (error) {
- this.adapter.catchError(error, "get_map_v1", duid), this.robotModel;
+ this.adapter.catchError(error, "get_map_v1", duid, this.robotModel);
}
}
}
@@ -286,7 +286,7 @@ class vacuum {
}
break;
case "map_status": {
- deviceStatus[0][attribute] = deviceStatus[0][attribute] >> 2 ?? -1; // to get the currently selected map perform bitwise right shift
+ deviceStatus[0][attribute] = this.adapter.getSelectedMap(deviceStatus);
if (isCleaning) {
this.adapter.startMapUpdater(duid);
@@ -331,7 +331,7 @@ class vacuum {
}
} else if (parameter == "get_room_mapping") {
const deviceStatus = await this.adapter.messageQueueHandler.sendRequest(duid, "get_status", []);
- const roomFloor = deviceStatus[0]["map_status"] >> 2 ?? -1; // to get the currently selected map perform bitwise right shift
+ const roomFloor = this.adapter.getSelectedMap(deviceStatus);
const mappedRooms = await this.adapter.messageQueueHandler.sendRequest(duid, "get_room_mapping", []);
// if no rooms have been named, processing them can't work
diff --git a/main.js b/main.js
index 28ed104e02..c587de001b 100644
--- a/main.js
+++ b/main.js
@@ -1,71 +1,63 @@
-"use strict";
-
const utils = require("@iobroker/adapter-core");
-
const axios = require("axios");
-const crypto = require("crypto");
-const websocket = require("ws");
+const { randomBytes, createHmac, createHash } = require("crypto");
+const WebSocket = require("ws");
const express = require("express");
-const childProcess = require("child_process");
-const go2rtcPath = require("go2rtc-static"); // Pfad zur Binärdatei
-
-const rrLocalConnector = require("./lib/localConnector").localConnector;
-const roborock_mqtt_connector = require("./lib/roborock_mqtt_connector").roborock_mqtt_connector;
-const rrMessage = require("./lib/message").message;
-const vacuum_class = require("./lib/vacuum").vacuum;
-const roborockPackageHelper = require("./lib/roborockPackageHelper").roborockPackageHelper;
-const deviceFeatures = require("./lib/deviceFeatures").deviceFeatures;
-const messageQueueHandler = require("./lib/messageQueueHandler").messageQueueHandler;
+const { spawn } = require("child_process");
+const go2rtcPath = require("go2rtc-static");
+
+const {
+ localConnector: rrLocalConnector,
+ roborock_mqtt_connector,
+ message: rrMessage,
+ vacuum: vacuum_class,
+ roborockPackageHelper,
+ deviceFeatures,
+ messageQueueHandler,
+} = require("./lib");
+
let socketServer, webserver;
const dockingStationStates = ["cleanFluidStatus", "waterBoxFilterStatus", "dustBagStatus", "dirtyWaterBoxStatus", "clearWaterBoxStatus", "isUpdownWaterReady"];
class Roborock extends utils.Adapter {
- /**
- * @param {Partial} [options={}]
- */
- constructor(options) {
- super({
- ...options,
- name: "roborock",
- useFormatDate: true,
- });
+ constructor(options = {}) {
+ super({ ...options, name: "roborock", useFormatDate: true });
this.on("ready", this.onReady.bind(this));
this.on("stateChange", this.onStateChange.bind(this));
// this.on("objectChange", this.onObjectChange.bind(this));
// this.on("message", this.onMessage.bind(this));
this.on("unload", this.onUnload.bind(this));
+
this.localKeys = null;
this.roomIDs = {};
this.vacuums = {};
this.socket = null;
-
this.idCounter = 0;
- this.nonce = crypto.randomBytes(16);
+ this.nonce = randomBytes(16);
this.messageQueue = new Map();
-
+ this.pendingRequests = new Map();
+ this.localDevices = {};
+ this.remoteDevices = new Set();
this.roborockPackageHelper = new roborockPackageHelper(this);
-
this.localConnector = new rrLocalConnector(this);
this.rr_mqtt_connector = new roborock_mqtt_connector(this);
this.message = new rrMessage(this);
-
this.messageQueueHandler = new messageQueueHandler(this);
-
- this.pendingRequests = new Map();
-
- this.localDevices = {};
- this.remoteDevices = new Set();
}
/**
* Is called when databases are connected and adapter received configuration.
*/
async onReady() {
+ if (!this.config.username || !this.config.password) {
+ this.log.error("Username or password missing!");
+ return;
+ }
+
this.log.info(`Starting adapter. This might take a few minutes depending on your setup. Please wait.`);
this.sentryInstance = this.getPluginInstance("sentry");
-
this.translations = require(`./admin/i18n/${this.language || "en"}/translations.json`); // fall back to en for test-and-release.yml
await this.setupBasicObjects();
@@ -74,29 +66,20 @@ class Roborock extends utils.Adapter {
let clientID = "";
try {
const storedClientID = await this.getStateAsync("clientID");
- if (storedClientID) {
- clientID = storedClientID.val?.toString() ?? "";
- } else {
- clientID = crypto.randomUUID();
- await this.setStateAsync("clientID", { val: clientID, ack: true });
- }
+ clientID = storedClientID?.val?.toString() || crypto.randomUUID();
+ await this.setState("clientID", { val: clientID, ack: true });
} catch (error) {
- this.log.error(`Error while retrieving or setting clientID: ${error.message}`);
- }
-
- if (!this.config.username || !this.config.password) {
- this.log.error("Username or password missing!");
- return;
+ this.log.error(`Fehler beim Abrufen oder Setzen der clientID: ${error.message}`);
}
// Initialize the login API (which is needed to get access to the real API).
this.loginApi = axios.create({
baseURL: "https://euiot.roborock.com",
headers: {
- header_clientid: crypto.createHash("md5").update(this.config.username).update(clientID).digest().toString("base64"),
+ header_clientid: createHash("md5").update(this.config.username).update(clientID).digest().toString("base64"),
},
});
- await this.setStateAsync("info.connection", { val: true, ack: true });
+ await this.setState("info.connection", { val: true, ack: true });
// api/v1/getUrlByEmail(email = ...)
const userdata = await this.getUserData(this.loginApi);
@@ -111,8 +94,8 @@ class Roborock extends utils.Adapter {
this.sentryInstance.getSentryObject().captureException("Failed to login. Most likely wrong token! Deleting HomeData and UserData. Try again! " + error);
}
}
- this.deleteStateAsync("HomeData");
- this.deleteStateAsync("UserData");
+ this.delObjectAsync("HomeData");
+ this.delObjectAsync("UserData");
}
const rriot = userdata.rriot;
@@ -123,12 +106,12 @@ class Roborock extends utils.Adapter {
this.api.interceptors.request.use((config) => {
try {
const timestamp = Math.floor(Date.now() / 1000);
- const nonce = crypto.randomBytes(6).toString("base64").substring(0, 6).replace("+", "X").replace("/", "Y");
+ const nonce = randomBytes(6).toString("base64").substring(0, 6).replace("+", "X").replace("/", "Y");
let url;
if (this.api) {
url = new URL(this.api.getUri(config));
const prestr = [rriot.u, rriot.s, nonce, timestamp, md5hex(url.pathname), /*queryparams*/ "", /*body*/ ""].join(":");
- const mac = crypto.createHmac("sha256", rriot.h).update(prestr).digest("base64");
+ const mac = createHmac("sha256", rriot.h).update(prestr).digest("base64");
config.headers["Authorization"] = `Hawk id="${rriot.u}", s="${rriot.s}", ts="${timestamp}", nonce="${nonce}", mac="${mac}"`;
}
@@ -156,7 +139,7 @@ class Roborock extends utils.Adapter {
const scene = await this.api.get(`user/scene/home/${homeId}`);
- await this.setStateAsync("HomeData", {
+ await this.setState("HomeData", {
val: JSON.stringify(homedataResult),
ack: true,
});
@@ -223,7 +206,7 @@ class Roborock extends utils.Adapter {
this.log.info(`Starting adapter finished. Lets go!!!!!!!`);
} else {
this.log.info(`Most likely failed to login. Deleting UserData to force new login!`);
- await this.deleteStateAsync(`UserData`);
+ await this.delObjectAsync(`UserData`);
}
}
} catch (error) {
@@ -253,7 +236,7 @@ class Roborock extends utils.Adapter {
throw new Error("Login returned empty userdata.");
}
- await this.setStateAsync("UserData", {
+ await this.setState("UserData", {
val: JSON.stringify(userdata),
ack: true,
});
@@ -261,8 +244,8 @@ class Roborock extends utils.Adapter {
return userdata;
} catch (error) {
this.log.error(`Error in getUserData: ${error.message}`);
- await this.deleteStateAsync("HomeData");
- await this.deleteStateAsync("UserData");
+ await this.delObjectAsync("HomeData");
+ await this.delObjectAsync("UserData");
throw error;
}
}
@@ -372,7 +355,7 @@ class Roborock extends utils.Adapter {
const enabledPath = `Devices.${duid}.programs.${programID}.enabled`;
await this.createStateObjectHelper(enabledPath, "enabled", "boolean", null, null, "value");
- this.setStateAsync(enabledPath, enabled, true);
+ this.setState(enabledPath, enabled, true);
const items = JSON.parse(param).action.items;
for (const item in items) {
@@ -386,7 +369,7 @@ class Roborock extends utils.Adapter {
if (typeOfValue == "object") {
value = value.toString();
}
- this.setStateAsync(objectPath, value, true);
+ this.setState(objectPath, value, true);
}
}
}
@@ -445,7 +428,7 @@ class Roborock extends utils.Adapter {
}
async startWebsocketServer() {
- socketServer = new websocket.Server({ port: 7906 });
+ socketServer = new WebSocket.Server({ port: 7906 });
let parameters, robot;
socketServer.on("connection", async (socket) => {
@@ -774,7 +757,7 @@ class Roborock extends utils.Adapter {
const homedata = home.data.result;
if (homedata) {
- await this.setStateAsync("HomeData", {
+ await this.setState("HomeData", {
val: JSON.stringify(homedata),
ack: true,
});
@@ -803,7 +786,7 @@ class Roborock extends utils.Adapter {
if (targetConsumable) {
const val = value >= 0 && value <= 100 ? parseInt(value) : 0;
- await this.setStateAsync(`Devices.${duid}.consumables.${attribute}`, { val: val, ack: true });
+ await this.setState(`Devices.${duid}.consumables.${attribute}`, { val: val, ack: true });
}
}
}
@@ -867,7 +850,7 @@ class Roborock extends utils.Adapter {
},
native: {},
});
- this.setStateAsync("Devices." + duid + ".updateStatus." + state, {
+ this.setState("Devices." + duid + ".updateStatus." + state, {
val: update.data.result[state],
ack: true,
});
@@ -1137,6 +1120,12 @@ class Roborock extends utils.Adapter {
return "Error in getRobotVersion. Version not found.";
}
+ getSelectedMap(deviceStatus) {
+ const mapStatus = deviceStatus[0].map_status;
+
+ return mapStatus >> 2; // to get the currently selected map perform bitwise right shift
+ }
+
getRequestId() {
if (this.idCounter >= 9999) {
this.idCounter = 0;
@@ -1210,7 +1199,7 @@ class Roborock extends utils.Adapter {
if (cameraCount > 0) {
try {
- const go2rtcProcess = childProcess.spawn(go2rtcPath.toString(), ["-config", JSON.stringify(go2rtcConfig)], { shell: false, detached: false, windowsHide: true });
+ const go2rtcProcess = spawn(go2rtcPath.toString(), ["-config", JSON.stringify(go2rtcConfig)], { shell: false, detached: false, windowsHide: true });
go2rtcProcess.on("error", (error) => {
this.log.error(`Error starting go2rtc: ${error.message}`);
@@ -1374,7 +1363,7 @@ class Roborock extends utils.Adapter {
if (typeof state.val == "boolean") {
this.commandTimeout = this.setTimeout(() => {
- this.setStateAsync(id, false, true);
+ this.setState(id, false, true);
}, 1000);
}
}
@@ -1421,7 +1410,7 @@ if (require.main !== module) {
////////////////////////////////////////////////////////////////////////////////////////////////////
function md5hex(str) {
- return crypto.createHash("md5").update(str).digest("hex");
+ return createHash("md5").update(str).digest("hex");
}
// function md5bin(str) {