Skip to content

Commit

Permalink
fix(vendor.roborock): Properly parse and handle lab_status and map_st…
Browse files Browse the repository at this point in the history
…atus

this fixes #1424
  • Loading branch information
Hypfer committed Feb 21, 2022
1 parent cc5e37b commit e158ac4
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 31 deletions.
31 changes: 29 additions & 2 deletions backend/lib/robots/roborock/RoborockValetudoRobot.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,20 @@ class RoborockValetudoRobot extends MiioValetudoRobot {
}

if (data["lab_status"] !== undefined && this.hasCapability(capabilities.RoborockPersistentMapControlCapability.TYPE)) {
this.capabilities[capabilities.RoborockPersistentMapControlCapability.TYPE].persistentMapState = data["lab_status"] === 1;
/*
lab_status is a byte that consists of
XXXXXXMP
X is currently (2022-02-21) unused
M is the multi-map flag
P is the persistent-map flag
*/

this.labStatus = {
persistentMapEnabled: !!(data["lab_status"] & 0b00000001),
multiMapEnabled: !!(data["lab_status"] & 0b00000010)
};
}

if (data["water_box_status"] !== undefined) {
Expand Down Expand Up @@ -311,7 +324,21 @@ class RoborockValetudoRobot extends MiioValetudoRobot {
}

if (data["map_status"] !== undefined) {
this.mapStatus = data["map_status"];
/*
map_status is a byte that consists of
IIIIIISM
I being all part of the current mapId 0-63
S being a "segment present" flag
M being a "map present" flag
*/

this.mapStatus = {
mapPresent: !!(data["map_status"] & 0b00000001),
segmentsPresent: !!(data["map_status"] & 0b00000010),
mapSlotId: data["map_status"] >> 2
};
}

this.emitStateAttributesUpdated();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,33 @@ class RoborockMultiMapMapResetCapability extends MapResetCapability {
* @returns {Promise<void>}
*/
async reset() {
if (this.robot.mapStatus === MAP_STATUS.NO_MAP) {
if (!this.robot.mapStatus) {
throw new Error("Unknown map status: " + this.robot.mapStatus + ". Unable to reset map.");
}

if (this.robot.mapStatus.mapPresent === false) {
throw new Error("Map doesn't exist. Nothing to reset.");
}
if (this.robot.mapStatus === MAP_STATUS.NEW_MAP) {
// saving the map takes a bit of time, usually up to 5 secs


/*
63 means new map and isn't a proper mapId
We're trying to manually trigger the segmentation to store the map into a real map slot
*/
if (this.robot.mapStatus.mapSlotId === 63) {
// segmenting the map takes a bit of time, usually up to 5 secs
let res = await this.robot.sendCommand("manual_segment_map", [{"map_flag":-1}], {timeout: 10000});

if (!(Array.isArray(res) && res[0] === 1)) {
throw new Error("Map is incomplete and not saved. Attempt to save map failed.");
throw new Error("Map is incomplete and not saved. Attempt to segment map failed.");
}

await this.robot.pollState();
}

const multiMapId = MAP_STATUS_TO_MUTIMAP_ID[this.robot.mapStatus];
if (multiMapId === undefined) {
throw new Error("Unknown map status: " + this.robot.mapStatus + ". Unable to reset map.");
}
let res = await this.robot.sendCommand("del_map", [multiMapId], {});


let res = await this.robot.sendCommand("del_map", [this.robot.mapStatus.mapSlotId], {});

if (!(Array.isArray(res) && res[0] === "ok")) {
throw new Error("Failed to reset map: " + res);
Expand All @@ -35,20 +44,5 @@ class RoborockMultiMapMapResetCapability extends MapResetCapability {
}
}

const MAP_STATUS = Object.freeze({
NO_MAP: 252,
NEW_MAP: 253, // a full clean-up hasn't been completed yet, map is incomplete and not saved into a slot
MAP_0: 3,
MAP_1: 7,
MAP_2: 11,
MAP_3: 15,
});

const MAP_STATUS_TO_MUTIMAP_ID = Object.freeze({
[MAP_STATUS.MAP_0]: 0,
[MAP_STATUS.MAP_1]: 1,
[MAP_STATUS.MAP_2]: 2,
[MAP_STATUS.MAP_3]: 3,
});

module.exports = RoborockMultiMapMapResetCapability;
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
const RoborockPersistentMapControlCapability = require("./RoborockPersistentMapControlCapability");


class RoborockMultiMapPersistentMapControlCapability extends RoborockPersistentMapControlCapability {
/**
* @returns {Promise<void>}
*/
async enable() {
await this.robot.sendCommand("set_lab_status", [{lab_status: 1}], {});
await this.robot.pollState(); //The labStatus is part of the status response and gets stored in the robot instance

const payload = {
lab_status: 1
};

/*
If the robot was previously used with the official app, it might be in multi-map mode.
In those cases, this command needs a reserve_map or else it will fail.
We use the current multi-map slot for that
"Enabling" persistent maps with multi-maps enabled actually disables multi-maps
Whether that is a good design decision is TBD

This comment has been minimized.

Copy link
@Lanchon

Lanchon Feb 21, 2022

i wish i had taken a close look at all this before rooting yesterday. i think multimap nonpersistent mode is not allowed: labStatus is never 2.

i don't like the dustbuilder setup that much. i can't really experiment. i'd like standard fw images for every robot, with hooked startup where an overlay filesystem can be installed and further init could be run, and a safe mode button combo that will disable the startup hook (because my robot does not have a recovery system, it just bricks). switching from valetudo to OEM cloud would be as easy as running a script via ssh.

*/
if (this.robot.labStatus?.multiMapEnabled === true && this.robot.mapStatus?.mapSlotId !== undefined) {
payload["reserve_map"] = this.robot.mapStatus.mapSlotId;
}

await this.robot.sendCommand("set_lab_status", [payload], {});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,15 @@ class RoborockPersistentMapControlCapability extends PersistentMapControlCapabil
*/
constructor(options) {
super(options);

this.persistentMapState = undefined;
}

/**
* @returns {Promise<boolean>}
*/
async isEnabled() {
await this.robot.pollState(); //fetching robot state populates the capability's internal state. somewhat spaghetti :(
await this.robot.pollState(); //The labStatus is part of the status response and gets stored in the robot instance

return this.persistentMapState;
return this.robot.labStatus?.persistentMapEnabled ?? undefined;
}

/**
Expand Down

0 comments on commit e158ac4

Please sign in to comment.