homebridge-roborock-matter 3.19.1 → 3.19.2
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 +18 -0
- package/README.md +4 -3
- package/package.json +1 -1
- package/roborockLib/lib/messageQueueHandler.js +21 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.19.2
|
|
4
|
+
|
|
5
|
+
**Two releases in a row gated one caller each against the same defect. This one changes the shape of the error that kept producing them, so the next caller is calm without having to know.**
|
|
6
|
+
|
|
7
|
+
3.19.0 stopped the status loop polling a Q10 (`ss*`) for a value the dialect cannot return. 3.19.1 did the same for the live-room loop. Both were the same class one loop apart, and a sweep of every send site has since confirmed no third loop is left. What had not been fixed was the reason the class kept surfacing as a warning rather than a debug line.
|
|
8
|
+
|
|
9
|
+
The send choke point refuses an untranslatable Q10 read correctly and by design. But it built that refusal with `ROBOROCK_TRANSPORT_REFUSED`, and `catchError`'s calm early exit matches only `B01_METHOD_UNSUPPORTED`. So the refusal missed the calm branch, picked up the transient-warning path, and came out as `Failed to execute get_status on robot … Future transient warnings for this robot will be logged at most once every 360 minutes` — a line that reads as a failing robot when the plugin declined to send by design.
|
|
10
|
+
|
|
11
|
+
**A Q10 having no equivalent for a read is a capability fact, not a transport fault.** It is permanent, identical for every Q10, and the same kind of condition as the B01/Q7 unsupported-method case that has always logged at debug. It now carries the unsupported code, so any caller that reaches it is quiet by construction rather than by remembering to gate itself.
|
|
12
|
+
|
|
13
|
+
**The reclassification is deliberately narrow, and the guard is part of the change.** The same helper builds three genuine transport refusals — an offline robot, an unavailable cloud link, a missing local socket — and those must stay warnings, because for those the robot really is unreachable and the user does need to know. Only the dialect-capability refusal is reclassified; the three transport refusals are pinned by tests that fail if a future change widens it.
|
|
14
|
+
|
|
15
|
+
One existing test asserted the old code. It was not wrong about the code — the code was the defect — and its two message assertions are untouched.
|
|
16
|
+
|
|
17
|
+
**A flaky test in the release gate is fixed, and it was found by this release rather than reported.** Two tests start a real Node child process and wait for it to exit, on jest's default 5-second timeout — the only tests in the suite whose cost is a cold interpreter start. Under full-suite load that is a coin flip: two consecutive runs each failed one of the two, a different one each time, while the file passed 21 of 21 in nine seconds on its own. Both now carry an explicit ceiling generous enough that only a genuine hang reaches it, and the suite-wide default is raised from jest's 5 seconds to 20 seconds because the class is wider than those two — a socket test connecting to a closed port failed the next run for the same reason. Twenty seconds is roughly 220 times the suite's mean test, so a test that reaches it is stuck rather than unlucky. A gate that fails at random either blocks releases it should not or teaches whoever reads it to wave failures through, and the second is the worse outcome.
|
|
18
|
+
|
|
19
|
+
Troubleshooting documentation for the Apple Home "Updating…" / "No Response" tile is corrected on two points, both from field reports rather than reasoning. An iOS update is no longer presented as the confirmed cure: one reporter's tile has stayed up since 26.6.1, while another on the same version has watched it lapse and return for six months, so restarting the affected Apple device is now the remedy the page leads with. And a note explains why the symptom appears on a robot vacuum and no other accessory — Apple Home requires a vacuum to be its own Matter node, so it is the only accessory Homebridge publishes outside the bridge, and therefore the only one whose subscription can die alone.
|
|
20
|
+
|
|
3
21
|
## 3.19.1
|
|
4
22
|
|
|
5
23
|
**The live-room loop was polling a Q10 for a map it cannot answer, and counting each refusal as a failure. Same defect 3.19.0 fixed in the status loop, one loop over.**
|
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.
|
|
40
|
+
- 🛡️ **Verified by Homebridge.** Reviewed and endorsed by the Homebridge team. 1620 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
|
|
|
@@ -243,8 +243,9 @@ The complete path — robot → plugin → Homebridge → matter.js store — wa
|
|
|
243
243
|
- **Diagnostics first:** the plugin settings include per-device connection state, the last cloud/local transport used, a live **Test Local Connection** probe, and a **redacted diagnostics report** you can paste straight into a GitHub issue.
|
|
244
244
|
- **Robot shows "Updating…" or "No Response" in Apple Home:** those two wordings are one tile condition, and Apple chooses between them for reasons of its own — so do not triage them as separate problems. **Open the same tile on a second Apple device before you change anything.** If another controller in the same Home draws it correctly at that same moment, the plugin is publishing and the pairing is sound, and re-pairing cannot help: what is broken is the controller in your hand. Then, cheapest first:
|
|
245
245
|
1. **Restart the Apple device that shows it.** That cleared it outright for the reporter in [#11](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/11) — a restart makes the controller drop what it thinks it knows about the accessory and subscribe again.
|
|
246
|
-
2. **Update to iOS
|
|
246
|
+
2. **Update to the newest iOS you can — but do not expect it to be the cure.** The underlying event is the controller declaring its own subscription invalid (Matter status `0x7D INVALID_SUBSCRIPTION`) and then never subscribing again, so one controller keeps rendering the accessory while another has no live subscription to it — the same tile, dead on the phone and alive on the Mac at the same moment. The bridge does the right thing in that situation (it drops the dead subscription and re-announces over mDNS), but no Matter device can force a controller to subscribe, so there is nothing to fix on this side. **The evidence on versions is mixed and is reported here as it stands:** one reporter's tile has stayed up since 26.6.1 ([#7](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/7)), while another reporter on 26.6.1 still sees it, having watched it lapse and return for six months with remissions of up to a week. So treat step 1 as the reliable remedy and an OS update as worth doing but unproven.
|
|
247
247
|
3. **`Add Accessory → More Options → Cancel`** revives the tile for about a minute, because it forces the controller to re-resolve and briefly re-subscribe. A stopgap, not a fix.
|
|
248
|
+
- **"It only happens to my robot vacuum, never to my other Matter accessories" — that is expected, and it is not evidence the plugin is at fault.** Apple Home requires a robot vacuum to be its own Matter node, so Homebridge publishes each one on its own dedicated Matter server with its own port and its own pairing code, while every other accessory you own sits behind the single bridge node. In Homebridge 2.4.x the robot vacuum is the _only_ device type treated that way (`EXTERNAL_DEVICE_TYPES` in `dist/matter/MatterAPIImpl.js` contains `RoboticVacuumCleaner` and nothing else). A controller that loses one subscription therefore loses exactly one tile if that subscription was to a vacuum, whereas losing the bridge's subscription would blank out dozens of accessories at once and be unmistakable. **Your vacuum is not the accessory that breaks most often; it is the only one that can break alone.** So "only the vacuum is affected" is what the controller-side explanation above predicts, rather than something that contradicts it.
|
|
248
249
|
- **Robot shows "Updating…" on every Apple device at once:** _now_ remove the robot from Apple Home and pair it again — a pairing carrying state over from an earlier install is the usual cause (tracked upstream in homebridge/homebridge#3951). What finally worked for the reporter in [#5](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/5) was the full teardown in this exact order: unpair, **uninstall** the plugin, install the current version, pair fresh. A re-pair on top of the existing install did not work for him, so the order is part of the remedy.
|
|
249
250
|
- **Rooms missing for a Q7/B01 robot:** wait for the `B01 rooms for ...` log line, then re-pair once so the Service Area cluster is announced with room data.
|
|
250
251
|
- **Debug logging needs two switches, not one:** the plugin's own **Debug Mode** only decides whether it _calls_ the debug logger — Homebridge decides whether anything is _printed_, and it suppresses plugin debug output unless Homebridge itself runs with `-D`. Turn on **Homebridge Settings → Homebridge Debug Mode** as well, or the log will look exactly the same as before.
|
|
@@ -252,7 +253,7 @@ The complete path — robot → plugin → Homebridge → matter.js store — wa
|
|
|
252
253
|
|
|
253
254
|
## Contributing
|
|
254
255
|
|
|
255
|
-
Model reports, diagnostics exports, and pull requests are very welcome. The codebase ships with
|
|
256
|
+
Model reports, diagnostics exports, and pull requests are very welcome. The codebase ships with 1620 tests (protocol fixtures verified against the [python-roborock](https://github.com/Python-roborock/python-roborock) reference), strict TypeScript checking, and CI across Node 22/24 × Homebridge 1.11/2.x — `npm test` before you push and you're set.
|
|
256
257
|
|
|
257
258
|
## Support the project
|
|
258
259
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-roborock-matter",
|
|
3
|
-
"version": "3.19.
|
|
3
|
+
"version": "3.19.2",
|
|
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": {
|
|
@@ -15,17 +15,26 @@ const { describeDevice } = require("./describeDevice");
|
|
|
15
15
|
* matters: an unclassified refusal is logged as a plugin error with a stack,
|
|
16
16
|
* once per poll, for as long as the robot is away. The reason now travels as
|
|
17
17
|
* a code and the prose is free to change.
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
*
|
|
19
|
+
* `code` defaults to the transport code because most refusals ARE transport
|
|
20
|
+
* conditions — an offline robot, a dead cloud link, a missing local socket —
|
|
21
|
+
* and those must stay visible as warnings. A refusal that reflects what a
|
|
22
|
+
* robot family can never do is a capability fact instead, and passing
|
|
23
|
+
* `B01_METHOD_UNSUPPORTED` puts it on `catchError`'s calm branch by
|
|
24
|
+
* construction rather than leaving each caller to gate itself. Do not widen
|
|
25
|
+
* that to the transport cases: it would tell a user nothing is wrong while
|
|
26
|
+
* their robot is unreachable.
|
|
27
|
+
*
|
|
20
28
|
* @param {string} kind
|
|
21
29
|
* @param {string} message
|
|
30
|
+
* @param {string} [code]
|
|
22
31
|
* @returns {Error & { code: string, transientKind: string }}
|
|
23
32
|
*/
|
|
24
|
-
function refusal(kind, message) {
|
|
33
|
+
function refusal(kind, message, code = "ROBOROCK_TRANSPORT_REFUSED") {
|
|
25
34
|
const error = /** @type {Error & { code: string, transientKind: string }} */ (
|
|
26
35
|
new Error(message)
|
|
27
36
|
);
|
|
28
|
-
error.code =
|
|
37
|
+
error.code = code;
|
|
29
38
|
error.transientKind = kind;
|
|
30
39
|
return error;
|
|
31
40
|
}
|
|
@@ -327,9 +336,16 @@ class messageQueueHandler {
|
|
|
327
336
|
const q10 = b01Q10Adapter.translateOutgoing(method, params);
|
|
328
337
|
|
|
329
338
|
if (!q10) {
|
|
339
|
+
// A capability fact, not a transport fault: this dialect has no
|
|
340
|
+
// equivalent for the method and never will. Carrying the unsupported
|
|
341
|
+
// code keeps `catchError` calm BY CONSTRUCTION, so a caller that
|
|
342
|
+
// reaches here logs debug instead of "Failed to execute …" on warn.
|
|
343
|
+
// 3.19.0 and 3.19.1 were each one gate for one loop of exactly this
|
|
344
|
+
// class; the shape of the error is what kept producing them.
|
|
330
345
|
throw refusal(
|
|
331
346
|
"b01 q10 method unsupported",
|
|
332
|
-
`${describeDevice(this.adapter, duid)} speaks the B01 Q10 dialect, which has no equivalent for ${method}, so it was not sent. The Q10 dialect (${model || "ss*"}) writes numbered datapoints and sends no reply, so a request that exists to read a value cannot be answered over it; see issue #19
|
|
347
|
+
`${describeDevice(this.adapter, duid)} speaks the B01 Q10 dialect, which has no equivalent for ${method}, so it was not sent. The Q10 dialect (${model || "ss*"}) writes numbered datapoints and sends no reply, so a request that exists to read a value cannot be answered over it; see issue #19.`,
|
|
348
|
+
"B01_METHOD_UNSUPPORTED"
|
|
333
349
|
);
|
|
334
350
|
}
|
|
335
351
|
|