homebridge-roborock-matter 3.4.8 → 3.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.4.9
4
+
5
+ - **A live-room miss now says where the rooms actually are.** Two Q7s produced position cells around 22,000 while a Roborock map is a couple of thousand cells across at most — so those robots were never "between rooms", their computed position was nowhere near the map. One of them reported x exactly equal to y, which is arithmetic rather than a place a robot stood. The position on its own cannot separate a unit mismatch from a wrong origin, so the miss line now carries the range the room outlines occupy plus the map origin and resolution the transform used. This changes no behaviour; it turns the next log from a hypothesis into a measurement. The bounding box is computed only on the failure path, so a run that resolves every position pays nothing.
6
+
3
7
  ## 3.4.8
4
8
 
5
9
  **Selecting "Vacuum" and getting a vacuum-and-mop was not a display bug — one timed-out command cancelled the one that mattered.** skmzwanke reported in [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) that he selected Vacuum for a single room and the robot mopped it anyway, and his 3.4.5 log names the cause outright.
package/README.md CHANGED
@@ -37,7 +37,7 @@ This is the most feature-packed, most thoroughly engineered Roborock plugin for
37
37
  - 📍 **See where it's cleaning — live.** Apple Home shows _"Cleaning — Kitchen"_ with the room the robot is actually inside, updating as it moves from room to room. Works even for cleans started from the robot's button or the Roborock app. No other Homebridge plugin does this.
38
38
  - 🧭 **One robot, one tile — and as many robots as you own.** Sign in once and your whole fleet comes along: every vacuum on your account appears as its own clean, native accessory in Apple Home. No clutter of fake fans and helper switches, and rooms appear with the names you gave them in the Roborock app.
39
39
  - ⚡ **Fast and reliable.** Commands go directly to the robot over your own network whenever possible, with the Roborock cloud as automatic backup — and built-in diagnostics in the settings if you ever want to look under the hood.
40
- - 🛡️ **Verified by Homebridge.** Reviewed and endorsed by the Homebridge team. 419 automated tests, zero known vulnerabilities, no analytics, and a startup designed to never crash your Homebridge — even when your Wi-Fi or the Roborock cloud has a bad day.
40
+ - 🛡️ **Verified by Homebridge.** Reviewed and endorsed by the Homebridge team. 463 automated tests, zero known vulnerabilities, no analytics, and a startup designed to never crash your Homebridge — even when your Wi-Fi or the Roborock cloud has a bad day.
41
41
 
42
42
  ## Features
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-roborock-matter",
3
- "version": "3.4.8",
3
+ "version": "3.4.9",
4
4
  "description": "The most complete Roborock plugin for Apple Home. Supports the entire Roborock lineup — from the classic S-series to the new 2025 Q7 series that no other plugin can control. Sign in with your Roborock account and get native start/stop, room cleaning, suction levels, battery, and live 'cleaning in the kitchen' room tracking. Verified by Homebridge.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -504,7 +504,9 @@ function resolveLiveRoomId(liveState) {
504
504
  * @returns {{roomId: number | null,
505
505
  * reason: "resolved" | "no-map-header" | "no-pose" | "no-room-outlines" | "pose-outside-outlines",
506
506
  * outlineCount: number,
507
- * cell: {x: number, y: number} | null}}
507
+ * cell: {x: number, y: number} | null,
508
+ * outlineBounds?: {minX: number, minY: number, maxX: number, maxY: number} | null,
509
+ * head?: {minX: number, minY: number, resolution: number}}}
508
510
  */
509
511
  function describeLiveRoomResolution(liveState) {
510
512
  const head = liveState?.head;
@@ -541,9 +543,44 @@ function describeLiveRoomResolution(liveState) {
541
543
  reason: "pose-outside-outlines",
542
544
  outlineCount,
543
545
  cell,
546
+ outlineBounds: outlineBoundingBox(chains),
547
+ head: { minX: head.minX, minY: head.minY, resolution },
544
548
  };
545
549
  }
546
550
 
551
+ /**
552
+ * Bounding box of every room outline, in the same cell space the
553
+ * point-in-polygon test uses.
554
+ *
555
+ * Field logs showed two Q7s reporting position cells around 22,000 while a
556
+ * Roborock map is at most a couple of thousand cells across — so the position
557
+ * is not "between rooms", it is nowhere near the map. Whether that is a unit
558
+ * mismatch (pose in millimetres against a resolution in metres) or a wrong
559
+ * origin cannot be told from the position alone: it needs the range the
560
+ * outlines actually occupy. Printing both next to each other turns a guess
561
+ * into a measurement.
562
+ *
563
+ * @param {Array<{points: Array<{x: number, y: number}>}>} chains
564
+ * @returns {{minX: number, minY: number, maxX: number, maxY: number} | null}
565
+ */
566
+ function outlineBoundingBox(chains) {
567
+ let minX = Infinity;
568
+ let minY = Infinity;
569
+ let maxX = -Infinity;
570
+ let maxY = -Infinity;
571
+
572
+ for (const chain of chains) {
573
+ for (const point of chain.points || []) {
574
+ if (point.x < minX) minX = point.x;
575
+ if (point.y < minY) minY = point.y;
576
+ if (point.x > maxX) maxX = point.x;
577
+ if (point.y > maxY) maxY = point.y;
578
+ }
579
+ }
580
+
581
+ return Number.isFinite(minX) ? { minX, minY, maxX, maxY } : null;
582
+ }
583
+
547
584
  /**
548
585
  * Standard ray-casting point-in-polygon test over a room boundary chain.
549
586
  * @param {number} x @param {number} y
@@ -95,6 +95,34 @@ const B01_LIVE_ROOM_MISS_REASONS = {
95
95
  "the robot's position did not fall inside any known room outline (it may be between rooms, or the map may still be building)",
96
96
  };
97
97
 
98
+ /**
99
+ * The outline range and map origin, appended to a live-room miss.
100
+ *
101
+ * A position cell on its own cannot distinguish "the robot is between rooms"
102
+ * from "the position was computed in the wrong units" — and the field logs
103
+ * showed cells near 22,000 where a Roborock map is a couple of thousand cells
104
+ * at most. Printing the range the outlines occupy, plus the origin and
105
+ * resolution the transform used, makes the difference measurable from one log
106
+ * line instead of inferable from none.
107
+ *
108
+ * @param {{outlineBounds?: {minX: number, minY: number, maxX: number, maxY: number} | null,
109
+ * head?: {minX: number, minY: number, resolution: number}}} resolution
110
+ * @returns {string}
111
+ */
112
+ function describeOutlineBounds(resolution) {
113
+ const bounds = resolution?.outlineBounds;
114
+ if (!bounds) {
115
+ return "";
116
+ }
117
+
118
+ const head = resolution.head;
119
+ const origin = head
120
+ ? `, map origin ${head.minX},${head.minY} at ${head.resolution}/cell`
121
+ : "";
122
+
123
+ return `, outlines span ${Math.round(bounds.minX)}-${Math.round(bounds.maxX)} x ${Math.round(bounds.minY)}-${Math.round(bounds.maxY)}${origin}`;
124
+ }
125
+
98
126
  const B01_STATUS_TICK_MS = 15000;
99
127
  const B01_STATUS_FORCED_GAP_MS = 1500;
100
128
  const B01_STATUS_ACTIVE_GAP_MS = 12000;
@@ -4291,7 +4319,7 @@ class Roborock {
4291
4319
  // because they call for different fixes.
4292
4320
  liveState.unresolvedPoseCount =
4293
4321
  (liveState.unresolvedPoseCount || 0) + 1;
4294
- const message = `Live room for ${this.describeDevice(duid)}: ${B01_LIVE_ROOM_MISS_REASONS[resolution2.reason]} (attempt ${liveState.unresolvedPoseCount} this run, ${resolution2.outlineCount} room outline(s) in the map${resolution2.cell ? `, position cell ${Math.round(resolution2.cell.x)},${Math.round(resolution2.cell.y)}` : ""}).`;
4322
+ const message = `Live room for ${this.describeDevice(duid)}: ${B01_LIVE_ROOM_MISS_REASONS[resolution2.reason]} (attempt ${liveState.unresolvedPoseCount} this run, ${resolution2.outlineCount} room outline(s) in the map${resolution2.cell ? `, position cell ${Math.round(resolution2.cell.x)},${Math.round(resolution2.cell.y)}` : ""}${describeOutlineBounds(resolution2)}).`;
4295
4323
  if (liveState.unresolvedPoseCount % 5 === 0) {
4296
4324
  this.log.info(message);
4297
4325
  } else {