homebridge-roborock-matter 3.9.0 → 3.9.1
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 +21 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/roborockLib/roborockAPI.js +37 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.9.1
|
|
4
|
+
|
|
5
|
+
**The first status request of every startup was refused, on every Q7, on every restart.**
|
|
6
|
+
|
|
7
|
+
The log said so plainly and had for weeks: `B01 status for 1. Sal recovered after 1 failed attempt(s).` It looked like the Roborock cloud being briefly unreachable, so it was left alone.
|
|
8
|
+
|
|
9
|
+
It was not the cloud. Measured over 30,224 log lines covering 49 restarts: 92 recoveries, one per Q7 per restart, without a single exception. Always exactly one failed attempt, never two. Always between 2 and 32 seconds after startup, never later. The robot on the older protocol had none of them.
|
|
10
|
+
|
|
11
|
+
A flaky connection does not look like that. It gives a varying number of attempts at varying times. One attempt, every time, only at startup, only on the cloud-only protocol is a race — and it was an ordering mistake in the startup sequence. The dedicated Q7 status loop was started at the end of device creation, and it polls immediately; the sequence did not wait for the MQTT session until after device creation had returned. A Q7 request is cloud-only by construction, so that first poll was rejected before anything reached the wire. The wait was already there, with a comment explaining this exact hazard for the two calls after it. The loop start had simply slipped in front of it.
|
|
12
|
+
|
|
13
|
+
The same event explains the other half, which had been observed fourteen times and never connected to it: for about 27 seconds after every restart, a Q7's tile in Apple Home showed `battery=100%, operationalState=0, runMode=0` — the snapshot taken at registration rather than the robot. That window is not a separate phenomenon. A refused attempt still stamps the request throttle, so the 15-second tick that followed fell inside the 25-second idle gap and was dropped, and the robot's real status did not arrive until the tick at 30 seconds. Measured median: 31 seconds.
|
|
14
|
+
|
|
15
|
+
Two changes, because the ordering fix alone leaves the hazard reachable — the wait resolves on a 10-second timeout whether or not the broker came up:
|
|
16
|
+
|
|
17
|
+
- The loop is started by the login sequence, immediately after it has waited for the MQTT session, instead of at the end of `createDevices()`. A source-level test now asserts that order and that device creation does not start the loop, so it cannot drift back.
|
|
18
|
+
- The loop's own boot poll is skipped when the cloud session is known to be down. A request that is never sent cannot stamp the throttle either, so even in that case the first real status arrives at the next tick rather than the one after it. A connector that cannot report its state is treated as usable, so nothing changes for callers without a live session.
|
|
19
|
+
|
|
20
|
+
Verified red against 3.9.0: 7 of 12 assertions failed, and the symptom test failed by producing the field log line verbatim.
|
|
21
|
+
|
|
22
|
+
934 tests, up from 922.
|
|
23
|
+
|
|
3
24
|
## 3.9.0
|
|
4
25
|
|
|
5
26
|
**Automations can now be triggered by the robot.**
|
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. 934 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
|
|
|
@@ -197,7 +197,7 @@ The complete path — robot → plugin → Homebridge → matter.js store — wa
|
|
|
197
197
|
|
|
198
198
|
## Contributing
|
|
199
199
|
|
|
200
|
-
Model reports, diagnostics exports, and pull requests are very welcome. The codebase ships with
|
|
200
|
+
Model reports, diagnostics exports, and pull requests are very welcome. The codebase ships with 934 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.
|
|
201
201
|
|
|
202
202
|
## Support the project
|
|
203
203
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-roborock-matter",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.1",
|
|
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": {
|
|
@@ -1767,6 +1767,13 @@ class Roborock {
|
|
|
1767
1767
|
// up releases the wait and the requests fail as they would have.
|
|
1768
1768
|
await this.rr_mqtt_connector.waitUntilConnected();
|
|
1769
1769
|
|
|
1770
|
+
// First on the wire once the session is up, because it is the one
|
|
1771
|
+
// request an Apple Home tile is waiting for: until a Q7's real
|
|
1772
|
+
// status lands, the tile shows the registration snapshot from the
|
|
1773
|
+
// Matter store. Started here rather than at the end of
|
|
1774
|
+
// createDevices() so the boot poll cannot precede the wait above.
|
|
1775
|
+
this.startB01StatusLoop();
|
|
1776
|
+
|
|
1770
1777
|
await this.getNetworkInfo();
|
|
1771
1778
|
await this.initializeDeviceUpdates();
|
|
1772
1779
|
|
|
@@ -2158,10 +2165,14 @@ class Roborock {
|
|
|
2158
2165
|
this.subscribeStates("Devices." + duid + ".deviceInfo.online");
|
|
2159
2166
|
}
|
|
2160
2167
|
|
|
2161
|
-
//
|
|
2162
|
-
// initializedVacuumDuids, which is only fully populated at this
|
|
2163
|
-
//
|
|
2164
|
-
|
|
2168
|
+
// The B01 status loop is deliberately NOT started here. Its device gate
|
|
2169
|
+
// reads initializedVacuumDuids, which is only fully populated at this
|
|
2170
|
+
// point, so this looked like the earliest safe place — but the loop polls
|
|
2171
|
+
// immediately, and a B01 request is cloud-only by construction, and the
|
|
2172
|
+
// caller does not wait for the MQTT session until after this method
|
|
2173
|
+
// returns. The first status of every single startup was therefore refused
|
|
2174
|
+
// with "cloud unavailable". It is started by the caller instead, straight
|
|
2175
|
+
// after that wait.
|
|
2165
2176
|
}
|
|
2166
2177
|
|
|
2167
2178
|
async initializeDeviceUpdates() {
|
|
@@ -4240,7 +4251,28 @@ class Roborock {
|
|
|
4240
4251
|
// First poll immediately: after a restart the Matter store holds the
|
|
4241
4252
|
// registration snapshot (HomeData fallback), and the sooner the real
|
|
4242
4253
|
// values land, the sooner controllers receive a genuine change report.
|
|
4243
|
-
|
|
4254
|
+
//
|
|
4255
|
+
// But only into a cloud session that is actually up. A B01 request is
|
|
4256
|
+
// cloud-only, so one issued before the MQTT session is established is
|
|
4257
|
+
// refused before it reaches the wire — and a refused attempt still stamps
|
|
4258
|
+
// the attempt throttle, which pushes the retry past the 15s tick and into
|
|
4259
|
+
// the one at 30s. Skipping the attempt costs nothing and buys both: no
|
|
4260
|
+
// spurious "recovered after 1 failed attempt(s)" line, and a first real
|
|
4261
|
+
// status at the next tick instead of the one after it.
|
|
4262
|
+
//
|
|
4263
|
+
// A connector that cannot answer the question is treated as usable, so a
|
|
4264
|
+
// caller without a live connector keeps the behaviour it always had.
|
|
4265
|
+
const cloudSessionUp =
|
|
4266
|
+
typeof this.rr_mqtt_connector?.isConnected === "function"
|
|
4267
|
+
? this.rr_mqtt_connector.isConnected()
|
|
4268
|
+
: true;
|
|
4269
|
+
if (cloudSessionUp) {
|
|
4270
|
+
pollAllB01({ force: true });
|
|
4271
|
+
} else {
|
|
4272
|
+
this.log.debug(
|
|
4273
|
+
"Holding the first B01/Q7 status poll until the Roborock cloud session is up; the loop tick will take it."
|
|
4274
|
+
);
|
|
4275
|
+
}
|
|
4244
4276
|
this.b01StatusLoopHandle = this.setInterval(pollAllB01, B01_STATUS_TICK_MS);
|
|
4245
4277
|
if (typeof this.b01StatusLoopHandle?.unref === "function") {
|
|
4246
4278
|
this.b01StatusLoopHandle.unref();
|