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

Viomi minor bugfixes #820

Merged
merged 7 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions assets/kaitai_structs/viomi_map_file.ksy
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ types:
- id: position
type: coordinate

- id: orientation
- id: angle
type: f4

virtual_wall:
Expand Down Expand Up @@ -229,8 +229,9 @@ types:
- id: target
type: coordinate

- id: unk2
type: u4
# Very wild guess
- id: angle
type: f4

realtime_pose:
seq:
Expand All @@ -240,8 +241,9 @@ types:
- id: pose
type: coordinate

- id: unk2
type: u4
# Very wild guess
- id: angle
type: f4

map:
seq:
Expand Down Expand Up @@ -350,6 +352,7 @@ types:
- id: unk1
size: 6

# TODO: reverse-engineer properly
- id: rooms2
type: rooms2
#if: rooms_len != 0
Expand All @@ -362,31 +365,37 @@ types:
repeat: expr
repeat-expr: unk_pose_stuff_len

# Hack - I wasn't able to understand this structure, it has a variable length that seems grow together with the
# number of segments, but not linearly, exponentially or anything else.
# This simply tries to eat up as many bytes until the start of the known tag value is found. Note that the tag
# varies from vacuum to vacuum
- id: unk2
type: u1
repeat: until
repeat-until: _ == rooms2.first_b_of_tag
if: map_id != 0

- id: some_header_ignore
size: 3
if: map_id != 0

- id: unk_pose_stuff
type: u8
repeat: expr
repeat-expr: 6
if: map_id != 0

- id: unk3
size: 3
if: map_id != 0

- id: pose_len
type: u4
if: map_id != 0

- id: pose
type: pose
repeat: expr
repeat-expr: pose_len
if: map_id != 0

instances:
map_id:
pos: 0x4
type: u4
66 changes: 53 additions & 13 deletions lib/robots/viomi/ViomiMapParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ class ViomiMapParser {
}
if (featureFlags & 0b000000010000000) {
let realtimePose = this.take(21, "realtime");
this.realtimePose = {position: this.readFloatPosition(realtimePose, 9)};
this.realtimePose = {
position: this.readFloatPosition(realtimePose, 9),
orientation: realtimePose.readFloatLE(13)
};
}

if (featureFlags & 0b000100000000000) {
Expand All @@ -198,11 +201,13 @@ class ViomiMapParser {
this.take(8, "unknown8");
this.parseRooms();

this.points = [];
try {
this.parsePose();
} catch (e) {
Logger.warn("Unable to parse Pose", e); //TODO
if (this.mapId !== 0) {
this.points = [];
try {
this.parsePose();
} catch (e) {
Logger.warn("Unable to parse Pose", e); //TODO
}
}
}
this.take(this.buf.length - this.offset, "trailing");
Expand All @@ -217,13 +222,30 @@ class ViomiMapParser {
path: {points: this.history},
goto_target: this.navigateTarget && this.navigateTarget.position,
robot: this.realtimePose && this.realtimePose.position,
robot_angle: this.realtimePose?.orientation,
charger: this.chargeStation && this.chargeStation.position,
charger_angle: this.chargeStation?.orientation,
virtual_wall: this.virtual_wall,
no_go_area: this.no_go_area,
clean_area: this.clean_area
});
}

/**
* Convert viomi angles to valetudo angles
*
* @private
* @param {number} angle
* @return {number}
*/
viomiToValetudoAngle(angle) {
let result = (-180 - (angle * 180 / Math.PI)) % 360;
while (result < 0) {
result += 360;
}
return result;
}

/**
* This is a temporary conversion function which should at some point be replaced with a complete rewrite
* of the viomi parser.
Expand All @@ -240,13 +262,19 @@ class ViomiMapParser {
* @param {object} [mapContents.clean_area]
* @param {object} [mapContents.goto_target]
* @param {object} [mapContents.robot]
* @param {number} [mapContents.robot_angle]
* @param {object} [mapContents.charger]
* @param {number} [mapContents.charger_angle]
*/
convertToValetudoMap(mapContents) {
const layers = [];
const entities = [];

let angle = 0;
// The charger angle is usually always provided.
// The robot angle may be 0, usually when the robot is docked.
let chargerAngle = mapContents.charger_angle !== undefined ? this.viomiToValetudoAngle(mapContents.charger_angle) : 0;
let robotAngle = mapContents.robot_angle !== undefined || mapContents.robot_angle !== 0 ? this.viomiToValetudoAngle(mapContents.robot_angle) : chargerAngle;
Logger.trace("Raw robot angle", mapContents.robot_angle, mapContents.robot_angle * 180 / Math.PI, "calculated", robotAngle);

if (mapContents.image) {
layers.push(new Map.MapLayer({
Expand Down Expand Up @@ -285,16 +313,15 @@ class ViomiMapParser {
type: Map.PathMapEntity.TYPE.PATH
}));

//TODO: This might actually be provided by the robot
//If yes, use that and only use this as a fallback
// Calculate robot angle from path if possible - the robot-reported angle is less accurate
if (mapContents.path.points.length >= 4) {
angle = (Math.round(Math.atan2(
robotAngle = (Math.round(Math.atan2(
mapContents.path.points[mapContents.path.points.length - 1] -
mapContents.path.points[mapContents.path.points.length - 3],

mapContents.path.points[mapContents.path.points.length - 2] -
mapContents.path.points[mapContents.path.points.length - 4]
) * 180 / Math.PI) + 90) % 360; //TODO: No idea why
) * 180 / Math.PI) + 270) % 360; //TODO: No idea why
}
}

Expand All @@ -309,7 +336,7 @@ class ViomiMapParser {
entities.push(new Map.PointMapEntity({
points: mapContents.robot,
metaData: {
angle: angle
angle: robotAngle
},
type: Map.PointMapEntity.TYPE.ROBOT_POSITION
}));
Expand All @@ -318,6 +345,9 @@ class ViomiMapParser {
if (mapContents.charger) {
entities.push(new Map.PointMapEntity({
points: mapContents.charger,
metaData: {
angle: chargerAngle
},
type: Map.PointMapEntity.TYPE.CHARGER_LOCATION
}));
}
Expand Down Expand Up @@ -458,15 +488,25 @@ class ViomiMapParser {
//Repeated for every room there is
this.take(unkLength * 2, "room data");

if (this.mapId === 0) {
// Sometimes the map is truncated here, when that happens the second word is zeroes
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could these be incremental map updates or do those still contain a full map image etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, it contains everything except the "extended" room data + room outlines.

For some reason this is sent as soon as the robot touches the dock and in no other circumstance.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe thats when room splitting stuff is recalculated or something like that 🤔

Copy link
Contributor Author

@depau depau Apr 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed I just noticed it wants the same map ID for editing segments. It probably returns map ID set to 0 so that segment editing won't work until it's done.

return;
}

// Hack - I wasn't able to understand this structure, it has a variable length that seems grow together with the
// number of segments, but not linearly, exponentially or anything else.
// This simply tries to eat up as many bytes until the start of the known tag value is found. Note that the tag
// varies from vacuum to vacuum
let takenBytes;
let count = 0;
do {
takenBytes = this.peek(4);
this.offset += 1;
} while (Buffer.compare(takenBytes, rooms2header.slice(0, 4)));
count += 1;
if (count >= 300) {
throw new Error("Unable to seek to end of room data");
}
} while (Buffer.compare(takenBytes, rooms2header.slice(0, 4)) && this.offset < this.buf.length);

this.take(3, "rest of tag");
}
Expand Down
28 changes: 9 additions & 19 deletions lib/robots/viomi/ViomiValetudoRobot.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,24 +183,6 @@ class ViomiValetudoRobot extends MiioValetudoRobot {
return super.sendCloud(msg, options);
}

onCloudConnected() {
super.onCloudConnected();

setTimeout(() => {
this.sendCommand("get_prop", ["timezone"], {timeout: 12000}).then((res) => {
if (res.length > 0) {
const timezone = res[0];
if (timezone !== 0) {
// Set timezone to UTC
this.sendCommand("set_timezone", [0], {timeout: 12000}).then(_ => {
Logger.info("Viomi timezone adjusted to UTC");
});
}
}
});
}, 5000);
}

onMessage(msg) {
switch (msg.method) {
case "_sync.gen_tmp_presigned_url":
Expand All @@ -227,7 +209,7 @@ class ViomiValetudoRobot extends MiioValetudoRobot {
}
}

if (msg.method.startsWith("prop.")) {
if (msg.method?.startsWith("prop.")) {
this.parseAndUpdateState({
[msg.method.substr(5)]: msg.params[0]
});
Expand Down Expand Up @@ -474,6 +456,13 @@ class ViomiValetudoRobot extends MiioValetudoRobot {
}));
}

// Adjust timezone if != UTC
if (data["timezone"] !== undefined && data["timezone"] !== 0) {
this.sendCommand("set_timezone", [0], {timeout: 12000}).then(_ => {
Logger.info("Viomi timezone adjusted to UTC");
});
}

this.emitStateAttributesUpdated();
}

Expand Down Expand Up @@ -582,6 +571,7 @@ const STATE_PROPERTIES = [
"has_map",
"is_mop",
"has_newmap",
"timezone"
];

const STATUS_MAP = Object.freeze({
Expand Down