Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #676

Merged
merged 15 commits into from
Oct 14, 2024
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion admin/jsonConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"type": "number",
"label": "Update interval",
"newLine": true,
"min": 1,
"min": 60,
"max": 240,
"style": {
"width": "198px"
Expand Down
20 changes: 19 additions & 1 deletion io-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "</i><img style='width:24px;margin-bottom:-6px;' src='/adapter/admin/roborock.png'><i>"
},
"localLink": "%web_protocol%://%ip%:%webserverPort%/map.html",
"dependencies": [
{
"js-controller": ">=5.0.19"
Expand Down
19 changes: 7 additions & 12 deletions lib/RRMapParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -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));

Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 7 additions & 2 deletions lib/deviceFeatures.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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": [
Expand Down Expand Up @@ -796,6 +799,7 @@ class deviceFeatures {
"set_clean_percent",
"set_rdt",
"set_switch_status",
"set_cleaned_area",
],
// S4
"roborock.vacuum.s4": ["setCleaningRecordsInt", "setConsumablesString"],
Expand Down Expand Up @@ -855,6 +859,7 @@ class deviceFeatures {
"set_in_warmup",
"set_map_flag",
"set_task_id",
"set_dss",
],
// Roborock Qrevo S
"roborock.vacuum.a104": [
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -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,
};
68 changes: 30 additions & 38 deletions lib/mapCreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -692,7 +685,6 @@ class MapCreator {
} else {
return [createCanvas(1, 1).toDataURL(), createCanvas(1, 1).toDataURL()]; // return empty canvas
}

}
}

Expand Down
8 changes: 4 additions & 4 deletions lib/vacuum.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
Loading