homebridge-roborock-matter 3.40.0 → 3.40.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 +348 -2537
- package/README.md +86 -205
- package/dist/hap_schedule_accessory.js +161 -96
- package/dist/hap_schedule_accessory.js.map +1 -1
- package/dist/matter_vacuum_accessory.js +61 -6
- package/dist/matter_vacuum_accessory.js.map +1 -1
- package/dist/schedule_write_reconciliation.js +18 -0
- package/dist/schedule_write_reconciliation.js.map +1 -0
- package/docs/mqtt-presence-logging.md +14 -0
- package/docs/mqtt-session-diagnostics.md +7 -1
- package/docs/schedule-write-reconciliation.md +56 -0
- package/package.json +1 -1
- package/roborockLib/lib/mqttSessionDiagnostics.js +13 -0
- package/roborockLib/lib/roborock_mqtt_connector.js +47 -15
- package/roborockLib/roborockAPI.js +9 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,2808 +1,619 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
**The same failures kept coming back because the plugin handled its connections differently from python-roborock, the library Home Assistant uses, in exactly the places where that library had already fixed them. A full review against it found those differences and the faults around them. This release closes them.**
|
|
6
|
-
|
|
7
|
-
### My own mistakes first
|
|
8
|
-
|
|
9
|
-
- The "Failed to execute …" line was printed at most once every 6 hours per robot and error. Every weekly check read the log through it: on my own server 36 lines in a week stood for an estimated 1,400 timeouts. One line per robot per hour now says how its requests fared on the LAN and the cloud, and the warning repeats after an hour, not six.
|
|
10
|
-
- 3.36.0's rule "3 cloud requests unanswered in a row → fresh MQTT session" could not fire on an account with a Q7: any Q7 reply reset the count of a classic robot that had stopped answering. My own S8 Pro Ultra's cloud path stayed dead for 2.3 days that way.
|
|
11
|
-
- 3.35.0's resume fix (#28) never worked in production. The robot's `in_cleaning` was not kept from its live status, so Resume always sent a whole-home start, and a paused room clean became a whole-home clean. The tests passed because they set the value themselves.
|
|
12
|
-
- I told #26 that Matter has no code for a full dirty-water tank. It has (74, since Matter 1.4.2), and Homebridge 2.4.0's matter.js accepts it.
|
|
13
|
-
- A start that failed and then succeeded on its retry never published the robots. Every robot showed "No Response" until a manual restart.
|
|
14
|
-
|
|
15
|
-
### The local connection
|
|
16
|
-
|
|
17
|
-
- Every local connection gets python-roborock's ping every 10 s and is reconnected at once after a drop. A request whose connection dies under it, or that times out on the LAN, is sent again over the cloud straight away instead of failing. That is the `Local connect state: false` in #24.
|
|
18
|
-
- A connection that stops answering is closed and reopened. It used to leave the robot on the cloud until Homebridge restarted, with the settings page still saying "Local connected".
|
|
19
|
-
- Map lists, map switches, maps and network info always go over the cloud, as python-roborock sends them.
|
|
20
|
-
- A timeout no longer cuts another reply in half, and a robot removed from the account no longer keeps a connection.
|
|
21
|
-
|
|
22
|
-
### The cloud connection
|
|
3
|
+
Entries before 3.39.0 are short summaries. The full notes for every release are on the [GitHub releases page](https://github.com/mathiashornbek/homebridge-roborock-matter/releases) and in the git history of this file.
|
|
23
4
|
|
|
24
|
-
|
|
25
|
-
- The 3-silence restart counts classic robots' cloud requests only, as python-roborock's HealthManager does.
|
|
26
|
-
- A request whose connection went away under it is never counted as a robot that stopped answering.
|
|
5
|
+
## 3.40.2
|
|
27
6
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- Resume continues a paused room clean (`resume_segment_clean`).
|
|
31
|
-
- The clean mode follows the robot's own water setting, live, not the 3-minute home-data snapshot.
|
|
32
|
-
- A standing tank warning is no longer withdrawn and raised again by a Homebridge restart, by 15 minutes without a live status, or by dock chores (emptying the bin, washing or drying the mop). One notification per cleaning run remains while the tank is not dealt with, on purpose.
|
|
33
|
-
- A full dirty-water tank is reported as DirtyWaterTankFull (#26). Missing is not: the dock does not tell full from not fitted.
|
|
34
|
-
- Rooms are marked cleaned only when a run actually finished; a Stop marks the rest skipped. Mop washing and other busy states without a Matter name no longer end the run on the tile.
|
|
35
|
-
- The room selection survives a restart. A briefly incomplete room list no longer shrinks it, and a room filed under no map no longer freezes the room list.
|
|
36
|
-
- A write the Matter store refused is repaired on the next publish instead of being believed.
|
|
37
|
-
|
|
38
|
-
### Startup, login and the account
|
|
39
|
-
|
|
40
|
-
- A login session Roborock no longer accepts is replaced (with a password) or reported once (without one), instead of retried forever as "temporary". A transient error never counts as a refusal.
|
|
41
|
-
- Logins send the agreement version and country Roborock asks for, avoiding error 3006, and are capped at python-roborock's rates. Only an attempt Roborock answered counts toward the cap.
|
|
42
|
-
- Robots shared with the account get a runtime and their owner's room names. The 3-minute refresh applies renamed robots and room names, and removes a robot only after two readings in a row lack it.
|
|
43
|
-
- A self-hosted [local_roborock_server](https://github.com/Python-roborock/local_roborock_server) can be used for the login (#43). Leave the setting empty and nothing changes.
|
|
44
|
-
- Both cloud REST clients give up after 30 s, so a hung request cannot block a Routine or schedule switch until restart.
|
|
45
|
-
- Shutdown during a start, a retry or a restart no longer leaves a client or timer behind.
|
|
7
|
+
**A robot away from its dock no longer tells Apple Home that its battery is charging.**
|
|
46
8
|
|
|
47
|
-
|
|
9
|
+
- **Battery charge state** ([#47](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/47), @pponce). An S7 MaxV asleep off its dock reported `charge_status` 1, and Apple Home showed "Charging: Yes". The battery now follows the same rule 3.40.1 gave the Docked sensor: a robot that is idle, sleeping, paused, cleaning or on its way home is not charging, even at 100 %. Charging, full and dock-maintenance states are unchanged.
|
|
48
10
|
|
|
49
|
-
|
|
50
|
-
- A timed-out timer read no longer hides the Routines, and is retried. A schedule deleted while Homebridge was stopped loses its switch. A request numbered 429 is not a rate limit.
|
|
11
|
+
2,630 tests. 19 of the 30 new tests fail on 3.40.1.
|
|
51
12
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- A Q7's live room comes from the map it pushes while cleaning, never from the saved map a request returns; that saved map's placeholder was the "placeholder instead of the robot's position" line. Its map list is fetched once per run, not every 10 s.
|
|
55
|
-
- Trapped, brush-jammed and similar Q7 and Q10 faults reach Apple Home while the robot is out of its dock. 2103 at the dock is no longer a fault (python-roborock: "not an error").
|
|
56
|
-
- A Q7 answer that does not say what the robot is doing, or a state the tables cannot translate, reads as unknown instead of idle. A Q7 started from Apple Home is not asked to read back its suction.
|
|
57
|
-
- A Q10 reads its own units for suction, clean mode and faults, shows its rooms, continues a paused room clean, and is asked for its status once a minute (#19). A B01 model that is neither Q7 nor Q10 is no longer sent Q7 frames.
|
|
13
|
+
## 3.40.1
|
|
58
14
|
|
|
59
|
-
|
|
15
|
+
**A robot that stops away from its dock no longer reads as docked, a start from an automation resumes a paused clean, and three contributions from @pponce are in.**
|
|
60
16
|
|
|
61
|
-
|
|
17
|
+
- **The Docked sensor (#28).** A robot that stopped at a pin & go spot read Docked, and the tile could say Charging there. Robots report `charge_status` 1 off the dock too (CooperCGN's own S8 does while paused mid-room), so Idle, Charger Disconnected and the states on the way home now place the robot from its state alone, as python-roborock does.
|
|
18
|
+
- **A start resumes a paused clean (#28).** Automations and Shortcuts have no Resume; they set the robot to Cleaning. On a paused robot that started the run over, or turned a paused room clean into a whole-home clean. A start now resumes the paused clean, unless other rooms have been selected since. The Start Cleaning switch does the same.
|
|
19
|
+
- **Schedule switches** ([#32](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/32), @pponce). A write Roborock confirms is no longer reported as failed because the other schedule source is rate-limited. Only unconfirmed writes fail, and no second write is sent during the 65-minute pause. Repeated taps share one write while Apple Home waits for the answer.
|
|
20
|
+
- **Presence lines** ([#36](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/36), @pponce). The "online" snapshot the broker replays at startup is no longer logged as the robot coming back. A robot's first live offline report is still a warning, and the LAN reassurance is only given to robots that have a LAN.
|
|
21
|
+
- **Diagnostics** ([#46](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/46), @pponce). The MQTT diagnostics keep the last eligible read timeout across sessions, for comparison.
|
|
22
|
+
- **A quieter log.** A failed robot-list refresh during a network outage is a warning that says the robots keep working, instead of "Failed to update updateHomeData" at error level. The note about a Q7's pushed datapoints is debug.
|
|
23
|
+
- **Shorter documents.** The README is rewritten for everyday users at a third of its length, and this changelog is cut down.
|
|
62
24
|
|
|
63
|
-
|
|
25
|
+
2,600 tests. 7 of the 12 new docked-sensor tests and 3 of the 5 new resume tests fail on 3.40.0.
|
|
64
26
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
## 3.39.0
|
|
27
|
+
## 3.40.0
|
|
68
28
|
|
|
69
|
-
**
|
|
29
|
+
**The same failures kept coming back because the plugin handled its connections differently from python-roborock, the library Home Assistant uses, in exactly the places where that library had already fixed them. A full review against it found those differences and the faults around them.**
|
|
70
30
|
|
|
71
31
|
### My own mistakes first
|
|
72
32
|
|
|
73
|
-
-
|
|
74
|
-
- 3.
|
|
75
|
-
- 3.
|
|
76
|
-
-
|
|
77
|
-
|
|
78
|
-
### Selecting every room (#24)
|
|
79
|
-
|
|
80
|
-
[@Marrand](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/24) could build a selection room by room, but picking all 8 snapped back to the 2 rooms of that morning's clean. Apple Home sends an empty list for "all rooms", which Matter defines as no area restriction, and the plugin accepted it. But progress is persisted, so the morning's 2 finished rooms were still published, and with nothing selected Apple Home showed those. A selection made between runs now starts with empty progress; a run in progress keeps its own. Start with every room selected cleans every room, as before.
|
|
81
|
-
|
|
82
|
-
### A refused schedule write
|
|
33
|
+
- The "Failed to execute …" line was printed at most once every 6 hours per robot and error, so the log hid most failures (36 lines in a week stood for about 1,400 timeouts on my own server). One line per robot per hour now sums up its LAN and cloud requests, and the warning repeats after an hour.
|
|
34
|
+
- 3.36.0's "3 unanswered cloud requests → fresh MQTT session" could not fire on an account with a Q7: any Q7 reply reset the count. My own S8 Pro Ultra's cloud path stayed dead for 2.3 days that way.
|
|
35
|
+
- 3.35.0's resume fix (#28) never worked in production: `in_cleaning` was not kept from the live status, so Resume always sent a whole-home start.
|
|
36
|
+
- Matter does have a code for a full dirty-water tank (74, since Matter 1.4.2); I told #26 otherwise.
|
|
37
|
+
- A start that failed and then succeeded on its retry never published the robots, so every robot showed "No Response" until a restart.
|
|
83
38
|
|
|
84
|
-
|
|
39
|
+
### Connections
|
|
85
40
|
|
|
86
|
-
|
|
41
|
+
- Every local connection is pinged every 10 s, as python-roborock does, and reconnected at once after a drop. A request whose connection dies, or that times out on the LAN, is sent again over the cloud instead of failing (#24). Map lists, maps and network info always go over the cloud.
|
|
42
|
+
- Each MQTT connection is a new client with its own id, opened after python-roborock's back-off (10 s, ×1.5, at most 10 minutes). Every cloud request carries the security block. The 3-silence restart counts classic robots' cloud requests only, and a request whose connection went away is never counted as a silent robot.
|
|
87
43
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### Cloud requests, sent the way python-roborock sends them
|
|
44
|
+
### Apple Home
|
|
91
45
|
|
|
92
|
-
|
|
46
|
+
- Resume continues a paused room clean (`resume_segment_clean`). The clean mode follows the robot's own water setting, live.
|
|
47
|
+
- A standing tank warning is no longer withdrawn and raised again by a restart, a quiet spell or a dock chore. A full dirty-water tank is reported as DirtyWaterTankFull (#26).
|
|
48
|
+
- Rooms are marked cleaned only when a run finished; a Stop marks the rest skipped. The room selection survives a restart, and an incomplete room list no longer shrinks or freezes it.
|
|
93
49
|
|
|
94
|
-
|
|
50
|
+
### Startup, login and schedules
|
|
95
51
|
|
|
96
|
-
|
|
52
|
+
- A login session Roborock no longer accepts is replaced or reported once, instead of retried forever. Logins send the agreement version Roborock asks for (avoiding error 3006) and are capped at python-roborock's rates.
|
|
53
|
+
- Shared robots get a runtime and their owner's room names; renamed robots and rooms are picked up on the 3-minute refresh, and a robot is removed only after two readings without it.
|
|
54
|
+
- A self-hosted [local_roborock_server](https://github.com/Python-roborock/local_roborock_server) can be used for the login (#43).
|
|
55
|
+
- The cloud REST clients give up after 30 s. A schedule switch answers Apple Home in time and confirms afterwards; a write the cloud does not confirm is switched back, with the reason in the log.
|
|
97
56
|
|
|
98
|
-
|
|
99
|
-
- A home-data refresh that landed after shutdown started the polling loops again.
|
|
100
|
-
- A B01 map request in flight at shutdown fired its 20-second timer into a stopped adapter.
|
|
101
|
-
- A fresh MQTT session started in the seconds before shutdown brought the closed client back afterwards.
|
|
102
|
-
- A request refused before sending could delete the pending entry of another request with the same id, which then timed out although its reply arrived.
|
|
103
|
-
- A poll still on the wire when Homebridge stopped was logged as "Failed to execute … Homebridge is shutting down" at error level. Seen on my own server at the 3.38.0 restart.
|
|
57
|
+
### Q7 and Q10
|
|
104
58
|
|
|
105
|
-
|
|
59
|
+
- A Q7's live room comes from the map it pushes while cleaning, not from the saved map. Trapped, brush-jammed and similar Q7 and Q10 faults reach Apple Home. A Q7 state the tables cannot translate reads as unknown, not idle.
|
|
60
|
+
- A Q10 reads its own units for suction, clean mode and faults, shows its rooms, resumes a paused room clean and is asked for its status once a minute (#19).
|
|
106
61
|
|
|
107
|
-
|
|
62
|
+
Contains @pponce's [#30](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/30) and [#42](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/42). 2,541 tests; four independent reviews ran before release.
|
|
108
63
|
|
|
109
|
-
|
|
64
|
+
## 3.39.0
|
|
110
65
|
|
|
111
|
-
|
|
66
|
+
**Cloud requests go out the way python-roborock sends them, and my own S8 Pro Ultra answered the map request again after weeks of silence.**
|
|
112
67
|
|
|
113
|
-
|
|
68
|
+
- Cloud requests use QoS 0 and a lowercase security nonce, matching python-roborock; a publish made while the link is down is refused at once instead of reaching the robot minutes later.
|
|
69
|
+
- Selecting every room in Apple Home no longer shows the rooms of the last run (#24). A room selection spanning two maps is refused so the old one is kept.
|
|
70
|
+
- A schedule write the cloud refused no longer looks like it succeeded: the switch stays where it was and Apple Home is told the write failed.
|
|
71
|
+
- HomeKit names such as "Q5 Max+" no longer make HAP warn (#40): `+` reads as "Plus", and other unsupported characters become spaces.
|
|
72
|
+
- Fixed a crash of the child bridge 30 s after a retried start during a network outage (beta only), and several leftovers at shutdown and restart. About 640 lines of dead code removed.
|
|
73
|
+
- 2,255 tests. CI now runs the suite in one process, as the release workflow does.
|
|
114
74
|
|
|
115
75
|
## 3.38.0
|
|
116
76
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
Nothing about how the plugin talks to a robot changes in this release. What changes is what it says when something fails, in the two places where the sentence it printed was the reason a diagnosis stalled.
|
|
120
|
-
|
|
121
|
-
### "Please report this — it is a bug here" was wrong (#28)
|
|
122
|
-
|
|
123
|
-
[@CooperCGN](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/28)'s log says, about his S8 Pro Ultra:
|
|
124
|
-
|
|
125
|
-
> NOTE: 30 map reply frame(s) for this robot were received and then discarded by the plugin (addressed-elsewhere: 30), so the robot IS answering and this side is throwing it away. Please report this — it is a bug here, not on your robot.
|
|
126
|
-
|
|
127
|
-
It is not a bug here. `addressed-elsewhere` means the protocol-301 frame carried a different controller's endpoint, and the endpoint identifies a **controller on the account**, not this plugin: the Roborock app open on a phone, a second Homebridge, a voice assistant skill each have their own, and all of them share the robot's MQTT topic. Their replies reach this handler and are discarded, which is correct — they were never ours. His 30 frames arrived while a scheduled clean was being watched in the app.
|
|
128
|
-
|
|
129
|
-
One case genuinely is ours: our own endpoint being wrong. It is told apart by a measurement the counter was missing — the frames that **were** addressed to us. If the key were wrong, nothing would ever be, so one accepted frame acquits it and convicts the other controller instead.
|
|
130
|
-
|
|
131
|
-
The give-up line now distinguishes three outcomes instead of asserting one:
|
|
132
|
-
|
|
133
|
-
- Frames addressed to this plugin and still discarded → unchanged; that is a bug here and still says so.
|
|
134
|
-
- Frames for another controller, with frames of ours accepted → named as another controller's, with the Roborock app given as the usual cause, and no bug report asked for.
|
|
135
|
-
- Frames for another controller with **nothing** ever accepted → that is the real bug's shape, and the line now points at the account key rather than at the robot.
|
|
136
|
-
|
|
137
|
-
`accepted` rides along in the diagnostic report's `discardedReplyFrames`, so the next report settles it without a round trip.
|
|
138
|
-
|
|
139
|
-
### A refused room selection now says what it refused (#24)
|
|
140
|
-
|
|
141
|
-
[@Marrand](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/24) can add rooms one at a time and the selection holds; selecting all 8 reverts it to the 2 rooms from an earlier clean, repeatably. The only log line covering it read:
|
|
142
|
-
|
|
143
|
-
> Matter service area selection request for S8: none.
|
|
144
|
-
|
|
145
|
-
Three different causes print that: the controller really did send an empty list, the controller sent 8 ids and none of them could be read, or the set spanned 2 Roborock maps and was refused — and a refusal **is** a revert, because a controller that gets `InvalidSet` keeps the selection it last had accepted. At one run a week, that is a week per guess.
|
|
146
|
-
|
|
147
|
-
- The request line now names what arrived as well as what was understood: no list, an empty list, or _n_ entries of which _m_ could not be read as an area id.
|
|
148
|
-
- The multi-map refusal names the maps and the areas, and says in words that the selection is refused rather than cleared, so the controller will keep showing the previous one.
|
|
149
|
-
|
|
150
|
-
### Tests
|
|
151
|
-
|
|
152
|
-
2,201 tests, 14 more than 3.37.0, including the endpoint guard driven through the real protocol-301 handler for both an accepted frame and another controller's.
|
|
77
|
+
- Map give-up log lines no longer accuse the plugin of a bug when the discarded replies were meant for another controller on the account, usually the Roborock app (#28).
|
|
78
|
+
- The `Matter service area selection request` log line now shows what actually arrived, and a selection refused for spanning two maps says so and explains why it reverts (#24).
|
|
153
79
|
|
|
154
80
|
## 3.37.0
|
|
155
81
|
|
|
156
|
-
**
|
|
157
|
-
|
|
158
|
-
All of this release is [@pponce](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/29)'s work, in [#29](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/29), the first of a four-part series. It is deliberately passive: nothing here reconnects, gates a request, changes transport selection, or touches the unanswered-method breaker. It only writes down what was observed.
|
|
159
|
-
|
|
160
|
-
### Why
|
|
161
|
-
|
|
162
|
-
A cloud timeout has always been ambiguous. `MQTT connection state: true` says the link believes it is up, which is exactly what it says when the link is up and delivering nothing — the failure #24, #27 and #28 all run into. Until now there was no way to tell those apart after the fact.
|
|
163
|
-
|
|
164
|
-
The snapshot now separates things that used to be one word:
|
|
165
|
-
|
|
166
|
-
- **`connected` and `subscriptionAcknowledged` are different claims.** A session can be connected with no usable subscription behind it.
|
|
167
|
-
- **Inbound is measured in 4 stages** — the raw MQTT callback, attribution to a known robot, successful decoding, and correlation to a pending request. A link that delivers frames nobody can attribute looks very different from one that delivers nothing, and both used to read as silence.
|
|
168
|
-
- **`rawSilenceDuringRequest`** records whether _any_ raw MQTT callback arrived between sending an eligible read and its timeout. This is a claim about the session, not about one robot, so unlike the two-robot correlation rule it works on a single-robot account — which is what #22 and #24 both are.
|
|
169
|
-
|
|
170
|
-
The report carries ages and counts only: no robot IDs, no topics, no credentials, no payloads.
|
|
171
|
-
|
|
172
|
-
### Found in review
|
|
173
|
-
|
|
174
|
-
Two things were wrong when this was first proposed, and both were the same shape as faults in 3.30.0 and 3.31.0 — a value that is published but never reaches the disk the reader reads from:
|
|
175
|
-
|
|
176
|
-
- **The snapshot never persisted.** `MqttSessionDiagnostics` was published through `setStateAsync` but was in neither `PERSISTED_STATE_IDS` nor `DEBOUNCED_PERSIST_IDS`, while the settings page reads it from `storagePath`. The new block would have rendered in tests and never in production. The suite was green because the UI test wrote the file itself. Both sets now contain it, and the shutdown flush was moved to _after_ transport teardown so the final disconnected snapshot reaches disk.
|
|
177
|
-
- **A subscription could never be acknowledged after a reconnect.** mqtt.js resubscribes on its own, before our handler runs, so our `subscribe` callback comes back with no error and an empty grant list. Read as a result, that meant every reconnected session reported "subscription not acknowledged" forever. Acknowledgement is now taken from real SUBACK packets on the client's `packetreceive` event, and an empty grant list is treated as the no-op it is. A SUBACK whose grants are refusals still counts as unacknowledged.
|
|
178
|
-
|
|
179
|
-
The second one was only visible because the test was rewritten to drive the real mqtt.js client against a loopback broker instead of a mock. The mock had been asserting the behaviour it was built to expect.
|
|
180
|
-
|
|
181
|
-
### Tests
|
|
182
|
-
|
|
183
|
-
2,187 tests, 36 more than 3.36.0, including a regression that drops the socket mid-session, withholds the second SUBACK, and verifies the session becomes acknowledged only when that packet actually arrives.
|
|
82
|
+
- **New MQTT session diagnostics**: when a cloud request times out, the diagnostics report now shows whether the cloud connection was actually delivering anything, contributed by @pponce (#29).
|
|
83
|
+
- Diagnostics only: nothing about connecting or sending requests changes, and the report holds only counts and ages, no robot IDs, topics, credentials or payloads.
|
|
184
84
|
|
|
185
85
|
## 3.36.0
|
|
186
86
|
|
|
187
|
-
**
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
[@Marrand](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/24) and [@CooperCGN](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/28) both have an S8 (`a51`) that accepts the LAN connection and then answers nothing, while python-roborock — the library Home Assistant uses — gets answers from the same robots. Comparing the 2 side by side, this plugin differed in 4 places, and every one of them can produce exactly that symptom:
|
|
192
|
-
|
|
193
|
-
- **No hello.** python-roborock opens every local connection with a hello, first in `1.0` and then in `L01`, and encrypts by whichever the robot answers. This plugin sent none for a `1.0` robot, and took the LAN protocol from the account's `pv`, which python-roborock says outright is a different thing. A robot whose firmware has moved to `L01` on the LAN ignores every `1.0` frame.
|
|
194
|
-
- **The wrong L01 handshake.** Where it did try `L01`, it sent a protocol-1 frame. Protocol 1 is the robot's answer, not the question, so nothing answered it.
|
|
195
|
-
- **Datapoint 4.** A local request went out on datapoint 4, the protocol number. python-roborock sends datapoint 101 on both transports.
|
|
196
|
-
- **Protocol 4 only.** A local reply was accepted only on protocol 4; one on 5 or 102 was dropped without a word.
|
|
197
|
-
|
|
198
|
-
All 4 now follow python-roborock. Against a fake robot built from python-roborock's own codec in 5 firmware variants, 3.35.0 got an answer from 1 and 3.36.0 from all 5.
|
|
199
|
-
|
|
200
|
-
- The hello result is logged once: `answered the local hello in 1.0`, or that the robot speaks `L01` on the LAN, or that it answered no hello. That line is what I need from #24 and #28.
|
|
201
|
-
- A robot that answers no hello keeps 3.35.0's behaviour exactly. Its first request after a connect waits up to 10 seconds for the hellos; after that, not again. A robot listed as `L01` always waits, because no local frame can be built without the hello's nonces.
|
|
202
|
-
|
|
203
|
-
I ran it as 3.36.0-beta.1 on my own server first. My S8 Pro Ultra answered the hello in 1.0 in 4-18 ms on 4 restarts, and its local requests kept being answered for the 10 minutes I watched. Nobody here has a robot that needs `L01`, so that part is tested against python-roborock's codec, not against hardware.
|
|
204
|
-
|
|
205
|
-
### A cloud session that says it is up and delivers nothing
|
|
206
|
-
|
|
207
|
-
python-roborock restarts its MQTT session after 3 cloud timeouts in a row, at most once every 30 minutes, because "the MQTT connection appears to be alive but no messages are being received". This plugin only reconnected when the link reported itself down. CooperCGN's log counts 574 cloud messages by 08:54 and 576 by 12:11, while his robot started, paused and docked in between, and [@pponce](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/27) measured a recreated session answering within 369 ms. Same rule now, with one addition from review: a timeout only counts when nothing at all arrived from that robot while the request waited, so a map request whose acknowledgement arrives and whose map does not is not mistaken for a dead session.
|
|
208
|
-
|
|
209
|
-
A cloud reply on protocol 4 or 5 that carries datapoint 102 is now read as a reply, as python-roborock reads it.
|
|
210
|
-
|
|
211
|
-
### Found in review
|
|
212
|
-
|
|
213
|
-
- A hello still in flight when the socket closed carried on into the dead socket, and a reconnect made in that window could inherit the old socket's hello and never get one of its own. Each hello now belongs to its socket, and a request that waited on a hello re-checks the socket before it is sent.
|
|
214
|
-
- The session restart now never runs while Homebridge is stopping.
|
|
215
|
-
- A socket that closed during its hello while the cloud was also down got the request written into it anyway, and counted it as a mute socket. It is now refused at once.
|
|
216
|
-
|
|
217
|
-
### Tests
|
|
218
|
-
|
|
219
|
-
2,153 tests, 22 more than 3.35.0. Measured against the 3.35.0 sources, file by file: 12 of 13 fail for the hello and the reply rules, 3 of 5 for the session restart, 4 of 4 for the socket each hello belongs to. The 2 that pass there guard against a restart 3.35.0 could not make.
|
|
87
|
+
- **Robots that accept the LAN connection but never answer should now respond**: local connections start with a protocol hello and match python-roborock's message format (#24, #28).
|
|
88
|
+
- The log now says once how the robot answered the hello, e.g. `answered the local hello in 1.0`; please include that line in #24 and #28.
|
|
89
|
+
- A cloud session that reports itself connected but delivers nothing is now restarted after 3 timeouts in a row, at most once every 30 minutes.
|
|
220
90
|
|
|
221
91
|
## 3.35.0
|
|
222
92
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
3.34.0 sent `resume_segment_clean` whenever `in_cleaning` was non-zero, on the assumption that 0 meant whole-home and anything else meant rooms. Roborock's numbering (python-roborock `RoborockInCleaning`) is 0 complete, 1 whole-home not complete, 2 zone not complete, 3 room not complete. A paused full clean reads 1, so the case [@CooperCGN](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/28) had confirmed working got the room verb. Each value now gets its own: 3 → `resume_segment_clean`, 2 → `resume_zoned_clean`, 0/1 → `app_start`. That is the same choice Home Assistant makes. B01 is unchanged.
|
|
228
|
-
|
|
229
|
-
### An empty tank no longer hides a running clean (#35)
|
|
230
|
-
|
|
231
|
-
[@pponce](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/35) decoded an outgoing Matter report: the plugin logged `operationalState=1 … fault=68`, and the wire carried `operationalState=3`. In matter.js 0.17.9, writing any `operationalError` other than NoError sets the state to Error, and clearing it does not give the old state back. "Running with a warning" does not exist on the version Homebridge ships. So Apple Home showed "Refill the water tank" while his robot mopped 2 rooms.
|
|
232
|
-
|
|
233
|
-
- A fault is now only published for a robot at rest (Stopped, Charging, Docked) or one really in Error. A robot that is cleaning, paused, driving home or busy at the dock gets NoError, and the publish line says `fault=68 held back while the robot works`.
|
|
234
|
-
- That check is made after a Matter command's optimistic state as well, and on the robot's own state as well as the one Apple Home is sent. Mid-run states this plugin has no name for (33 attaching the mop, 6301-6310 the mopping states and a few more) count as working, not at rest: DSimeone's a144 reports 33 in the middle of a room clean.
|
|
235
|
-
- When a fault clears, the robot's real state is written back in the same publish. Before, the tile stayed on Error until the robot changed state or the 10th heartbeat.
|
|
236
|
-
- While a fault stands, the state is never re-written. 3.31.0's every-10th-heartbeat resync was wiping and re-raising it: one "Refill the water tank" notification about every 10 minutes for as long as the tank was empty. The resync now re-asserts the fault itself, which is a no-op when it landed.
|
|
237
|
-
|
|
238
|
-
The comments that said the state is "deliberately NOT forced to Error" were wrong on 0.17.9 and are corrected. The tests now run against a replay of the real 0.17.9 reactors (`test-support/matter-0.17.9-store.js`). The old stand-in modelled the wipe but not the forcing, which is how 4 tests came to assert stores 0.17.9 can never hold.
|
|
239
|
-
|
|
240
|
-
### The dock's own tank word, and 2 new sensors (#22, #26)
|
|
241
|
-
|
|
242
|
-
[@DSimeone1989](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/22)'s Saros 10R let the water-empty automation fire in 1 run and not the other, and the cloud settings had nothing to do with it. `dock_error_status` holds 1 code at a time. In run 1 both tanks needed attention and the code was 39 (dirty tank full), so the empty clean tank never showed. The dock reports each tank separately in `dss`, which this plugin had never read.
|
|
243
|
-
|
|
244
|
-
- Water Tank Empty now reads the clean-water field of `dss` where the dock sends one. When it says the tank is fine, a lingering `water_shortage_status` no longer overrules it, which may be what [@n0rt0nthec4t](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/26) is seeing after a refill. A 38 still wins, and docks without `dss` work exactly as before.
|
|
245
|
-
- 2 new optional sensors: **Dirty Water Tank Full** and **Cleaning Fluid Empty**. The second is the detergent warning DSimeone asked for; only docks with automatic dosing report it.
|
|
246
|
-
- `dss` is ignored on plain chargers and auto-empty-only docks, which have no tanks for it to describe.
|
|
247
|
-
- The robot's own `error_code` 38 and 39 ("check the clean/dirty water tank") are known now, so nobody is asked to report them.
|
|
248
|
-
- Diagnostic reports keep `dss`, `rss` and `wash_status`. All 3 were cut at the key limit, including in #26.
|
|
249
|
-
|
|
250
|
-
### Q10 state was read with the Q7 table (#33)
|
|
251
|
-
|
|
252
|
-
[@yquirion](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/33)'s Q10 S5 flapped between Stopped and Docked. Home data was translated with the Q7 work-status table for every B01 robot, while a pushed datapoint was read as v1, and the 2 disagree: a charging Q10 (8) became Stopped. The Q10 numbers its states the v1 way plus a few of its own (python-roborock `YXDeviceState`), so it now gets its own table, on both paths. The Q7 L5 half of #33 ("Updating…") is not explained by anything here yet.
|
|
253
|
-
|
|
254
|
-
### The give-up register
|
|
255
|
-
|
|
256
|
-
- A robot that answers with an error has answered. A refusal did not reset the count, so 5 silences, 1 refusal and 1 silence gave the method up.
|
|
257
|
-
- One silent `get_map_v1` counted twice: once in the message layer, once in the live-room fetch. Live-room tracking was paused after 3 silent fetches instead of 6. CooperCGN's log shows both lines in the same second.
|
|
258
|
-
- Shutting Homebridge down no longer counts as an answer.
|
|
259
|
-
|
|
260
|
-
### A reply that comes too late is called late
|
|
261
|
-
|
|
262
|
-
A reply that arrives after its 10-second timeout was dropped without a word on the local socket, and a late map frame was counted as "discarded by the plugin — a bug here". The give-up line also told #24 and #28 that `get_server_timer` replies were "not arriving at all", on a counter that only ever looks at map frames. Timed-out requests are now remembered for 10 minutes. A reply that matches one is logged and counted, and the give-up line says "did arrive, but only after the plugin had stopped waiting" when that is what happened. The map-frame claim is made only about `get_map_v1`.
|
|
263
|
-
|
|
264
|
-
### Service Area writes matter.js refused
|
|
265
|
-
|
|
266
|
-
matter.js refuses a whole Service Area write when 1 progress entry names a room that no longer exists, and refuses to register the endpoint when 2 rooms or 2 maps share a name. Progress is persisted across restarts, so a room merged away in the Roborock app froze the cluster until the next run. Progress and current area are now filtered against the published rooms, and a second "Bedroom" is "Bedroom 2".
|
|
267
|
-
|
|
268
|
-
### Left as it is, on purpose
|
|
269
|
-
|
|
270
|
-
3.33.0 and 3.34.0 said the room poll "normally" reads the floor from the status the plugin already polled. That cache is never written under Homebridge, so every room poll has asked `get_status` after `get_room_mapping` all along. I made the cache real during this release, and review showed why it should stay empty: a status up to 1 minute old files a map switched in the Roborock app under the wrong map, in the room cache that is persisted. 1 extra request every few minutes is cheaper. The comment now says so.
|
|
271
|
-
|
|
272
|
-
### Tests
|
|
273
|
-
|
|
274
|
-
2,131 tests, 65 more than 3.34.0. How many of the new ones fail on the 3.34.0 sources, measured file by file: 5 of 5 for the #35 fault, 3 of 3 for the gate after review, 11 of 12 for the Q10 table, 9 of 13 for the dock tanks, 2 of 7 for resume, 2 of 2 for the register, 5 of 10 for late replies, 4 of 4 for Service Area.
|
|
93
|
+
- Fixed a 3.34.0 regression where resuming a paused full clean used the room-clean command; each paused clean type now resumes with its own command, as in Home Assistant.
|
|
94
|
+
- **An empty water tank no longer turns a running clean into an error** in Apple Home, and the refill notification no longer repeats every 10 minutes (#35, thanks to @pponce).
|
|
95
|
+
- New optional Dirty Water Tank Full and Cleaning Fluid Empty sensors; Water Tank Empty reads the dock's per-tank status (#22, #26); Q10 robots no longer flip between Stopped and Docked (#33).
|
|
275
96
|
|
|
276
97
|
## 3.34.0
|
|
277
98
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
### The premise was about someone else's network
|
|
281
|
-
|
|
282
|
-
3.33.0 stopped the classic room-mapping poll from opening with `get_status`, so that the method the caller claims — `get_room_mapping` — is the method the give-up register actually sees. It kept one fallback: if no status has landed yet, ask for one. The reasoning was that the status poll runs on its own 60-second interval, so that branch could only fire on the first cycle after a restart.
|
|
283
|
-
|
|
284
|
-
[@Marrand](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/24) ran 3.33.0 overnight on his `a51` and reported the opposite:
|
|
285
|
-
|
|
286
|
-
```
|
|
287
|
-
17:04 get_room_mapping → get_status timed out
|
|
288
|
-
23:04 get_room_mapping → get_status timed out
|
|
289
|
-
05:04 get_room_mapping → get_status timed out
|
|
290
|
-
|
|
291
|
-
521 similar warning(s) across get_room_mapping (120), get_status (360), …
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
Seven other governed methods reached their six-strike cooldown that same night. `get_room_mapping` did not. [@DSimeone1989](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/22)'s `a144` log says the same thing — `get_status (248)`, `get_room_mapping (83)`, side by side.
|
|
295
|
-
|
|
296
|
-
The cache the fix reads, `deviceStatus.map_status`, is written by a **successful** status poll. On both of those robots `get_status` is the method that never answers. So the cache is never written, the fallback is not the first cycle but **every** cycle forever, and the branch went on opening with the one request the register is forbidden to count — and still died there before `get_room_mapping` was ever sent.
|
|
297
|
-
|
|
298
|
-
### The claimed method now goes first, whatever else happens
|
|
299
|
-
|
|
300
|
-
The room request is sent before anything else, unconditionally. The floor is resolved afterwards, because a floor is a filing detail and the request is the part that has to be seen.
|
|
301
|
-
|
|
302
|
-
- On a robot whose rooms go unanswered, six polls now reach the cooldown, under the name the caller claimed.
|
|
303
|
-
- When the floor cannot be resolved because the status request died too, the rooms that **did** arrive are no longer thrown away — but they are not stamped with an invented floor either. `updateRoomMappingCache` writes that number onto every room as its map id, and a working Matter service-area cache must not be overwritten with a map that does not exist. They stay unfiled for that cycle and are filed correctly on the next one that has a floor.
|
|
304
|
-
- A status reply that carries no `map_status` is still an answer — the robot has no map selected — and still files the rooms under the long-standing `-1`. Only an _unanswered_ request means nothing was learnt.
|
|
305
|
-
|
|
306
|
-
The rule the tests pin is unchanged and now stated where it can be checked: a poll that claims a method must put that method on the wire first, and no fallback may decide whether the claimed request happens at all.
|
|
307
|
-
|
|
308
|
-
### Pausing a room clean and pressing play no longer starts a new one
|
|
309
|
-
|
|
310
|
-
[@CooperCGN](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/28) automates his house door: opening it pauses the robot, closing it resumes. Pause worked. Play started a whole new clean. Asked which kind of run had been interrupted, he tested both:
|
|
311
|
-
|
|
312
|
-
> _"you're absolutely right, it happens during a room clean. While a full clean is continued as expected a paused room clean is restarted as a new full clean."_
|
|
313
|
-
|
|
314
|
-
This is not a cosmetic complaint. He built the automation because he once left that door open, and the robot drove out of it and down a flight of stairs.
|
|
315
|
-
|
|
316
|
-
Matter's Resume has always dispatched `app_start`. On a paused full clean that continues it; on a paused **room** clean it is a fresh whole-home start. Roborock's own verb for the second case, `resume_segment_clean`, has been sitting in `deviceFeatures.js` since the library was imported and had never been sent from anywhere.
|
|
317
|
-
|
|
318
|
-
It is sent now — and only when the robot itself says a targeted clean is what is paused. `state` cannot answer that (a paused robot reports 10 and the clean type is gone from it), but `in_cleaning` survives the pause: 0 is whole-home, non-zero means selected areas. It is read from the robot rather than remembered, so a room clean started in the Roborock app resumes correctly too.
|
|
319
|
-
|
|
320
|
-
Deliberately narrow, because a resume verb sent into a full clean breaks the play button for everyone:
|
|
321
|
-
|
|
322
|
-
- `in_cleaning` absent or zero → `app_start`, exactly as before.
|
|
323
|
-
- B01/Q7 robots → `app_start`, exactly as before. Room cleaning there is `service.set_room_clean` with a `ctrl_value`, and the only values read out of that protocol are STOP, START and PAUSE. There may well be a continue value; nobody has measured one, and a guessed control code is not worth the tile.
|
|
324
|
-
- A continue that fails is **not** retried as `app_start`. That fallback would produce the exact unwanted whole-home run, at the moment the robot is least likely to be somewhere safe. A paused robot stays paused.
|
|
99
|
+
- The room poll now asks for rooms first, so robots that never answer it are finally paused after six tries instead of flooding the log, fixing what 3.33.0 missed (#22, #24).
|
|
100
|
+
- **Pressing play on a paused room clean** now continues it instead of starting a new full clean; a failed resume leaves the robot paused. B01/Q7 robots are unchanged (#28).
|
|
325
101
|
|
|
326
102
|
## 3.33.0
|
|
327
103
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
### Two users asked the same question, and one of them had the answer
|
|
331
|
-
|
|
332
|
-
[@DSimeone1989](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/22) and [@Marrand](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/24) run different robots — an `a144` and an `a51` — and both pasted a line with two method names in it that disagree. Marrand asked it outright: _"some messages say `Failed to execute get_room_mapping`, but the actual error underneath says `method get_status timed out`. I'm not sure whether that's expected, or whether the method name in that log line is misleading."_
|
|
333
|
-
|
|
334
|
-
```
|
|
335
|
-
Failed to execute get_room_mapping on robot Rocky (roborock.vacuum.a144):
|
|
336
|
-
Error: Cloud request with id 5563 with method get_status timed out after 10
|
|
337
|
-
seconds … 483 similar warning(s) across get_status (359), get_room_mapping
|
|
338
|
-
(119) … were suppressed.
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
The name is not misleading. It is the diagnosis, and it is the same one [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14) produced on a B01 robot in 3.11.0: the classic room-mapping poll opens by fetching `get_status`, purely to read `map_status` and derive a floor number. `get_room_mapping` is the caller's label. `get_status` is what went on the wire.
|
|
342
|
-
|
|
343
|
-
3.11.0 fixed that for B01 robots by returning early. On classic robots the request is answerable, so it was left alone as merely wasteful. It was not merely wasteful.
|
|
344
|
-
|
|
345
|
-
### Why that put the poll permanently out of the register's reach
|
|
346
|
-
|
|
347
|
-
3.32.0 moved the give-up register to the message layer, because that is the only place that knows whether a reply arrived. That layer sees **every** request, so a robot/method pair is counted only once a caller that can skip it has claimed it with `govern()`. `get_status` is never claimable — the tile lives on it.
|
|
348
|
-
|
|
349
|
-
`pollParameter` claims `get_room_mapping`. The wire saw `get_status`. The register discarded the timeout, exactly as designed, and the claimed pair recorded nothing — not a failure, not an answer. **Six strikes were unreachable.**
|
|
350
|
-
|
|
351
|
-
That is why Marrand's 3.32.0 report lists `get_multi_maps_list`, `get_consumable`, `get_server_timer`, `get_timer`, `get_carpet_mode`, `get_carpet_clean_mode` and `get_water_box_custom_mode` all reaching their cooldown — and not `get_room_mapping`, the method with 119 suppressed warnings in #22.
|
|
352
|
-
|
|
353
|
-
And there is a second half I had not seen. When `get_status` is the request that times out — which is DSimeone1989's case, 359 of them — the branch threw at the first `await`. **`get_room_mapping` was never sent at all.** The robot was not being asked the question it was failing to answer; it was being asked a different one, and the failure was filed under a name the register was built to ignore.
|
|
354
|
-
|
|
355
|
-
### The fix is to not make the request
|
|
356
|
-
|
|
357
|
-
The floor number is already known. The status poll runs on its own interval and stores `map_status` — already right-shifted — and `app_segment_clean` has read the floor from there since long before any of this. The room-mapping poll now reads the same value.
|
|
358
|
-
|
|
359
|
-
So on a classic robot the poll puts exactly one request on the wire, `get_room_mapping`, under its own name:
|
|
360
|
-
|
|
361
|
-
- One fewer cloud round-trip per poll cycle per robot, each with its own ten-second timeout.
|
|
362
|
-
- The register can count it. Six unanswered polls now stop the flood the same way they already stopped the other seven methods.
|
|
363
|
-
- When the robot is silent, it is silent about the thing it was actually asked.
|
|
364
|
-
|
|
365
|
-
`get_status` is still fetched when no status has landed yet — the first cycle after a restart starts both intervals together. Filing rooms under a guessed floor would hide them from `app_segment_clean`, which looks them up under the real one, so asking once is the lesser cost. A cached floor of `0`, the normal case for a home with one map, is a floor and not a missing value.
|
|
366
|
-
|
|
367
|
-
### The rule, pinned
|
|
368
|
-
|
|
369
|
-
The test is written against the class rather than this branch: **a poll that claims a method must put that method on the wire first.** Any future branch that fronts a governed poll with a different request re-opens this hole, and now fails in CI rather than in someone's log six weeks later.
|
|
370
|
-
|
|
371
|
-
2054 tests, 9 new, all 5 of the behavioural ones red against 3.32.0.
|
|
104
|
+
- The room poll on classic robots no longer starts with a status request, saving a cloud round-trip and letting the plugin pause a robot that never answers room requests (#22, #24).
|
|
105
|
+
- This explains log lines like `Failed to execute get_room_mapping` followed by `get_status timed out`; the fix only partly worked and was completed in 3.34.0.
|
|
372
106
|
|
|
373
107
|
## 3.32.0
|
|
374
108
|
|
|
375
|
-
**The give-up
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
3.30.0 introduced a register that stops asking a robot a question it never answers. 3.31.0 routed three more polls into it and fixed how it attributed blame. Both releases described it — in the changelog, and in the log line you read — as the thing that ends the flood of repeated timeouts, naming the seven methods from #22 and #24 that motivated it.
|
|
380
|
-
|
|
381
|
-
It could not have counted any of them. `pollParameter` decided the outcome from its own `try/catch`:
|
|
382
|
-
|
|
383
|
-
```js
|
|
384
|
-
try {
|
|
385
|
-
const answer = await vacuum.getParameter(duid, method);
|
|
386
|
-
this.noteMethodAnswered(duid, method); // ran on EVERY poll
|
|
387
|
-
return answer;
|
|
388
|
-
} catch (error) {
|
|
389
|
-
this.noteMethodUnanswered(duid, method, error); // unreachable
|
|
390
|
-
throw error;
|
|
391
|
-
}
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
`vacuum.getParameter` swallows its own errors. Its catch calls `catchError`, which only logs, and the function resolves `undefined`. So the catch here never ran. Every timeout looked like a success, and `noteMethodAnswered` **reset the counter on each failed poll**.
|
|
109
|
+
- **The give-up rule now actually works for all optional polls**: before, only map requests were ever counted, so other unanswered requests kept timing out instead of being paused.
|
|
110
|
+
- Network outages are now reliably kept out of the give-up rule; the previous check could never match.
|
|
111
|
+
- When `get_map_v1` is given up, the normal log line now says whether map replies were arriving and being discarded, so no debug logging is needed (#24).
|
|
395
112
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
The only methods it ever governed were the three map requests, because those go through `sendRequest` directly rather than `getParameter`. Everything else — `get_room_mapping`, `get_multi_maps_list`, `get_consumable`, `get_carpet_mode`, `get_carpet_clean_mode`, `get_water_box_custom_mode`, `get_timer` — was untouched. 3.31.0's "three polls were bypassing the register" fix wired them to nothing.
|
|
399
|
-
|
|
400
|
-
### The register now watches the wire
|
|
113
|
+
## 3.31.0
|
|
401
114
|
|
|
402
|
-
|
|
115
|
+
- Give-up rule fixes: Q7/B01 map fetches are counted per request, robots coming back online start fresh, and network blips should not count (that check only worked from 3.32.0).
|
|
116
|
+
- An Apple Home tile left stale by a rejected update now heals within about 10 minutes through the regular heartbeat.
|
|
117
|
+
- Map replies dropped by the plugin are now logged with the robot's name, a wrong address check is fixed, and one robot's interrupted photo transfer no longer blocks every robot's maps.
|
|
403
118
|
|
|
404
|
-
|
|
119
|
+
## 3.30.0
|
|
405
120
|
|
|
406
|
-
|
|
121
|
+
- **The water-tank notification repeating every two minutes is fixed**: it came from the plugin, not Apple; you can turn `enableMatterTankFaultReporting` back on (#5, #9, #26).
|
|
122
|
+
- A request a robot never answers is now paused for six hours after six silent tries; status and commands are exempt (only map requests were actually counted until 3.32.0).
|
|
123
|
+
- Unchecking schedules or routines now removes their tiles, and enabling both creates tiles for both without resetting the plugin (#22).
|
|
407
124
|
|
|
408
|
-
|
|
125
|
+
## 3.29.0
|
|
409
126
|
|
|
410
|
-
|
|
127
|
+
- Robots that refuse device-side schedules, like the Saros 10R in #22, now show their timed app Routines as switches in the robot's Schedules accessory, named after each Routine.
|
|
128
|
+
- **Run Routines from Apple Home**: optional `enableHomeKitRoutineSwitches` (off by default) adds a Routines accessory with one switch per app Routine, usable from Siri and Home automations.
|
|
129
|
+
- The debug log no longer prints your robots' local keys, serial numbers or account token; if you posted an older debug log publicly, consider removing it.
|
|
411
130
|
|
|
412
|
-
|
|
131
|
+
## 3.28.0
|
|
413
132
|
|
|
414
|
-
|
|
133
|
+
- Vacuums appear in Apple Home much sooner after a restart, especially robots that don't answer locally (#22), and the first status shows immediately instead of after 15 seconds.
|
|
134
|
+
- Much less background traffic: about 160 to 52 requests an hour per classic robot and 60 to 6 for a Q7, by checking slow-changing readings every 30 minutes.
|
|
415
135
|
|
|
416
|
-
3.
|
|
136
|
+
## 3.27.0
|
|
417
137
|
|
|
418
|
-
|
|
138
|
+
- Schedule diagnostic for #22 (debug logging only): the scene route allows only deleting, so the probe now asks two neighbouring routes which methods they accept, with safety controls.
|
|
419
139
|
|
|
420
|
-
|
|
421
|
-
Stueetage has not answered get_map_v1 6 times in a row, so the plugin stops
|
|
422
|
-
asking for about 6 hour(s)… No reply frames for this robot were received and
|
|
423
|
-
discarded, so the reply is not arriving at all rather than being lost on this
|
|
424
|
-
side.
|
|
425
|
-
```
|
|
140
|
+
## 3.26.0
|
|
426
141
|
|
|
427
|
-
|
|
142
|
+
- Schedule diagnostic for #22 (debug logging only): the probe now asks the scene route which request methods it accepts, without trying any, and logs the answer. Still not a feature.
|
|
428
143
|
|
|
429
|
-
|
|
430
|
-
…NOTE: 6 map reply frame(s) for this robot were received and then discarded by
|
|
431
|
-
the plugin (addressed-elsewhere: 6), so the robot IS answering and this side is
|
|
432
|
-
throwing it away. Please report this — it is a bug here, not on your robot.
|
|
433
|
-
```
|
|
144
|
+
## 3.25.1
|
|
434
145
|
|
|
435
|
-
|
|
146
|
+
- Schedule diagnostic for #22 (debug logging only): a refused request now logs the server's actual explanation instead of just a bare status code 400.
|
|
436
147
|
|
|
437
|
-
|
|
148
|
+
## 3.25.0
|
|
438
149
|
|
|
439
|
-
|
|
150
|
+
- Schedule diagnostic for #22 (debug logging only) now reads one cloud schedule directly to see whether the plugin could ever switch it; switching schedules from HomeKit is still not built.
|
|
440
151
|
|
|
441
|
-
|
|
152
|
+
## 3.24.2
|
|
442
153
|
|
|
443
|
-
|
|
154
|
+
- Fixed shutdown hanging and leaving a network socket open when Homebridge stopped during the five-second LAN discovery window.
|
|
444
155
|
|
|
445
|
-
|
|
156
|
+
## 3.24.1
|
|
446
157
|
|
|
447
|
-
|
|
158
|
+
- A robot that gets a new IP address from DHCP now finds its way back to the fast local connection instead of staying on the cloud until Homebridge restarts (#21).
|
|
448
159
|
|
|
449
|
-
3.
|
|
160
|
+
## 3.24.0
|
|
450
161
|
|
|
451
|
-
|
|
162
|
+
- Schedule diagnostic for #22 found where newer robots like the Saros 10R keep their schedules: as app Routines with a timer in the cloud, not on the robot.
|
|
163
|
+
- The debug log now shows every such schedule readably (name, time, enabled, what it runs) instead of cutting it off; switching them from HomeKit is not built yet.
|
|
452
164
|
|
|
453
|
-
|
|
165
|
+
## 3.23.1
|
|
454
166
|
|
|
455
|
-
|
|
456
|
-
… timed out after 10 seconds. MQTT connection state: false
|
|
457
|
-
… timed out after 10 seconds Local connect state: false
|
|
458
|
-
```
|
|
167
|
+
- Normal lifecycle codes are no longer logged as possible faults: 2102 (clean finished) and 2100 (recharge and resume) on Q7, and 407 on Q10. Apple Home was never affected.
|
|
459
168
|
|
|
460
|
-
|
|
169
|
+
## 3.23.0
|
|
461
170
|
|
|
462
|
-
|
|
171
|
+
- New read-only diagnostic (debug logging only) asks two cloud routes where newer robots like the Saros 10R keep their app schedules, and logs the answer and saves it in diagnostics.
|
|
172
|
+
- These schedules are not shown in HomeKit yet; this release only measures their shape.
|
|
463
173
|
|
|
464
|
-
|
|
174
|
+
## 3.22.0
|
|
465
175
|
|
|
466
|
-
|
|
176
|
+
- Schedule switches use far fewer cloud calls: longer caching, backoff after failures, merged rapid toggles, one queue per account and a pause after rate limiting. Contributed by @pponce (#23).
|
|
177
|
+
- Fixed a deadlock where reading a schedule switch during a write could freeze schedule traffic for every vacuum on the account until Homebridge restarted.
|
|
467
178
|
|
|
468
|
-
|
|
179
|
+
## 3.21.4
|
|
469
180
|
|
|
470
|
-
|
|
181
|
+
- A robot refusing a request, like a Saros 10R's "Not FCC robot", no longer logs a stack trace every poll; it is noted once, then logged at debug. Thanks to @DSimeone1989.
|
|
471
182
|
|
|
472
|
-
|
|
183
|
+
## 3.21.3
|
|
473
184
|
|
|
474
|
-
|
|
185
|
+
- When a robot refuses a request, the log and error now give the robot's own reason instead of `Result: undefined`. Reported by @DSimeone1989 in #22 about missing app schedules.
|
|
475
186
|
|
|
476
|
-
|
|
187
|
+
## 3.21.2
|
|
477
188
|
|
|
478
|
-
|
|
189
|
+
- Rooms the robot only drives through are no longer marked cleaned; the 3.19.7 fix fell short, so a room now needs sightings spanning 90 seconds (#9, reported by @vp-debug12).
|
|
479
190
|
|
|
480
|
-
|
|
191
|
+
## 3.21.1
|
|
481
192
|
|
|
482
|
-
|
|
193
|
+
- Fixed a login retry that could restart the plugin after Homebridge had already shut it down, for example a restart soon after booting without network.
|
|
483
194
|
|
|
484
|
-
|
|
195
|
+
## 3.21.0
|
|
485
196
|
|
|
486
|
-
|
|
197
|
+
- **Change clean mode mid-run**: a mode picked while the robot is cleaning or paused is now sent to it immediately; if the robot rejects it, the picker rolls back.
|
|
198
|
+
- Requires Homebridge 2.4.1-beta.11 or newer (homebridge/homebridge#4001) and follows `enableMatterCleanMode`; on Homebridge 2.4.0 nothing changes.
|
|
199
|
+
- The empty-water-tank warning now respects a mode changed mid-run, so it won't warn about water the robot has already turned off.
|
|
487
200
|
|
|
488
|
-
|
|
201
|
+
## 3.20.1
|
|
489
202
|
|
|
490
|
-
|
|
203
|
+
- Fixed seven resource problems: shutdown now really closes robot connections and pending requests, and a list that grew by about 86,400 entries a month per robot no longer grows.
|
|
204
|
+
- A brief network failure at boot no longer deletes your saved login session and cached device list; only an actual login refusal does.
|
|
491
205
|
|
|
492
|
-
|
|
493
|
-
if (!endpoint.startsWith(data2.endpoint)) return;
|
|
494
|
-
```
|
|
206
|
+
## 3.20.0
|
|
495
207
|
|
|
496
|
-
|
|
208
|
+
- A robot that dropped offline briefly, or was offline when Homebridge started, is polled again when it returns instead of freezing its tile until a restart.
|
|
209
|
+
- A Roborock cloud outage during startup no longer leaves the plugin idle forever; it now retries from 1 minute, backing off to every 10 minutes, without giving up.
|
|
497
210
|
|
|
498
|
-
|
|
211
|
+
## 3.19.8
|
|
499
212
|
|
|
500
|
-
|
|
213
|
+
- A "recovered" log line now appears only if the failure was announced too, so default logs no longer report recoveries from problems you were never told about.
|
|
501
214
|
|
|
502
|
-
|
|
215
|
+
## 3.19.7
|
|
503
216
|
|
|
504
|
-
|
|
217
|
+
- Tried to stop rooms the robot only drives through from being marked cleaned by requiring two consecutive sightings (#9, reported by @vp-debug12); superseded by 3.21.2, because it was not enough.
|
|
505
218
|
|
|
506
|
-
|
|
219
|
+
## 3.19.6
|
|
507
220
|
|
|
508
|
-
-
|
|
509
|
-
- **Three polls were bypassing the register entirely** — `get_multi_maps_list` and `get_room_mapping`, two of the seven methods named in the 647 suppressed timeouts that motivated the rule. The test that was supposed to catch this asserted "exactly 3 call sites", a number that silently excluded them. It now asserts the rule instead: no optional poll reaches the robot except through the register.
|
|
510
|
-
- **`cleaning_info` is printed in full while a robot is cleaning.** Not a feature — a measurement. Every status poll already receives this object and throws it away, and it is documented as carrying `{target_segment_id, segment_id, …}`. If `segment_id` tracks the room, live-room tracking on classic robots needs no map at all, which would route around the timeout above entirely. One clean answers it.
|
|
221
|
+
- Removed an unused cloud firmware check that had never actually run; nothing changes for you, and a new test stops it returning as about 480 wasted requests per robot daily.
|
|
511
222
|
|
|
512
|
-
|
|
223
|
+
## 3.19.5
|
|
513
224
|
|
|
514
|
-
|
|
225
|
+
- Room-list refreshes that cannot succeed are no longer retried every poll: skipped on a Q10, and backed off up to 30 minutes on a Q7 whose map channel is down.
|
|
515
226
|
|
|
516
|
-
|
|
227
|
+
## 3.19.4
|
|
517
228
|
|
|
518
|
-
|
|
229
|
+
- Log lines for an unknown `error_code` now include the robot's state at the time, so reports are useful; codes 2104, 2105 and 2110 are still not mapped.
|
|
519
230
|
|
|
520
|
-
|
|
231
|
+
## 3.19.3
|
|
521
232
|
|
|
522
|
-
|
|
233
|
+
- Fixed a robot mid-clean briefly showing as docked when an older cloud snapshot overwrote live status. Reported by @jbyhb in #20.
|
|
234
|
+
- Troubleshooting docs for the "Updating…" tile now say restarting the Apple device is what helps, and only for days to a week (#7).
|
|
523
235
|
|
|
524
|
-
|
|
236
|
+
## 3.19.2
|
|
525
237
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
operationalState, different value (0x41) .. 68 -> 0 WIPED
|
|
529
|
-
currentPhase, same value .................. 68 -> 68 kept
|
|
530
|
-
currentPhase, different value ............. 68 -> 68 kept
|
|
531
|
-
phaseList ................................. 68 -> 68 kept
|
|
532
|
-
operationalStateList ...................... 68 -> 68 kept
|
|
533
|
-
```
|
|
238
|
+
- A Q10 read the plugin deliberately doesn't send no longer logs a "Failed to execute" warning; genuine connection problems still warn.
|
|
239
|
+
- Troubleshooting docs for the "Updating…" / "No Response" tile now lead with restarting the Apple device rather than updating iOS.
|
|
534
240
|
|
|
535
|
-
|
|
241
|
+
## 3.19.1
|
|
536
242
|
|
|
537
|
-
|
|
243
|
+
- Q10 robots no longer cause repeated `Live-room map fetch has failed` warnings while cleaning; the plugin skips the map read and notes once that rooms are not reported mid-clean.
|
|
538
244
|
|
|
539
|
-
|
|
245
|
+
## 3.19.0
|
|
540
246
|
|
|
541
|
-
|
|
247
|
+
- **Q10 commands in the regular release**: confirmed working on a Q10 S5 by @niclasreich, unchanged since the beta; a Q10 tile still doesn't show a clean in progress (#19).
|
|
248
|
+
- A Q10 is no longer polled for status, ending false `B01 status has failed` warnings every few minutes on healthy robots.
|
|
249
|
+
- Cloud timeout messages now say their message count covers cloud traffic only, so a low number on a locally connected robot is normal.
|
|
542
250
|
|
|
543
|
-
|
|
251
|
+
## 3.19.0-beta.1
|
|
544
252
|
|
|
545
|
-
|
|
253
|
+
- Beta channel only, not pushed to existing installs: first Q10 commands (start, stop, pause, dock, empty bin, room clean, suction, clean mode) for #19, not yet verified on hardware.
|
|
254
|
+
- Known limits: resume restarts a whole-home clean, find me is refused, and Q10 status still comes from cloud home data. Q7 robots are unchanged.
|
|
546
255
|
|
|
547
|
-
|
|
256
|
+
## 3.18.0
|
|
548
257
|
|
|
549
|
-
-
|
|
550
|
-
-
|
|
551
|
-
-
|
|
258
|
+
- Found the real cause of #14: Q10-series robots (e.g. Q10 S5) were sent commands in the Q7 format, which they silently ignore (reported by @niclasreich).
|
|
259
|
+
- Q10 commands are now refused right away with a clear message instead of timing out; full Q10 support is tracked in #19.
|
|
260
|
+
- Q7-series robots are unchanged.
|
|
552
261
|
|
|
553
|
-
|
|
262
|
+
## 3.17.8
|
|
554
263
|
|
|
555
|
-
|
|
264
|
+
- Corrected the 3.17.7 cloud-timeout message, which could wrongly claim the robot never replied; replies the plugin cannot match to a robot are now counted and reported (#14, found by @niclasreich).
|
|
556
265
|
|
|
557
|
-
|
|
266
|
+
## 3.17.7
|
|
558
267
|
|
|
559
|
-
|
|
268
|
+
- A cloud request that times out now logs whether the robot sent anything at all, to tell a silent robot from a plugin bug; diagnostics only (#14, prompted by @niclasreich's log).
|
|
560
269
|
|
|
561
|
-
|
|
270
|
+
## 3.17.6
|
|
562
271
|
|
|
563
|
-
|
|
272
|
+
- The Empty Bin switch on robots like the S7 now keeps its custom name and Home placement after a restart (#16, reported by @pponce; fixed by @jbyhb in #17).
|
|
273
|
+
- Schedule switches now keep their custom names after a restart, and the schedule tile is no longer recreated (reported and fixed by @pponce in #15).
|
|
564
274
|
|
|
565
|
-
|
|
275
|
+
## 3.17.5
|
|
566
276
|
|
|
567
|
-
|
|
277
|
+
- Fixed the Roborock cloud connection staying offline after a network outage until Homebridge was restarted; it now reconnects on its own.
|
|
568
278
|
|
|
569
|
-
|
|
279
|
+
## 3.17.4
|
|
570
280
|
|
|
571
|
-
|
|
281
|
+
- The "no mapping for these fields" warning now names only fields the plugin genuinely doesn't know, not ones it already maps (#6, from @jcoz00's log; no change for #8).
|
|
572
282
|
|
|
573
|
-
|
|
283
|
+
## 3.17.3
|
|
574
284
|
|
|
575
|
-
|
|
285
|
+
- Q7- and Q10-series robots no longer make a useless cloud request every poll, removing the `get_room_mapping … timed out` error and repeated `No room mappings returned` notices (#14, reported by @niclasreich).
|
|
576
286
|
|
|
577
|
-
|
|
578
|
-
- **"No room mappings returned"** now says what to do about it: the usual reason is that the rooms on the map have not been named yet. Open the Roborock app, edit the map, name each room. From #25, which the reporter closed himself with exactly that discovery.
|
|
579
|
-
- **The Q7 fault list was left alone on purpose.** Eight days of measurement suggested widening the informational set from `{0, 407, 2100, 2102}` to eleven codes. Two guard tests refused it — one says 501 is not shared across B01 families, the other says codes upstream cannot explain must keep surfacing, because silencing them would be a guess. On inspection the unmapped-code notice already logs once per code per session, so the noise was a fraction of what it looked like, and the guards were right. The change was reverted. That is what those tests are for.
|
|
287
|
+
## 3.17.2
|
|
580
288
|
|
|
581
|
-
|
|
289
|
+
- The Qrevo CurvX's dock (dock type 20) is now recognised as auto-empty, so the optional Empty Bin switch can be offered for it (#6, confirmed by @jcoz00).
|
|
582
290
|
|
|
583
|
-
|
|
291
|
+
## 3.17.1
|
|
584
292
|
|
|
585
|
-
|
|
293
|
+
- Closing the plugin's settings page no longer prints a Node `Error: write EPIPE` crash dump into the Homebridge log (#6, reported by @jcoz00).
|
|
294
|
+
- The dock capability is now announced only when it changes, stopping a `Not publishing the Empty Bin switch…` debug line every minute per robot.
|
|
586
295
|
|
|
587
|
-
3.
|
|
296
|
+
## 3.17.0
|
|
588
297
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
OPTIONS user/scene/<id>/enable → the resource allows: PUT,OPTIONS
|
|
592
|
-
OPTIONS user/scene/device/<duid> (positive control) → the resource allows: GET,HEAD,OPTIONS
|
|
593
|
-
OPTIONS user/scene/<id>/no-such-subresource-control (negative control) → 400, no Allow
|
|
594
|
-
```
|
|
298
|
+
- Added an optional Empty Bin switch in Apple Home for compatible auto-empty docks (contributed by @jbyhb in #13).
|
|
299
|
+
- Fixed live dock-type detection, so classic robots like the S7 can expose dock features missing from their account data (found and fixed by @jbyhb).
|
|
595
300
|
|
|
596
|
-
|
|
301
|
+
## 3.16.0
|
|
597
302
|
|
|
598
|
-
|
|
303
|
+
- Turn on **Add Home app schedule switches** to enable or disable each Roborock app schedule from Apple Home (contributed by @pponce, requested in #3).
|
|
304
|
+
- A failed schedule refresh now keeps the existing switches instead of deleting them, so names, rooms and automations survive cloud timeouts.
|
|
305
|
+
- Fixed a race that could undo a successful schedule change; the new option is now documented in the settings page and README.
|
|
599
306
|
|
|
600
|
-
|
|
601
|
-
- `PUT user/scene/<id>/enable` with a JSON body → `400 parameter.error`. With a **form** body `enabled=true` → `success:true`, nothing changed (it was already on). With `enabled=false` → the scene-level `enabled` went false; the timer's own flag did not move. Set back to true, confirmed.
|
|
602
|
-
- `PUT …/param` with the TIMER trigger's nested `enabled` set to false → the flag went false, the action came back byte for byte, the scene-level flag stayed true — **exactly the change the reporter's app made when he switched a schedule off**. Set back to true, confirmed.
|
|
307
|
+
## 3.15.5
|
|
603
308
|
|
|
604
|
-
|
|
309
|
+
- Q10-series robots now use their own suction scale and fault codes instead of the Q7's, fixing Max+ and finished cleans showing as faults; a precaution before any Q10 report.
|
|
605
310
|
|
|
606
|
-
|
|
311
|
+
## 3.15.4
|
|
607
312
|
|
|
608
|
-
|
|
313
|
+
- Apple Home's Model row now shows just the model name (e.g. "Qrevo S") instead of repeating "Roborock" from the Manufacturer row; no re-pairing needed (#10, raised by n0rt0nthec4t).
|
|
609
314
|
|
|
610
|
-
|
|
315
|
+
## 3.15.3
|
|
611
316
|
|
|
612
|
-
|
|
317
|
+
- An empty water tank no longer triggers Apple Home's repeated "Fill the water tank" notification on vacuum-only runs; the Water Tank Empty sensor is unchanged (#9, reported by vp-debug12).
|
|
613
318
|
|
|
614
|
-
|
|
319
|
+
## 3.15.2
|
|
615
320
|
|
|
616
|
-
|
|
321
|
+
- Cleans started outside Apple Home (Roborock app, schedules, the robot's button, voice assistants) now also show "Cleaning" instead of "Traveling to Room", completing 3.15.1's fix (#8, #9).
|
|
617
322
|
|
|
618
|
-
|
|
323
|
+
## 3.15.1
|
|
619
324
|
|
|
620
|
-
|
|
325
|
+
- A whole-home clean now shows "Cleaning" in Apple Home instead of "Traveling to Room" for the entire run (#9, reported by vp-debug12; also #8 from skmzwanke).
|
|
621
326
|
|
|
622
|
-
|
|
327
|
+
## 3.15.0
|
|
623
328
|
|
|
624
|
-
|
|
329
|
+
- Accessory details now show a real model name (e.g. "Roborock Qrevo S") instead of a code like `roborock.vacuum.a104`, for 34 models; unknown models keep their code.
|
|
330
|
+
- Apple Home still showed Manufacturer "Homebridge" because Homebridge discarded these values; fixed upstream in Homebridge 2.4.1-beta.3 (homebridge/homebridge#3996), with no re-pairing needed.
|
|
625
331
|
|
|
626
|
-
|
|
332
|
+
## 3.14.3
|
|
627
333
|
|
|
628
|
-
-
|
|
629
|
-
- `homebridge-ui`: a Routines checkbox next to Schedules; `config.schema.json`: `enableHomeKitRoutineSwitches`.
|
|
334
|
+
- A mop run no longer switches to "Vacuum + Mop" in Apple Home while the dock washes the mop, empties the bin or updates the map, extending 3.12.3's fix.
|
|
630
335
|
|
|
631
|
-
|
|
336
|
+
## 3.14.2
|
|
632
337
|
|
|
633
|
-
|
|
338
|
+
- Urgent fix: 3.14.0 froze the robot's status, battery and faults in Apple Home whenever the dock was idle (e.g. stuck on "Cleaning Mop"); updates now get through again.
|
|
634
339
|
|
|
635
|
-
|
|
340
|
+
## 3.14.1
|
|
636
341
|
|
|
637
|
-
|
|
342
|
+
- The Homebridge log's publish line now shows the dock phase (e.g. `phase=Drying mop`), so you can check whether Apple Home displays 3.14.0's phases.
|
|
638
343
|
|
|
639
|
-
|
|
344
|
+
## 3.14.0
|
|
640
345
|
|
|
641
|
-
|
|
346
|
+
- The dock's jobs, including the 2 to 4 hours of mop drying after every mop clean, are now published to Apple Home as Matter phases; whether Apple Home shows them is unmeasured.
|
|
347
|
+
- Mop drying is detected on both classic robots and B01/Q7 robots.
|
|
348
|
+
- Set `enableMatterDockPhases: false` in `config.json` to turn phases off if a controller dislikes them.
|
|
642
349
|
|
|
643
|
-
|
|
350
|
+
## 3.13.1
|
|
644
351
|
|
|
645
|
-
|
|
352
|
+
- Correction to 3.13.0: unknown robot error codes (such as `error_code: 2105` on healthy docked Q7s) no longer show a fault in Apple Home; they are logged once instead.
|
|
646
353
|
|
|
647
|
-
|
|
354
|
+
## 3.13.0
|
|
648
355
|
|
|
649
|
-
|
|
356
|
+
- Apple Home now shows why a robot stopped: stuck, jammed wheel, blocked brush, dust bin missing or full, unreachable dock, flat battery and more.
|
|
357
|
+
- Unrecognised error codes reported a generic fault and were logged for reporting (the generic fault was withdrawn in 3.13.1).
|
|
358
|
+
- Fixed error codes being lost between polls and on local connections; no re-pairing needed, and `enableMatterTankFaultReporting: false` still turns it off.
|
|
650
359
|
|
|
651
|
-
|
|
360
|
+
## 3.12.5
|
|
652
361
|
|
|
653
|
-
|
|
362
|
+
- Corrected code comments and settings-page text that misstated the Matter specification; no behaviour changes.
|
|
654
363
|
|
|
655
|
-
|
|
656
|
-
| ------------- | ---------- | ---------- |
|
|
657
|
-
| classic robot | 160 / hour | ~52 / hour |
|
|
658
|
-
| Q7 (B01) | 60 / hour | ~6 / hour |
|
|
364
|
+
## 3.12.4
|
|
659
365
|
|
|
660
|
-
|
|
366
|
+
- Corrected the README, which for 17 minutes wrongly said Homebridge puts all robots on one shared Matter node; each robot has its own. No code changes.
|
|
661
367
|
|
|
662
|
-
|
|
368
|
+
## 3.12.3
|
|
663
369
|
|
|
664
|
-
|
|
370
|
+
- A mop run no longer briefly shows "Vacuum + Mop" in Apple Home while a classic S- or Q-series robot drives back to the dock.
|
|
665
371
|
|
|
666
|
-
|
|
372
|
+
## 3.12.2
|
|
667
373
|
|
|
668
|
-
|
|
374
|
+
- Changing debug mode, region, email or a device row in the settings page no longer silently switches off untouched HomeKit sensor options or writes your password into `config.json`.
|
|
669
375
|
|
|
670
|
-
|
|
671
|
-
OPTIONS user/scene/<id> → "" — the resource allows: DELETE,OPTIONS
|
|
672
|
-
```
|
|
376
|
+
## 3.12.1
|
|
673
377
|
|
|
674
|
-
|
|
378
|
+
- The empty-tank warning (Water Tank Empty sensor and the Apple Home fault) now actually fires; the plugin had never kept the tank status it needed.
|
|
675
379
|
|
|
676
|
-
|
|
380
|
+
## 3.12.0
|
|
677
381
|
|
|
678
|
-
-
|
|
679
|
-
-
|
|
382
|
+
- Apple Home now shows a "refill the water tank" warning on the tile when the robot reports an empty clean-water tank (prompted by vp-debug12's screenshot in #9).
|
|
383
|
+
- Removed the Apple Home Features switches from the settings page; those features are now all on, and existing `config.json` values are still honoured.
|
|
384
|
+
- **Re-pair once** if your robot was paired before this release with any of those features off.
|
|
680
385
|
|
|
681
|
-
|
|
386
|
+
## 3.11.2
|
|
682
387
|
|
|
683
|
-
-
|
|
684
|
-
- `user/scene/<id>/no-such-subresource-control` — a **negative** control. If a path with nothing behind it also answers with an `Allow` header, the server answers everything and every positive here is an artefact.
|
|
388
|
+
- Failing map fetches during a run now back off (up to 5 minutes) instead of retrying constantly, and the "failed N times in a row" counters now reset each run.
|
|
685
389
|
|
|
686
|
-
|
|
390
|
+
## 3.11.1
|
|
687
391
|
|
|
688
|
-
|
|
392
|
+
- If the robot's LAN port accepts connections but never answers, the plugin now switches to the cloud after 3 timeouts instead of losing 10 seconds on every request (#8).
|
|
689
393
|
|
|
690
|
-
## 3.
|
|
394
|
+
## 3.11.0
|
|
691
395
|
|
|
692
|
-
|
|
396
|
+
- Q7 placeholder positions are no longer reported as "between rooms" or counted as misses in live room tracking.
|
|
397
|
+
- A resolved room now logs its map cell, making position problems easier to compare.
|
|
398
|
+
- Removed the `Skipping known get_status attribute without a Homebridge state object` debug line that flooded the log about 50 times a minute per robot.
|
|
693
399
|
|
|
694
|
-
3.
|
|
400
|
+
## 3.10.2
|
|
695
401
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
{"code":"servlet.exception","msg":"Request method 'GET' is not supported", ...}
|
|
699
|
-
```
|
|
402
|
+
- Apple Home no longer shows Vacuum when the robot never confirmed the vacuum-only command and keeps mopping; the mode is pinned only once the robot acknowledges it (#8).
|
|
403
|
+
- Skipped heartbeat publishes are now logged at debug, so a gap in the log really means something stopped (#7).
|
|
700
404
|
|
|
701
|
-
|
|
405
|
+
## 3.10.0
|
|
702
406
|
|
|
703
|
-
|
|
407
|
+
- Added an optional **Water Tank Empty** contact sensor per robot, so a Home notification can tell you when the clean-water tank is empty (requested by Wazza151 in #5; also #9).
|
|
408
|
+
- The sensor only reports and never blocks a mop run; it is off by default and needs no re-pairing.
|
|
409
|
+
- Fixed new sensors showing a wrong state on startup, such as `Cleaning` announcing a finished clean that never happened.
|
|
704
410
|
|
|
705
|
-
|
|
411
|
+
## 3.9.4
|
|
706
412
|
|
|
707
|
-
|
|
413
|
+
- A robot sent home mid-clean is no longer wrongly treated as docked because of a leftover charging flag, which also fixes the Docked sensor and the dock-command retry (#8).
|
|
708
414
|
|
|
709
|
-
|
|
415
|
+
## 3.9.3
|
|
710
416
|
|
|
711
|
-
The
|
|
417
|
+
- The dock no longer announces a phantom cleaning start and finish when the robot briefly reports "returning to dock" after emptying its bin, which also falsely triggered the `Cleaning` sensor (#9).
|
|
712
418
|
|
|
713
|
-
## 3.
|
|
419
|
+
## 3.9.2
|
|
714
420
|
|
|
715
|
-
|
|
421
|
+
- After a restart, the Apple Home tile now shows the robot's real status straight away instead of a stale idle/Vacuum state for about 30 seconds.
|
|
716
422
|
|
|
717
|
-
3.
|
|
423
|
+
## 3.9.1
|
|
718
424
|
|
|
719
|
-
|
|
425
|
+
- Fixed the first status request failing on every Q7 at every startup (`recovered after 1 failed attempt(s)`), which also left the tile showing stale status for about 30 seconds.
|
|
720
426
|
|
|
721
|
-
|
|
427
|
+
## 3.9.0
|
|
722
428
|
|
|
723
|
-
|
|
429
|
+
- Added optional read-only contact sensors per robot (e.g. `Vicky Docked`, `Vicky Cleaning`) so the robot's state can trigger Apple Home automations (based on pponce's feedback in #3).
|
|
430
|
+
- The sensors follow the robot's real state, ignore dock chores, and wait for the robot to report in, avoiding false automation triggers after a restart.
|
|
431
|
+
- Off by default and no re-pairing; they pair using the plugin's child bridge HomeKit QR code.
|
|
724
432
|
|
|
725
|
-
|
|
433
|
+
## 3.8.0
|
|
726
434
|
|
|
727
|
-
|
|
435
|
+
- **The settings page now follows Homebridge's dark theme**, switching as soon as you change the theme; the QR code stays white so cameras can still read it.
|
|
436
|
+
- The plugin's icon now appears beside the settings page title, as on other plugins' settings pages.
|
|
728
437
|
|
|
729
|
-
|
|
438
|
+
## 3.7.1
|
|
730
439
|
|
|
731
|
-
|
|
440
|
+
- The Matter Pairing section no longer tells you to pair the bridge first: each robot pairs with its own Matter code and does not need the bridge. Wording only (#7).
|
|
732
441
|
|
|
733
|
-
|
|
442
|
+
## 3.7.0
|
|
734
443
|
|
|
735
|
-
|
|
444
|
+
- **New Start Cleaning switch** for automations, next to Return to Dock, Pause and Find; it starts the same clean as the Home tile's play button, including any rooms selected there.
|
|
445
|
+
- Off by default and no re-pairing needed; the log says which surface started a clean, e.g. `Starting Vicky from the Home switch.`
|
|
736
446
|
|
|
737
|
-
|
|
447
|
+
## 3.6.2
|
|
738
448
|
|
|
739
|
-
|
|
449
|
+
- A dock chore such as emptying the dust bin no longer makes Apple Home announce a clean starting and finishing (#9); docs note Debug Mode also needs Homebridge's `-D`.
|
|
740
450
|
|
|
741
|
-
## 3.
|
|
451
|
+
## 3.6.1
|
|
742
452
|
|
|
743
|
-
|
|
453
|
+
- Two 3.6.0 log fixes now actually work: the poll-profile line prints once per model, and `Loading accessory 'X' from cache.` moved to debug.
|
|
744
454
|
|
|
745
|
-
|
|
455
|
+
## 3.6.0
|
|
746
456
|
|
|
747
|
-
|
|
457
|
+
- Settings page: a new install no longer saves default-on features as off, the password is no longer re-saved after a 2FA login, and the switches option now publishes them.
|
|
458
|
+
- The page now reports failed saves, greys out settings that need another one turned on, and no longer loads Google Fonts, which stalled it on offline Homebridge boxes.
|
|
459
|
+
- Calmer, clearer log: duplicate lines removed, Roborock outages logged as short warnings instead of stack traces, and 14 more lines name the robot instead of a raw ID.
|
|
748
460
|
|
|
749
|
-
|
|
461
|
+
## 3.5.4
|
|
750
462
|
|
|
751
|
-
|
|
463
|
+
- 3.5.3's new startup line pointed child-bridge users at the wrong QR code; it now reads the bridge setup from `config.json` and covers both bridges when unsure.
|
|
752
464
|
|
|
753
|
-
## 3.
|
|
465
|
+
## 3.5.3
|
|
754
466
|
|
|
755
|
-
|
|
467
|
+
- If the switches don't show in Apple Home, the log, settings page and README now explain why: the child bridge needs Enable HAP turned on, then its own QR code paired.
|
|
756
468
|
|
|
757
|
-
|
|
469
|
+
## 3.5.2
|
|
758
470
|
|
|
759
|
-
|
|
471
|
+
- Apple Home no longer shows Vacuum and mop for the first minute or two of a vacuum-only clean started from Home; the clean itself was always right (#8, thanks to @skmzwanke).
|
|
760
472
|
|
|
761
|
-
|
|
473
|
+
## 3.5.1
|
|
762
474
|
|
|
763
|
-
|
|
475
|
+
- Docs only: the README now records that Pause is offered as an Apple Home automation action, but a Matter vacuum cannot be an automation trigger (#3, thanks to @pponce).
|
|
764
476
|
|
|
765
|
-
|
|
766
|
-
- A reconnect also re-consults the LAN in the background. The robot's UDP broadcast is the one signal that means "this robot is on _this_ network at _this_ address", so it is the right source for the correction — the cloud's `get_network_info` cannot serve here, because a robot whose local connect just failed has already been marked cloud-only, and that mark is what gates the write of its address. The correction is written where every other caller reads it, so a moved robot is picked up by ordinary commands too, not only by the retry.
|
|
477
|
+
## 3.5.0
|
|
767
478
|
|
|
768
|
-
|
|
479
|
+
- **New Home app switches for Return to Dock, Pause and Find**, so automations can send a robot home, which Apple Home does not offer for a Matter vacuum (#3, thanks to @pponce).
|
|
480
|
+
- Off by default, per robot and per action, and momentary; no re-pairing needed, but the Homebridge bridge itself must be paired before the switches appear.
|
|
481
|
+
- README corrected: Apple Home automations can already start a clean, whole home or chosen rooms, and stop one; only sending the robot to its dock is missing.
|
|
769
482
|
|
|
770
|
-
|
|
483
|
+
## 3.4.19
|
|
771
484
|
|
|
772
|
-
|
|
485
|
+
- Docs only: the README no longer promises automations can dock a Matter vacuum, and no longer blames fault reporting for a tile stuck on "Updating…", which was a stale pairing.
|
|
773
486
|
|
|
774
|
-
|
|
487
|
+
## 3.4.18
|
|
775
488
|
|
|
776
|
-
|
|
489
|
+
- A robot dropping off the Roborock cloud no longer fills the log with stack traces on every poll; it now logs one warning, then a summary of suppressed repeats.
|
|
777
490
|
|
|
778
|
-
|
|
779
|
-
- `GET user/scene/device/<duid>` → **all three schedules**, with cron, timezone, enable flag and the room task.
|
|
491
|
+
## 3.4.17
|
|
780
492
|
|
|
781
|
-
|
|
493
|
+
- Installing the package no longer triggers a build, which failed for git installs and made npm 11.16+ print `npm warn allow-scripts` for this plugin. No plugin behaviour changes.
|
|
782
494
|
|
|
783
|
-
|
|
495
|
+
## 3.4.16
|
|
784
496
|
|
|
785
|
-
|
|
497
|
+
- The clean mode shown on the Home tile is now applied before every clean started from Apple Home, so an untouched Vacuum selection no longer lets the robot mop (#8).
|
|
786
498
|
|
|
787
|
-
|
|
788
|
-
… — user/scene/device/<duid> carries 3 timer-driven scene(s):
|
|
789
|
-
… "Saugen+" (scene …) — 09:00 on Wed (Europe/Berlin), enabled, runs do_scenes_segments over 7 segment(s)
|
|
790
|
-
```
|
|
499
|
+
## 3.4.15
|
|
791
500
|
|
|
792
|
-
|
|
501
|
+
- The chosen clean mode no longer times out before the clean starts (`prep timed out after 2500 ms`); commands stop waiting on a slow status read (#8, thanks to @skmzwanke).
|
|
793
502
|
|
|
794
|
-
## 3.
|
|
503
|
+
## 3.4.14
|
|
795
504
|
|
|
796
|
-
|
|
505
|
+
- Fixed vacuum-only room cleans that still mopped because the start command overtook the mode command; mode setup now fits its time window, Q7 robots included (thanks to @skmzwanke).
|
|
797
506
|
|
|
798
|
-
|
|
507
|
+
## 3.4.13
|
|
799
508
|
|
|
800
|
-
|
|
509
|
+
- Cleanup with no behaviour change: unused translations and dead code removed, and false claims fixed, including a startup line promising a HomeKit accessory that no longer exists.
|
|
801
510
|
|
|
802
|
-
|
|
803
|
-
- **2100** — "Low battery. Resume cleaning after recharging." The robot announcing normal auto-recharge-and-resume.
|
|
511
|
+
## 3.4.12
|
|
804
512
|
|
|
805
|
-
|
|
513
|
+
- Suction or clean-type changes made in the Roborock app or by SmartPlan now reach Apple Home; live updates carrying only that change were being discarded.
|
|
806
514
|
|
|
807
|
-
|
|
515
|
+
## 3.4.11
|
|
808
516
|
|
|
809
|
-
|
|
517
|
+
- Robots using `enableFanPowerCleanModes` no longer flip their Apple Home clean mode to plain Vacuum and back; an unreadable suction level now leaves the shown mode unchanged.
|
|
810
518
|
|
|
811
|
-
## 3.
|
|
519
|
+
## 3.4.10
|
|
812
520
|
|
|
813
|
-
|
|
521
|
+
- Diagnostics only: the Q7 position used for live-room tracking proved to be a constant, so the room-miss log line now surveys the map data to locate the real position.
|
|
814
522
|
|
|
815
|
-
|
|
523
|
+
## 3.4.9
|
|
816
524
|
|
|
817
|
-
|
|
525
|
+
- Diagnostics only: when live-room tracking cannot place a robot, the log now shows where the map's rooms lie, to explain Q7 positions far outside the map.
|
|
818
526
|
|
|
819
|
-
|
|
820
|
-
- `user/scene/device/{duid}` — the app's Routines
|
|
527
|
+
## 3.4.8
|
|
821
528
|
|
|
822
|
-
|
|
529
|
+
- Choosing Vacuum no longer mops: a timed-out suction command could stop the water-off command being sent; it now goes first, and a partial apply logs a warning (#8, thanks to @skmzwanke).
|
|
823
530
|
|
|
824
|
-
|
|
531
|
+
## 3.4.7
|
|
825
532
|
|
|
826
|
-
|
|
533
|
+
- The diagnostics report no longer tells Q7 owners their robot failed to reach the LAN; these models are cloud-only by design and now show `Cloud control (this model)` (#7, thanks to @jawnlydon).
|
|
827
534
|
|
|
828
|
-
## 3.
|
|
535
|
+
## 3.4.6
|
|
829
536
|
|
|
830
|
-
|
|
537
|
+
- Trying cloud-only mode no longer leaves a robot marked "Cloud only" after it is switched off, and the diagnostics report now quotes the saved setting (#7, thanks to @jawnlydon).
|
|
831
538
|
|
|
832
|
-
|
|
539
|
+
## 3.4.5
|
|
833
540
|
|
|
834
|
-
|
|
541
|
+
- The `Matter publish for` log line now appears on every change, not only when the battery changed, so Apple Home display problems show up in the log (#8, thanks to @skmzwanke).
|
|
542
|
+
- The battery resync line no longer claims to force an update; a frozen battery percentage in Apple Home is an Apple-side gap no plugin can work around.
|
|
835
543
|
|
|
836
|
-
|
|
837
|
-
- the batch finishes writing, waits, and adopts that refresh as its verification;
|
|
838
|
-
- the refresh cannot start until the batch releases the queue, and the batch cannot release it until the refresh returns.
|
|
544
|
+
## 3.4.4
|
|
839
545
|
|
|
840
|
-
|
|
546
|
+
- Nine Saros 10 status fields are now recognised, so its log no longer asks the owner to report them (#8, thanks to @skmzwanke).
|
|
841
547
|
|
|
842
|
-
|
|
548
|
+
## 3.4.3
|
|
843
549
|
|
|
844
|
-
|
|
550
|
+
- Robots without a model profile, such as the Saros 10 and Qrevo CurvX, now report unknown status fields once per robot instead of an `Unsupported attribute` warning every poll (#8, #6).
|
|
551
|
+
- 12 more log lines, including the device-offline line, now name the robot instead of a raw device ID.
|
|
845
552
|
|
|
846
|
-
|
|
553
|
+
## 3.4.2
|
|
847
554
|
|
|
848
|
-
|
|
555
|
+
- Q7-series robots no longer log unsupported-method notices on every restart for requests like `get_water_box_custom_mode`; the plugin stops asking them for things they cannot answer.
|
|
849
556
|
|
|
850
|
-
|
|
851
|
-
Cloud message with protocol 102 and id 5 received. No result; reply was {"id":5,"error":{"code":-10007,"message":"Not FCC robot"}}
|
|
852
|
-
```
|
|
557
|
+
## 3.4.1
|
|
853
558
|
|
|
854
|
-
|
|
559
|
+
- **Matter fault details are withdrawn**: Apple Home never showed them and they could stick the tile on "Updating…"; Show Dock & Tank Warnings as Errors is gone (#5, thanks to @Wazza151).
|
|
560
|
+
- Report Faults in Apple Home still shows a genuinely halted robot as Error, and the diagnostics report now reflects saved settings rather than unsaved checkboxes.
|
|
855
561
|
|
|
856
|
-
|
|
857
|
-
Failed to execute get_server_timer on robot Rocky (roborock.vacuum.a144): Error: The robot refused get_server_timer (cloud id 5): Not FCC robot (code -10007)
|
|
858
|
-
at MqttClient.<anonymous> (…/roborock_mqtt_connector.js:420:17)
|
|
859
|
-
at MqttClient.emit (node:events:514:28)
|
|
860
|
-
… eight more frames
|
|
861
|
-
```
|
|
562
|
+
## 3.4.0
|
|
862
563
|
|
|
863
|
-
**
|
|
564
|
+
- **If you turned on Report Faults in 3.3.0, update**: it could leave the Apple Home tile stuck on "Updating…"; faults are now only published alongside the Error state.
|
|
565
|
+
- Dock and tank warnings moved to a new opt-in setting, Show Dock & Tank Warnings as Errors, which may stop Apple Home starting the robot (removed again in 3.4.1).
|
|
566
|
+
- Robot faults such as stuck, blocked brush or wheel, missing dust bin, flat battery or unreachable dock work as before.
|
|
864
567
|
|
|
865
|
-
|
|
568
|
+
## 3.3.2
|
|
866
569
|
|
|
867
|
-
|
|
570
|
+
- Finished 3.3.1's work: 11 more log lines, including the battery resync line, now name the robot instead of a raw device ID.
|
|
868
571
|
|
|
869
|
-
## 3.
|
|
572
|
+
## 3.3.1
|
|
870
573
|
|
|
871
|
-
|
|
574
|
+
- Clearer logs: a live-room miss now says which of four problems occurred instead of always "between rooms", and more lines name the robot instead of a raw device ID.
|
|
575
|
+
- The diagnostics report now lists which Apple Home features are switched on.
|
|
872
576
|
|
|
873
|
-
|
|
577
|
+
## 3.3.0
|
|
874
578
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
Unable to reliably read Roborock schedules …: get_server_timer returned undefined
|
|
879
|
-
```
|
|
579
|
+
- **New setting: Report faults in Apple Home** (off by default): a stuck robot, missing dust bin or flat battery now shows as Error instead of Ready (#5, requested by @Wazza151).
|
|
580
|
+
- Dock and tank conditions, such as an empty clean-water tank, were sent as a fault without forcing Error; this part was withdrawn in 3.4.1 because Apple Home never showed it.
|
|
581
|
+
- Diagnostics now always keep fault fields such as `dock_error_status` instead of truncating them away.
|
|
880
582
|
|
|
881
|
-
|
|
583
|
+
## 3.2.0
|
|
882
584
|
|
|
883
|
-
|
|
585
|
+
- On Q7-series robots, the first room of a clean now appears in Apple Home as soon as the robot is seen cleaning, instead of up to minutes later.
|
|
586
|
+
- When a robot cannot place itself in any room, the log now says so with a count instead of staying silent.
|
|
884
587
|
|
|
885
|
-
|
|
588
|
+
## 3.1.1
|
|
886
589
|
|
|
887
|
-
|
|
590
|
+
- The settings page checkbox now reads Dock & Returning status, matching 3.1.0's rename, and explains that Emptying and Washing show only while the dock does them.
|
|
888
591
|
|
|
889
|
-
## 3.
|
|
592
|
+
## 3.1.0
|
|
890
593
|
|
|
891
|
-
**
|
|
594
|
+
- **Emptying the dust bin and Washing the mop now reach Apple Home**; the setting is renamed Dock & Returning Status, still opt-in and needs one re-pair (#5).
|
|
595
|
+
- Changing suction in the Roborock app no longer makes a cleaning robot show as charging (#6), robots no longer borrow each other's features, and LAN control recovers on its own.
|
|
596
|
+
- Security: the cloud session and LAN keys are no longer world-readable, Logout really logs out, and diagnostics no longer leak your Wi-Fi name or robot MAC.
|
|
892
597
|
|
|
893
|
-
|
|
598
|
+
## 3.0.2
|
|
894
599
|
|
|
895
|
-
|
|
600
|
+
- Fixed two ways Homebridge could crash (a stray LAN packet, and the startup watchdog when the network comes up late) and a cloud hiccup at startup unregistering every robot.
|
|
601
|
+
- Cloud commands no longer report failure after succeeding, LAN discovery and Q7 clean mode and suction work for the first time, and dry-only robots no longer offer mop modes.
|
|
896
602
|
|
|
897
|
-
|
|
603
|
+
## 3.0.1
|
|
898
604
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
90 seconds is picked against the two durations it has to separate rather than tuned: a crossing is bounded by the room's size divided by travel speed, which puts even a long traverse near 35 seconds, while covering a room in the robot's back-and-forth pattern takes minutes.
|
|
902
|
-
|
|
903
|
-
**If it errs, it errs toward saying less.** A room genuinely cleaned that somehow falls short of the dwell shows as pending mid-run, and the end of the run marks everything completed regardless — so that mistake corrects itself. Claiming a clean that never happened does not correct itself, which is the whole complaint.
|
|
904
|
-
|
|
905
|
-
4 new tests, 3 of which fail against the old rule, including a replay of a slow crossing and of one sample read by two publish cycles.
|
|
906
|
-
|
|
907
|
-
## 3.21.1
|
|
908
|
-
|
|
909
|
-
**A login retry could restart the plugin after Homebridge had already shut it down.**
|
|
910
|
-
|
|
911
|
-
3.20.0 added a retry for the case where the Roborock cloud is unreachable at boot, and wired it to shutdown properly. It was not the only retry in the file. The older one, on the login step itself, kept its timer handle in a local variable — so `clearTimersAndIntervals` had nothing it could clear even in principle, and nothing checked whether the adapter was still alive before the timer fired.
|
|
912
|
-
|
|
913
|
-
The window is up to 10 minutes wide. A boot where the network is not up yet arms the retry; if Homebridge stops the plugin inside that window — a restart from the UI, a config change, a child bridge reload — the timer still fires into the shut-down adapter and runs the full startup again: a fresh login, a new MQTT client, and a new set of poll intervals attached to an instance whose sockets had already been destroyed and whose pending requests had already been rejected.
|
|
914
|
-
|
|
915
|
-
**Both retries now check a shutdown latch, not just `bInited`.** That distinction is the actual fix. `bInited` is false both before a successful start and after a shutdown, so a retry callback that read it could not tell "still needs starting" from "already stopped" — which means the 3.20.0 retry had the same race, narrower but real, in the gap between the timer being picked up by the event loop and the clear running. A deliberate `startService` clears the latch, so restarting a stopped adapter in-process still works.
|
|
916
|
-
|
|
917
|
-
**The suite's `You are trying to require a file after the Jest environment has been torn down` is gone, and it was not what the last release assumed.** It was read as a stray cloud-retry timer in the library. The real source was the retry's own test file: the backoff test arms 8 real timers in a loop, nulling the handle between arms so the "only one in flight" guard lets the next one through, which orphans the 7 before it. Those fired 1 to 10 minutes later, inside whatever test happened to be running by then, and called `startService` — whose first statement requires the translations file. Unref'd timers cannot hold the worker open, so nothing failed; it just printed a warning that pointed at the library instead of at the test. All 8 are disarmed now.
|
|
918
|
-
|
|
919
|
-
5 new tests.
|
|
920
|
-
|
|
921
|
-
## 3.21.0
|
|
922
|
-
|
|
923
|
-
**You can now change the clean mode while the robot is already cleaning. This needed a change in Homebridge itself, and that change has now shipped.**
|
|
924
|
-
|
|
925
|
-
Until now a mode picked mid-run only moved the picker. The prep sequence that sends the mode to the robot runs before a start and nowhere else, so the robot carried on with the settings it began with, and the tile quietly disagreed with the floor. That was not an oversight in this plugin: Matter's RvcCleanMode cluster forbids a mode change outside idle unless the device advertises the `DirectModeChange` feature, and Homebridge had no way for a plugin to advertise it. [homebridge/homebridge#4001](https://github.com/homebridge/homebridge/pull/4001) added one, and it is merged and released in `2.4.1-beta.11`.
|
|
926
|
-
|
|
927
|
-
**What happens now.** A mode change while the robot is running or paused is sent to the robot immediately, and the picker holds your choice until the robot's own status agrees with it — up to 150 seconds, after which the picker gives up and follows the robot again with a warning line naming what it asked for. If the command fails outright, every piece of selection state rolls back to what it was and the controller is told the change failed, so a rejected change cannot leave the picker showing a mode the robot never took.
|
|
928
|
-
|
|
929
|
-
**On the empty-tank warning, which had to move with it.** The tank check deliberately read the robot's live water level rather than the picker, on the reasoning that a mid-run selection was never applied and so could not be trusted. That reasoning no longer holds, and left alone it would have raised a tank warning for water the robot had already acknowledged turning off. The applied-type pin is now the authority during the confirmation window.
|
|
930
|
-
|
|
931
|
-
**What you need to run it.** Homebridge `2.4.1-beta.11` or newer. On `2.4.0` the feature declaration is inert — that version has no concept of plugin-advertised Matter features, so nothing changes and nothing breaks. It follows the existing `enableMatterCleanMode` setting: leave it on and live changes are advertised, turn it off and neither the declaration nor the handler exists.
|
|
932
|
-
|
|
933
|
-
6 new tests.
|
|
934
|
-
|
|
935
|
-
## 3.20.1
|
|
936
|
-
|
|
937
|
-
**The resource half of the same review: seven things that leaked, hung on, or would have taken the bridge down given the right unlucky moment.**
|
|
938
|
-
|
|
939
|
-
**Every timer this library creates is now unref'd, and cannot become an unhandled rejection.** `src/timers.ts` has stated the policy since it was written — "a pending timer must never be why Homebridge cannot shut down" — and `src/` honours it at every one of its own call sites. `roborockLib` never imported that module, so all ~20 of its timers were ref'd, including each in-flight request timeout of up to 30 seconds, none of which were cleared on shutdown. Both fixes live in the 2 wrapper methods every timer in the library already goes through, so they cover all of them at once. The rejection guard matters more than it looks: Homebridge's `uncaughtException` handler is `process.kill(SIGTERM)` and node routes unhandled rejections into it, so one rejecting poll callback takes the whole bridge down. None can reject today — but `updateDataMinimumData` has no `try` in its 114 lines and survives only because each of its callees happens to catch, which is a contract nobody had written down.
|
|
940
|
-
|
|
941
|
-
**Shutdown now actually stops things.** `stopService()` cleared timers and nothing else. `client.end()` existed in exactly 1 place in the codebase, inside `reconnectClient`, and never on the shutdown path — so in the seconds between Homebridge's SIGTERM and its forced exit, robot frames kept arriving and being dispatched into disposed accessories, and a local socket closing in that window scheduled a _new_ reconnect and wrote diagnostics files into a bridge being dismantled. Both transports are closed now, and every pending request is rejected rather than left hanging.
|
|
942
|
-
|
|
943
|
-
**One throwing `dispose()` no longer skips the rest of shutdown.** The SHUTDOWN handler was a straight-line sequence, so a throw anywhere in it skipped `stopService()` — the step that stops the polling and closes the transports. A shutdown that half-runs is worse than one that fails loudly.
|
|
944
|
-
|
|
945
|
-
**A list that grew by 2 entries a minute, forever.** `processDockType()` runs on every status poll carrying `dock_type`, and the poll site's comment says that is safe because the function is idempotent. It is — for `commands`, `deviceStates` and `consumablesString`, which all use keyed assignment. `resetConsumables` was the one member using `.push`. Measured: 6 entries became 2016 after 1005 polls, about 86,400 a month per robot.
|
|
946
|
-
|
|
947
|
-
**A transient network failure no longer deletes your saved session.** The login error path made no distinction between "Roborock rejected your password" and "DNS was not up yet", and deleted both the stored session and the cached device list for either. On a boot where the router is still coming up that destroyed the only offline snapshot the plugin has. Only an actual refusal clears it now, and an unrecognised failure is treated as transport — deleting a good session costs a re-login, keeping a dead one costs one failed request.
|
|
948
|
-
|
|
949
|
-
**Two smaller ones.** A guard reading `photoGzipChunks != []` compared against a fresh array literal and was therefore always true, so it never guarded anything. And two concurrent `createClient` calls could each build a socket, with the later assignment orphaning the first — its handlers correctly declined to reconnect, so there was no storm, but the file descriptor stayed open for the life of the process. The new claim is released in a `finally`, because a leaked claim would be worse than the leak it prevents: that robot could never reconnect again.
|
|
950
|
-
|
|
951
|
-
17 new tests.
|
|
952
|
-
|
|
953
|
-
## 3.20.0
|
|
954
|
-
|
|
955
|
-
**Two ways the plugin could stop working and never start again. Both silent, both permanent until somebody restarted Homebridge.**
|
|
956
|
-
|
|
957
|
-
Neither was a crash, which is why neither had been noticed. The plugin stayed up and stopped doing its job.
|
|
958
|
-
|
|
959
|
-
### A classic robot that flapped offline was never polled again
|
|
960
|
-
|
|
961
|
-
`manageDeviceIntervals` stops both polling intervals when a robot reads as offline and restarts them when it reads as online. The restart half was unreachable for a classic robot: the only caller sits **inside the `get_status` handler**, so it runs only when a status poll succeeds — and `getStatusIntervalHandle` is the one thing driving those polls. Once they were cleared, nothing was left alive to notice the robot had come back. The one other caller, the home-data supervisor, filtered on B01 and skipped every classic robot deliberately.
|
|
962
|
-
|
|
963
|
-
`onlineChecker` reads the cached home-data snapshot, which lags by up to one refresh, so the trigger was ordinary rather than exotic: robot drops off wifi, comes back, its LAN socket reconnects first, the next local poll succeeds, the snapshot still says offline, both intervals die. From then on the tile froze on the last known state until the user pressed a button or restarted.
|
|
964
|
-
|
|
965
|
-
Same dead end at boot, for a different reason: the intervals are only started `if (device.online)`, so a robot that was offline when Homebridge started was **never polled at all** for the life of the process.
|
|
966
|
-
|
|
967
|
-
The supervisor now covers every robot. It runs on the home-data cadence, which is the same clock that decides `onlineChecker`'s answer, so a robot that comes back is picked up on the tick that notices it.
|
|
968
|
-
|
|
969
|
-
### A cloud outage during startup wedged the plugin permanently
|
|
970
|
-
|
|
971
|
-
`getUserData` returns a stored session **without touching the network**, so any install that has logged in once — essentially all of them — never reaches the login retry with backoff. It reaches `getHomeDetail` instead. When that failed, the plugin logged one warning and stopped.
|
|
972
|
-
|
|
973
|
-
The ordering is what made it terminal: `homedataInterval` and `reconnectIntervall` are created _after_ that call, and `initUser` was never reached, so there was no MQTT client either. **Not one timer existed that would ever try again.** A Pi rebooting after a power cut, with the router still coming up, registered nothing and sat idle until a human intervened. The README's "retries with increasing backoff, up to 10 attempts" described only the login step, which this path skips.
|
|
974
|
-
|
|
975
|
-
There is now a retry: 1 minute, doubling to a 10-minute ceiling, then holding there. Deliberately with no attempt cap — the failure it recovers from is "the network was not ready yet" and the device is unattended, so giving up means a person has to notice. One timer at a time, unref'd so it can never hold Homebridge open, cleared on shutdown, and reset by a success so a later outage starts from 1 minute again.
|
|
976
|
-
|
|
977
|
-
9 new tests, and the supervisor's own test previously asserted the skip that caused the first bug.
|
|
978
|
-
|
|
979
|
-
## 3.19.8
|
|
980
|
-
|
|
981
|
-
**A recovery line now names only a failure the log actually announced — and the test that says so had been failing on disk, uncommitted, for 3 days.**
|
|
982
|
-
|
|
983
|
-
Two things, and the second is the more uncomfortable.
|
|
984
|
-
|
|
985
|
-
**The log rule.** A B01 status attempt is logged at debug until the 10th consecutive failure, but the _recovery_ was announced at info from the first. With debug off, which is the default, a single transient miss that healed on the next tick told the user a channel had recovered from a problem they were never told about. On a robot with a flaky link that is a steady drip of good news about invisible bad news. The recovery is now announced exactly when the failure was — and no more than that, because a streak that did reach the warning must still get its closing line, or the log's last word on the channel stays "broken" long after it healed. The same rule was already written down in this file's own neighbour, `noteLiveRoomFetchRecovered`, whose doc comment says to announce a recovery exactly when the thing it reports on was itself announced "so the message and the behaviour it reports on cannot drift apart".
|
|
986
|
-
|
|
987
|
-
**The part worth saying out loud.** The test suite for that rule was written on 29 August, saved to disk, and never committed. `git status` had been showing it as untracked ever since. So it failed on every local `npm test` run for 3 days while CI stayed green — because CI only runs what is in the repository, and this was not. A suite that is red locally and green remotely is worse than no suite: it trains whoever runs it to expect failures and to scroll past them.
|
|
988
|
-
|
|
989
|
-
The test is now in the repository, where it fails 5 times against the old behaviour and passes against the new. Nothing about the release gate changes; the gate was never wrong. What was wrong was a file that only existed on one machine.
|
|
990
|
-
|
|
991
|
-
## 3.19.7
|
|
992
|
-
|
|
993
|
-
**Driving through a room is not cleaning it.**
|
|
994
|
-
|
|
995
|
-
Reported by vp-debug12 in [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9): with several rooms selected, a room the robot merely crossed on its way to the first target was reported as cleaned.
|
|
996
|
-
|
|
997
|
-
Exactly right, and the mechanism is one line. A room joined the confirmed-visited set on its **first** live-room detection, and a confirmed room is reported Completed the moment the robot moves on. One reading taken while crossing the hall was therefore enough to call the hall done — while it still had its whole clean ahead of it.
|
|
998
|
-
|
|
999
|
-
A room now counts as visited only after the robot has been detected in it on **2 consecutive** live-room readings. Readings are at least 10 seconds apart, so this asks the robot to still be there next time: true of a room being cleaned, false of a room being crossed.
|
|
1000
|
-
|
|
1001
|
-
Erring low is deliberate and is why 2 is enough rather than some larger number picked by feel. A room that really was cleaned but missed its second reading falls back to Pending mid-run, and the end of the run marks everything Completed anyway — so under-confirming corrects itself. Over-confirming is the one that lies, and it lies about work the robot has not done.
|
|
1002
|
-
|
|
1003
|
-
Crossing a room early does not disqualify it either: confirmation is about the current visit, so a room crossed at the start and cleaned properly later still completes.
|
|
1004
|
-
|
|
1005
|
-
3 tests, 2 of which fail against the old behaviour.
|
|
1006
|
-
|
|
1007
|
-
## 3.19.6
|
|
1008
|
-
|
|
1009
|
-
**The periodic poll no longer carries a cloud firmware check whose answer nothing reads. The check has in fact never run — a missing `await` disabled it — and the tempting one-character repair would have added roughly 480 discarded cloud round-trips per robot per day.**
|
|
1010
|
-
|
|
1011
|
-
`checkForNewFirmware()` sat in the poll chain and asked the Roborock cloud for `ota/firmware/<duid>/updatev2` once per robot per poll interval. Its own gate read `const isLocalDevice = !this.isRemoteDevice(duid)`, and `isRemoteDevice` is `async`. Negating a promise is false for every promise there has ever been, so the gate never opened and the body behind it has been unreachable for the life of this plugin. Every other caller of that method awaits it; this one line did not.
|
|
1012
|
-
|
|
1013
|
-
Adding the missing `await` was the obvious repair and it was the wrong one. The request's result goes to `setObjectNotExistsAsync`, which is a documented no-op here, and to `setStateAsync("Devices.<duid>.updateStatus.<field>")`, which nothing in the plugin, the settings UI or the diagnostics export ever reads. The firmware revision Apple Home shows comes from a different source entirely — the `fv` field in HomeData. So the repair would have bought an awaited HTTPS round-trip on the poll thread, per robot, every three minutes, for an answer with no reader, plus a `catchError` path able to log a warning once per poll whenever the OTA endpoint was unhappy. That is the same never-throttled retry loop that 3.19.0, 3.19.1 and 3.19.5 each closed one instance of, and it would have been introduced rather than found.
|
|
1014
|
-
|
|
1015
|
-
The call and the unreachable method are removed instead. Because the code could never execute, this changes no runtime behaviour on any robot: the same requests go out, the same values reach Apple Home, and nothing that had a reader lost one.
|
|
1016
|
-
|
|
1017
|
-
What is new is the guard. A test now pins the outcome rather than the implementation — a periodic poll may spend no cloud round-trip on an answer no one can read — and it was verified by applying the naive `await` and watching it go red at exactly 480 requests per simulated day.
|
|
1018
|
-
|
|
1019
|
-
## 3.19.5
|
|
1020
|
-
|
|
1021
|
-
**A room-list refresh that cannot succeed is no longer re-attempted by every periodic poll. On a Q10 it is not attempted at all, and on a Q7 whose map channel is down it now backs off instead of running 480 guaranteed-to-fail map reads a day.**
|
|
1022
|
-
|
|
1023
|
-
Room names on B01 robots come from the map channel, and because rooms rarely change a successful fetch is good for six hours. That six-hour stamp was written only on success — correct for the happy path, and the whole story for a refresh that never completes one. A robot that cannot answer never closes the throttle, so it was asked again by every periodic poll for as long as the plugin ran.
|
|
1024
|
-
|
|
1025
|
-
**On a Q10 (`ss*`) the answer could never arrive.** `get_map_list` has no Q10 translation, so the send choke point refuses it by name before anything reaches the wire. That refusal is correct and is caught quietly at debug level, but it is certain before the request is made, and it was being repeated every three minutes forever. This is the third loop of that exact shape: the status loop was gated in 3.19.0 and the live-room loop in 3.19.1, and this one was missed both times. It is now gated at the same place and for the same reason — at the function entry, because both call sites reach it through a check that matches _both_ B01 dialects.
|
|
1026
|
-
|
|
1027
|
-
**On a Q7 (`sc*`) the request is not refused, so the same missing guard cost real work.** A robot whose map channel is down ran a `get_map_list` on the wire plus a map read that waited out its full 20-second timeout, once per poll cycle — roughly 480 attempts a day for a room list that was not going to arrive, and in the uncached case it delayed the rest of the poll chain by that timeout each time.
|
|
1028
|
-
|
|
1029
|
-
Repeated failures now widen the gap, following the same rule the live-room fetch already used. The first failure is deliberately not slowed, so a single lost frame on a healthy channel still costs nothing. Past that the gap doubles from two poll cycles and is capped at 30 minutes, which stays far below the six-hour success cadence — a channel that comes back is picked up within the same half hour rather than at the next scheduled refresh. A success clears the accumulated penalty outright.
|
|
1030
|
-
|
|
1031
|
-
A reply that arrives but reports no current map is explicitly not counted as a failure. That is a robot still building its first map, and it must not be asked ever more rarely precisely while the answer is about to become available.
|
|
1032
|
-
|
|
1033
|
-
This changes retry timing only. No new request is introduced, and nothing is published to Apple Home that was not published before.
|
|
1034
|
-
|
|
1035
|
-
## 3.19.4
|
|
1036
|
-
|
|
1037
|
-
**An unmapped `error_code` is now logged with the state the robot was in when it appeared, because these codes turn out to describe transitions rather than faults — and a bare number cannot be reported usefully or mapped later.**
|
|
1038
|
-
|
|
1039
|
-
When a robot reports an `error_code` this plugin has no entry for, nothing is published to Apple Home and the number is named once in the log so it can be reported and mapped. That behaviour is unchanged and correct. What was missing was the context that makes the number mean anything.
|
|
1040
|
-
|
|
1041
|
-
Three distinct unmapped codes appeared on two B01/sc05 robots in a single log during one ordinary scheduled run, and each one was tied to a state change: 2105 only while docked and charging at 100%, 2110 one second after the run started, 2104 on the way back to the dock. Nothing was wrong with any of the robots — the run started, ran and docked normally. Establishing that required hand-correlating each code against the neighbouring publish lines across a 2574-line log. Someone filing a model report pastes the one line, so that context never arrived with it.
|
|
1042
|
-
|
|
1043
|
-
The line now names the robot's own state number and the Matter operational state derived from it. It also makes its own question answerable: "if the robot really is in trouble right now" cannot be judged from a number alone, and it was being asked while a robot was demonstrably cleaning normally.
|
|
1044
|
-
|
|
1045
|
-
The once-per-code-per-robot-per-run rule is deliberately unchanged. Keying the de-duplication on code _and_ state would name a lingering code again at every transition it survived, which is the per-poll log burial that rule exists to prevent.
|
|
1046
|
-
|
|
1047
|
-
No mapping is claimed for 2104, 2105 or 2110. They are still published as nothing, which remains the right answer for a number nobody has explained.
|
|
1048
|
-
|
|
1049
|
-
## 3.19.3
|
|
1050
|
-
|
|
1051
|
-
**A periodic cloud snapshot could overwrite live status with its own slightly older view, so a robot mid-clean briefly showed as docked. Reported with a measured sequence by [@jbyhb](https://github.com/jbyhb) in [#20](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/20).**
|
|
1052
|
-
|
|
1053
|
-
During an active clean an S7 published 99% / running, a periodic HomeData refresh then published 88% / stopped, and the next live frame restored 99% / running one second later. No robot loses and regains 11% of its battery in a second, so the two readings were not a sequence of events — they were two views of the same moment, and the slower one won.
|
|
1054
|
-
|
|
1055
|
-
**The cause is that HomeData was being promoted into the live cache.** Status reads prefer the live cache while it is fresh and fall back to the cloud snapshot once it is not, which is the right order. But parsing a HomeData poll into that cache also stamped it with the current time, so the poll presented itself as the newest live reading. Its values then took precedence over the genuinely newer transport frames until the next one arrived — and the freshness window it reset is the same one that decides whether live data is trusted at all.
|
|
1056
|
-
|
|
1057
|
-
HomeData is no longer written into the live cache. It stays what it is: the fallback. The 15-minute staleness window is unchanged, so if live reporting genuinely goes quiet the snapshot still takes over and the tile still self-heals — that path is now pinned by a test that advances the clock past the window and asserts the snapshot wins, so this fix cannot quietly turn into a stale tile pinned forever.
|
|
1058
|
-
|
|
1059
|
-
This corrects reporting only. It adds no command path and cannot move the robot.
|
|
1060
|
-
|
|
1061
|
-
Troubleshooting documentation for the Apple Home "Updating…" tile is revised again, and this time it makes the remedy stronger rather than weaker. The reporter in [#7](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/7) clarified when his tile recovered during six months of chasing it: only ever immediately after installing an iOS release, perhaps twice, and never spontaneously. Installing a release restarts the device. So the recoveries that read as version fixes are the same restart step 1 already recommends, and the page now says so — including the part that matters to anyone living with it, which is that a restart buys days to a week rather than a permanent fix.
|
|
1062
|
-
|
|
1063
|
-
## 3.19.2
|
|
1064
|
-
|
|
1065
|
-
**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.**
|
|
1066
|
-
|
|
1067
|
-
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.
|
|
1068
|
-
|
|
1069
|
-
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.
|
|
1070
|
-
|
|
1071
|
-
**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.
|
|
1072
|
-
|
|
1073
|
-
**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.
|
|
1074
|
-
|
|
1075
|
-
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.
|
|
1076
|
-
|
|
1077
|
-
**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.
|
|
1078
|
-
|
|
1079
|
-
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.
|
|
1080
|
-
|
|
1081
|
-
## 3.19.1
|
|
1082
|
-
|
|
1083
|
-
**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.**
|
|
1084
|
-
|
|
1085
|
-
3.19.0 stopped the dedicated B01 status loop asking a Q10 (`ss*`) for `get_status`. The live-room loop has the same shape and was missed. It sends `get_map_list`, which has no Q10 translation and is not answered neutrally, so the send choke point refuses it by name and throws — correctly. The catch then counted that refusal as a failure and logged `Live-room map fetch has failed N times in a row` at warn level every fifth one.
|
|
1086
|
-
|
|
1087
|
-
It reached a Q10 because `refreshLiveRoomForDevice` gates on `pv === "B01"`, and `pv === "B01"` is both dialects — which is the entire premise of [#19](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/19). The Matter accessory drives it whenever the robot is in a cleaning run and `enableMatterServiceArea` is not false, and that setting defaults to on.
|
|
1088
|
-
|
|
1089
|
-
**That timing is what makes it worth a release on its own: the live-room loop only runs while the robot is actively cleaning.** The first two attempts are 10 seconds apart before the backoff widens, so the first warning lands roughly two and a half minutes into a clean — during the exact operation [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14)'s reporter had just confirmed working on the only Q10 in the field. A fourth round of chasing this plugin's own designed refusal was one clean away.
|
|
1090
|
-
|
|
1091
|
-
A Q10 is now skipped before the request is built, and says once per robot that the dialect sends no reply to a map read, so no room is reported during a clean. No state entry is allocated and `null` is returned — the same answer the disabled-tracking branch already gives, so every caller already handles it. The gate sits at the loop's entry rather than at its two call sites, because one of those call sites is the `pv === "B01"` test that caused this.
|
|
1092
|
-
|
|
1093
|
-
A Q7 is unchanged and pinned as such: it is still fetched, an unrecognised B01 model is still treated as a Q7, and a Q7 that genuinely stops answering still raises the warning. The warning is kept off a robot that cannot answer by design, not removed.
|
|
1094
|
-
|
|
1095
|
-
The 6-hourly room-name refresh also sends `get_map_list` and is also refused on a Q10, but it logs at debug and is left alone. It wastes a request; it does not tell anyone their robot is failing.
|
|
1096
|
-
|
|
1097
|
-
## 3.19.0
|
|
1098
|
-
|
|
1099
|
-
**The B01 Q10 command dialect has now been run on a Q10, and it works. That measurement is the whole reason this goes to `latest`.**
|
|
1100
|
-
|
|
1101
|
-
[@niclasreich](https://github.com/niclasreich) installed `3.19.0-beta.1` on his Q10 S5 (`roborock.vacuum.ss07`) and reported on 27 August 2026 that the robot starts cleaning from both the Homebridge interface and the Home app, and that commands are accepted. Every datapoint code in the dialect was read from python-roborock's docstrings rather than measured here, and the beta shipped saying so; his robot is the first `ss*` device this project has ever had a result from. Commands, return to dock and the state the tile shows while returning all behave as the dialect predicts.
|
|
1102
|
-
|
|
1103
|
-
Nothing in the command path changed between the beta and this release. It is the same code with a field result behind it, which is the only thing the beta was waiting for. Details of what the dialect covers are in the `3.19.0-beta.1` notes below.
|
|
1104
|
-
|
|
1105
|
-
**A Q10 is no longer polled for status, and no longer reports its own by-design refusal as a failure.** The dedicated B01 status loop asked every Q10 for `get_status` every 25 seconds. The send choke point refused each one correctly — a fire-and-forget dialect cannot answer a read — but a refused request was still a counted one: each refusal incremented the consecutive-failure tally and every tenth logged `B01 status has failed N times in a row` at warn level. On a healthy Q10 that is a warning every four minutes about a robot doing exactly what its protocol says. It was the first thing the beta put in the one field log this project has, and a diagnostic that fires on a working robot is worse than none — it is the same false alarm that made [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14) take three rounds to diagnose. The loop now skips a Q10 before the request is built and states once, per robot, that the dialect sends no reply, that state comes from home data over HTTPS instead, and where the remaining work is tracked. Asking a question whose refusal is certain before it is asked was never a diagnostic.
|
|
1106
|
-
|
|
1107
|
-
A Q7 is unchanged and pinned as such: it is still polled, its answer is still mapped and dispatched, an unrecognised B01 model is still treated as a Q7, and a Q7 that genuinely stops answering still raises the warning. The warning is kept off a robot that cannot answer by design, not removed.
|
|
1108
|
-
|
|
1109
|
-
**Known and not fixed by this release: a Q10's tile does not follow the robot into a clean.** State on a Q10 still comes from the home data snapshot, so docked and charging read correctly while a run in progress does not. Reading state from the datapoint updates the robot pushes is the remaining half of [#19](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/19). It is deliberately not in this release: it is the one change that has to touch the incoming MQTT path that Q7 and Q10 share, and bundling it with the promotion of a command path that has exactly one field measurement behind it would risk three working Q7 robots to save a release.
|
|
1110
|
-
|
|
1111
|
-
A cloud request that times out without anything having arrived during it now says what its message total actually counts. It used to end with a bare `(8 since startup)`, and that figure is incremented in the MQTT receiver alone — a robot answering on the local socket never touches it, while the 180-second poll chain a reader would compare it against runs over whichever transport is up.
|
|
1112
|
-
|
|
1113
|
-
Measured on an S8 Pro Ultra (`roborock.vacuum.a70`) on 27 August 2026: one transient cloud timeout reported `(8 since startup)` after eight and a half hours of polling. Read as a like-for-like ratio that says a link dropping about 95 % of replies; read correctly it says nothing is wrong, because the robot had been answering locally the whole time. The sentence now states that the total covers cloud traffic only and that a low number on a locally-answering robot is normal. A diagnostic that exists to stop wrong conclusions should not hand the reader a ratio that cannot be taken.
|
|
1114
|
-
|
|
1115
|
-
## 3.19.0-beta.1
|
|
1116
|
-
|
|
1117
|
-
**Beta channel only — `npm install homebridge-roborock-matter@beta`. This does not go to `latest` and no existing installation will pick it up.** It contains the first implementation of the B01 Q10 command dialect, and there is no Q10 robot available to this project to verify it on. Every datapoint code in it is read from python-roborock, where the docstrings mark them verified live against `ss07` hardware, but none of it has been measured here. Shipping that to everyone on `latest` on the strength of someone else's docstrings would be the same mistake [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14) already cost three rounds of wrong diagnosis.
|
|
1118
|
-
|
|
1119
|
-
3.18.0 stopped this plugin sending Q7 frames to a Q10, which the robot discards, and refused instead. That made the plugin honest on a Q10; it did not make one work. This is the command half of [#19](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/19).
|
|
1120
|
-
|
|
1121
|
-
A Q10 command is now published in the dialect the robot speaks — a direct write to a numbered datapoint, `{"dps":{"201":1}}`, with no method name, no `msgId` and no datapoint 10000. Start, stop, pause, return to dock, empty the dustbin, room clean, suction level and clean mode are covered.
|
|
1122
|
-
|
|
1123
|
-
**The dialect is fire-and-forget, and that shapes the rest of the change.** A Q10 sends no RPC reply at all, so a Q10 request no longer registers a pending request or arms a timeout. Arming one would guarantee expiry on a perfectly healthy link, and reporting that expiry is exactly the false "the Roborock cloud has gone silent" that sent #14's reporter chasing a fault at Roborock. A command resolves when the write leaves the plugin, which means published and not acknowledged; the state Apple Home shows still comes from the optimistic-state machinery, which corrects itself against what the robot reports.
|
|
1124
|
-
|
|
1125
|
-
**Reads stay refused on a Q10, and that is the dialect rather than unfinished work.** `get_status`, `get_map_list` and `get_prop` exist to return a value, and a protocol that never answers cannot return one. Serving them would hand the caller something the robot never sent and publish that non-answer to Apple Home as the robot's state. A Q10's status continues to come from home data over HTTPS, a separate transport measured working in #14. Reading state from the datapoint updates the robot pushes is the remaining half of #19.
|
|
1126
|
-
|
|
1127
|
-
Two known limitations, stated rather than papered over. Resume restarts a whole-home clean instead of resuming, because the plugin has no distinct resume method to map onto the dialect's dedicated `205`. And `find_me` is refused: upstream has a datapoint number for it but no verified payload, and guessing one on hardware nobody here owns is how #14 happened.
|
|
1128
|
-
|
|
1129
|
-
The two clean-type tables are not interchangeable and neither are the two suction scales — Q7 is vacuum=0, vac+mop=1, mop=2 while Q10 is vac+mop=1, vacuum=2, mop=3, and Max+ is 5 on a Q7 and 8 on a Q10. The numbers overlap, so the wrong table does not fail loudly; it mops when it was asked to vacuum. Both directions are pinned by tests.
|
|
1130
|
-
|
|
1131
|
-
Q7 is unchanged. Three `sc*` robots run on the maintainer's own bridge, and the regression cover asserts that a Q7 still builds the RPC envelope on datapoint 10000, still arms its timeout, still registers its pending request, and that nothing addressed to a Q10 can ever carry the Q7 form — checked against the whole B01 method surface rather than a list of today's commands, with a guard that fails if that check ever becomes vacuous.
|
|
1132
|
-
|
|
1133
|
-
## 3.18.0
|
|
1134
|
-
|
|
1135
|
-
**`B01` is two wire protocols, this plugin only implements one of them, and it has been sending the wrong one to Q10 robots.** This is the root cause behind [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14), where [@niclasreich](https://github.com/niclasreich)'s Q10 S5 (`roborock.vacuum.ss07`) could not run a single command. The two diagnostic releases before this one, 3.17.7 and 3.17.8, both measured the symptom correctly and both pointed at Roborock's cloud. The cause was here.
|
|
1136
|
-
|
|
1137
|
-
Robots reporting `pv === "B01"` fall into two families that share the 23-byte framing and the AES-128-CBC payload encryption but not the request format. The Q7 series (`sc*`) carries an RPC envelope on datapoint 10000, with a method name and a `msgId` to correlate the reply against. The Q10 series (`ss*`) writes numbered datapoints directly — no method, no `msgId`, no datapoint 10000 — and does not reply at all, because the dialect is fire-and-forget.
|
|
1138
|
-
|
|
1139
|
-
This plugin built the Q7 envelope for every B01 device. On a Q10 that means every command ever sent was a well-formed, correctly encrypted frame addressed to a datapoint the robot does not have, which it discards, followed by the plugin waiting out a full timeout for an acknowledgement the protocol never sends. `b01FamilyForModel()` has classified `ss*` as Q10 since 3.15.5, but that flag only ever reached the suction scale and the fault tables — never the wire format. Getting the enums right for a dialect that is never spoken correctly bought nothing.
|
|
1140
|
-
|
|
1141
|
-
Q10 commands are now refused at the send choke point, immediately and by name, instead of being published in a form the robot cannot read. The refusal is classified as transient, so it is throttled like any other and does not arrive as a plugin error with a stack. Methods that are answered without touching the wire are unaffected, which keeps the room-mapping fix from 3.17.3 intact — the timeout the same reporter originally opened #14 about.
|
|
1142
|
-
|
|
1143
|
-
This makes the plugin honest on a Q10 rather than functional on one. It is a smaller change than it sounds and deliberately so: Q7 and Q10 share this code path, Q7 devices work, and implementing an unverifiable Q10 dialect in the same change that could regress them is a bad trade. Real Q10 support — the dialect encoder, the datapoint tables, a fire-and-forget command path, and status from pushed datapoint updates — is tracked in [#19](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/19).
|
|
1144
|
-
|
|
1145
|
-
Q7 behaviour is unchanged and covered by regression tests asserting that `sc*` models, and any unrecognised B01 model, still publish exactly as before. Two comments in `b01Q7Adapter.js` that listed `ss07` as a Q7 model have been corrected; that claim is what made #14 take three rounds to diagnose.
|
|
1146
|
-
|
|
1147
|
-
## 3.17.8
|
|
1148
|
-
|
|
1149
|
-
**The cloud-timeout diagnostic added in 3.17.7 drew a conclusion it could not support, and this corrects it.** Found by [@niclasreich](https://github.com/niclasreich) acting on it in [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14): he updated, read the new sentence, and reported the answer it gave — that no reply had ever arrived from his Q10 S5.
|
|
1150
|
-
|
|
1151
|
-
When no message had been attributed to a robot, 3.17.7 stated that "the reply never arrived rather than arriving unrecognised". That only follows if every inbound frame is either counted or logged where a user can see it, and one path is neither. The MQTT receiver drops a message before the counter whenever its topic matches no known robot, and that path logs at debug. A reply arriving on a topic the plugin cannot attribute is precisely "arriving unrecognised" — the one possibility the sentence ruled out. The other two ways a frame can be discarded are already visible, so they were never the gap: an undecodable message logs at error, and a missing local key warns once.
|
|
1152
|
-
|
|
1153
|
-
Frames that match no known robot are now counted account-wide — attribution is what failed, so there is no robot to file them under — and a cloud timeout reports total silence only when nothing arrived on any topic. If unattributed frames did arrive, the timeout now says so and names it as a fault on this side, because that is the difference between a robot that never answers and one whose answers this plugin is throwing away. An adapter that cannot supply the count states the observation and draws no conclusion at all.
|
|
1154
|
-
|
|
1155
|
-
Diagnostics only, as in 3.17.7. No change to when requests are sent, how long they wait, or what is retried, and a local timeout remains untouched.
|
|
1156
|
-
|
|
1157
|
-
## 3.17.7
|
|
1158
|
-
|
|
1159
|
-
**A cloud request that times out now says whether the robot answered at all.** Raised by the log [@niclasreich](https://github.com/niclasreich) posted in [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14), where every single Roborock request to a Q10 S5 timed out — status polls, the clean-type write, the start command — while the MQTT connection state read `true` throughout.
|
|
1160
|
-
|
|
1161
|
-
That combination has two causes needing opposite responses. Either no reply ever arrived, which points at the robot or the account, or a reply did arrive and could not be matched to its request, which points at a correlation bug in this plugin. The plugin already knew which: the MQTT receiver attributes every message it decodes to a device. But the three paths that could have said so — an unmatched topic, an unsolicited message, an ordinary cloud reply — all log at debug, so the fact never reached the warning a user reports. Two rounds of questions went by without it.
|
|
1162
|
-
|
|
1163
|
-
A cloud timeout now names what the link did while the request was outstanding: nothing at all since startup, some messages while this request was pending, or messages earlier but none during it. The counter is incremented only after a message has been attributed to a robot _and_ decrypted, so a link delivering only garbage cannot read as alive — an undecodable message already logs its own error and is deliberately not counted.
|
|
1164
|
-
|
|
1165
|
-
Diagnostics only. No change to when requests are sent, how long they wait, or what is retried; a local timeout is untouched, because MQTT receipts say nothing about a local socket.
|
|
1166
|
-
|
|
1167
|
-
## 3.17.6
|
|
1168
|
-
|
|
1169
|
-
**Optional HomeKit switches keep the name and the place in Home you gave them across a Homebridge restart.** Two independent reports of the same class of fault, from the two people using these switches most, and both are fixed here by their own pull requests.
|
|
1170
|
-
|
|
1171
|
-
**The Empty Bin switch.** Reported by [@pponce](https://github.com/pponce) in [#16](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/16) and fixed by [@jbyhb](https://github.com/jbyhb) in [#17](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/17), who had independently hit it on an S7. A renamed switch reverted to its generated name on restart, and one removed from Home View came back — the second symptom being the useful one, because it says HomeKit watched the accessory leave and return rather than merely redraw.
|
|
1172
|
-
|
|
1173
|
-
The cause is a question asked one poll too early. Whether a robot can auto-empty is read from its dock type, and some classic robots — the S7 among them — omit that capability from the account data and report the attached dock only in the first live status poll. Startup reconciliation ran before that poll arrived, read the absence as "this robot has no auto-empty dock", and unregistered the cached switch; the first `get_status` then supplied `dock_type: 1` and the capability update built it again. That unregister/register pair is a new accessory as far as HomeKit is concerned, and a custom name, a room and a Home View placement all belong to the old one. The reporter's second robot never showed the fault because its dock capability _is_ in the account data, so its switch was never in doubt.
|
|
1174
|
-
|
|
1175
|
-
An already-cached Empty Bin switch is now preserved through that short window where the dock is not yet known, and only through it. Nothing new is published for a robot whose dock is genuinely unsupported, and once a live capability update does arrive for a robot it is authoritative — a supported dock keeps the same accessory identity, an unsupported one still removes the switch. Both halves of that rule are pinned by a test.
|
|
1176
|
-
|
|
1177
|
-
**The schedule switches.** Reported and fixed by [@pponce](https://github.com/pponce) in [#15](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/15), found in live use of the schedule support he contributed in 3.16.0. A custom schedule name did not survive a restart and the schedule group tile was recreated.
|
|
1178
|
-
|
|
1179
|
-
One teardown path was serving two unrelated purposes. Stopping schedule work at a normal Homebridge shutdown and deliberately withdrawing the switches — because the feature was turned off, or the robot removed — ran the same code, and that code removed the Switch services and rewrote the cached accessory topology. For the shutdown case that discarded HomeKit's side of every schedule switch on the way out, every time. The two are now separate: a normal shutdown stops in-memory work and leaves the services exactly as they are, while intentional withdrawal still clears them.
|
|
1180
|
-
|
|
1181
|
-
Names and identity are also now kept apart. A schedule service's identity stays its schedule-ID-derived subtype, its generated display name is derived from the vacuum, and a name you set in Home is preserved across refreshes, schedule renumbering and restarts — while a blank or whitespace-only name is repaired to the generated one. A schedule service is removed only when a refresh that is known to have succeeded no longer lists its ID; a failed or untrusted refresh changes nothing, and a schedule merely switched off stays present and off.
|
|
1182
|
-
|
|
1183
|
-
No change to cloud request behaviour: no new polling, and the existing cache, coalescing, backoff and post-write verification are untouched. Verified in the field by the reporter across 15 schedule services on two vacuums, including a custom Home name surviving a restart and enable/disable round-tripping to the Roborock app both ways.
|
|
1184
|
-
|
|
1185
|
-
## 3.17.5
|
|
1186
|
-
|
|
1187
|
-
**A network outage no longer takes the Roborock account offline until Homebridge is restarted.** Found on the maintainer's own server, which lost DNS for about 75 minutes on 25 August 2026. Every other plugin on that bridge recovered by itself — the Tado platform was making successful API calls again 35 minutes after its last name-resolution error. This one did not. It logged `B01 status has failed 1070 times in a row … the Roborock cloud connection is not available` continuously for **1 hour and 44 minutes after the network was healthy again**, through three scheduled hourly reconnects, and came back only when a plugin update restarted the child bridge — instantly, on the very same saved session, which is what ruled out the credentials and pointed at the reconnect itself.
|
|
1188
|
-
|
|
1189
|
-
The reconnect tore the client down without forcing it. An unforced teardown waits for the MQTT client's outgoing queue to drain first, and a link that has just died still holds messages nobody will ever acknowledge, so that wait never ends. The teardown therefore never completed, the client stayed permanently marked as disconnecting, and a reconnect declines to act on a client in that state. The latch also fed itself: each later teardown returned early on the same flag, so every hourly retry after the first was a silent no-op. That is why three hours of log carried no MQTT error, no close and no connect — nothing was happening at all.
|
|
1190
|
-
|
|
1191
|
-
Reconnects are now forced, which is the only correct behaviour on the single path that calls them: there is no reason to wait for a queue to drain over a connection already concluded to be dead. The upstream detail the fix depends on is pinned by a test, so if the MQTT library ever changes that contract the suite reports it rather than the field doing so.
|
|
1192
|
-
|
|
1193
|
-
## 3.17.4
|
|
1194
|
-
|
|
1195
|
-
**The "no mapping for these fields" warning no longer asks you to report fields this plugin already maps.** Raised by the log [@jcoz00](https://github.com/jcoz00) posted in [#6](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/6), whose Qrevo CurvX was told the plugin has no mapping for **eighteen** `get_status` fields and that a GitHub model report quoting the line is how they get added. Fifteen of those eighteen are named in `deviceFeatures.js` already. There was nothing for a report to add, and the three fields that genuinely were news sat buried in a list of fifteen that were not.
|
|
1196
|
-
|
|
1197
|
-
The message was asking the wrong question. A robot's status table starts as a copy of the plugin's baseline and capability detection adds to it, so "this field is not switched on for **this robot**" and "this plugin has never heard of this field" are different questions — and only the second one is worth a user's time. The warning asked the first and reported the answer as the second.
|
|
1198
|
-
|
|
1199
|
-
Each case now says what it means. A field no table anywhere names is still warned about once, by name and value, and still worth a model report; for the CurvX that is three fields rather than eighteen. A field the plugin maps but this robot's capability gate did not switch on is a debug line that says so, once per field per robot, and does not point anyone at GitHub. The repeat line that followed it on every subsequent poll is gone for that case — fifteen lines a minute saying nothing the first one did not.
|
|
1200
|
-
|
|
1201
|
-
**No change for [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8), and the first draft of this entry said otherwise.** The nine fields [@skmzwanke](https://github.com/skmzwanke)'s Saros 10 reported went into the baseline table in 3.4.4, which means they have been recognised for every robot ever since and that warning has been quiet for months. What made them look outstanding was a test fixture that mocked them as unmapped — a fiction the old code could not detect and the new code can. The fixture is corrected; the shipped behaviour for that robot is unchanged.
|
|
1202
|
-
|
|
1203
|
-
Nothing about which fields are read, published or acted on changed — this release changes only what the log claims. The declared set of capability-installable fields is derived from the source by a test that scans for every writer, so a new capability cannot reintroduce the wrong warning without the suite failing.
|
|
1204
|
-
|
|
1205
|
-
## 3.17.3
|
|
1206
|
-
|
|
1207
|
-
**Q7- and Q10-series robots no longer spend a cloud request per poll on an answer the plugin cannot read.** Reported with a diagnostics export by [@niclasreich](https://github.com/niclasreich) in [#14](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/14), whose Q10 S5 (`roborock.vacuum.ss07`) logged `Failed to execute get_room_mapping … method prop.get timed out after 10 seconds` while the MQTT connection was reported as up.
|
|
1208
|
-
|
|
1209
|
-
The two method names in that line disagree, and that was the clue. `get_room_mapping` is the caller's label; `prop.get` is what actually went on the wire. The classic room-mapping routine opens by fetching `get_status` in order to read `map_status` and derive a floor number — and on these robots `get_status` translates to a real `prop.get`. `map_status` is a v1-only field that a Q7/Q10 status dictionary has never carried, so the reply could not have been used whatever it said. The request itself was already answered locally from the dialect's neutral table without touching the network, which is exactly why the existing skip did not catch this: the harmless call was making a second, expensive one.
|
|
1210
|
-
|
|
1211
|
-
The classic flow is now skipped outright for these robots, which is where their room data was never coming from in the first place — it arrives over the protobuf map channel. That removes one cloud round-trip per poll cycle per robot, along with the `No room mappings returned` notice and the empty room-list announcement that repeated at the same rate. Robots on the classic protocol are unaffected and still read `map_status` exactly as before.
|
|
1212
|
-
|
|
1213
|
-
**This does not by itself explain a robot that ignores commands from Apple Home**, which is the other half of that report; it removes a wasted request and the misleading error line it produced.
|
|
1214
|
-
|
|
1215
|
-
## 3.17.2
|
|
1216
|
-
|
|
1217
|
-
**The Qrevo CurvX's dock can now offer the Empty Bin switch.** Reported with a diagnostics export, and then settled by hand, by [@jcoz00](https://github.com/jcoz00) in [#6](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/6). His a185 reports `dock_type: 20`, and the dock table this plugin inherited stops at 9 — so the CurvX fell through to "unknown dock" and was treated as having no auto-empty capability, which kept the optional Empty Bin switch added in 3.17.0 from ever being offered for it. Dock type 20 is now a named, recognised auto-empty dock.
|
|
1218
|
-
|
|
1219
|
-
The switch is still opt-in and still off by default, so nothing changes for anyone who has not asked for it.
|
|
1220
|
-
|
|
1221
|
-
**Only the auto-empty is granted, and only because its owner confirmed it.** Upstream also names a dozen dock codes above 9 that this project has had no report for, and none of those was added — a capability granted on a table alone is what cost a Q Revo S owner a suction level its robot does not have in [#10](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/10). Wash and dry are unconfirmed on this dock and stay unclaimed. A test now pins both halves: dock type 20 is in the set because an owner said so, and the codes nobody has reported stay out until one does.
|
|
1222
|
-
|
|
1223
|
-
## 3.17.1
|
|
1224
|
-
|
|
1225
|
-
**Closing the plugin's settings page could print a Node crash dump into your Homebridge log.** Reported with the log to prove it by [@jcoz00](https://github.com/jcoz00) in [#6](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/6). The Homebridge UI runs the settings-page server as a child process and closes its IPC channel the moment the page goes away; every reply that server sends is a `process.send()`, including the `ready()` handshake it fires before serving a single request. A send that loses the race against that close is reported asynchronously as an unhandled `'error'` event, which is fatal — so a closed settings page ended in `Error: write EPIPE`, a stack trace and a `Node.js v24.19.0` banner in the log. Nothing was broken and nothing in the log said so. A dead channel now ends that child process quietly; every other error stays exactly as loud as it was.
|
|
1226
|
-
|
|
1227
|
-
**A robot's dock capability is announced when it changes, instead of on every poll.** `dock_type` rides along in nearly every `get_status`, and 3.17.0 told the platform about it each time, re-running the HomeKit action-switch sync roughly once a minute per robot. At default settings that sync returns immediately, but anyone who had switched the Empty Bin action on for a robot whose dock cannot auto-empty collected a `Not publishing the Empty Bin switch…` debug line every minute per robot — enough to shorten the useful reach of the debug log. Detection itself still runs on every poll; only the announcement is gated, and a dock type that genuinely changes is still announced.
|
|
1228
|
-
|
|
1229
|
-
## 3.17.0
|
|
1230
|
-
|
|
1231
|
-
**Compatible auto-empty docks can now expose an optional Empty Bin action switch in Apple Home.** Contributed by [@jbyhb](https://github.com/jbyhb) in [#13](https://github.com/mathiashornbek/homebridge-roborock-matter/pull/13). It uses the same opt-in HomeKit action-switch bridge as Start, Dock, Pause and Find, appears only when the robot reports dust-collection support, and sends the dock's native `app_start_collect_dust` command through the normal confirmed command path. A cached status that does not show the robot docked is advisory rather than a hard gate: the robot is the authoritative judge and its refusal follows the existing command-error path.
|
|
1232
|
-
|
|
1233
|
-
**Live dock-type capability detection is fixed.** Found and fixed by [@jbyhb](https://github.com/jbyhb). The inherited status handler passed the literal field name `"dock_type"` to `processDockType()` instead of the numeric value reported by the robot, so that path never enabled any dock capability. Worse, under Homebridge the surrounding loop never reached the call at all: the branch sits behind an ioBroker object-database check that can only fail here, so it was dead code on top of a wrong argument. The live value now reaches the feature detector before that compatibility gate, allowing classic robots such as the S7 to expose capabilities their HomeData feature bits omit. A cross-layer test pins the auto-empty dock types so the API's early-discovery list and the command table cannot drift apart.
|
|
1234
|
-
|
|
1235
|
-
## 3.16.0
|
|
1236
|
-
|
|
1237
|
-
**The schedules you built in the Roborock app are now switches in Apple Home — the project's first feature from an outside contributor.** Contributed by [@pponce](https://github.com/pponce) over four review rounds, requested in [#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3).
|
|
1238
|
-
|
|
1239
|
-
A Roborock schedule has always been invisible to Apple Home. You could build a weekday morning clean in the Roborock app, but nothing in the Home app could see it, so no automation could suspend it while you were away — the only way to pause a schedule was to open the Roborock app and do it by hand.
|
|
1240
|
-
|
|
1241
|
-
Turn on **Add Home app schedule switches** and each robot gains a grouped `<robot> Schedules` accessory holding one switch per schedule on your account. Off disables that schedule on the Roborock side; on enables it again. Unlike the action switches these are not momentary — each one reports whether its schedule is currently active, so "is the weekday clean on?" is answerable from the Home app.
|
|
1242
|
-
|
|
1243
|
-
- **They enable and disable; they do not author.** Days, times, rooms and clean modes stay in the Roborock app, because that is where those settings live. A schedule deleted there takes its switch with it on the next refresh, and a new one gains a switch the same way.
|
|
1244
|
-
- **Switches are named positionally** — `<robot> Schedule 1`, `Schedule 2` — because the Roborock cloud does not give schedules names to borrow. Rename them in the Home app if the order is not enough.
|
|
1245
|
-
- **Off by default,** because turning it on adds accessories to your Home app. Like the action switches, these are HomeKit accessories on the plugin's child bridge and need their own pairing — not the robot's Matter code.
|
|
1246
|
-
|
|
1247
|
-
**A failed schedule refresh no longer deletes the tiles.** This is the part worth reading even if you never turn the feature on, because it is the failure mode the review rounds were spent on. An earlier revision unregistered the schedule accessory whenever the cloud request failed, and a transient Roborock timeout is not evidence that your schedules are gone. An accessory that disappears takes its room assignment, its name and every automation pointing at it with it, and none of that comes back when the next refresh succeeds. Three outcomes are now told apart:
|
|
1248
|
-
|
|
1249
|
-
- **The cloud answered and reported schedules** — sync them, nothing else.
|
|
1250
|
-
- **The cloud answered and reported no schedules** — the account genuinely has none, so removing the accessory is correct.
|
|
1251
|
-
- **The request failed** — keep the switches Homebridge restored from its cache and reattach their handlers, so they heal themselves on the next successful read instead of being torn down.
|
|
1252
|
-
|
|
1253
|
-
**A verification read can no longer join a refresh that started before the write it is verifying.** Writes are coalesced against in-flight refreshes, and a `verify()` that attached to an older request could observe pre-write state and roll a successful write back. The refresh now records when it started, and a verification only joins one started at or after its own write.
|
|
1254
|
-
|
|
1255
|
-
**Also in this release:** the plugin's own settings schema documented every one of its 26 options except the new one; that description now exists, and the feature is documented in the README alongside the action switches and sensors rather than only in the changelog.
|
|
1256
|
-
|
|
1257
|
-
## 3.15.5
|
|
1258
|
-
|
|
1259
|
-
**B01 is two protocol families, and this plugin treated them as one.**
|
|
1260
|
-
|
|
1261
|
-
`pv === "B01"` covers the Q7 series (`sc01`, `sc05`) and the Q10 series (`ss07`). They are not the same wire protocol, and everything B01 went through the Q7 tables. On a Q10 that meant:
|
|
1262
|
-
|
|
1263
|
-
- **Max+ sent `wind: 5`, a value that does not exist in its scale.** The Q7 scale is 1–4 with Max+ at 5; the Q10's is 0–4 with Max+ at **8**.
|
|
1264
|
-
- **The robot's own Max+ never came back.** An inbound `wind: 8` resolved to undefined, so the mode the user had actually selected never reached Apple Home.
|
|
1265
|
-
- **A finished clean looked like a fault.** Upstream confirms `501` on Q10 hardware as "cleaning completed, returning to the dock", firing after every task — but 501 is not in the Q7 informational set, so a Q10 would sit at a non-zero error code permanently. The 2 families are documented upstream as disagreeing about 500, 501, 503, 569 and 570.
|
|
1266
|
-
- **`wind: 0`, a genuine off level on the Q10, had nowhere to go**, because the Q7 has none.
|
|
1267
|
-
|
|
1268
|
-
Both scales are now read from python-roborock's own enums — `SCWindMapping` and `YXFanLevel` — rather than inferred. The family is derived from the model suffix, **anchored**: upstream uses an unanchored `if "ss" in model_part`, which would misroute any future model that merely contains those letters, and that is deliberately not copied. Anything unrecognised stays Q7, which is what every B01 device was treated as before, so no unknown model is made worse.
|
|
1269
|
-
|
|
1270
|
-
**Nobody has reported a Q10 yet.** This is a correctness fix ahead of the first one rather than a repair, and it is shipped now because the alternative is that the first `ss07` owner installs the plugin and finds a Max+ button that sends a value their robot has never heard of.
|
|
1271
|
-
|
|
1272
|
-
6 new tests, 5 of which fail against the single-family version.
|
|
1273
|
-
|
|
1274
|
-
Found by a systematic audit of this plugin's model tables against upstream — which also confirmed the plugin does **not** share 3 upstream defects worth naming, since the temptation on reading a reference implementation is to converge on it: an unknown model here grants no capabilities rather than silently becoming a top-of-range Qrevo MaxV; model matching is anchored rather than substring; and dock drying comes from the robot's own capability bit rather than being inferred by excluding 3 known dock types.
|
|
1275
|
-
|
|
1276
|
-
## 3.15.4
|
|
1277
|
-
|
|
1278
|
-
**Apple Home read "Roborock" and then "Roborock Qrevo S". The Model row no longer repeats the Manufacturer row.**
|
|
1279
|
-
|
|
1280
|
-
n0rt0nthec4t opened [#10](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/10) asking for two rows: Manufacturer "Roborock" and Model "Q Revo S". Both halves have now landed — the Manufacturer row was Homebridge's, fixed upstream in [homebridge/homebridge#3996](https://github.com/homebridge/homebridge/pull/3996) and out in `2.4.1-beta.3`, and the Model row was ours, given a real name in 3.15.0. Put together, they read as a duplicate, which is what he came back to say.
|
|
1281
|
-
|
|
1282
|
-
3.15.0 argued the opposite — that a name without the brand "reads as a bare SKU" — and that reasoning was wrong for a reason worth recording: there is no surface where this string appears without the manufacturer beside it. Apple Home's accessory details, the Homebridge UI and the HAP sensors' Model characteristic are all fed from `manufacturer` and `model` together, and the first is unconditionally "Roborock".
|
|
1283
|
-
|
|
1284
|
-
- The Model row now reads **"Qrevo S"**, **"S8 Pro Ultra"**, **"Saros 10R"** and so on, on all 34 known models.
|
|
1285
|
-
- **The name table is unchanged and still carries the brand.** Every entry is upstream `copystring/ioBroker.roborock`'s own `VacuumProfile.name` verbatim, and the cross-check that catches a name hung on the wrong robot compares in that form. The de-branding happens at the display edge, not in the data.
|
|
1286
|
-
- **A model with no upstream name is still shown as its raw code, untouched.** `roborock.vacuum.sc05` is the robot's own string, not a name this plugin composed, and shortening it would invent a model that does not exist.
|
|
1287
|
-
- Still display-only. Every poll profile, feature lookup, capability branch and `isSupportedDevice` call keys on the raw code, and the test that reads the source and fails if a model _comparison_ is ever fed a display value is unchanged.
|
|
1288
|
-
- No re-pairing. `ProductName` is a Fixed-quality Matter attribute, so it is never persisted and the new value applies on the next restart.
|
|
1289
|
-
|
|
1290
|
-
Also settled in #10, with no code change needed: the **Q Revo S offers Quiet, Balanced, Turbo and Max** and nothing above it, so `roborock.vacuum.a104` stays out of the Max+ suction list and no owner pays a re-pair for a level their robot does not have.
|
|
1291
|
-
|
|
1292
|
-
## 3.15.3
|
|
1293
|
-
|
|
1294
|
-
**An empty water tank told Apple Home a vacuum-only run was blocked, and Apple said so every 2 minutes.**
|
|
1295
|
-
|
|
1296
|
-
vp-debug12 confirmed in [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9) that 3.15.2 fixed the status line — the tile now reads "Limpiando" during a run instead of "Desplazándose" — and reported this in the same breath: a **"Fill the water tank"** notification, arriving every 2 minutes, on a robot set to **Vacuum**.
|
|
1297
|
-
|
|
1298
|
-
Apple does not draw the Matter fault attribute as a passive warning. It draws WaterTankEmpty as a **blocking** condition, and it says so in words:
|
|
1299
|
-
|
|
1300
|
-
> Rellena el depósito de agua
|
|
1301
|
-
> "Roborock Qrevo" empezará a limpiar cuando se llene el depósito de agua.
|
|
1302
|
-
>
|
|
1303
|
-
> _(Fill the water tank. "Roborock Qrevo" will start cleaning once the water tank is filled.)_
|
|
1304
|
-
|
|
1305
|
-
On a vacuum-only run every word of that is false. The robot is not waiting for water, it will not start cleaning when the tank is filled, and it needs nothing from anybody. This was never a question of when a warning is welcome — the plugin was asserting a block that did not exist, and Apple re-notifies about a block for as long as it stands.
|
|
1306
|
-
|
|
1307
|
-
**The fault is now published only when water is actually in play.** An empty tank on a vacuum-only run publishes `NoError`, so switching the tile to Vacuum stops the notifications rather than merely not starting them.
|
|
1308
|
-
|
|
1309
|
-
- "Vacuum-only" has to be something that was **said** — by the user picking a mode, or by the robot reporting one. The selection defaults to Vacuum on every restart and is not persisted, so treating that default as a choice would have silenced the tank warning on every robot until somebody happened to touch the mode picker.
|
|
1310
|
-
- Suction levels reduce to their base clean type, so Max Vacuum counts as vacuum-only like the rest of the family.
|
|
1311
|
-
- During a run the robot's own report wins, deliberately unlike the mode picker: a mode picked mid-run is not applied mid-run, so a robot physically mopping with an empty tank is still blocked whatever the picker shows. The mode picker reports intent; this reports what is happening to the floor.
|
|
1312
|
-
- A real robot fault is not swallowed with the tank — once the tank is ruled out, the robot's own error takes the attribute as usual.
|
|
1313
|
-
- The HomeKit **Water Tank Empty** contact sensor is unchanged and still reports the tank whatever the mode is. It states a fact and makes no claim about what the robot is going to do, and automations are built on it.
|
|
1314
|
-
|
|
1315
|
-
## 3.15.2
|
|
1316
|
-
|
|
1317
|
-
**3.15.1 only fixed the runs you start in Apple Home, and most runs are not.**
|
|
1318
|
-
|
|
1319
|
-
3.15.1 stopped a whole-home clean from telling Apple Home that the robot had not started yet. It worked — but it ran from the Matter start handler, which means it only ever fired for a clean somebody tapped in the Home app. A clean started **in the Roborock app**, by a **schedule stored in that app**, by the **button on the robot** or by a **voice assistant** never touches that handler. It arrives here as a status change and nothing else, so the progress list stayed empty — or stale all-completed from the last Home-app run — for the entire run, and Apple went on saying "Traveling to Room" / "Desplazándose" exactly as before.
|
|
1320
|
-
|
|
1321
|
-
That is very likely how [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) and [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9) start their robots, so for them 3.15.1 may well have changed nothing at all.
|
|
1322
|
-
|
|
1323
|
-
The asymmetry was the tell, and it is worth writing down as a habit: **finishing a run was already status-driven and starting one was not.** When one half of a pair reads the robot and the other half only reads commands, the half that reads commands is a bug waiting for a user who does not use commands.
|
|
1324
|
-
|
|
1325
|
-
- A robot that reports it is cleaning now announces the run's scope as `operating`, however the run was started.
|
|
1326
|
-
- The guard is the robot's **own** state, not the one shown on the tile. With extended operational states switched off, a dock washing the mop or emptying the bin is reported as `Running` — and a dock doing its chores must not claim the robot is cleaning every room in the house.
|
|
1327
|
-
- A room clean picked in Apple Home is never widened. Anything still `operating` or `pending` is a run that was already announced, with a narrower and better-known scope than this can produce.
|
|
1328
|
-
- `currentArea` still stays empty on a run of unknown position, and live map-position tracking still narrows the claim to one room the moment it resolves one.
|
|
1329
|
-
|
|
1330
|
-
## 3.15.1
|
|
1331
|
-
|
|
1332
|
-
**A whole-home clean told Apple Home the robot had not started yet, for the whole run.**
|
|
1333
|
-
|
|
1334
|
-
vp-debug12 reported it in [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9): the tile says "Desplazándose" — Moving — when the robot leaves the dock, and never changes to "Limpiando" while it cleans. skmzwanke reported the same thing in [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) months and many versions ago, in English: "Traveling to Room" for an entire run.
|
|
1335
|
-
|
|
1336
|
-
Apple's status line during a run comes from the Service Area progress list, not from the operational state. It reads "nothing is operating" as _the robot is on its way_. For a room clean the plugin marks the requested room operating and Apple says the right thing. For a **whole-home** clean it marked every room `Pending`, because the robots do not report which room they are physically inside — so Apple was told, truthfully in one sense, that the robot had started none of them, and said so for an hour.
|
|
1337
|
-
|
|
1338
|
-
2.3.1 already tried this. It moved a full clean from an empty list to an all-pending list hoping the label would improve, and said out loud that whether it did was up to Apple's renderer. It did not, and 2 users have now reported the same symptom against it.
|
|
1339
|
-
|
|
1340
|
-
Matter has 4 progress values and none of them means "in this run, exact position unknown", so both available encodings are imperfect: every area operating asserts the robot is in all of them, every area pending asserts it is in none of them. This release picks the one that is true at the only place a person looks. **The robot is operating; it is not on its way.**
|
|
1341
|
-
|
|
1342
|
-
- `currentArea` still stays null on a whole-home clean. No room is named that the plugin is not sure of, because naming the wrong room is worse than naming none.
|
|
1343
|
-
- Live map-position tracking still collapses the wide claim to the accurate single room the moment it resolves one, and the run still flips wholly to completed on the charger.
|
|
1344
|
-
- A room clean is unchanged.
|
|
1345
|
-
|
|
1346
|
-
## 3.15.0
|
|
1347
|
-
|
|
1348
|
-
**A Q Revo S owner asked for the accessory details to read like a native device. Half of that is fixed here; the other half is Homebridge's and is now filed.**
|
|
1349
|
-
|
|
1350
|
-
`accessory.model` has always been the raw string the robot reports itself as, so every surface that shows a model showed `roborock.vacuum.a104`. The HAP contact sensors carried it visibly today — their Model characteristic read "roborock.vacuum.a70 Docked" — and it is what the Matter node would show the moment Homebridge stops discarding it.
|
|
1351
|
-
|
|
1352
|
-
There is now a marketing-name table: `roborock.vacuum.a104` reads "Roborock Qrevo S", `a70` reads "Roborock S8 Pro Ultra", and 34 models are covered. Every entry is upstream `copystring/ioBroker.roborock`'s own `VacuumProfile.name`, cross-checked against this repository's model comments — the test that does the cross-check found a real disagreement on the a97 while it was being written. Models with no upstream profile are deliberately absent and keep showing the code, including the maintainer's own sc05: a wrong marketing name is worse than a code, because a code is at least unambiguous.
|
|
1353
|
-
|
|
1354
|
-
**The table is display-only, and that is the part worth guarding.** Every poll profile, feature lookup, capability branch and `isSupportedDevice` call keys on the raw code. A name resolved where a model is _compared_ would break model detection silently — a robot whose name we happen to know would stop matching its own profile, and the stale-accessory sweep would unregister it, which costs its owner a re-pair. So `__tests__/the-model-row-is-a-name-not-a-code.test.js` enumerates the rule instead of asserting the cases: it reads the source and fails if a model comparison is ever fed the display helper.
|
|
1355
|
-
|
|
1356
|
-
**What this does not fix, measured end to end before shipping so nobody re-measures it:** for an external Matter accessory, Homebridge hardcodes `vendorName: 'Homebridge'` and derives `productName` from the display name, discarding the manufacturer and model the plugin hands it. `ServerConfig.ts` validates and truncates both to the Matter 32-character limit and returns them; `ServerLifecycle.ts:319-326` then never reads either. So Apple Home shows Manufacturer "Homebridge" and a Model row containing the robot's _name_, and no plugin change could alter that. Filed as homebridge/homebridge#3996 with the three-line fix. The values are correct on this side so they are right the moment it lands.
|
|
1357
|
-
|
|
1358
|
-
**Since fixed upstream, and out: `homebridge@2.4.1-beta.3`.** Measured on a robot commissioned long before the beta and not otherwise touched, Manufacturer now reads "Roborock" and the Model row reads the actual model. No re-pairing and no removal from the Home app — both attributes are Fixed quality and therefore never persisted, so the new values simply apply on the next start. The upstream fix is gated on `externalAccessory`, because reading the config unconditionally would have renamed every commissioned bridge as a side effect.
|
|
1359
|
-
|
|
1360
|
-
## 3.14.3
|
|
1361
|
-
|
|
1362
|
-
**The wind-down fix from 3.12.3 was too narrow by exactly one dock.**
|
|
1363
|
-
|
|
1364
|
-
3.12.3 stopped a mop run from reporting "Vacuum + Mop" while the robot drove home. Three hours later the same robot did the same thing one step further along, and the log is as plain as the first one:
|
|
1365
|
-
|
|
1366
|
-
```
|
|
1367
|
-
21:04:33 publish … operationalState=1, cleanMode=1 mopping the hall
|
|
1368
|
-
21:07:43 publish … operationalState=64, cleanMode=1 driving home, correctly held
|
|
1369
|
-
21:09:31 publish … operationalState=68, cleanMode=2 washing its mop
|
|
1370
|
-
```
|
|
1371
|
-
|
|
1372
|
-
The freeze covered the drive home and nothing else. A dock washing a mop runs water with the fan off and on again, which is the same signature that made the robot look like it was vacuuming in the first place — so the moment it arrived and started washing, the derivation woke up and changed the user's mode under them.
|
|
1373
|
-
|
|
1374
|
-
Everything the plugin already counts as part of a run except actually running or paused is now the wind-down: driving home, emptying the bin, washing the mop, updating the map. During those the fan power and the water box belong to the dock's business, not to what the user asked for. A mode genuinely changed in the Roborock app while the robot is still cleaning reaches Apple Home exactly as before, and that case keeps the test it has had since 3.12.3.
|
|
1375
|
-
|
|
1376
|
-
Two new tests replay the 21:04 to 21:09 window frame by frame. Both fail against 3.12.3.
|
|
1377
|
-
|
|
1378
|
-
## 3.14.2
|
|
1379
|
-
|
|
1380
|
-
**3.14.0 froze the operational state on the tile, and it did it to every robot that was not actively doing a dock job. Fix immediately.**
|
|
1381
|
-
|
|
1382
|
-
Measured on a real mop run, 20 minutes after the release went out. The robot was asked to mop the hall from Apple Home. It went to the dock, wetted its mop, drove out and mopped — and the tile went on saying "Cleaning Mop" for 5 minutes while it worked, and would have gone on saying it until the next time the dock did something.
|
|
1383
|
-
|
|
1384
|
-
The cause is a rule in Matter's own server implementation, and this project should have read it before shipping. From matter.js's `OperationalStateServer`:
|
|
1385
|
-
|
|
1386
|
-
```
|
|
1387
|
-
if (currentPhase === null || currentPhase < 0 || currentPhase >= this.state.phaseList.length) {
|
|
1388
|
-
throw new ImplementationError(`Current phase ${currentPhase} is out of bounds ...`);
|
|
1389
|
-
}
|
|
1390
|
-
```
|
|
1391
|
-
|
|
1392
|
-
A null `CurrentPhase` beside a non-empty `PhaseList` throws. Homebridge swallows the throw, so the **entire cluster write** is discarded without a word in the log — not just the phase, but the operational state, the battery and the fault attribute with it. The controller keeps whatever it last accepted.
|
|
1393
|
-
|
|
1394
|
-
3.14.0 published a constant 4-entry list with a null phase whenever the dock was idle, which is nearly all the time. So the last write any controller accepted was the last one where the dock genuinely was emptying, washing or updating, and everything after it was thrown on the floor.
|
|
1395
|
-
|
|
1396
|
-
The list is now present only while there is a phase to point into, and absent otherwise. That is the specification's own encoding for "the current mode has no phases", so it is also the more faithful reading; the anti-flap argument that made the list a constant is untouched, because the list is still either that exact constant or nothing at all.
|
|
1397
|
-
|
|
1398
|
-
**The tests did not catch it because they publish into a mock that accepts anything.** That is the same shape of gap as 3.12.1, one layer further out: the logic was right and the contract was not. There is now a test that encodes matter.js's rule directly, quoting it, and walks every state a robot can be in plus a whole run frame by frame, asserting the pair is one the real server would take. It fails 16 times against 3.14.0.
|
|
1399
|
-
|
|
1400
|
-
## 3.14.1
|
|
1401
|
-
|
|
1402
|
-
**The publish line now names the phase, so 3.14.0 can actually be measured.**
|
|
1403
|
-
|
|
1404
|
-
3.14.0 shipped a feature whose status is "unmeasured": nobody knows whether Apple Home draws a Matter phase. That is a fine thing to ship — an attribute no controller reads costs nothing, and drying the mop has no other route to the tile — but it is only worth shipping if the answer can be found afterwards.
|
|
1405
|
-
|
|
1406
|
-
It could not have been. A tile showing nothing during a dry would have been ambiguous between "the controller ignored it" and "the plugin never sent it", and that exact ambiguity is what cost the empty-tank warning 2 withdrawn releases and 3 field tests before 3.12.1 found the plugin had never sent anything at all.
|
|
1407
|
-
|
|
1408
|
-
So the evidence line says it: `phase=Drying mop` while the dock dries, `phase=Washing mop` while it washes, and nothing at all when the dock is idle. One look at the log and one look at the tile now answer different halves of the same question.
|
|
1409
|
-
|
|
1410
|
-
## 3.14.0
|
|
1411
|
-
|
|
1412
|
-
**Your dock spends 2 to 4 hours drying the mop after every mop clean, and until now there was no way for Apple Home to know.**
|
|
1413
|
-
|
|
1414
|
-
Matter gives a robot vacuum an operational state for emptying the dust bin, one for washing the mop and one for updating the map. This plugin has published all 3 since 3.12.0. There is no state for drying — not in Matter 1.2, not in 1.6, not anywhere — so for the whole of that time the tile has said "Docked" while the dock worked.
|
|
1415
|
-
|
|
1416
|
-
The one place it can be said is `PhaseList` and `CurrentPhase` on the same cluster, and this release says it there. The dock's 4 jobs are announced as a fixed list of phases, and `CurrentPhase` steps through them: washing, then drying for as long as the dock takes, then nothing.
|
|
1417
|
-
|
|
1418
|
-
**The list never changes, and that is the entire safety argument.** Both attributes were null from 1.4.58 until today, and the reason was real even though the explanation written down for it was not: 1.4.58 removed a version that changed phases as a refresh trick and flapped them at every Apple Home hub in the house. The answer to flapping is a list that cannot move, not an empty one. The list is a module constant, only `CurrentPhase` moves, and a test fails if any future edit builds the list from anything else. A second test walks a full mop run frame by frame and asserts the list is byte-for-byte identical at every step.
|
|
1419
|
-
|
|
1420
|
-
Drying is detected on both protocols. A classic S- or Q-series robot with a drying dock reports `dry_status` itself. A B01/Q7 reports raw status 10, air-drying, which the adapter maps to "docked" so the tile does not claim a working robot — that mapping is correct and it stays, because Apple Home may refuse a Start command to a robot it thinks is busy, but it was also where the information disappeared. The adapter now writes the fact out under the same field name the v1 robots use, so both roads arrive at the same phase.
|
|
1421
|
-
|
|
1422
|
-
`dry_status` went into the live cache with it. Drying starts while the robot is parked and idle, which is exactly when the frames are sparsest and the cloud snapshot is stalest — without the cache the phase would light for 1 frame and go out on the next heartbeat, which is worse than never showing it. That is the same hole 3.12.1 found in the tank fields and 3.13.0 found in the error code, and the tests take the robot's own route through the live handler rather than stubbing the status reader.
|
|
1423
|
-
|
|
1424
|
-
**Whether Apple Home draws a phase at all is unmeasured, and this release does not pretend otherwise.** An attribute nobody reads costs nothing, and drying is worth the attempt because no other route to it exists. `enableMatterDockPhases: false` in `config.json` puts both attributes back to null if a controller dislikes them — not on the settings page, which 3.12.0 removed, but there for the person who would otherwise be reinstalling.
|
|
1425
|
-
|
|
1426
|
-
## 3.13.1
|
|
1427
|
-
|
|
1428
|
-
**A correction to 3.13.0, found on the maintainer's own robots within the hour, by the release that caused it.**
|
|
1429
|
-
|
|
1430
|
-
3.13.0 gave an unrecognised `error_code` the generic Matter fault rather than silence. The argument was that a robot which has stopped and says nothing is worse than one which says something vague, and it was wrong.
|
|
1431
|
-
|
|
1432
|
-
Within 30 minutes of the deploy, 2 robots that were docked, charging, at 100 % and in perfect health were both carrying `error_code: 2105` and both had a fault drawn on their Apple Home tile. Neither was in any kind of trouble. The reason is written in this repository already: a B01/Q7 robot's fault field is a separate diagnostic channel where informational codes linger after harmless events, which is why the adapter has always zeroed 407 — "cleaning in progress, scheduled cleanup ignored" — before it reaches anything else.
|
|
1433
|
-
|
|
1434
|
-
So an unrecognised number is not evidence of a fault. It is evidence of a number. It is now named once in the log, per code and per robot, with a link for reporting it, and nothing is published to Apple Home for it. An existing fault is not cleared by one either.
|
|
1435
|
-
|
|
1436
|
-
The same release stops reading a B01/Q7 robot's fault through Roborock's v1 error table at all. The 2 numbering spaces share a field name and nothing else, so translating 254 from a Q7 as "dust bin full" would have been a coincidence rather than a reading.
|
|
1437
|
-
|
|
1438
|
-
Curated codes are unaffected: stuck, jammed wheel, blocked brush, dust bin missing or full, unreachable dock, flat battery and the rest still reach the tile exactly as 3.13.0 shipped them.
|
|
1439
|
-
|
|
1440
|
-
## 3.13.0
|
|
1441
|
-
|
|
1442
|
-
**Apple Home could always tell you a robot had stopped. It could never tell you why. Now it can.**
|
|
1443
|
-
|
|
1444
|
-
A robot wedged under the sofa, a jammed wheel, a blocked brush, a dust bin someone took out and forgot to put back — Roborock reports every one of these as a numbered error code, and this plugin has polled that field on every cycle since the fork without ever showing it to anyone. The Matter side had half the answer already: operational state 3, Error, so the tile stops claiming the robot is Ready. The other half, the attribute that names the fault, has only ever carried 1 value out of 19 — an empty clean water tank.
|
|
1445
|
-
|
|
1446
|
-
This release maps the rest. Stuck, wheel jammed or floating, main or side brush blocked, dust bin missing, dust bin full, an unpowered or unreachable dock, a flat battery, a no-go zone in the way, a dirty laser or cliff or wall sensor, a failed suction fan. An error code the plugin has never seen still reports a fault rather than silence, and the raw number reaches the Homebridge log so it can be reported and mapped properly — `error_code: 2105` on a Q7 is exactly that case and is why the branch exists.
|
|
1447
|
-
|
|
1448
|
-
**The published codes deliberately stop at 71, and that is the interesting decision.** Matter 1.5 added names that fit several of these faults exactly: `WheelsJammed`, `BrushJammed`, `NavigationSensorObscured`. Everything up to 71 has been in the cluster since Matter 1.2. Nothing establishes which revision Apple implements, and this plugin has already measured what Apple Home does with a value it does not recognise in the neighbouring attribute — the tile sticks on "Connecting" forever, which is why `operationalStateList` ships bare ids and no labels. A robot reported as `Stuck` when the accurate word was `WheelsJammed` has lost a little precision. A robot whose tile will not finish connecting has lost the robot. The accurate 1.5 name is written to the log beside the code that was sent, so the day somebody watches a real tile with 76 on it, the mapping moves and the log already says which rows to move. A test fails if any future edit reaches for the accurate name without that measurement.
|
|
1449
|
-
|
|
1450
|
-
An empty clean water tank still outranks the robot's own fault when both are true at once. It is the one code measured all the way to a rendered tile, and it is the one the person standing in the kitchen can fix in 30 seconds.
|
|
1451
|
-
|
|
1452
|
-
**The plumbing got the same treatment the tank fields got in 3.12.1, for the same reason.** `error_code` was in neither the live cache nor, on the local transport, the list of dps keys the plugin reads — dps 120 was dropped on the floor, which on a B01 or Q7 is the single most likely way a fault arrives. Both are fixed, and the tests take the robot's own route through `notifyDeviceUpdater` rather than stubbing the status reader, because stubbing it is precisely how the tank feature passed its tests for 2 releases while being unable to fire.
|
|
1453
|
-
|
|
1454
|
-
Nothing needs re-pairing. An error attribute is a live value, not a capability. `enableMatterTankFaultReporting: false` still switches the whole attribute off; the key name predates the wider mapping and is kept so that anyone who turned it off stays turned off.
|
|
1455
|
-
|
|
1456
|
-
## 3.12.5
|
|
1457
|
-
|
|
1458
|
-
**3 things this project told you about the Matter specification were not in the Matter specification.**
|
|
1459
|
-
|
|
1460
|
-
An audit of the plugin's own claims against the specification text found 3 statements that were invented rather than read. None of them changes what the plugin does; all 3 were being used as reasons not to build something, which makes them expensive to leave standing.
|
|
1461
|
-
|
|
1462
|
-
`buildOperationalStateCluster` said "RVC Operational State requires PhaseList and CurrentPhase to be null". It does not. Both attributes are mandatory on that cluster and both are nullable, and null is the specification's own way of saying the current mode has no phases. The real reason they are null is history: 1.4.58 removed a version that changed phases as a refresh trick and flapped them at every Apple Home hub. That is an argument against flapping phases, not against having them, and it matters because `PhaseList` is free-form text up to 32 entries and is the one place the dock's own jobs could be named.
|
|
1463
|
-
|
|
1464
|
-
Which is also why the settings page no longer says mop drying "cannot be reported by any plugin". It has no operational state of its own, which is what the sentence should have said. Whether Apple Home would draw a phase is unmeasured, and the description no longer implies the question is closed.
|
|
1465
|
-
|
|
1466
|
-
The Vacuum + Mop clean mode carried a comment saying Matter has no dedicated tag for it. Matter has had `VacuumThenMop`, 0x4003, since the cluster was defined. This release does not switch to it, and the comment now says why: `SupportedModes` is fixed at commissioning, so changing a tag would leave every already-paired robot needing a re-pair before its mode picker worked again. Combining the 2 standard tags is legal, it is what has always shipped, and it stays.
|
|
1467
|
-
|
|
1468
|
-
No behaviour changes in this release.
|
|
1469
|
-
|
|
1470
|
-
## 3.12.4
|
|
1471
|
-
|
|
1472
|
-
**A correction. For one release this README said the opposite of the truth about how your robots reach Apple Home.**
|
|
1473
|
-
|
|
1474
|
-
3.12.3 rewrote the paragraph on Matter fault reporting around 2 beliefs: that Homebridge puts every Matter accessory on 1 shared node, and that the field tests which found Apple Home drawing no vacuum faults had therefore been run on a bridge. Both are wrong, and the evidence was in the maintainer's own log the whole time.
|
|
1475
|
-
|
|
1476
|
-
Homebridge has never bridged a robot vacuum. Its Matter layer keeps a list of device types that must be published on a dedicated Matter server of their own, and `RoboticVacuumCleaner` has been on that list since the first Matter commit landed on 23 February 2026 — before any released Homebridge could speak Matter at all. Every robot gets its own server, its own port and its own pairing code. On the maintainer's host that is ports 5532, 5533 and 5534 and 3 distinct bridge identifiers, printed in the Homebridge log at every startup.
|
|
1477
|
-
|
|
1478
|
-
So the sentence 3.12.3 deleted was correct and the one it added was not. "Bridged versus not" cannot explain why Apple Home drew nothing in those 3 controlled tests and drew the tap icon on this maintainer's own tile on 20 August, because every one of those measurements was taken on a standalone node. The condition is still unknown, and the README says so again.
|
|
1479
|
-
|
|
1480
|
-
The wrong version was live for 17 minutes. Nothing in the plugin changed in this release; the code shipped by 3.12.3 is untouched.
|
|
1481
|
-
|
|
1482
|
-
## 3.12.3
|
|
1483
|
-
|
|
1484
|
-
**A robot asked to mop said it was vacuuming — for the 40 seconds it spent driving home.**
|
|
1485
|
-
|
|
1486
|
-
Mathias asked his S8 Pro Ultra for a mop run from Apple Home and watched the tile report Mop, then Vacuum + Mop, then Mop again. The log is unambiguous: `cleanMode=1` at 16:55:16, `cleanMode=2` at 16:56:16 the second it was sent back to the dock, `cleanMode=1` at 16:56:54 once docked. He had asked for one thing.
|
|
1487
|
-
|
|
1488
|
-
The robot was not misbehaving. A classic S- and Q-series robot does not report its clean type, so the plugin derives it: fan power 105 means the fan is off, which means mop-only, and otherwise an active water level means vacuum + mop. That derivation is a single sample, and sending a robot home resets its fan power while leaving the water box configured — which is exactly the signature it reads as vacuum + mop.
|
|
1489
|
-
|
|
1490
|
-
The derivation is now frozen while the robot is driving home, and only then. It stays authoritative for the rest of a run, because a mode genuinely changed in the Roborock app mid-clean must still reach Apple Home; that is a real case and it has its own test. A first attempt held the type for the whole run and broke it, which is how the scope got settled. Robots that report their clean type directly — the B01 and Q7 generation — were never affected either way, because their answer is not a guess.
|
|
1491
|
-
|
|
1492
|
-
## 3.12.2
|
|
1493
|
-
|
|
1494
|
-
**Flipping debug mode could switch off 9 HomeKit sensors nobody had touched.**
|
|
1495
|
-
|
|
1496
|
-
`autoSave()` and the device-row toggle both went through `saveCredentials()`, which spread the entire settings form into the patch. The Apple Home checkboxes sat in their own panel with their own Save button and deliberately had no autoSave binding, so the intended flow was tick-then-Save. But any change to debug mode, region, email or a device row committed whatever those 4 keys happened to be in the DOM at that moment.
|
|
1497
|
-
|
|
1498
|
-
The signature is in the config diffs: `debugMode` false to true and `enableHomeKitStateSensors` true to false in the same write. One debug-mode toggle, and an untouched checkbox rode along and unpublished 9 HAP accessories without anyone pressing Save. It happened 3 times in one day.
|
|
1499
|
-
|
|
1500
|
-
`updatePluginConfig` is a merge, so a key left out of the patch keeps its saved value. An implicit save now writes only the fields whose own controls triggered it. The account password is off that list too, for the same reason `login()` deletes it: blurring the email field should not write a cleartext password back into `config.json`.
|
|
1501
|
-
|
|
1502
|
-
What this does not claim: nothing unticks that box on its own. `syncFeatureDependencies` is empty and no path outside `loadConfig` assigns to `.checked`. The bug was never a checkbox with a mind of its own — it was an unrelated control persisting it.
|
|
1503
|
-
|
|
1504
|
-
The new test enumerates the rule rather than the case: every control wired to `autoSave()` must have its key on the list, or that control silently stops saving.
|
|
1505
|
-
|
|
1506
|
-
## 3.12.1
|
|
1507
|
-
|
|
1508
|
-
**The empty-tank warning could never fire on a real robot, and it took the Roborock app saying "Out of water" next to a silent tile to prove it.**
|
|
1509
|
-
|
|
1510
|
-
3.10.0 added the `Water Tank Empty` sensor and 3.12.0 added the Matter fault on the tile. Both read `dock_error_status` and `water_shortage_status` through `getNumberStatus`, which checks the live cache first and the HomeData snapshot second. Neither field is in the snapshot, and the live handler remembered 7 fields — state, charge status, battery, clean area, clean time, fan power and clean type. The tank was not among them. So both features asked for a value that had nowhere to come from, got null, and correctly said nothing at all. On my own S8 Pro Ultra the raw frames carried `dock_error_status: 38` for days while the sensor stayed Open and the tile stayed clean.
|
|
1511
|
-
|
|
1512
|
-
The live cache now remembers both fields. Nothing else changes.
|
|
1513
|
-
|
|
1514
|
-
The reason no test caught it is worth writing down, because it is the sort of gap that repeats: every test for this feature stubbed `getVacuumDeviceStatus` and handed the code the value it was asking about. They proved the logic and nothing about the plumbing — a feature can be entirely correct and still be wired to a socket with no power in it. There are now 4 tests that take the robot's own route instead, pushing a live frame in through `notifyDeviceUpdater` with a snapshot that knows nothing about tanks, including the sparse-frame case where one message carries the tank and the next carries only the battery. All 4 fail against 3.12.0.
|
|
1515
|
-
|
|
1516
|
-
## 3.12.0
|
|
1517
|
-
|
|
1518
|
-
**An empty water tank now shows on the tile, and the page of switches that used to decide such things is gone.**
|
|
1519
|
-
|
|
1520
|
-
Apple Home draws a tap icon on the play button with "refill the water tank" when a robot publishes Matter's `WaterTankEmpty` fault. This plugin has written that attribute twice and withdrawn it twice — 1.4.61, then 3.3.0 into 3.4.1 — because 3 controlled tests on an S8 Pro Ultra with a genuinely empty tank produced nothing at all in Apple Home, and wedged the tile on "Updating…" for good measure. Then vp-debug12 posted a screenshot in [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9) of the same attribute rendered correctly by the same controller. One counterexample is worth more than my explanation, so it is back.
|
|
1521
|
-
|
|
1522
|
-
It is deliberately narrow. `WaterTankEmpty` goes out when the robot says the tank is empty and `NoError` when it says the tank is full, because an attribute only ever written when something is wrong never clears and a warning that survives a refill is worse than no warning. A robot that has not reported its tank gets no attribute at all rather than a cheerful all-clear. And the robot is **not** dragged into the Matter Error state along with it: that was the third of Wazza151's tests, Apple still drew nothing, and a robot in Error may be refused a Start command — a real cost for a robot that is docked, charging and perfectly able to vacuum without water. No re-pairing is needed; an error attribute is a live value, not a capability.
|
|
1523
|
-
|
|
1524
|
-
Both robots that have been measured with an empty tank disagree about how they say so. The S8 Pro Ultra sets `dock_error_status: 38` and leaves `water_shortage_status` at 0; the Q Revo sets both. Either is enough, which is the same rule the `Water Tank Empty` contact sensor already used.
|
|
1525
|
-
|
|
1526
|
-
**And the Apple Home Features section of the settings page is gone.** 9 switches, several of them marked "⚠ re-pair", every one of them a way to end up with a robot that shows less than it could. They are all on now. Rooms and map selection, live room tracking, cleaning and suction modes, battery, dock and returning status, charging and docked on the tile, fault reporting, and the tank warning — that is what a Roborock in Apple Home is, and it should not have been a quiz.
|
|
1527
|
-
|
|
1528
|
-
The switches were not just clutter, they were sharp. 3.10.1 measured what turning a mode set **off** does: Matter persists `CurrentMode` and does not persist `SupportedModes`, so a stored mode 6 meeting a freshly shrunken list of 0, 1, 2 throws inside `RvcCleanModeServer.initialize`, the endpoint rolls back, and the accessory never registers again — on that restart and every restart after it. The settings page was offering that as a checkbox. Growing a list has no such failure: the stored mode stays valid, which is why turning everything on is the safe direction and turning things off never was.
|
|
1529
|
-
|
|
1530
|
-
Every key is still read from `config.json`, so `"enableMatterFaultReporting": false` still works for anyone who wants the old silence, and an existing config that already says `false` is left alone. Nothing on the settings page writes them any more, which also closes a quieter bug: a save could previously write `false` for a feature the page had no opinion about.
|
|
1531
|
-
|
|
1532
|
-
**If your robot was paired before this release, re-pair it once.** Matter fixes an accessory's announced capabilities at commissioning, so a robot that was paired with 3 clean modes and 4 operational states keeps showing 3 and 4 until Apple Home is shown the new shape. Remove the robot in Apple Home, then add it again with the same Matter code. Anyone who already had these switched on has nothing to do.
|
|
1533
|
-
|
|
1534
|
-
## 3.11.2
|
|
1535
|
-
|
|
1536
|
-
**"Attempt 12 this run" counted every run since Homebridge started. So did "failed 10 times in a row", and the once-per-run explanation of why no room could be named was only ever printed on the first run of the process.**
|
|
1537
|
-
|
|
1538
|
-
Caught on my own a70. It ran a two-room clean from Apple Home, the cloud map channel was timing out that morning, and `get_map_v1` failed all ten times it was asked — ten guaranteed-to-fail cloud requests, 10 seconds of timeout each, spread across one ten-minute clean, and not one room named.
|
|
1539
|
-
|
|
1540
|
-
The clear that runs at every run boundary exists so nothing leaks into the next run. It dropped the cached room and nothing else. Every counter behind the log lines survived for the lifetime of the process, so three lines were telling you about a window that was not the one they named. A run that fails every attempt is exactly the run that leaves the counters high, and that run never had a cached room to clear in the first place — which is why the leak survived a release that went looking for the same class of thing. 3.11.0 stopped placeholder poses from inflating the miss count; it did not make the count per-run. It is per-run now.
|
|
1541
|
-
|
|
1542
|
-
**The failing fetch is also no longer retried at live-display cadence for the whole run.** After 2 failures in a row the gap doubles with each further failure, capped at 5 minutes, and drops straight back to the live cadence the moment one succeeds. The first two failures are deliberately not slowed: a single lost frame on a healthy channel must not make a working live room sluggish, the same rule 3.11.1's local-mute limit follows. A streak long enough to have been slowed now says so when it ends, because "failed N times in a row" at warn level had no counterpart and a channel that recovered left the log's last word saying it was broken.
|
|
1543
|
-
|
|
1544
|
-
Both protocol paths are covered — the classic `get_map_v1` fetch and the Q7/B01 SCMap fetch had the same two defects in the same shape. The test enumerates the rule across every live-room state the plugin keeps rather than the two call sites that happened to be found, so a third path added later fails the test instead of leaking quietly.
|
|
1545
|
-
|
|
1546
|
-
Not addressed, because there is no measurement to justify it: why the cloud map channel timed out at all. `get_map_v1` has the default 10-second timeout and had been resolving every position on this same robot nine days earlier, so a longer timeout would be a guess. The morning also carried an unrelated cloud `get_prop` timeout on the same robot, which points at the account's cloud rather than at the map request.
|
|
1547
|
-
|
|
1548
|
-
## 3.11.1
|
|
1549
|
-
|
|
1550
|
-
**The LAN port was open, the robot never answered on it, and the plugin kept asking for the life of the process — 10 seconds thrown away on every poll and every command.**
|
|
1551
|
-
|
|
1552
|
-
The reporter of [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) runs Homebridge on a NAS in one VLAN and his Saros 10 in another. Port 58867 is reachable across that boundary, so the local client completes its TCP handshake and records `Local connect state: true` — and then every single request dies of silence 10 seconds later. `get_prop` on both startups, `app_segment_clean`, `app_pause`, `app_start`: all of them, every time.
|
|
1553
|
-
|
|
1554
|
-
The plugin only ever gave up on the LAN when the _connect_ failed. A socket that connected and then answered nothing was retried forever, which is why he saw a `get_prop` timeout at every restart and why his commands were slow before they worked at all. A successful handshake proves the port is reachable. It does not prove the robot is listening.
|
|
1555
|
-
|
|
1556
|
-
3 local timeouts in a row on a socket that still reports itself connected now write the LAN off for that robot and use the cloud instead, with 1 log line that says the port is open and the robot is not replying — the distinction that decides whether there is any point rewriting a firewall rule. Any local reply resets the count, so a single lost frame on a healthy network changes nothing; permanently exiling a robot to the cloud over one dropped packet would be worse than the bug being fixed.
|
|
1557
|
-
|
|
1558
|
-
The diagnostics report names this case separately from a failed connect, because those two look identical in a log and lead to opposite conclusions.
|
|
1559
|
-
|
|
1560
|
-
## 3.11.0
|
|
1561
|
-
|
|
1562
|
-
**A Q7 said it was between rooms 226 times during one clean. It was in the bedroom the whole time.**
|
|
1563
|
-
|
|
1564
|
-
I caught a run on my own robots and pulled the numbers rather than the impressions. One Q7, 47 minutes, 227 live-room fetches. 226 of them placed it at cell 22280,22100 — the same cell every time — while the room outlines on that map span 38 to 293. The pose behind it was exactly (1100, 1100), which is the same constant two other people's Q7s reported back in August. The remaining fetches resolved Stue, then Gang, then Soveværelse, in the order the robot actually moved.
|
|
1565
|
-
|
|
1566
|
-
So the robot does send a real position. It just serves a placeholder in between, and every one of those was being written up as "the robot's position did not fall inside any known room outline (it may be between rooms, or the map may still be building)". That sentence was wrong twice over: the robot was not between rooms, and there was nothing anyone could do about it. It also fed the miss counter, which is how a robot cleaning one bedroom produced "after 46 unresolved position(s)".
|
|
1567
|
-
|
|
1568
|
-
A position further outside the map than the map is wide is now recognised for what it is and named as a placeholder, once per run at a level you see and quietly thereafter. The test is geometry rather than the number 1100: the robot cannot be somewhere it has never mapped, which stays true if Roborock picks a different constant. A robot genuinely outside every outline — a doorway, a hallway nobody named, a strip the outlines do not cover — is still a real miss and still counts as one, because that is the case the miss line exists for. Classic S- and Q-series robots resolve every fetch and are untouched.
|
|
1569
|
-
|
|
1570
|
-
Two things fall out of it. The room still updates on a Q7, on the fetches that carry a true position; nothing about the tile changes. And a resolved room now prints the cell it resolved at, so a working position and a failing one can be compared in one log instead of across two field sessions — which is what this one cost.
|
|
1571
|
-
|
|
1572
|
-
**Also: 50 log lines a minute, per robot, that could never mean anything.** With debug on, every known status attribute produced `Skipping known get_status attribute without a Homebridge state object` on every poll. That check dates from this library's ioBroker origins, where the object it looks for exists; under Homebridge it never does, so the branch fired for every attribute forever and reported only that the plugin is not ioBroker. On my own server the log ring had shrunk to 90 minutes — the window you need when something real goes wrong. It is gone. An attribute nobody has mapped yet is still named once with its value, which is the half that carries information.
|
|
1573
|
-
|
|
1574
|
-
## 3.10.2
|
|
1575
|
-
|
|
1576
|
-
**You asked for vacuum-only, the robot mopped, and Apple Home showed vacuum anyway — because the plugin believed a command it had already logged that it lost.**
|
|
1577
|
-
|
|
1578
|
-
The reporter of [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) selected two rooms, vacuum only, and his Saros 10 ran a vacuum-and-mop over them. His log contains both halves of the mistake, 83 seconds apart:
|
|
1579
|
-
|
|
1580
|
-
```
|
|
1581
|
-
13:50:57 Applying Vacuum mode to Weebo before starting.
|
|
1582
|
-
13:50:59 Roborock did not confirm the water mode and suction level ...
|
|
1583
|
-
the robot may keep its previous settings for this run
|
|
1584
|
-
13:52:22 Roborock still reports Vacuum + Mop ... after Vacuum was applied
|
|
1585
|
-
and acknowledged
|
|
1586
|
-
```
|
|
1587
|
-
|
|
1588
|
-
The second line is untrue, and the first line is the plugin saying so in advance.
|
|
1589
|
-
|
|
1590
|
-
Before a Matter start, the plugin makes the robot match the mode being displayed. On a v1 robot the difference between Vacuum and Vacuum-and-mop _is_ the water-box mode, so that one command carries the whole choice. When it lands, the plugin pins the clean type for the run so a robot whose own report lags by a minute or two cannot make the tile flicker to a mode nobody asked for — that is 3.10.0's behaviour and it is correct.
|
|
1591
|
-
|
|
1592
|
-
The pin was taken on the wrong evidence. It was taken whenever the prep sequence _resolved_, and the prep resolves on a partial apply too: it deliberately never gives up early, because a suction command that times out must not cancel the command carrying the user's actual choice. So it sends what it can, warns about what the robot never confirmed, and returns normally. "Acknowledged" and "sent, unconfirmed, the robot may keep its previous settings" reached the caller as the same answer — nothing.
|
|
1593
|
-
|
|
1594
|
-
So on his run the water command went unanswered, the robot kept mopping as the warning predicted, and the pin then suppressed the robot's honest vacuum-and-mop report in favour of a promise the plugin had already recorded it could not keep. The only place the failure remained visible was the floor.
|
|
1595
|
-
|
|
1596
|
-
A pin is either known ground truth or it is not taken. The prep now hands back what the robot confirmed, and the clean type is pinned only when the command carrying it was acknowledged. An unconfirmed suction level does not disturb it, because a level inside a type says nothing about which type is running — and the warning about it is unchanged either way. This is the rule the failed-apply path has always followed, finally extended to the apply that fails while resolving.
|
|
1597
|
-
|
|
1598
|
-
The cloud timeouts underneath this are not the plugin's to fix and are not new; what is fixed is that they can no longer be hidden from you.
|
|
1599
|
-
|
|
1600
|
-
**Also: the plugin's own heartbeat left no trace of itself, and that cost [#7](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/7) a wasted round of testing.**
|
|
1601
|
-
|
|
1602
|
-
Every robot gets a forced full Matter write every 60 seconds. Its evidence line is written only when the rendered line would read differently from the last one — deliberately, since 3.10.0, so an idle robot does not fill the log with identical minutes.
|
|
1603
|
-
|
|
1604
|
-
The reporter of #7 was asked to check whether those lines were still appearing while his Apple Home tile was dead. It is the cheapest way to separate "the plugin stopped" from "the Matter session died underneath a healthy plugin". His robot was docked at 100 %, so every line rendered identically, so the last one was written at startup and none followed. He looked, correctly reported 11 minutes of nothing, and the answer was worth nothing — absence was the deduplication working, not evidence about whether anything ran. The question was unanswerable from the log at any level, which for a liveness signal is the wrong outcome.
|
|
1605
|
-
|
|
1606
|
-
A suppressed publish is now recorded at debug, naming the robot, the values and what triggered it. The info log stays exactly as quiet as before, and with plugin debug on, 11 minutes of a docked robot leaves 11 traces — so a gap in them means something.
|
|
1607
|
-
|
|
1608
|
-
## 3.10.0
|
|
1609
|
-
|
|
1610
|
-
**An empty clean-water tank can now notify you, which Matter has never had a way to say.**
|
|
1611
|
-
|
|
1612
|
-
The robot vacuum device type has no water-tank attribute of any kind — not level, not presence, nothing. I have had to write that sentence to three people now: to Wazza151 in [#5](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/5), who asked for the tank warning he used to get from another bridge, and twice in [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9). Two releases tried to say it through the fault attribute instead and both came back out, and the plugin has been reading the condition accurately the whole time with nowhere to put it.
|
|
1613
|
-
|
|
1614
|
-
A HomeKit contact sensor has no such gap. Turn on **Water Tank Empty** under the Home app sensors and each robot gets a read-only sensor that reads Closed while it has no clean water. Point a Home notification at it and a mop run that would have been wasted becomes a message instead. Nothing about the tile changes, no re-pairing is needed, and it is off by default like the other two.
|
|
1615
|
-
|
|
1616
|
-
Robots do not agree on how they say it, which is the whole difficulty. Wazza151's S8 Pro Ultra reports `dock_error_status: 38` and leaves `water_shortage_status` at 0 — he emptied and refilled the tank and watched the first field track it exactly. vp-debug12's Q Revo sets both. A robot that carries its water onboard and has no dock tank is the mirror case and sets only the second. So both are read, and the a70 is the reason it is an OR rather than a preference order: consulting the shortage flag first and believing its 0 would report a full tank on the very robot the condition was measured on. Only 38 counts as empty — that field also carries a full waste-water tank, a missing dust bag and a blocked duct, and a sensor that sent you to fill an already-full tank would be worse than none.
|
|
1617
|
-
|
|
1618
|
-
It reports and nothing else. It will not refuse to start a mop run, and that is deliberate: this plugin has already shipped the bug where it declined to forward a command because its own cached snapshot said the command was unnecessary, and a robot that will not start because the plugin believes the tank is empty is that bug with a worse failure mode. The Roborock app owns the tank sensor and is the right place for a hard block.
|
|
1619
|
-
|
|
1620
|
-
**Also fixed, and it was live for both existing sensors.** With no reading yet — a fresh install, or a sensor just switched on — every sensor answered Closed, on the reasoning that Closed is the resting state of a robot on its dock. That is true for `Docked` and false for `Cleaning`, which therefore claimed a robot was cleaning until the robot first said otherwise, and then moved. Moving is exactly what an automation triggers on, so a new `Cleaning` sensor announced a finished cleaning on its first startup, from a robot that had done nothing. Each sensor now declares its own resting state, and a test pins the general rule: whatever a robot sitting idle in its dock reports, that is what every sensor must already be showing.
|
|
1621
|
-
|
|
1622
|
-
## 3.9.4
|
|
1623
|
-
|
|
1624
|
-
**A robot out cleaning your hallway was recorded as sitting in its dock, because of a charging flag it left behind there.**
|
|
1625
|
-
|
|
1626
|
-
The reporter of [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) was asked whether a particular warning appeared every time he sent his Saros 10 home mid-clean, or only sometimes. He reproduced it: twice out of two attempts, on two different plugin versions. In one of them the plugin had been publishing "running" for eight minutes, with the battery falling 100 → 97 % and live room tracking following the robot from room to room, and still called its own snapshot docked.
|
|
1627
|
-
|
|
1628
|
-
Two fields disagreed, and the wrong one won. A non-zero `charge_status` was treated as sufficient proof of being in the dock — enough to overrule a `state` that positively said the robot was out cleaning. The plugin already applies the opposite rule when it decides what to publish, where the charging flag is consulted only if the state says nothing useful, so the same two values read at the same instant produced "running" in one place and "docked" in another.
|
|
1629
|
-
|
|
1630
|
-
They disagree because they are not the same age. A live frame carrying only the state field moves `state` and leaves `charge_status` at whatever it held before the robot undocked, and any field a live frame omits falls back to the slower cloud snapshot. "Room cleaning" beside "charging" is not a robot contradicting itself; it is one fresh reading next to one stale one.
|
|
1631
|
-
|
|
1632
|
-
The charging flag is now a tiebreaker for a state that does not answer the question, never an override of one that does.
|
|
1633
|
-
|
|
1634
|
-
The visible half of this was a log line. The costly half was silent: the retry that re-sends a dock command when the first one times out asks whether the robot is docked before it asks whether it is still cleaning, so it gave up on the leftover flag. That retry was therefore disarmed for precisely the robots it was written for — the ones on cloud-only connections whose commands time out in the first place. The **Docked** state sensor was reporting docked for a robot out on the floor for the length of a run, which any automation built on it would have acted on.
|
|
1635
|
-
|
|
1636
|
-
Robots that really are in the dock are unchanged, and the charging flag still decides for a robot that reports no usable state at all.
|
|
1637
|
-
|
|
1638
|
-
## 3.9.3
|
|
1639
|
-
|
|
1640
|
-
**The dock still announced a phantom cleaning — one second after the one 3.6.2 removed.**
|
|
1641
|
-
|
|
1642
|
-
3.6.2 stopped the dust bin emptying from announcing a cleaning, and the reporter of [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9) confirmed in the field that it worked. He then added one sentence: after the emptying, the robot briefly returns to the dock and sends another notification.
|
|
1643
|
-
|
|
1644
|
-
That is a second bug with the same shape. When the chore ends the robot reports "returning to dock" for about a second before it charges again, and that state counted as cleaning. So the blip published Cleaning and then Idle — a cleaning that started and finished, from a robot that never left its dock. It fired the `cleaning` state sensor too, so any automation built on the robot starting or finishing a clean ran with it.
|
|
1645
|
-
|
|
1646
|
-
Transit now inherits the run mode instead of deciding one, exactly as a dock chore does. Driving somewhere is never the start of a cleaning; it is how a real run ends. One rule covers both directions: a robot that was cleaning keeps saying Cleaning until it is actually home, and a robot that never left its dock stays Idle throughout.
|
|
1647
|
-
|
|
1648
|
-
The rule reads the robot's own state rather than the one shown to the controller, which is what let this survive 3.6.2: with **Extended Operational States** off, "returning to dock" is rewritten one level below and never reached the test, so the bug hit only the users who had turned that toggle on. Two smaller faults fell out with it — with the toggle off, a mid-run trip to wash the mop announced the run had finished and restarted, and Apple Home announced a cleaning finished the moment the robot set off home instead of when it docked. The toggle no longer changes whether, or when, a cleaning is announced.
|
|
1649
|
-
|
|
1650
|
-
## 3.9.2
|
|
1651
|
-
|
|
1652
|
-
**The tile showed a robot that was idle on plain Vacuum for the first half-minute after every restart — even after 3.9.1 made the robot report itself correctly one second in.**
|
|
1653
|
-
|
|
1654
|
-
3.9.1 fixed the refused first status request, and the field log proves it worked: both Q7s reported `B01 status online … state=8` a second after startup, with no recovery lines at all. The tile was still wrong for 28 seconds afterwards, which means the two symptoms were never the same event — the status was already online _before_ the accessory was added.
|
|
1655
|
-
|
|
1656
|
-
The status simply had nowhere to go. Discovery runs after the status loop is already answering, and a live status that arrives before its accessory exists is dropped twice over in silence: once because there is no accessory registered for that robot yet, and again because an accessory that is not yet registered ignores live updates by design. Nothing redelivered it. The tile therefore fell back on the cloud snapshot — pairing-day values — and corrected itself only on the next poll tick.
|
|
1657
|
-
|
|
1658
|
-
Now an accessory that has just become usable is handed the status the robot already reported, replayed on the same channel every other update arrives on, so one piece of code interprets it. Robots that have reported nothing are left alone rather than being given a guess: an invented value would move when real data arrived, and moving is what automations trigger on.
|
|
1659
|
-
|
|
1660
|
-
Both discovery paths get it, not only the one that was measured — a robot restored from the accessory cache had the same gap.
|
|
1661
|
-
|
|
1662
|
-
## 3.9.1
|
|
1663
|
-
|
|
1664
|
-
**The first status request of every startup was refused, on every Q7, on every restart.**
|
|
1665
|
-
|
|
1666
|
-
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.
|
|
1667
|
-
|
|
1668
|
-
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.
|
|
1669
|
-
|
|
1670
|
-
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.
|
|
1671
|
-
|
|
1672
|
-
The same event explains the other half, which had been observed 14 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.
|
|
1673
|
-
|
|
1674
|
-
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:
|
|
1675
|
-
|
|
1676
|
-
- 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.
|
|
1677
|
-
- 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.
|
|
1678
|
-
|
|
1679
|
-
Verified red against 3.9.0: 7 of 12 assertions failed, and the symptom test failed by producing the field log line verbatim.
|
|
1680
|
-
|
|
1681
|
-
934 tests, up from 922.
|
|
1682
|
-
|
|
1683
|
-
## 3.9.0
|
|
1684
|
-
|
|
1685
|
-
**Automations can now be triggered by the robot.**
|
|
1686
|
-
|
|
1687
|
-
Apple Home does not accept a Matter vacuum as an automation trigger at all. pponce measured that in [#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3) and confirmed it again after the action switches shipped: the switches are commands an automation _sends_, so nothing the robot does can _start_ one. A contact sensor is a trigger source in every Home client, so each robot can now publish read-only sensors mirroring its state — `Vicky Docked` and `Vicky Cleaning`.
|
|
1688
|
-
|
|
1689
|
-
Two states, in the order he ranked them when asked which he would actually trigger on: docked first ("I'd use the docked feature on its own for sure"), cleaning second. He also named the pair he wants them for — not docked **and** not cleaning means the robot is probably stuck somewhere — which is why both ship together, and why there is no third "stuck" sensor: that one is a timeout over these two, and the timeout is his to pick.
|
|
1690
|
-
|
|
1691
|
-
Closed means the state the sensor is named after is true, in every sensor. Nothing is ever sent to the robot.
|
|
1692
|
-
|
|
1693
|
-
Three things the implementation is careful about, each enumerated as a rule rather than fixed for the case that prompted it:
|
|
1694
|
-
|
|
1695
|
-
- **The value comes from the robot's own state, never from the state Apple Home was told.** Two unrelated display toggles rewrite the published operational state: CHARGING and DOCKED become STOPPED without **Charging/Docked status**, and the dock chores become RUNNING without **Dock & Returning status**. A `Docked` sensor built on the published value would have worked only for users who had ticked a box about something else — the same fault form as [#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9)'s fix and [#5](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/5)'s. Verified red against that exact wrong implementation: 13 assertions failed, on the docked-robot and dock-chore rows specifically.
|
|
1696
|
-
- **A dock chore is not a cleaning run, and interrupting a run does not end it.** `Cleaning` mirrors the run mode that actually reached Matter, so it carries 3.6.2's inheritance rule and cannot disagree with the tile. The test asserts that agreement as an identity rather than as a second hand-written truth table, plus a guard that both values genuinely occur.
|
|
1697
|
-
- **Nothing is claimed before the robot has reported in.** Roborock state 0 is not a real state; it maps to STOPPED, which is indistinguishable from a robot idle on the floor. A Q7 on the maintainer's own account reports it for 27 seconds after every restart, so a sensor that believed it would report "not docked" for a docked robot and then move — firing every automation watching for that, on every Homebridge restart. The sensors hold their last known reading and only move on real data. Verified red with the guard removed.
|
|
1698
|
-
|
|
1699
|
-
The partition that keeps both HAP accessory kinds alive is the fourth: `discoverDevices()` unregisters cached HAP accessories it does not recognise, and each kind's sync removes what its own config no longer asks for. A sensor has no `action` in its context, so before this the switch sync would have deleted every sensor on the first discovery pass. Both directions are asserted, and verified red against the unpartitioned version.
|
|
1700
|
-
|
|
1701
|
-
Off by default, and no re-pairing: like the switches these are HAP accessories on this plugin's child bridge, not Matter. They need that bridge's own HomeKit QR code — the plugin says which one at every start.
|
|
1702
|
-
|
|
1703
|
-
922 tests, up from 907.
|
|
1704
|
-
|
|
1705
|
-
## 3.8.0
|
|
1706
|
-
|
|
1707
|
-
**The settings page follows Homebridge's dark theme, and the icon is in the header.**
|
|
1708
|
-
|
|
1709
|
-
The page stayed white inside a dark Homebridge, in every version. It was not reading the operating system's setting — that would have been wrong too. Homebridge UI reaches into this plugin's iframe and puts classes on our own `<body>`: `dark-mode` when the user picks a dark theme, `config-ui-x-<theme>` when they pick a light one. The stylesheet had `color-scheme: light` hard-coded and one set of colours, so there was nothing to follow it with.
|
|
1710
|
-
|
|
1711
|
-
Every colour is now a token, the dark set is declared once, and `index.js` decides which applies and writes it to `<html data-theme>`. Homebridge's own choice wins over the OS, because someone who picked light in Homebridge on a dark Mac meant light; the OS is the fallback for a page opened outside Homebridge. A MutationObserver keeps up when the theme changes while the page is open — the switch is one screen away, and the parent applies it by mutating our body rather than reloading us.
|
|
1712
|
-
|
|
1713
|
-
Two details worth writing down. Homebridge also assigns `body.style.backgroundColor = "#242424 !important"`; the CSSOM rejects a value carrying `!important`, so that line has never done anything and the class is the only signal there is. And the QR tile stays white in both themes on purpose — a camera cannot read a code without a light quiet zone. It is the one colour on the page that is not a token, and the test names it so the exemption cannot grow.
|
|
1714
|
-
|
|
1715
|
-
**The plugin's icon now sits beside the title**, as on the other plugins' settings pages. It is `assets/icon.png` byte for byte rather than a redrawing or a resize, and a test holds the two files identical, so the tile here and the tile on the Homebridge plugin list cannot drift apart.
|
|
1716
|
-
|
|
1717
|
-
787 tests, up from 773. Verified red against 3.7.1: 14 of 14 fail.
|
|
1718
|
-
|
|
1719
|
-
## 3.7.1
|
|
1720
|
-
|
|
1721
|
-
**The Matter Pairing section told you to pair the wrong thing first.**
|
|
1722
|
-
|
|
1723
|
-
It read "Pair the Roborock child/daughter bridge first, then add the external vacuum accessory if Apple Home asks for it." That sequence cannot happen. Homebridge publishes a robotic vacuum as a Matter node of its own, so the robot never arrives inside the bridge node, and Apple Home is never in a position to offer it as an extra afterwards ([#7](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/7) — reported by a user who had to reverse the instruction to get paired with a single code, and confirmed against a second installation where each robot is its own `matterOnly` node with its own setup code).
|
|
1724
|
-
|
|
1725
|
-
Each robot's own code is now what the panel and the per-code hints point at, and the bridge entry says plainly that the robots do not need it. Both hints also name the code as a **Matter** code, because the other code in this product — the child bridge's HomeKit QR code for the action switches — is the one people reasonably confuse it with.
|
|
1726
|
-
|
|
1727
|
-
Wording only; nothing about pairing itself changed, and no re-pairing is needed. `__tests__/matter-pairing-guidance-points-at-the-robots-own-code.test.js` enumerates the rule over all three surfaces in both directions: no surface may make the robot's code conditional on the bridge, and every surface has to say positively that the robot's own code adds the robot. The negative half alone is satisfied by deleting the paragraph.
|
|
1728
|
-
|
|
1729
|
-
773 tests, up from 749. The README's count said 726 — it was not updated when 3.7.0 shipped.
|
|
1730
|
-
|
|
1731
|
-
## 3.7.0
|
|
1732
|
-
|
|
1733
|
-
**A Start Cleaning switch for automations.**
|
|
1734
|
-
|
|
1735
|
-
A fourth Home app switch per robot, alongside Return to Dock, Pause and Find. It starts exactly the clean the Home tile's play button would, rooms selected on the tile included, because it calls the same method the Matter run-mode handler calls — a switch with its own idea of what starting means would be a second command path, and the room selection it ignored would be the one the user is looking at. Clear the selection on the tile to get a whole-home clean.
|
|
1736
|
-
|
|
1737
|
-
It comes with the clean-mode prep, the acknowledgement wait, the timing line and the optimistic tile update the tile's own start already had, for the same reason: one path. The log says which surface asked — `Starting Vicky from the Home switch.` against `Starting Vicky from Matter.` — so a misfiring automation is findable.
|
|
1738
|
-
|
|
1739
|
-
Off by default like the other three, and no re-pairing: the switches are HAP accessories on this plugin's child bridge, not Matter. `__tests__/every-switch-is-wired-all-the-way-through.test.js` enumerates the rule over all five places an action has to be declared, because the failure mode of missing one is a tickable box that publishes nothing.
|
|
1740
|
-
|
|
1741
|
-
749 tests, up from 726.
|
|
1742
|
-
|
|
1743
|
-
## 3.6.2
|
|
1744
|
-
|
|
1745
|
-
**A dock emptying its own dust bin announced a cleaning that never happened.**
|
|
1746
|
-
|
|
1747
|
-
Apple Home reads Matter's `RvcRunMode` as the answer to "is this robot cleaning?" — it announces a cleaning that started when the mode becomes Cleaning and one that finished when it returns to Idle. The plugin published Cleaning for all three dock chores (emptying the dust bin, washing the mop, updating maps), so a robot sitting idle in its dock produced a start and a finish notification every time the dock emptied itself ([#9](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/9), Q Revo).
|
|
1748
|
-
|
|
1749
|
-
A dock chore now inherits the run mode that was published before it began, rather than deciding one of its own. That is deliberately "inherit" and not "always Idle": a robot that empties its bin in the middle of a run must not announce that the run finished and started again either. Both directions are covered.
|
|
1750
|
-
|
|
1751
|
-
The chore is recognised from the robot's own status code, not from the state shown to the controller — with **Extended Operational States** off, emptying the dust bin is rewritten to Running one level below, so a rule that read the controller-facing state would have worked only for the users who had enabled that toggle.
|
|
1752
|
-
|
|
1753
|
-
The operational state is unchanged: with the toggle on, the tile still says the dock is emptying the bin. Only the run mode stopped claiming that it was a cleaning.
|
|
1754
|
-
|
|
1755
|
-
Also documented: the plugin's **Debug Mode** is not enough on its own — Homebridge suppresses plugin debug output unless Homebridge itself runs with `-D`.
|
|
1756
|
-
|
|
1757
|
-
## 3.6.1
|
|
1758
|
-
|
|
1759
|
-
**Two of 3.6.0's own log fixes did not survive contact with my server.**
|
|
1760
|
-
|
|
1761
|
-
The per-model poll-profile line still printed twice for two robots of the same model. 3.6.0 moved the dedupe key from the duid to the rendered line — and then interpolated the robot's name into that line, which made the key per-robot again. The key is now the model-derived sentence alone and the robot is named after the key is taken.
|
|
1762
|
-
|
|
1763
|
-
The `Loading accessory 'X' from cache.` demotion was lost to a batch edit that failed on its second replacement and wrote neither. Its Matter twin shipped at debug; this one shipped at info.
|
|
1764
|
-
|
|
1765
|
-
Both are pinned by rules rather than by the two lines: the dedupe key must carry no robot identity, and the emitted line must still name one.
|
|
1766
|
-
|
|
1767
|
-
## 3.6.0
|
|
1768
|
-
|
|
1769
|
-
**A full pass over the log and the settings page. No new features; a lot of things that were quietly wrong.**
|
|
1770
|
-
|
|
1771
|
-
The settings page had four defects with the same shape — the form and the saved config disagreed and nobody was told:
|
|
1772
|
-
|
|
1773
|
-
- **A brand-new install wrote `false` for the four features that default to on.** Every checkbox was initialised inside `loadConfig`'s `else` branch, so a plugin that had never been configured skipped the lot, and the first keystroke in the email field auto-saved nine unchecked boxes. Absent means on and `false` means off, so a first-time user silently disabled room selection, clean mode, battery and live room tracking. Three of those are re-pair settings.
|
|
1774
|
-
- **The cleartext password went back into `config.json` after a 2FA login.** The password path clears the field; the token path only hid the row, and a hidden input keeps its value, so the next auto-save of anything wrote it back.
|
|
1775
|
-
- **Ticking "add the switches" and pressing Save published nothing.** It saved `homeKitActionSwitches: []`, and an empty array is an array, so the plugin's fallback to `["dock"]` never fired. The user then went hunting for the QR code the page had just told them to scan.
|
|
1776
|
-
- **The pairing callout flashed on every page load** and stayed up permanently if the config failed to load, because its initial state lived only in a callback.
|
|
1777
|
-
|
|
1778
|
-
Also on that page: saves report failure instead of looking like they worked, the three settings that do nothing without a prerequisite are greyed out until it is on, a clamped number is written back into the field instead of showing the rejected value, the Devices list no longer races the skip list, and the Google Fonts import is gone — a render-blocking request to Google from a local admin page that stalled the whole settings page on an offline Homebridge box.
|
|
1779
|
-
|
|
1780
|
-
**The log.** Two lines were removed as duplicates: the poll-profile notice was keyed per robot while its text is per model, so two robots of one model printed the same sentence twice, naming neither; and every room change was announced by both the library and the Matter layer with the same prefix. `Service started` was printed on the failure path — the `getHomeDetail` catch falls through to the same callback — directly under the stack trace saying it had failed; it now says what actually happened. That stack trace is gone too: a Roborock outage or a DNS blip is a warning with a sentence, not an error with a Node stack. `Starting adapter. This might take a few minutes` (it takes one second) and `Lets go!!!!!!!` are gone with the rest of the ioBroker vocabulary, `Adapter not inited. Command not executed.` now names the robot and says to try again in a few seconds, and a robot going offline is a warning that says what to check — with the matching "back online" line uncommented after who knows how long.
|
|
1781
|
-
|
|
1782
|
-
**14 more log lines were printing a raw 22-character duid to users.** `log-lines-name-the-robot` only inspected template literals written inside the logging call, so anything built into a variable or an `Error` first was invisible — it was checking 39 of 59 calls in one file alone. It now follows the three laundering channels as well, and everything it found is fixed.
|
|
1783
|
-
|
|
1784
|
-
**One resource leak.** `localConnector.js` opened its UDP discovery socket at module load, so requiring the file bound a socket a cloud-only install never uses, a second discovery pass attached a second set of handlers to it, and the first pass's `close()` left it unbindable for the next. It is now created per run and closed once. That also removes the "A worker process has failed to exit gracefully" warning the suite has printed for months, which was masking any real leak.
|
|
1785
|
-
|
|
1786
|
-
**And one coupling that broke while I was fixing the wording.** The transient-error classifier read the reason out of the refusal message with a regex, so making those messages readable turned a calm transport condition back into an error with a stack trace once per poll. Refusals now carry the reason as a code on the error and the prose is free to change.
|
|
1787
|
-
|
|
1788
|
-
688 tests, up from 672.
|
|
1789
|
-
|
|
1790
|
-
## 3.5.4
|
|
1791
|
-
|
|
1792
|
-
**3.5.3's log line named the wrong bridge on exactly the setup it was written for.** It read `_bridge` off the platform config, and Homebridge's `childBridgeFork` deletes that key before a plugin loads — "some plugins do not like unknown config". So on a child bridge it fell through to the main-bridge branch and pointed at the status page QR code: the wrong instruction, in the release about giving the right one. My own server printed it four minutes after publish.
|
|
1793
|
-
|
|
1794
|
-
The block is now read from `config.json`, from the platform entry matching this one. Anything unreadable — missing file, bad JSON, no matching block — falls through to a line that covers both bridges rather than asserting one, because a confident wrong answer is the thing being fixed. Three branches, one shared set of strings, so a later edit cannot correct one and leave two.
|
|
1795
|
-
|
|
1796
|
-
Verified red against 3.5.3: 3 of 29 fail, exactly the disk-read and fallback rules.
|
|
1797
|
-
|
|
1798
|
-
## 3.5.3
|
|
1799
|
-
|
|
1800
|
-
**Turning the switches on registered them, logged them, and showed nothing in Apple Home.** My child bridge carried `hap: { enabled: false }` — reasonable for a Matter-only setup, since this plugin published nothing over HAP before 3.5.0. In that state the switches exist inside Homebridge and are advertised to nobody, and no QR code helps until HAP is switched back on.
|
|
1801
|
-
|
|
1802
|
-
3.5.0 mentioned pairing in one sentence, and the sentence was wrong: it assumed the bridge needed pairing, not enabling.
|
|
1803
|
-
|
|
1804
|
-
The startup log now answers which of three situations you are in, once per start, and warns rather than informs when HAP is off — an info line about a feature that cannot work reads like the 90 other info lines a start produces. The settings page shows the steps under the toggle when the feature is on, and the README and the setting's own description carry the same three: **Plugins → homebridge-roborock-matter → ⋮ → Child Bridge Config**, check **Enable HAP**, restart, then **Connect to HomeKit** on that screen and scan that QR code. All four surfaces name the two codes that look right and are not — the main Homebridge code, and the robot's Matter code, which covers the vacuum only.
|
|
1805
|
-
|
|
1806
|
-
`__tests__/the-switches-say-which-qr-code-to-scan.test.js` enumerates the rule over the surfaces, because the original failure was that only one surface mentioned pairing at all. Matching ignores markup, so `<strong>` and `**bold**` count as the same instruction. Verified red against 3.5.2: 26 of 26 fail.
|
|
1807
|
-
|
|
1808
|
-
## 3.5.2
|
|
1809
|
-
|
|
1810
|
-
**Apple Home showed a clean mode nobody asked for for the first minute or two of every vacuum-only clean started from Home.** The clean itself was always correct; only the tile lied.
|
|
1811
|
-
|
|
1812
|
-
Measured in [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) (skmzwanke, Saros 10, 12 August 2026) — 114 seconds of it:
|
|
1813
|
-
|
|
1814
|
-
```
|
|
1815
|
-
16:09:20 Applying Vacuum mode to Weebo before starting.
|
|
1816
|
-
16:09:20 ...acknowledged by Roborock in 791 ms via cloud
|
|
1817
|
-
16:09:22 Matter publish for Weebo: ... runMode=1, cleanMode=0 <- what was asked for
|
|
1818
|
-
16:09:29 Matter publish for Weebo: ... runMode=1, cleanMode=2 <- vacuum+mop
|
|
1819
|
-
16:11:23 Matter publish for Weebo: ... runMode=1, cleanMode=0 <- the robot caught up
|
|
1820
|
-
```
|
|
1821
|
-
|
|
1822
|
-
That `2` is derived from the robot's water-box level, which was still reporting its old value seven seconds after the robot had **acknowledged** the command to turn water off. So this is not the robot being slow and the plugin being right — the plugin contradicted itself. The prep path already documents that this exact reading lies in this exact window and refuses to consult it when deciding whether to send; the reporting path published the same reading as truth.
|
|
1823
|
-
|
|
1824
|
-
**The fix is about knowledge, not about the water box.** A clean type this plugin sent _and had acknowledged_ for the run in progress now outranks a clean type merely _derived_ from the robot's status, until the robot's own report agrees with it once. Same rule as 3.4.11: when the plugin does not know, it says nothing new rather than something untrue.
|
|
1825
|
-
|
|
1826
|
-
It is deliberately bounded, because a pin that outlived its run would break the feature it sits inside — a clean started in the Roborock app is supposed to be reported in the mode the robot is actually running:
|
|
1827
|
-
|
|
1828
|
-
- Released the moment the robot's own report agrees, so a clean type changed mid-run in the Roborock app is still followed.
|
|
1829
|
-
- Released when the run it was applied for ends — but **not** before that run has been seen running, or a publish landing in the gap between the acknowledgement and the robot reporting it had started would have released it before it did anything.
|
|
1830
|
-
- Dropped by an explicit Apple Home selection, and never taken at all when the apply failed: without an acknowledgement there is nothing known, and pinning an unconfirmed intent would hide a real failure.
|
|
1831
|
-
- The disagreement is reported **once per run on warn**, not silently and not once per publish. A robot that acknowledges the command and then ignores it is a different and worse fault than a robot that lags, and the log is the only way to tell them apart without debug logging on.
|
|
1832
|
-
|
|
1833
|
-
The clean-type family reduction (a suction-level mode is a vacuum-family variant) was written out by hand in the settings builder and was needed in a second place for this. It is now one helper called from both, and a test counts the copies — two hand-written copies of one fact drifting apart is the most repeated defect in this codebase.
|
|
1834
|
-
|
|
1835
|
-
`__tests__/applied-clean-type-outranks-a-lagging-robot-report.test.js` (20 tests). **Verified red against untouched 3.5.1: 16 of 20 fail**, and the symptom test fails with the symptom itself — `Expected: 0, Received: 2`, exactly the 16:09:29 publish. The 4 that pass in both are the no-regression guards.
|
|
1836
|
-
|
|
1837
|
-
## 3.5.1
|
|
1838
|
-
|
|
1839
|
-
**The README shipped saying two things were unverified, eight minutes after they had been verified.** No behaviour changes in this release.
|
|
1840
|
-
|
|
1841
|
-
pponce finished the survey in [#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3) while 3.5.0 was being published: **pausing** a running clean _is_ offered as an automation action, and a Matter vacuum is **not** offered as an automation _trigger_ at all — the vacuum cannot be selected when setting a trigger, only when choosing an action. The page that went out with 3.5.0 still called both of those unmeasured.
|
|
1842
|
-
|
|
1843
|
-
The trigger finding is the one worth reading twice. It means "when the robot finishes cleaning, do X" cannot be built in Apple Home today — and the switches added in 3.5.0 do not change it, because they are inputs an automation turns on, not accessories that report what the robot is doing. Something read-only would be needed for that. It is on the roadmap as a question, not a plan, because the right shape depends on what people actually want to automate on.
|
|
1844
|
-
|
|
1845
|
-
`__tests__/readme-claims-match-what-was-measured.test.js` is now driven by one registry of findings instead of a constant per finding, and that is the real fix. The same drift has now happened twice in three days, both times because a measurement landed, one sentence was corrected, and a second sentence about the same fact was left behind. Each row carries the command, the verdict, and — for an absent finding — the denial its own claim has to make. The rules then demand that every offered command is positively stated, every absent one is denied wherever the README pairs it with automations _and_ stated as absent at least once, every unmeasured one stays qualified, and no command sits in both lists — which is exactly the shape of the 3.5.0 miss. Verified red against the shipped 3.5.0 README: 2 of 20 fail.
|
|
1846
|
-
|
|
1847
|
-
## 3.5.0
|
|
1848
|
-
|
|
1849
|
-
**Apple Home cannot send a Matter vacuum to its dock from an automation, so the plugin now offers a switch that can.** The measurement is pponce's, in [#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3): the commands all work from the tile and from Siri, but "send the vacuum to its dock" is not on Apple's list of automation actions for a Matter vacuum, and he moved that part of his setup to a HAP-based plugin rather than go without it. 3.4.19 stopped the README from promising what could not be delivered. This release delivers it.
|
|
1850
|
-
|
|
1851
|
-
Turning on **Add Home app switches for Dock, Pause and Find** publishes one plain HomeKit switch per robot per action you select — `Vicky Return to Dock`, `Vicky Pause`, `Vicky Find`. A switch is an automation action everywhere, so the schedule that could not reach the tile can reach the switch. Each one is momentary: it turns itself off again 1.5 s after it is pressed, because there is no docking state worth mirroring and a second state machine racing the same laggy Roborock snapshot is exactly what issues #4 and #12 were about.
|
|
1852
|
-
|
|
1853
|
-
**The press takes the existing command path rather than a second one.** It routes into the same `returnToDock` / `pauseCleaning` / `identifyVacuum` the Matter cluster handlers use, so it inherits the acknowledgement wait and timing log (#12), the decision to forward a command the cached snapshot claims is unnecessary (#4), the retry when Roborock times out while the robot is still cleaning, and the optimistic cluster write that moves the tile so a robot driving home does not read Ready. The log now names the surface that asked: `Sending Vicky back to dock from the Home switch.` next to `Sending Vicky back to dock from Matter.` — the first question when a schedule misfires is which one sent it.
|
|
1854
|
-
|
|
1855
|
-
Three things this had to get right that are not in the feature description:
|
|
1856
|
-
|
|
1857
|
-
- **The Matter-only sweep would have deleted them.** `discoverDevices()` has always unregistered every cached HAP accessory without looking at what it was, which was correct while this plugin registered none. A switch shipped against that rule would work until the first restart and then vanish out of every automation using it, while the log went on calling it a legacy accessory. The sweep now partitions on a context marker written into the accessory — not on its name, which is editable in the Home app.
|
|
1858
|
-
- **They are registered under the real package name.** `PLUGIN_NAME` has never matched package.json, and Homebridge stores whatever it is given as the accessory's owning plugin. On restore it falls back to searching by dynamic platform name, which repairs the mismatch with an alarming log line — and throws when two plugins claim the same platform name, at which point the accessory is called orphaned and removed. Matter keeps its own cache and cannot be moved without forcing every user to re-pair, so the correct identifier is introduced for HAP only.
|
|
1859
|
-
- **An empty device list does not remove anything.** The same trap `unregisterStaleMatterAccessories` documents: a failed startup arrives at discovery as "the account has no robots". Removing a switch because the config no longer asks for it is safe; removing one because the Roborock cloud had a bad minute is not.
|
|
1860
|
-
|
|
1861
|
-
Off by default, per robot per action, and the Find switch is only published for robots that report `find_me` at all. No re-pairing is needed to add or remove them — they are HomeKit accessories and arrive over the Homebridge bridge, which does mean a user who has only ever paired the Matter robot has to pair the bridge itself before they appear. The robot stays a Matter vacuum and is untouched.
|
|
1862
|
-
|
|
1863
|
-
`__tests__/action-switches-survive-the-legacy-sweep.test.js` enumerates the partition over context shapes rather than the two cases I happened to think of, `__tests__/action-switches-are-an-opt-in.test.js` covers the config and removal rules, and `__tests__/action-switch-press-uses-the-matter-command-path.test.js` pins that a press reaches Roborock through the shared path and is named apart from Matter in the log. Verified red: 4 of 8 fail with the old sweep restored, and the empty-device-list rule fails 1 of 17 with the guard removed.
|
|
1864
|
-
|
|
1865
|
-
**And the gap turns out to be narrower than this release was written to believe.** pponce went back into Shortcuts after the above was written and measured the rest of the list ([#3](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/3)): **starting a clean is offered as an automation action — whole home or a chosen set of rooms — and so is stopping one that is already running.** Only return-to-dock is missing. So an Apple Home schedule could already do the two things schedules are mostly built for, and the switches are for the one thing it could not: ending a clean early and sending the robot home. The README said "whether it offers the other commands as actions … has not been verified here", which was true the day it was written and false the day after — the same defect as a promise nobody checked, pointed the other way, and more expensive here, because it sends a user off to install a second plugin for a job this one never blocked. The feature table also claimed Apple offers no automation action for Pause and Find either; nobody has measured those, and it no longer says so. `readme-claims-match-what-was-measured.test.js` now enumerates both directions — the dock claim must deny itself in its own words, the two measured-present actions must be stated, and pause/resume/trigger must stay qualified. **Verified red against this release's own README: 3 of 10 failed, exactly the three positive claims.**
|
|
1866
|
-
|
|
1867
|
-
## 3.4.19
|
|
1868
|
-
|
|
1869
|
-
**Two things the README told users were not what had been measured.** No behaviour changes in this release; both are wording, and wording is what people choose a plugin on.
|
|
1870
|
-
|
|
1871
|
-
The feature table promised control "from the Home app, Siri, or automations". Nobody had ever checked the last word, and a user has now measured it: Apple Home does **not** offer sending a Matter vacuum to its dock as an automation action, which is why he had to move that part of his setup to a HAP-based plugin. The table no longer claims it, and a new [Automations in Apple Home](README.md#automations-in-apple-home) section says exactly what is known — the commands all work from the tile and from Siri, one automation action has been measured absent, and the rest is unverified rather than promised.
|
|
1872
|
-
|
|
1873
|
-
The fault-reporting section also blamed the "Report faults in Apple Home" setting for a tile that got stuck on "Updating…". A controlled test on the same robot, with fault reporting and dock-fault escalation both switched on and a genuinely empty clean-water tank, has now shown the tile staying in Ready throughout: the wedge was a stale pairing from an earlier install, which the Troubleshooting section already explained. The correction is written into the section rather than quietly deleted, because someone may have left the feature off on the strength of it. The same test also confirms the main finding more strongly than before — the fault was published beside a full Matter **Error** state this time, not just beside Charging, and Apple Home still drew nothing.
|
|
1874
|
-
|
|
1875
|
-
## 3.4.18
|
|
1876
|
-
|
|
1877
|
-
**A robot that drops off the Roborock cloud filled the log with stack traces about the plugin correctly deciding not to send.** When the transport a request would need is not there — the robot is marked offline, MQTT is down, or the local socket is not connected — the request queue declines to put it on the wire. That is a deliberate, calm decision, and it writes its own debug line where it happens. But the rejection then arrived at the error handler unclassified, so it was logged as a plugin error, with a full stack trace, once per poll, for as long as the condition lasted.
|
|
1878
|
-
|
|
1879
|
-
The shape it takes in a real log: a robot goes offline at 3:28 AM, and a single poll cycle produces six stack-traced errors in the same second, followed by one a minute after that. Nothing is wrong with the plugin in any of them.
|
|
1880
|
-
|
|
1881
|
-
These three refusals now go through the same throttle the request timeouts have always used: one warning, then a suppressed-count summary when the window reopens. Each reason keeps its own bucket, so a robot being offline does not silence the reporting of a separate MQTT outage. Errors that are genuinely the plugin's fault — including its failure to build a request at all — are untouched and still log with their stack.
|
|
1882
|
-
|
|
1883
|
-
`__tests__/refused-sends-are-not-plugin-errors.test.js` enumerates the rule over the source rather than over the three messages that were reported: every message the request queue can reject with must be one the classifier recognises, so a refusal path added later fails the test until it is classified. Verified red against 3.4.17: 10 of 15 failed.
|
|
1884
|
-
|
|
1885
|
-
## 3.4.17
|
|
1886
|
-
|
|
1887
|
-
**Installing this package asked npm to build it, and that build could only ever fail or warn.** `dist/` is in the published tarball and `main` points into it, so nothing a user installs needs compiling — but package.json still carried `"prepare": "npm run build"`, a hook npm runs at install time. The two things it could do to a user, both measured:
|
|
1888
|
-
|
|
1889
|
-
- **Fail.** Installing straight from the git repository clones the package to a temp directory and runs `prepare` there. The internal dependency install inherits `-g` and `--prefix` from the outer command, so the clone is left with no `node_modules` at all, and the build dies with `sh: rimraf: command not found` / `code 127`. Reproduced line by line from npm's own debug log; not fixable from this side, which is why prebuilt tarballs are now the way to install anything that is not on npm.
|
|
1890
|
-
- **Warn.** npm 11.16 and later will not run install scripts it has not been told to trust, and prints `npm warn allow-scripts homebridge-roborock-matter (prepare: npm run build)` for a tarball install — a supply-chain warning naming this package, for a build that did not need to happen. It cost a tester a round trip before it cost anyone else anything.
|
|
1891
|
-
|
|
1892
|
-
The build now runs on `prepack` instead, so packing or publishing still cannot produce a tarball without `dist`, while installing one asks for nothing. No behaviour in the plugin changes.
|
|
1893
|
-
|
|
1894
|
-
`__tests__/installing-the-package-runs-no-build.test.js` holds both halves of the rule: package.json may declare none of npm's install-time lifecycle scripts, and a packing script must perform the build. Dropping the hook without moving the build is the obvious way to get this wrong, so both are enumerated rather than assumed. Verified red against 3.4.16: 3 of 13 failed.
|
|
1895
|
-
|
|
1896
|
-
## 3.4.16
|
|
1897
|
-
|
|
1898
|
-
**The clean mode shown on the tile is now applied before every Matter-initiated start, changed or not.** The prep that applied it only ran when Apple Home sent a `ChangeToMode` command — and Home only sends that when the selection actually changes. So the most ordinary case of all went unhandled: the mode Home already displays is usually the mode the user wants, so they never tap it, nothing was sent, and the robot ran in whatever mode it had been left in.
|
|
1899
|
-
|
|
1900
|
-
Measured end to end in #8: a "Vacuum" start with no preceding mode request produced no `Applying ... mode` line at all and the robot mopped, while the very same start one explicit tap later sent it and vacuumed. It is deliberately not skipped when the robot looks like it already matches, because that reading is the one that lies. The user's levels are preserved — only the clean _type_ is pinned.
|
|
1901
|
-
|
|
1902
|
-
`__tests__/every-matter-start-applies-the-displayed-clean-mode.test.js` holds the rule over the source: every start dispatch must apply the mode, and stop/pause must not. Verified red against 3.4.15: 8 of 16 failed.
|
|
1903
|
-
|
|
1904
|
-
## 3.4.15
|
|
1905
|
-
|
|
1906
|
-
**The clean-mode prep was never losing its window to the commands it sends. It was losing it to a read nobody was waiting for.** skmzwanke's log from 3.4.14 (#8) has the whole thing in ten seconds:
|
|
1907
|
-
|
|
1908
|
-
```
|
|
1909
|
-
1:09:11 Applying Vacuum + Mop mode to Weebo before starting.
|
|
1910
|
-
1:09:13 Unable to apply Vacuum + Mop mode ...; prep timed out after 2500 ms.
|
|
1911
|
-
1:09:13 Matter service area clean command ... acknowledged in 2589 ms via cloud
|
|
1912
|
-
1:09:21 Roborock did not confirm the water mode and suction level for Weebo
|
|
1913
|
-
```
|
|
1914
|
-
|
|
1915
|
-
The water command was acknowledged over the cloud in about a tenth of a second. The report of its failure arrived eight seconds after the clean had already started — ten seconds after the prep began, which is the transport's default timeout and nothing else in this codebase.
|
|
1916
|
-
|
|
1917
|
-
After every `set_*` command, `vacuum.command` awaited the paired `get_*` to refresh this plugin's own state cache. That read was issued **with no options at all**: not the caller's transport, so it went out over the LAN of a user who has `preferCloudForMatterCommands` on and whose LAN times out every request at ten seconds; and not the caller's timeout, so it ignored the 2500 ms budget the two previous releases went to such lengths to compute. The window was spent before the fallback water command — the one that would have worked — ever got its turn.
|
|
1918
|
-
|
|
1919
|
-
- **A command is finished when the robot acknowledges it.** The state refresh that follows is bookkeeping: it is no longer awaited, it can no longer fail the command, and it can no longer delay one.
|
|
1920
|
-
- **It inherits the caller's transport, never the caller's deadline.** A caller that asked for cloud does not get a local request it never asked for.
|
|
1921
|
-
- **`getParameter` now carries the caller's options on every branch.** Only the `get_status` branch did, by hand; every other one silently reverted to the local transport and the ten-second default.
|
|
1922
|
-
- **One place decides which options travel with a request.** Two hand-kept copies of that list is what let the refresh drift away from the command that triggered it.
|
|
1923
|
-
- **A command with no `set` in its name is no longer re-sent to the robot as its own "refresh"** — `parameter.replace("set", "get")` returns the command unchanged for those.
|
|
1924
|
-
|
|
1925
|
-
`__tests__/command-refresh-stays-out-of-the-callers-budget.test.js` holds the rule over the source — no request issued on a caller's behalf may bypass the one option-carrying helper — and reproduces #8 end to end through the real `vacuum` class. Verified red against 3.4.14: 9 of 11 failed, the command took 3041 ms instead of resolving on its acknowledgement, and the fallback water command was never sent at all.
|
|
1926
|
-
|
|
1927
|
-
**Note for anyone reading the older prep tests:** they stub `api.vacuums[duid].command` wholesale. That is exactly why ten seconds could hide inside it for two releases. This one does not.
|
|
1928
|
-
|
|
1929
|
-
## 3.4.14
|
|
1930
|
-
|
|
1931
|
-
**A "vacuum only" room clean could still mop, because the command carrying that choice was started inside the prep window but not finished inside it.** 3.4.8 fixed the ordering — the water command goes first and no failure cancels a later command — and skmzwanke's log from the fixed version shows why ordering alone was not enough:
|
|
1932
|
-
|
|
1933
|
-
```
|
|
1934
|
-
9:44:57 Applying Vacuum mode to Weebo before starting.
|
|
1935
|
-
9:45:00 Unable to apply Vacuum mode to Weebo before starting; continuing with
|
|
1936
|
-
the start command. Matter clean mode prep timed out after 2500 ms.
|
|
1937
|
-
9:45:00 Matter service area clean command for Weebo was acknowledged ...
|
|
1938
|
-
```
|
|
1939
|
-
|
|
1940
|
-
The prep sequence sends up to three commands one after another, each with a two-second timeout, inside a window of 2500 ms that the caller races the whole sequence against before sending the start command. Three seconds of commands do not fit in two and a half. So the command carrying his "vacuum only" choice was merely _in flight_ when the window closed, the start command overtook it, and his Saros 10 mopped the room he had asked to be vacuumed — the same outcome as before the fix, arrived at by the clock instead of by an early return.
|
|
1941
|
-
|
|
1942
|
-
- **Each command is now sized against what is left of the window**, not given a fixed timeout. The command that carries the user's clean mode goes first and gets the window; a cosmetic one that no longer fits is reported rather than started. The sequence ends by itself instead of being cut off mid-command.
|
|
1943
|
-
- **A command is never sent with a non-positive timeout.** Below the prep, a timeout of zero or less is not an override — it silently restores the transport's own ten-second default, four times the whole window.
|
|
1944
|
-
- **Every way the prep can end without the robot having confirmed the selected mode now reports at warn, from one place.** One of those ways was debug-only: when the plugin believes water is controllable — so Apple Home is offering "Vacuum" and "Vacuum and mop" — but has no water command left to send, the mop ran anyway and nothing above debug said so.
|
|
1945
|
-
- **The Q7/B01 branch was silent about all of this** and now reports the same way. Its clean type is the same kind of command and it had the same arithmetic problem.
|
|
1946
|
-
|
|
1947
|
-
`__tests__/clean-mode-prep-fits-its-window.test.js` holds the rule over both dialects: no command may be started that cannot finish inside the window the caller is waiting on, and the window is handed down from the one constant that defines it rather than restated. Verified red against 3.4.13, where the suction command is started at t=2000 with a two-second timeout while the start command goes out at t=2500.
|
|
1948
|
-
|
|
1949
|
-
## 3.4.13
|
|
1950
|
-
|
|
1951
|
-
**Nothing in this release changes what the plugin does. It removes things that were never doing anything, and two of them were actively lying.**
|
|
1952
|
-
|
|
1953
|
-
- **Ten of the 11 shipped languages could never load.** `this.language` is only ever set from `options.language`; the sole production construction site passes none, the UI server hardcodes `"en"`, and no setting exposes the choice. So de, es, fr, it, nl, pl, pt, ru, uk and zh-cn — 78 KB of translations — were installed on every user's disk and read by nobody. They are gone. A test now enumerates the rule rather than the ten filenames: **a locale that ships must be selectable**, so adding one back fails until there is actually a way to pick it.
|
|
1954
|
-
- **The README claimed 463 automated tests in one paragraph and 263 in another.** Both were wrong. Two hand-written numbers describing one fact will drift apart and neither gets corrected, because nothing checks them. There is one number now, and a test checks it against what the suite actually declares. It deliberately does not pin an exact figure — `test.each` expands at runtime and no static reader can know by how much — it pins the two things that went wrong: state it once, and keep it in a defensible band.
|
|
1955
|
-
- **The publish log line still rendered `fault=…` from an attribute withdrawn in 3.4.1.** The branch was unreachable, and worse, it read as evidence the feature still existed. Removing it settles a real contradiction: `matter-fault-reporting.test.js` pinned that `operationalError` is never published, while `matter-publish-line-logs-every-change.test.js` hand-built one and asserted it rendered. Two tests disagreeing about whether a feature exists is worse than either answer.
|
|
1956
|
-
- **An orphaned ioBroker map viewer and a MITM sniffing script** (`roborockLib/lib/map/`, `roborockLib/lib/sniffing/`) were excluded from the npm package rather than deleted — which is exactly how they survived unreviewed for so long. Ignored by the package, invisible in review, referenced by nothing.
|
|
1957
|
-
- **Ten functions whose definition was their only occurrence in the entire tree** are gone: `getHomeID`, `decodeSniffedMessage`, `getConnector`, `updateDataExtraData`, `setupBasicObjects`, `getCleanSummary`, `resolve102Message`, `resolve301Message`, `BytesToInt`, `getErrorCodeDescription`, plus the unused `B01_REQUEST_DPS`/`B01_RESPONSE_DPS` constants and three exports nothing imported. `resolveLiveRoomId` went too — a one-line wrapper over `describeLiveRoomResolution` with no production callers, kept alive only by tests. Two ways to ask the same question is how one of them drifts.
|
|
1958
|
-
- **`errorCodes` was NOT removed**, though a first pass called it dead. `deviceFeatures.js` still uses the table for its `error_code` state mapping. Worth recording: the check that catches this is grepping the whole tree, not reasoning about one file.
|
|
1959
|
-
- **Two user-facing claims were false.** The `preferCloudForMatterCommands` setting promised to keep "the legacy HomeKit accessories unchanged", and a startup log line told users "The existing HomeKit accessory will continue to work." This fork removed every HAP accessory by design — there is nothing to fall back to. The log line now says what to actually do: enable Matter for the bridge.
|
|
1960
|
-
- **ROADMAP.md was eight releases stale**, still titled for a different package, pointing at an `AGENTS.md` that has never existed in the tree, and listing HomeKit controls as delivered features 13 lines above its own note that all HAP accessories were removed. Rewritten, with the pre-Matter-only entries labelled rather than deleted so the history stays readable.
|
|
1961
|
-
|
|
1962
|
-
## 3.4.12
|
|
1963
|
-
|
|
1964
|
-
**A live frame whose only field was the suction level or the clean type was thrown away before anything could read it.** A live message passes two checks on its way to Apple Home: a gate that decides whether it is a status message at all, and a check in the publish path that decides whether anything meaningful arrived. Both named their fields by hand, and they had drifted apart — the publish path was taught that a frame carrying only `fan_power` or only `matter_clean_type` counts (a suction or mop-mode change made in the Roborock app, or picked by SmartPlan, pushes exactly that), while the gate one level below still listed five of the seven fields and discarded such a frame before the publish path ever ran.
|
|
1965
|
-
|
|
1966
|
-
- **Both checks now derive from one list.** Adding a field the publish path reads covers the gate in the same edit, so the two cannot drift again.
|
|
1967
|
-
- **A frame carrying no meaningful field is still ignored**, so this widens the gate exactly as far as the publish path can actually use and no further.
|
|
1968
|
-
|
|
1969
|
-
The fix that added the two fields was made one level down from the gatekeeper, which is why it looked complete and was not. `__tests__/live-status-gate-matches-what-the-publish-reads.test.js` pins the rule over the source rather than the two field names, so a field added later is covered the moment it is read.
|
|
1970
|
-
|
|
1971
|
-
## 3.4.11
|
|
1972
|
-
|
|
1973
|
-
**Two docked Q7s flipped their Apple Home clean mode to "Vacuum" and back every 90 seconds, and the plugin was reporting a level it had never measured.** Caught in a log from a plugin author's own robots on 3.4.10: every battery tick produced a pair of publishes about a second apart, the first saying `cleanMode=0` and the second saying `cleanMode=6` — ten pairs in 14 minutes, on both robots, at the same battery value.
|
|
1974
|
-
|
|
1975
|
-
Mode 6 is "Max Vacuum", the level the robot is actually set to. Mode 0 is plain "Vacuum", a level nobody selected. When suction-level clean modes are announced (`enableFanPowerCleanModes`), the reported mode is derived from the robot's live fan power — but the derivation had no answer for "the fan power cannot be read right now". It fell through to the last Matter selection, which defaults to plain Vacuum, so a momentary gap in the reading was published as a definite statement about the robot's suction level.
|
|
1976
|
-
|
|
1977
|
-
- **An unreadable fan power now leaves the reported level unchanged.** Saying nothing new beats saying something untrue. This covers a value that reads fine but is not one of the announced levels (such as 105, "fan off") as well as no value at all — in both cases the plugin does not know which announced level to report, and inventing one is the defect.
|
|
1978
|
-
- **An explicit Apple Home selection still wins immediately.** Choosing a mode discards the remembered level, so a user's choice is never shadowed by what the robot said before they made it — including a deliberate choice of plain Vacuum.
|
|
1979
|
-
- **A robot whose fan power has never been readable is unaffected**, and so is any robot that does not announce suction levels.
|
|
1980
|
-
|
|
1981
|
-
This is the same class of defect as 3.4.6 and 3.4.7: reporting a value derived from missing data as though it had been measured. What makes the fan power intermittently unreadable on these robots is a separate question and is not answered here — but the reported mode no longer depends on the answer.
|
|
1982
|
-
|
|
1983
|
-
## 3.4.10
|
|
1984
|
-
|
|
1985
|
-
**The Q7 position that never resolved to a room is not a position at all.** 3.4.9 asked the two Q7s to report the range their room outlines occupy, and they answered: Garage sat in a map spanning cells 52–171 by 43–187, 1. Sal in one spanning 38–293 by 90–227. Back-computing through each map's own origin and resolution gives the same coordinate for both — exactly (1100.0, 1100.0), on two robots, two maps, and 12 minutes of active cleaning. A number that identical is arithmetic, not a place a robot stood, which means live-room tracking on these models has never worked from that field.
|
|
1986
|
-
|
|
1987
|
-
- **The miss line now surveys the payload rather than asserting anything about it.** It prints the size of every top-level field and every scalar inside the small ones, keyed by field path. Two consecutive lines are then a diff: the value that changed while the robot was driving is the position, and the submessage that grew is the trail behind it.
|
|
1988
|
-
- **Varints are surveyed, not just floats.** The pose message carries an `update` flag alongside its coordinates, so a float-only dump would have printed two plausible-looking numbers and hidden the field saying they were stale.
|
|
1989
|
-
- **The survey descends one level.** A pose trail's last point is by construction where the robot is now, and repeated paths overwrite, so the end of a trail lands in the log under a stable key.
|
|
1990
|
-
- **A bare scalar on the map itself is now visible.** The parse loop only ever descended into submessages, so a position stored as a plain float would not have appeared anywhere.
|
|
1991
|
-
- **It is bounded and it cannot throw.** The occupancy grid is measured rather than walked, the scalar count is capped, recursion is capped, and bytes that turn out not to be protobuf are swallowed. A diagnostic must never be the reason a robot stops reporting its room.
|
|
1992
|
-
|
|
1993
|
-
This changes no behaviour. It exists because guessing another field number would have been the third guess in a row on this code path, and the robots were running.
|
|
1994
|
-
|
|
1995
|
-
## 3.4.9
|
|
1996
|
-
|
|
1997
|
-
- **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.
|
|
1998
|
-
|
|
1999
|
-
## 3.4.8
|
|
2000
|
-
|
|
2001
|
-
**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.
|
|
2002
|
-
|
|
2003
|
-
On a v1 robot the difference between "Vacuum" and "Vacuum and mop" **is** the water-box mode: choosing Vacuum sends water-box OFF. Fan power only picks a suction level within the chosen mode. The prep sequence sent fan power first and, if that command timed out, returned — so the water command was never sent at all. In his log, `set_custom_mode` timed out after two seconds, `set_water_box_custom_mode` never appeared, and the robot kept the mopping setting it already had from the Roborock app. A cosmetic command that did not answer in time cancelled the one carrying the user's actual choice.
|
|
2004
|
-
|
|
2005
|
-
- **The water command now goes out first, and no command in the sequence is cancelled by another's failure.** Dropping the early return cannot delay the start: the caller already races the whole prep against its own timeout, so the early return was buying latency protection that was paid for one level up.
|
|
2006
|
-
- **A partial apply is now announced at warn level**, naming the robot and what was not confirmed. It was a debug line before, which meant that on a default log level the robot simply did the wrong job in silence while the Matter tile reported the mode that had been selected. That mismatch took two rounds of #8 to pin down.
|
|
2007
|
-
- **The rule is enumerated over the sequence, not over the two commands in it today:** no clean-mode prep command may return out of the middle of the sequence. A third setting added later is covered by construction.
|
|
2008
|
-
|
|
2009
|
-
## 3.4.7
|
|
2010
|
-
|
|
2011
|
-
**The diagnostic report told Q7 owners their robot had tried to reach the LAN and failed. It never tried.** Following [#7](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/7), jawnlydon unpaired his robot from Apple Home, uninstalled the plugin, reinstalled it and paired fresh — and his report still read `markedRemote: true`, `remoteReason: marked-remote-after-connect-failure`, `connectionStatus: Cloud fallback`, "usually because LAN TCP was not connected at that moment". His `roborock.vacuum.sc05` speaks the B01 protocol, which has no LAN request surface at all: the plugin marks these robots cloud-only at startup precisely so it never opens a local socket to them. Every line above described a network fault that could not occur, and he spent an evening chasing it.
|
|
2012
|
-
|
|
2013
|
-
- **A robot marked remote now records why it was marked.** Membership of the remote set could tell the report _that_ a robot was on cloud transport but not _why_, and the report assumed the most common cause — a failed local TCP connect — for every member. The two causes have nothing in common: one is a protocol with no local side, the other is a genuine LAN failure worth investigating. Both reasons now travel with the mark, from the one place that sets it.
|
|
2014
|
-
- **A future marking that forgets its reason degrades to "the vacuum is marked remote".** Uninformative, and deliberately so. Guessing the usual cause is what turned a design decision into a phantom network fault in the first place.
|
|
2015
|
-
- **The device card no longer calls the only transport a fallback.** A B01/Q7 robot now reads `Cloud control (this model)` with a hint saying its protocol has no LAN control surface and that a blank local IP, discovery state and TCP state are expected. The LAN connection test stops telling its owner to wait for a discovery that is never coming, and the robot is no longer flagged as a likely cloud fallback. A robot on a LAN-capable model that really is falling back is still reported exactly as before.
|
|
2016
|
-
- **The wording moved into plain JavaScript** (`roborockLib/lib/connectionState.js`), because the test job runs before any build and could therefore only grep the TypeScript UI server for these strings. They are not decoration — they are what an owner acts on when a robot will not respond — so they are now exercised directly.
|
|
2017
|
-
- **The rule is enumerated over the source tree**, not over the two call sites that exist today: no code path may mark a robot remote without stating its reason. A hand-written list of call sites is the same mistake as a hand-written list of files or log lines.
|
|
2018
|
-
|
|
2019
|
-
## 3.4.6
|
|
2020
|
-
|
|
2021
|
-
**Trying cloud-only mode once marked a robot "Cloud only" forever.** jawnlydon reported in [#7](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/7) that a "Cloud Only" instance of his robot "seems to have stuck around through several re-pairs" — surviving a bridge restart, repeated Apple Home re-pairings, and a complete uninstall and reinstall of the plugin. It was not a leftover accessory. It was one stale field.
|
|
2022
|
-
|
|
2023
|
-
- **The markers cloud-only mode writes are now retracted when it is switched off.** Enabling the mode stamps a robot's transport diagnostics to say local transport is disabled, and those diagnostics are persisted. `tcpConnectionState` is only ever rewritten when a LAN connection is actually attempted — and none is attempted for a robot no local IP was discovered for — so for such a robot the marker stayed on disk permanently, outliving the setting that wrote it. Startup now reconciles the markers in both directions, clearing only the fields that still hold the marker value so a live LAN connection is never stomped.
|
|
2024
|
-
- **The report stopped contradicting itself.** Reading that stale marker back, the device card said `connectionStatus: Cloud only` with the hint "Cloud-only mode is enabled, so local LAN discovery and local TCP control are disabled" — two lines under the same report's `cloudOnlyMode: disabled`. The report was pointing at a setting that was off, and it cost its reader an evening.
|
|
2025
|
-
- **Setting and clearing derive from one table**, so a marker added later is retracted later. A hand-written list of fields to put back is the same mistake as a hand-written list of files or log lines, one level down — the lesson 3.4.3 and 3.4.5 each learned in their own layer.
|
|
2026
|
-
- **`cloudOnlyMode` in the diagnostic report now quotes the saved config, not the checkbox.** The `matterFeatures` line was fixed for exactly this reason and the fix stopped at that line, leaving the line directly above it still reading its form control — so a report could state a setting the running plugin did not have. A test now enumerates the rule over the source: nothing the report builder reaches may read the settings form, except the helpers whose whole job is to warn that the form and the saved config disagree. Unsaved edits to cloud-only mode now raise that warning too.
|
|
2027
|
-
|
|
2028
|
-
## 3.4.5
|
|
2029
|
-
|
|
2030
|
-
**The one log line that exists to diagnose Apple Home display problems was hiding the transitions.** `Matter publish for <robot>: battery=…, operationalState=…, runMode=…, cleanMode=…` was added so an Apple Home display issue could be settled from a single log excerpt — and it was only ever emitted when the **battery** value changed. skmzwanke reported in [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8) that his Apple Home tile sat on "Traveling to Room"/"Preparing" for an entire cleaning run, and sent a log covering that whole run: every operational-state transition in it was invisible, because the line only appeared on the four polls where the battery happened to tick down. The log contained the answer and could not show it.
|
|
2031
|
-
|
|
2032
|
-
- **The line is now written whenever it would read differently from the last one written.** Not "on battery change", and not "on battery, state, run mode or clean mode change" either — a hand-written field list is the same failure mode as a hand-written line list, one level up (the lesson 3.4.3 learned about file lists). Comparing the rendered line means every value the line names triggers it by construction, including a value added to the message later.
|
|
2033
|
-
- **It now covers every publish path.** The decision moved into the publish routine itself, so state changes arriving on a live MQTT frame are logged too, not only those seen by the periodic poll. A heartbeat's forced republish of unchanged values still says nothing, so the self-healing full write stays silent.
|
|
2034
|
-
- **The battery resync line no longer claims something that is impossible.** It said it "forced a fresh Matter attribute report". `PowerSource.batPercentRemaining` carries the Matter "changes omitted" quality, and the specification is explicit that such an attribute "SHALL NOT have delta changes published as part of a Subscribe interaction"; matter.js closed the request to opt out of that as working-as-intended on 28 July 2026 ([matter.js#4163](https://github.com/project-chip/matter.js/issues/4163)), noting that ecosystems are expected to poll these attributes themselves. The resync still does what it can — republishing the attributes bumps the cluster data version, so a controller that reads gets a new version rather than a value untouched since pairing day — and the line now says that instead. A frozen battery percentage in Apple Home is an Apple-side gap, reportable through Apple's feedback process, and no bridge-side workaround will fix it.
|
|
2035
|
-
|
|
2036
|
-
## 3.4.4
|
|
2037
|
-
|
|
2038
|
-
- **Nine Saros 10 status fields are now mapped.** 3.4.3 started naming unknown `get_status` fields once instead of once a minute, and asked owners to paste that line into a model report. skmzwanke did exactly that for his `roborock.vacuum.a144` ([#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8)), so `home_sec_status`, `voice_chat_status`, `home_sec_enable_password`, `extra_time`, `sterilize_status`, `rst`, `cleaning_info`, `exit_dock` and `seq_type` are known from now on and his log is quiet. None of them drive behaviour — control, battery, rooms and state come from a model-agnostic path — so this is purely about not pestering the owner of a new model about fields the plugin had simply never met.
|
|
2039
|
-
|
|
2040
|
-
## 3.4.3
|
|
2041
|
-
|
|
2042
|
-
**A robot with no model profile no longer fills your log with the same request forever.** Models the plugin has no dedicated profile for — the Saros 10 in [#8](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/8), the Qrevo CurvX in [#6](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/6) — run on the capability-derived path, which works, but every status field the profile did not name produced its own `Unsupported attribute … Please contact the dev` warning on every status poll. For the Saros 10 that is eight warnings a minute: about 11,500 requests a day to report the same eight fields. Both issues were promised this would be quietened.
|
|
2043
|
-
|
|
2044
|
-
- **Each unmapped field is now reported once per robot, in one line, and then never again.** A firmware's set of unmapped fields is fixed, so a repeat says nothing a user can act on. Repeats go to debug. A time-based throttle was the wrong shape: it would bring the message back forever, which is the complaint rather than the fix.
|
|
2045
|
-
- **A field nobody has seen before still gets through.** If a robot starts sending something new after hours of uptime, it is reported on its own — quietening the noise must not also hide the signal, since these lines are the raw material for a model profile.
|
|
2046
|
-
- **The line is now usable in a model report.** It names the robot and model, lists every field with its value, and says plainly that control, battery, rooms and state do not depend on them. Object values are serialised instead of arriving as `[object Object]`, which is what `cleaning_info` looked like in #8 — the one field where the shape was the interesting part.
|
|
2047
|
-
- **Two startup tests no longer assert on the clock.** Both checked that per-robot probes run concurrently by timing them against a 180 ms budget — and on a quiet machine they finish in ~65 ms, so the assertion could only ever fail for a reason it was not testing. A scheduling hiccup was enough to fail a build with the concurrency perfectly correct. The check was also redundant: serialized probes give a peak concurrency of 1, which the neighbouring assertion already catches exactly. They now assert the property directly — every probe started before the first one finished — which holds on any machine under any load. Same defect 3.4.2 removed from the B01 full-chain simulation.
|
|
2048
|
-
- **The log-naming rule from 3.3.2 was itself only half enumerated.** It listed three files by hand, and the files it left out held 12 log lines still printing a bare 22-character duid — including `Device <duid> is offline.`, which is exactly the line someone quotes when asking why a robot dropped out. A hand-written file list is the same mistake as a hand-written line list, one level up. The rule now discovers the file list from the source tree, so a new file is covered the moment it exists, and all 12 lines now name the robot.
|
|
2049
|
-
|
|
2050
|
-
## 3.4.2
|
|
2051
|
-
|
|
2052
|
-
**Q7-series robots are no longer asked for things they cannot answer.** Every restart, each Q7-generation robot (`roborock.vacuum.sc05`, `ss07`, and the rest of the B01 family) logged an unsupported-method notice — most visibly for `get_water_box_custom_mode`, and also for `get_timer` and `get_carpet_clean_mode`. The message blamed the robot, and the robot was never involved: the plugin's own send path rejects v1-only requests for B01 devices before anything reaches the network, and the poller then recorded that self-rejection as though the robot had answered it.
|
|
2053
|
-
|
|
2054
|
-
- **The periodic poller now consults the dialect before asking.** For a B01 robot it skips exactly the requests that have neither a Q7 translation nor a neutral placeholder, and says so once per robot at debug level instead of once per robot as a notice. Classic S- and Q-series robots poll precisely as before.
|
|
2055
|
-
- **The check derives from the translation table itself**, not from a hand-written list of method names — a second list would drift the first time a translation was added, and the drift would only ever show up as noise in somebody's log.
|
|
2056
|
-
- **The poll-profile line stops promising a water-box probe it will not perform** on a robot whose water tank is filled by hand and has no electronic level to read.
|
|
2057
|
-
- **A test enumerates the rule rather than the three reported methods:** for a B01 robot, no periodic poll may be one the dialect cannot answer. Probes added later are covered without anyone having to remember this.
|
|
2058
|
-
- Also fixed: the B01 full-chain simulation ran under Jest's 5-second default, which quietly made suite-wide CPU load an implicit assertion — it began failing on an unrelated new test file. Its wall-clock time was never what it set out to verify.
|
|
2059
|
-
|
|
2060
|
-
## 3.4.1
|
|
2061
|
-
|
|
2062
|
-
**The Matter fault attribute is withdrawn.** Wazza151 ran three controlled tests on an S8 Pro Ultra with a genuinely empty clean water tank ([#5](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/5)) and the result was unambiguous: Apple Home drew no warning with the fault published beside a Charging state, drew no warning with it published beside a forced Error state either, and the tile went into a stuck "Updating…" that needed a manual poke to clear. Everything off, and the tile behaved perfectly.
|
|
2063
|
-
|
|
2064
|
-
So the feature cost people their tile and never once delivered the thing it promised. Apple Home does not appear to render Matter vacuum faults from a bridged accessory at all — the same conclusion 1.4.61 reached when it removed the original write. Rather than keep a switch that can only do harm, it is gone.
|
|
2065
|
-
|
|
2066
|
-
- **`operationalError` is no longer published in any configuration**, and a test pins that it stays that way. The mapping tables from Roborock error and dock codes to Matter error states are removed with it; they were accurate and they were useless.
|
|
2067
|
-
- **The 3.4.0 setting Show Dock & Tank Warnings as Errors is removed.** If it is still in your `config.json` it now does nothing — a stale key cannot resurrect the behaviour.
|
|
2068
|
-
- **Report Faults in Apple Home keeps its worthwhile half.** A robot that has genuinely halted — stuck, blocked brush or wheel, missing dust bin, flat battery, unreachable dock — still reports the Matter **Error** state instead of Ready. Apple renders operational states perfectly well; the same robot shows Charging, Docked, Emptying and Washing correctly. That was always the valuable part.
|
|
2069
|
-
- **The diagnostics report's `matterFeatures` line now reads the saved plugin config rather than the checkboxes on screen.** A tick that has not been saved and had the bridge restarted is not in effect, and a report claiming otherwise sends everyone chasing behaviour the plugin was never exhibiting. If the form has unsaved edits, the report now says so.
|
|
2070
|
-
|
|
2071
|
-
## 3.4.0
|
|
2072
|
-
|
|
2073
|
-
**If you turned on Report Faults in Apple Home in 3.3.0, update.** Field testing on an S8 Pro Ultra found that the feature could leave the Apple Home tile stuck on "Updating…", needing a manual poke to come back — and the same robot behaved perfectly the moment the setting was switched off. That is fixed here, and the reason it happened has changed how the feature works.
|
|
2074
|
-
|
|
2075
|
-
- **A fault is now only ever published alongside the Error state.** 3.3.0 wrote the attribute continuously — the live fault while one existed, NoError otherwise — so a robot sitting on the dock reported "Charging" and "clean water tank empty" in the same breath. A robot cannot be both, and the Matter specification agrees: OperationalError describes the condition "when the OperationalState attribute is populated with Error". A healthy robot's payload now contains no fault attribute at all, byte-identical to running with the feature off, and a cleared fault sends exactly one all-clear rather than attaching NoError to every snapshot forever.
|
|
2076
|
-
- **Dock and tank conditions moved behind their own switch, Show Dock & Tank Warnings as Errors.** The same test established the other half of the picture: with the fault published but the tile not in Error, Apple Home drew nothing. So reporting a dock condition without raising Error is all cost and no benefit. The new switch raises it — which makes the warning visible, at the price of a robot Apple Home may refuse to start even though it could still vacuum. That trade-off is now the user's to make, stated plainly in both settings screens. Off by default, and it does nothing unless fault reporting is on too.
|
|
2077
|
-
- **Robot faults are unchanged and still work:** stuck, blocked brush or wheel, missing dust bin, flat battery, unreachable dock. Those genuinely halt the robot, so state and fault agree and there is no contradiction to confuse a controller.
|
|
2078
|
-
|
|
2079
|
-
## 3.3.2
|
|
2080
|
-
|
|
2081
|
-
- **Finished a job 3.3.1 only half did.** That release converted the B01 log lines to use the robot's name and missed 11 others, including the live-room line for classic S/Q-series robots and the battery resync line — which then appeared in a field log directly above a line that did use the name: `Battery resync for 3tELc5hUekaTlOJEW3YetI` followed by `Matter publish for Garage`. Every user-visible log line now names the robot, and a test enumerates the rule rather than the instances, so the next line written cannot reintroduce it.
|
|
2082
|
-
|
|
2083
|
-
## 3.3.1
|
|
2084
|
-
|
|
2085
|
-
Two field reports arrived within an hour of 3.3.0 and both came down to the same thing: the log and the diagnostics report were answering questions nobody had asked while staying silent on the one that mattered. This release is almost entirely about making the plugin legible.
|
|
2086
|
-
|
|
2087
|
-
- **The live-room log said "the robot may be between rooms" for four different problems.** The resolver returns nothing when the map payload has no header, when it carries no robot position, when it carries no room outlines, or when the position genuinely falls outside every outline — and only the last of those is "between rooms". A day of field logs produced 51 of these messages, every one of them asserting a cause that may not have been the cause. Each case is now named, with the number of outlines in the map and the computed position cell, so a coordinate problem is visible in the log instead of needing a debug build.
|
|
2088
|
-
- **The message identifying a misbehaving robot was the one you could not read.** The failure line printed a raw 22-character duid while the success line beside it printed the robot's name. In a three-robot house that is the difference between a usable log and a wall of identifiers. The live-room, B01 status and B01 room lines all use the name now.
|
|
2089
|
-
- **The attempt counter appeared to reset at random.** A robot resolving back into the room it was already in silently zeroed the miss counter without logging anything, so "attempt 15" was followed by "attempt 5" with nothing in between. Re-entering a known room after a run of misses now says so.
|
|
2090
|
-
- **The startup line announced a cadence the code stopped using.** 3.2.0 changed the at-rest B01 poll from 45 s to 25 s and left the message advertising 45 s — misleading in exactly the area it was meant to explain. The cadence values are named constants now and the message is derived from them, so they cannot drift apart again.
|
|
2091
|
-
- **The diagnostics report now lists which Apple Home features are switched on.** A report that omits them cannot answer "why doesn't Apple Home show this?", which is the first question most of them are sent to answer — and it cost a full round-trip with a user who had run the test correctly.
|
|
2092
|
-
- **The Matter publish line names the robot, and reports a fault when one is being published.** Previously it printed a duid and said nothing about faults, so there was no way to tell whether Apple Home was showing nothing because the plugin sent nothing.
|
|
2093
|
-
- **`operationalError` is no longer part of the accessory's registration snapshot.** It is published on the first runtime update instead, seconds later. Matter commissions the endpoint from that snapshot, and 1.4.61 removed the plugin's fault write precisely because Apple Home reacted badly to it there — so a robot that happens to be faulted when Homebridge starts can no longer change what gets commissioned. The mandatory Matter default covers the gap.
|
|
2094
|
-
|
|
2095
|
-
## 3.3.0
|
|
2096
|
-
|
|
2097
|
-
A robot that has stopped because it is wedged under the sofa has always looked exactly like a robot that finished the job: **Ready**. This release lets the plugin say what is actually wrong — asked for by Wazza151 in [#5](https://github.com/mathiashornbek/homebridge-roborock-matter/issues/5), whose previous Matter bridge showed him when the clean-water tank ran empty.
|
|
2098
|
-
|
|
2099
|
-
- **New setting: Report faults in Apple Home** (off by default). The robot's own faults — stuck, blocked brush or wheel, missing dust bin, flat battery, unreachable dock — are published as the Matter Error state with the Roborock description attached, instead of being flattened to Ready. Dock and tank conditions — clean-water tank empty, waste-water tank full, dust bag missing, air duct blocked, mop-wash tank full — are published as a Matter fault too, but deliberately do **not** force the Error state: a robot whose waste-water tank is full can still vacuum, and an accessory in Error may be refused a Start command by the controller.
|
|
2100
|
-
- **The Error state was never gated for a reason.** `ERROR` (3) is a member of even the basic advertised operational state list, so publishing it was always legal — it was being rewritten to `STOPPED` alongside the states that genuinely did need a gate. That is why no released version has ever shown a Roborock fault in Apple Home.
|
|
2101
|
-
- **A detached water tank or mop pad is not a fault.** Both are the normal, correct configuration for a vacuum-only run, so reporting them would leave a permanent warning on every dry robot's tile. They are read, and deliberately ignored.
|
|
2102
|
-
- **The fault detail can never cost you the tile.** `operationalError` travels in the same cluster payload as the operational state, so a Matter build that refuses the attribute would otherwise freeze Cleaning/Docked along with it — the reason the explicit write was removed back in 1.4.61. If the write is rejected, the plugin immediately re-publishes without it, logs a warning naming the reason, and stops sending it for the rest of the session. An endpoint that is merely still starting up keeps its normal retry and does not disable the feature.
|
|
2103
|
-
- **Diagnostics no longer truncate away the answer.** A Roborock status payload runs to about 50 fields and the export kept the first 30 — which are largely housekeeping, while the 20 it dropped included `dock_error_status`, the single field a question about the dock's water tanks turns on. The fields that matter for a fault report are now always kept, however far down the payload they sit, with the size cap otherwise unchanged and secret redaction untouched.
|
|
2104
|
-
|
|
2105
|
-
## 3.2.0
|
|
2106
|
-
|
|
2107
|
-
Field feedback on the Q7 series: the live room in Apple Home lagged badly behind the robot. One run took 90 seconds to name the first room; another took seven minutes.
|
|
2108
|
-
|
|
2109
|
-
- **The first room of a run now appears as soon as the robot is seen cleaning.** Two throttles were stacking. The status poll backed off to 45 seconds while the robot looked idle, so a clean started from the Roborock app or a schedule was invisible for up to that long; then the live-room map fetch applied its own 20-second gap on top, counted from the last attempt rather than from the start of the run. The idle poll is now 25 seconds, the map gap is 10 seconds, and the transition from idle to cleaning clears the gap so the first fetch goes out immediately. Steady-state pacing during a run is unchanged in spirit — the map payload is still an order of magnitude heavier than a status read and is still paced deliberately.
|
|
2110
|
-
- **A robot that cannot place itself on the map now says so.** When the robot's position doesn't fall inside any known room outline, the plugin used to return silently at debug level. That is the most likely explanation for a run going minutes without a room — in the field a Q7 spent seven minutes doing exactly this — and it was indistinguishable from the feature being broken. Repeated misses are now reported at a level you can actually see, with a count.
|
|
2111
|
-
|
|
2112
|
-
## 3.1.1
|
|
2113
|
-
|
|
2114
|
-
- **The Apple Home Features checkbox still said "Returning status".** 3.1.0 renamed the setting in the config schema but not in the plugin's own settings UI, which is the one most people actually see — so the first person to install it reasonably wondered whether the update had applied at all. Both now read **Dock & Returning status**, and the description spells out that Emptying and Washing only appear while the dock is genuinely doing that, and that mop drying has no Matter equivalent.
|
|
2115
|
-
|
|
2116
|
-
## 3.1.0
|
|
2117
|
-
|
|
2118
|
-
Driven by two open model reports, plus the parts of the 3.0.2 audit that were too large to rush. Nothing here requires re-pairing unless you turn on the dock statuses below.
|
|
2119
|
-
|
|
2120
|
-
- **"Emptying the dust bin" and "Washing the mop" now actually reach Apple Home** (#5). The Extended Operational States toggle promised four statuses and delivered one. The other three were missing from the advertised operational state list — and Matter refuses to publish a state that is not advertised — and the code path that maps them rewrote all three to plain Running regardless of the setting. Both halves are fixed, and the setting is renamed to **Dock & Returning Status** to say what it does. Still opt-in, and still needs one remove-and-re-pair, because Matter fixes an accessory's capabilities at commissioning. Mop _drying_ has no equivalent in the Matter robot vacuum specification, so no plugin can report it; that is now stated in the setting's own description rather than left to be discovered.
|
|
2121
|
-
- **Changing suction no longer makes a cleaning robot claim it is charging.** Live push field 123 was read as charge status. It is fan power — charge status is 133 — so a suction change from the Roborock app, a schedule, or SmartPlan pushed a frame that the plugin interpreted as "charging" while the robot was mid-clean. Fan power, water level and charge status now come off the wire with their real meanings, and a suction change made outside Apple Home refreshes the clean-mode picker instead of being discarded as an empty update (#6).
|
|
2122
|
-
- **Capability detection no longer leaks between robots in the same account.** The per-model feature tables were module-level objects shared by every robot, and devices are set up one after another — so a plain S4 or S6 that happened to be created after an S8 Pro Ultra inherited its dock-wash, dust-collection and dryer commands, and which extras a robot got depended on the order its owner's account happened to list them in. The tables that drive the per-poll status handling were shared the same way. Each robot now has its own.
|
|
2123
|
-
- **A single malformed local frame no longer wedges a robot's LAN channel.** The chunk buffer was only reset after a successful parse, so one bad frame left the buffer in place, re-processed the same bytes on every subsequent packet, and threw at the same offset forever: unbounded memory growth on a Pi and every later local reply lost, with no self-heal. The reset is now unconditional, a corrupt length prefix is treated as a desync and recovered from instead of buffering without bound, and a chunk boundary landing inside a length prefix no longer misaligns the stream.
|
|
2124
|
-
- **Local transport recovers on its own again.** A failed reconnect attempt scheduled nothing further, so a robot that was unplugged, or offline when the retry happened to fire, stayed on cloud transport until Homebridge restarted. Retries now re-arm with backoff (60s, 2m, 4m, 8m, up to 15m) and reset the moment the robot answers.
|
|
2125
|
-
- **Security: the cloud session and every device's LAN key are no longer world-readable.** `roborock.UserData` and `roborock.HomeData` were written 0644 next to the AES key that `src/crypto.ts` carefully writes 0600 — so the encrypted-token feature bought nothing on any host with a second user or service account. Both are now owner-only, existing files are repaired in place on the next write, and the emergency temp-directory fallback is covered too.
|
|
2126
|
-
- **Security: logging out logs you out.** The account password was kept in `config.json` after a token was obtained (and rewritten on every later settings change), and Logout cleared only the token — so the next restart silently signed back in. The password is now dropped once a token exists, and Logout clears the password and the cached device keys as well.
|
|
2127
|
-
- **Security: the diagnostics report no longer leaks your Wi-Fi.** The raw robot RPC block was passed through a single IPv4 regex, so a `get_network_info` reply still in the buffer published the home SSID, the access point BSSID and the robot MAC verbatim into a report users are told to paste into public issues — and a BSSID resolves to a street address in public geolocation databases. Identifying fields are now redacted structurally, at any nesting depth, while the values that make a report useful are untouched.
|
|
2128
|
-
- Full suite: 352 tests, 37 new since 3.0.2. Every new test was checked to fail before its fix and pass after.
|
|
2129
|
-
|
|
2130
|
-
## 3.0.2
|
|
2131
|
-
|
|
2132
|
-
A full read of the codebase turned up defects that no test covered and that the logs had been reporting for weeks without anyone reading them properly. Two of these could end the Homebridge process; one meant a headline feature had never worked in any released version. Nothing here requires re-pairing.
|
|
2133
|
-
|
|
2134
|
-
- **A malformed UDP packet can no longer take Homebridge down.** The discovery socket is bound to `0.0.0.0:58866` and receives whatever the network broadcasts at it — the Roborock phone app doing its own discovery, a port scanner, a truncated retransmit. Neither the binary parser nor `JSON.parse` was guarded, and a synchronous throw inside a dgram handler is an uncaught exception. One stray datagram from any host on the LAN was enough to stop the bridge, and because such senders are usually periodic, to keep stopping it.
|
|
2135
|
-
- **The MQTT startup watchdog no longer crashes the process it was written to rescue.** After 30 seconds without a broker connection it called `restart()` on the adapter — a method this class has never had. The resulting TypeError inside an async timer is an unhandled rejection, which Node terminates on. The one situation it was meant to handle, a Pi that boots before the network is up, was therefore the one situation guaranteed to kill the bridge. It now logs a clear, throttled warning and lets the client keep reconnecting, and the timer is tracked and cancelled on shutdown.
|
|
2136
|
-
- **LAN discovery works for the first time.** `decryptECB` called the PKCS#7 helper without `this.`, so every broadcast raised a ReferenceError that the surrounding `catch` swallowed and turned into `null`. Robots were only ever reached locally when the cloud happened to report their IP; otherwise they stayed cloud-only, with slower commands and no resilience to a Roborock outage. Startup also spent its full five-second discovery window on a result that could not contain anything.
|
|
2137
|
-
- **Q7-series clean mode and suction now reach the robot.** Both commands were dispatched through an allow-list that contained neither of them, so they fell through to `Command set_clean_type not found.` and nothing was sent. The log line has been there since 13 July. They now take the same path the classic models already used, which also reports genuinely unsupported commands instead of discarding them silently.
|
|
2138
|
-
- **Cloud commands no longer time out after doing exactly what was asked.** The protocol-102 handler tried to detect a secure request by comparing the result to the string `"ok"`, but the wire format is the array `["ok"]`, and `["ok"] != "ok"` is false. Every ordinary cloud command that acknowledges this way — start, stop, pause, dock, suction, room clean — was left pending until the ten-second timeout and then reported as failed in Apple Home, while the robot had already carried it out. Secure requests are now tracked by an explicit flag, and a secure request that fails resolves instead of hanging.
|
|
2139
|
-
- **A cloud hiccup can no longer unregister every robot.** When the home-data fetch fails, startup logs the error and continues, so the device list reads as empty — and stale-accessory cleanup then removed every Matter accessory. Matter locks the mode list at commissioning, so that meant re-pairing the entire fleet, and a restart did not undo it. Cleanup is now skipped unless the API is initialised and actually returned robots, and a `result`-less cloud response no longer overwrites the cached device list on disk.
|
|
2140
|
-
- **Mop modes are no longer offered on robots that cannot mop.** Two capability entries were written as bare arrays where every neighbouring entry ends in `.includes(robotModel)` — and an array, empty or not, is truthy. Both were therefore true for every robot, which is why dry-only models (S4, S4 Max, S5, S6 Pure) showed Mop and Vacuum + Mop plus a water-level control, and had `set_water_box_custom_mode` fired at hardware with no tank.
|
|
2141
|
-
- **Fixed a corrupt header on robots reporting protocol version `\x81S\x19`.** The version is three raw bytes, but it was written as UTF-8, which encodes `0x81` as two bytes; the sequence number then overwrote the overflow. Both the version and the sequence number went out wrong, so those robots ignored every message. The decode side already used latin1.
|
|
2142
|
-
- A local reconnect failure can no longer surface as an unhandled rejection, and the byte-identical copy of the MQTT connector at the repository root — dead code that was being hand-synced with the real one, and had already drifted — is gone.
|
|
2143
|
-
- Full suite: 315 tests, 36 new. Each new test was checked to fail against the previous code and pass against this one.
|
|
2144
|
-
|
|
2145
|
-
## 3.0.1
|
|
2146
|
-
|
|
2147
|
-
Follow-up to 3.0.0 after measuring a real restart instead of trusting the reasoning. Two things needed fixing, one of them mine.
|
|
2148
|
-
|
|
2149
|
-
- **Startup now genuinely finishes in ~2 seconds instead of ~7.** 3.0.0 moved LAN discovery off the critical path but still waited for its full 5-second broadcast window before declaring startup finished, so the total barely moved — the claim in the 3.0.0 notes ("~8 s before, ~3 s after") was wrong, and the corrected numbers are below. Local transport is an optimisation over the cloud path, not a prerequisite for it, so it now attaches in the background after the robots are already live in Apple Home.
|
|
2150
|
-
- **Fixed: the first cloud request could fail on a cold start.** `get_network_info` was issued about a second after the MQTT handshake began and failed with "Cloud connection not available" — visible in real logs for a cloud-transported robot. Startup now waits for the broker session to actually come up (up to 10 s, then continues regardless) before the first requests, instead of relying on an unrelated delay to cover the gap.
|
|
2151
|
-
- The diagnostics export is no longer headed `homebridge-roborock-vacuum2 diagnostic report` — a leftover from the fork that made reports confusing to read.
|
|
2152
|
-
|
|
2153
|
-
Measured on a three-robot fleet (two Q7, one S8 Pro Ultra), "Starting adapter" to "Lets go!!!!!!!": 6–7 s on 2.9.x and 3.0.0, ~2 s on 3.0.1.
|
|
605
|
+
- Startup now really takes about 2 seconds instead of 6 to 7, and the first cloud request no longer fails on a cold start.
|
|
2154
606
|
|
|
2155
607
|
## 3.0.0
|
|
2156
608
|
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
-
|
|
2160
|
-
- **Multi-robot startup no longer costs extra time.** Each robot's first status read and network probe used to run one after another; they now run at once, so three robots start as quickly as one. New tests pin the concurrency down so it cannot silently regress.
|
|
2161
|
-
- **A dead status refresh has been repaired.** The periodic `get_status` refresh for classic (S/Q-series) robots was gated on a config key this plugin never sets, which made the condition permanently false — the refresh promised by the code has never run in any released version. It now polls each robot at most once a minute (forced refreshes are unaffected), so a dropped MQTT push self-corrects within a minute instead of waiting up to three for the slow full poll.
|
|
2162
|
-
- **~86,000 needless timer wake-ups per robot per day removed.** With the refresh properly throttled, the 1-second scheduler tick that served it is now 15 seconds — same refresh rate, a fraction of the idle CPU on Raspberry Pi class hardware.
|
|
2163
|
-
- One request removed from every startup: a scene list was fetched from the Roborock cloud and thrown away.
|
|
2164
|
-
- **Security:** a newly published high-severity advisory in a transitive dependency (`ip-address`, reached through the MQTT client) is resolved, and the build toolchain was refreshed. `npm audit` reports zero vulnerabilities for both the shipped package and the development tree.
|
|
2165
|
-
- Full suite: 279 passing (7 new startup/refresh tests).
|
|
2166
|
-
|
|
2167
|
-
## 2.9.9
|
|
2168
|
-
|
|
2169
|
-
- **Cleans started outside Apple Home now show the right clean mode.** Starting a vacuum+mop (or mop-only) clean from the Roborock app or the robot's buttons left Apple Home claiming plain "Vacuum". The Q7 series reports its active clean type in every status poll (the plugin sent it on start but never read it back); classic S/Q robots are derived from the mop-only suction signature and the active water-flow setting. Apple Home's mode picker now follows the robot live during a run — no re-pairing needed.
|
|
2170
|
-
- Live fan power and clean type are now also picked up from push messages, not only polls, so mode changes surface within one update.
|
|
2171
|
-
- The periodic `Matter publish` log line now includes `runMode` and `cleanMode` alongside `operationalState`, making Apple Home display issues diagnosable from a single log excerpt.
|
|
2172
|
-
- Full suite: 272 passing (9 new live clean-type tests).
|
|
2173
|
-
|
|
2174
|
-
## 2.9.8
|
|
2175
|
-
|
|
2176
|
-
- Fixed a long-standing quirk in state persistence: the file encoding argument was passed to `JSON.stringify` (where it is silently ignored) instead of `fs.writeFileSync`. Behavior was correct by luck (utf8 is the default); the code now says what it means.
|
|
2177
|
-
- npm search keywords expanded (s7, s8, q revo, saros, robotic vacuum) so the plugin is found by people searching for their model.
|
|
2178
|
-
- README: highlighted that one sign-in brings the whole fleet — every robot on the account appears as its own accessory.
|
|
2179
|
-
|
|
2180
|
-
## 2.9.7
|
|
2181
|
-
|
|
2182
|
-
- Positioning sharpened across the npm description and README: the entire Roborock lineup is supported (classic S/Q/Saros series through the 2025 Q7 series that no other plugin can control, with automatic adoption of future models), presented as the most complete Roborock plugin for Apple Home. No functional changes.
|
|
2183
|
-
|
|
2184
|
-
## 2.9.6
|
|
2185
|
-
|
|
2186
|
-
- **Friendlier plugin description and README.** The npm description shown in the Homebridge UI now leads with what the plugin does for you ("sign in with your Roborock account — start cleans, pick rooms, set suction power, and see live which room the robot is cleaning") instead of protocol terminology. The README's intro, feature matrix and live-room section were rewritten in plain language, with the technical depth preserved in collapsible under-the-hood sections. No functional changes.
|
|
2187
|
-
|
|
2188
|
-
## 2.9.5
|
|
2189
|
-
|
|
2190
|
-
- **One synchronous disk write per received robot message eliminated.** The per-device diagnostics states (last cloud/local message, transport history) were flushed to disk with a blocking `fs.writeFileSync` on EVERY message a robot pushed — every few seconds per robot while cleaning. They are served from memory (the settings UI never reads the file); the on-disk copy only needs to survive restarts. Disk flushes for these two states are now debounced to at most once per minute, with a guaranteed flush on shutdown. Result: event-loop stalls removed from the message hot path, and meaningfully less SD-card wear on Raspberry Pi installs. Critical states (credentials, HomeData, room caches) still persist immediately.
|
|
2191
|
-
- Full suite: 263 passing (3 new persistence-debounce tests).
|
|
2192
|
-
|
|
2193
|
-
## 2.9.4
|
|
2194
|
-
|
|
2195
|
-
Startup-cost cleanup release (also refreshes the npm README with the Donate button and the prominent Verified badge).
|
|
2196
|
-
|
|
2197
|
-
- **Two fewer RSA-2048 key generations at every startup.** The MQTT connector generated a protocol keypair that nothing ever read (removed), and the message layer generated its keypair eagerly even though it is only needed for the rare photo request path on camera-equipped models (now created lazily on first use). Measured ~50 ms per keygen on fast hardware — substantially more on a Raspberry Pi.
|
|
2198
|
-
- Removed dead code: the never-called `decryptWithPrivateKey` helper and the unused `scenesData` field (both HomeKit-era leftovers).
|
|
2199
|
-
|
|
2200
|
-
## 2.9.3
|
|
2201
|
-
|
|
2202
|
-
**The plugin is now Verified by Homebridge!** 🎉 Reviewed and endorsed by the Homebridge team (homebridge/plugins#1124), with specific praise for the encrypted at-rest session storage, the preserved fork attribution, and the per-release notes.
|
|
2203
|
-
|
|
2204
|
-
- Verified badge added to the README.
|
|
2205
|
-
- **Donate button enabled** on the plugin's Homebridge UI tile via the standard `funding` field (PayPal), plus a Support section in the README.
|
|
2206
|
-
- Verified plugins are bumped in Homebridge UI search results and distributed via the pre-bundled tarball pipeline for faster, more reliable installs on low-power devices.
|
|
2207
|
-
|
|
2208
|
-
## 2.9.2
|
|
2209
|
-
|
|
2210
|
-
- **Max+ ("Grundig"/"Deep Clean") suction mode now announced on the S8 Pro Ultra.** Field report from a re-paired fleet: the S8 Pro Ultra only showed four suction levels because Max+ was gated to B01/Q7. The classic gate now uses the upstream-vetted per-model feature data (`set_custom_mode_max_plus` in the model's action list) — currently confirming the S8 Pro Ultra (`a70`); further models are added as feature data or field reports with diagnostics exports confirm the level. NOTE: the robot must be re-paired once for the new mode to appear (Matter locks the mode list at commissioning).
|
|
2211
|
-
- Battery documentation corrected after upstream verification on homebridge#3958: `batPercentRemaining` is quality **Q (quieter)** as of Matter 1.4 (reports ARE sent via subscription, 10 s throttle) — a spec-compliant controller applies them; Apple Home in steady state does not. No plugin architecture change needed; the bridge already does the right thing.
|
|
2212
|
-
|
|
2213
|
-
## 2.9.1
|
|
2214
|
-
|
|
2215
|
-
Deep performance pass over the live-room hot paths, with honest before/after measurements.
|
|
2216
|
-
|
|
2217
|
-
- **Classic live-room lookup: ~23 ms + ~6.7 MB of allocations -> ~1 microsecond, zero allocations.** The RRMap was previously fully parsed every ~20 s while cleaning: parsedata materializes floor/obstacle/segment pixel arrays (hundreds of thousands of entries on a real 800x800 map) only for the tracker to look up a single pixel. The new `resolveLiveSegmentFromMapBuffer` fast path walks the block table once and reads exactly ONE pixel byte from the raw buffer (~19,000x faster, measured on an 800x800/700 KB map with a 20k-point path block). Equivalence with the full parser is locked by tests probing both paths across room, corridor and out-of-map positions.
|
|
2218
|
-
- **B01 room cache is only written to disk on actual change.** The live map fetch refreshed the persisted room-name cache every ~20 s during cleaning even when nothing changed; identical room lists no longer touch the disk.
|
|
2219
|
-
- **Hot debug lines no longer pay JSON.stringify when debug is off.** Template arguments are evaluated eagerly in JavaScript; the per-poll B01 status line and the per-message protocol-102 line are now gated behind the debug flag.
|
|
2220
|
-
- B01 SCMap parsing consolidated to a single walk (head, pose, rooms, chains in one pass). Measured honestly: no speed win (~0.05 ms either way, the grid field is skipped via its length prefix) — kept for the simpler structure.
|
|
2221
|
-
- Full suite: 259 passing (3 new fast-path equivalence/robustness tests).
|
|
2222
|
-
|
|
2223
|
-
## 2.9.0
|
|
2224
|
-
|
|
2225
|
-
- **All Apple Home feature toggles are now visible in the plugin settings UI.** The custom settings page previously exposed only a subset of the configuration; options like suction-level cleaning modes, live room tracking, room/map selection, cleaning mode selection, battery and Returning status could only be set by editing the JSON config by hand. They now live in a dedicated **Apple Home Features** section, with a clear "⚠ re-pair" marker on every option that changes the robot's announced Matter capabilities (Matter locks capabilities at commissioning — after toggling those, restart Homebridge, then remove and re-pair the robot).
|
|
2226
|
-
- No functional changes to the plugin itself.
|
|
2227
|
-
|
|
2228
|
-
## 2.8.1
|
|
2229
|
-
|
|
2230
|
-
- **Suction modes now render with proper localized names in Apple Home.** Field observation: Apple ignores Matter mode labels and renders its own localized names from the mode TAGS (a variant with only the Vacuum tag displays as plain "Vacuum"). Balanced and Turbo therefore now carry distinct intensity tags (Auto and Quick), matching Quiet and Max — in Apple Home the five levels render as Quiet / Automatic / Quick / Max (+ Deep Clean for Max+ on Q7). Remember: enabling `enableFanPowerCleanModes` requires one remove/re-pair of the robot, since Matter fixes the mode list at commissioning.
|
|
2231
|
-
|
|
2232
|
-
## 2.8.0
|
|
2233
|
-
|
|
2234
|
-
- **Suction changes made in the Roborock app now show up in Apple Home.** With suction-level modes enabled, the announced current clean mode is derived live from the robot's actual fan power (approach adopted from `homebridge-roborock-matter-vacuum` by Jake Gold, MIT): change the suction anywhere and the Matter mode picker follows. A pending Apple Home selection always wins until the robot has confirmed it, and mop-family selections are never overridden by fan-power readings.
|
|
2235
|
-
- Reviewed `homebridge-roborock-matter-vacuum`'s battery handling against this plugin's: its PowerSource payload is a subset of ours with the same publish mechanism, so it contains no additional fix for the Apple-side frozen-percentage limitation (see README); the upstream report in `docs/matter-battery-issue-draft.md` remains the correct path.
|
|
2236
|
-
- Full suite: 256 passing.
|
|
2237
|
-
|
|
2238
|
-
## 2.7.0
|
|
2239
|
-
|
|
2240
|
-
Live room tracking for the whole fleet, a fifth suction level for the Q7, and quieter transport logs.
|
|
2241
|
-
|
|
2242
|
-
- **New: live room tracking for classic S/Q-series robots.** The flagship feature no longer stops at B01/Q7: classic robots now fetch their RRMap via the secure `get_map_v1` request (the protocol 301 decrypt/gunzip transport already existed), and the robot's millimeter position is resolved against the map's per-pixel room segments (`pixelIndex | segmentId << 21` grid). Same design as the B01 path: ~20 s attempt throttle, single-flight, fetches only while actively cleaning (never while paused or docked), previous room retained while crossing unsegmented floor, and a change re-broadcast so Apple Home updates within seconds. The Service Area layer — honest per-room progress included — is shared and unchanged.
|
|
2243
|
-
- **New: Max+ suction mode for the Q7** (fifth wind level, v1 fan power 108) in the opt-in fan-power clean modes, tagged Vacuum + DeepClean. Only announced for robots whose protocol verifiably defines the level (B01/Q7); classic models stay at four levels until a reliable capability signal exists — model guessing is what this fork moves away from.
|
|
2244
|
-
- **Fixed misleading MQTT outage spam.** Connection-state events were routed through the per-robot command error path, producing `Failed to execute client.on("error") on robot undefined (unknown model)` twice per reconnect attempt, unthrottled, for as long as an outage lasted (observed during a real nighttime DNS outage). Connection issues now log one clear warning per distinct message per 5 minutes, downgrade to debug in between, and a single recovery line is logged when the connection comes back.
|
|
2245
|
-
- Battery upstream report (`docs/matter-battery-issue-draft.md`) finalized for filing against homebridge/homebridge, now including the resync-nudge finding and a reproduction section.
|
|
2246
|
-
- Full suite: 254 passing (6 new classic live-room tests exercising the real RRMap parser end to end, plus Max+ coverage).
|
|
2247
|
-
|
|
2248
|
-
## 2.6.0
|
|
2249
|
-
|
|
2250
|
-
- **New: opt-in suction-level cleaning modes.** With `enableFanPowerCleanModes` (default off), the Matter cleaning mode list gains **Quiet / Balanced / Turbo / Max Vacuum** variants with proper Matter mode tags (Vacuum + Quiet/Max), so suction can be chosen directly from Apple Home's mode picker. Selecting a variant pins the robot's fan power (v1 codes 101-104; the B01/Q7 adapter translates to wind levels 1-4) while behaving as a vacuum-family mode everywhere else (water box handling, mop rules). Off by default because Matter fixes an accessory's mode list at commissioning: toggling the option requires removing and re-pairing the robot once — this ships as a deliberate opt-in rather than a forced re-pair for everyone.
|
|
2251
|
-
- **README rebuilt from scratch** around what makes the plugin unique (2025 B01/Q7 support, live room tracking, Matter-only design), with a feature matrix, configuration reference, honest limitation notes, and the plugin icon.
|
|
2252
|
-
- Full suite: 247 passing (6 new clean-mode tests). No changes to default behavior anywhere.
|
|
2253
|
-
|
|
2254
|
-
## 2.5.0
|
|
2255
|
-
|
|
2256
|
-
Supply-chain, robustness and capability-detection release. Every Socket.dev alert with a code-level source is eliminated at the source, and the plugin now adapts itself to unknown robot models instead of guessing silently.
|
|
2257
|
-
|
|
2258
|
-
- **Custom UI server moved to native ESM loading — no more dynamic code evaluation.** The `homebridge-ui` directory is now marked `"type": "module"`, so `server.js` imports the pure-ESM `@homebridge/plugin-ui-utils` natively and instantiates the exported (side-effect-free) server class from the compiled output. The `new Function("return import(...)")` interop shim is gone, and with it the Socket.dev "uses eval" alert.
|
|
2259
|
-
- **Removed the dead ioBroker-era package/image downloader** (`roborockPackageHelper`) and its `jszip` dependency (12 packages out of the tree). The helper was never called by this fork, wrote to relative paths, and was the source of Socket.dev's AI-detected ZIP-slip/path-traversal alert. Deleting it removes the entire alert surface rather than patching around it.
|
|
2260
|
-
- **Self-healing capability detection.** Any periodic poll request a robot definitively answers with an unsupported-method error is now remembered per device and skipped until the next restart (firmware updates get a fresh probe) — exotic and brand-new models stop generating repeated warnings for requests they will never answer. Timeouts and transport errors never count as unsupported.
|
|
2261
|
-
- **Capability-derived poll profiles for unknown models.** Models without a dedicated poll profile (e.g. newly released Saros 10 / Q5 Max+ / QX Revo Plus-class devices) now derive their polls from the robot's own capability bitmask where available (carpet support), announce the chosen profile once in the log, and point to the model-report issue template. Known models keep their verified profiles unchanged.
|
|
2262
|
-
- **Clearer model lookup mismatch logs:** a device whose HomeData model string does not look like a Roborock vacuum now logs exactly what was reported and how to file a useful report, instead of a generic "unsupported model" line.
|
|
2263
|
-
- **Leaner npm package:** the mitmproxy sniffing script, the ioBroker map viewer, test files, and editor metadata no longer ship in the tarball.
|
|
2264
|
-
- ROADMAP refreshed against live upstream status: applemanj#12 (pause/dock) confirmed fixed and closed upstream; applemanj#4 (S8 local timeouts) still awaiting reporter retest; homebridge#3951 stable with no recurrence since June. The legacy "HomeKit scene/room controls" item is superseded by the Matter-only design.
|
|
2265
|
-
- Full suite: 241 passing (6 new capability-detection tests). Verified end to end under Homebridge 1.8.3 and 2.1.2-beta.3, including the plugin-verification harness's crash scenarios (invalid credentials, unreachable cloud).
|
|
2266
|
-
|
|
2267
|
-
## 2.4.2
|
|
2268
|
-
|
|
2269
|
-
Robustness and supply-chain release (Homebridge verification runtime checks + Socket.dev scan).
|
|
2270
|
-
|
|
2271
|
-
- **Startup failures can no longer crash Homebridge.** A rejected Roborock login previously escaped `startService` as an unhandled promise rejection — under Homebridge 2 / Node 22+ that reads as a plugin crash and can trigger a crash-restart loop. Wrong credentials now stop cleanly with a clear log message ("check the email and password ..."), while unreachable-cloud errors retry with increasing backoff (1-10 minutes, up to 10 attempts) since Homebridge often boots before the network is up. A belt-and-braces catch at the platform call site guarantees nothing escapes.
|
|
2272
|
-
- **node-forge removed** (flagged by Socket.dev: its prime-generation worker contains a Math.random() fallback). The protocol's RSA-2048 keypair is now generated by Node's built-in OpenSSL-backed `crypto.generateKeyPairSync` (CSPRNG entropy) with identical output format — the components are byte-for-byte compatible minimal hex strings, verified by new tests including a reconstruction/roundtrip check. One less production dependency.
|
|
2273
|
-
- Full suite: 235 passing.
|
|
2274
|
-
|
|
2275
|
-
## 2.4.1
|
|
2276
|
-
|
|
2277
|
-
- Added the standard `name` property to the config schema (Homebridge verification requirement) so the platform name is editable in the Homebridge UI.
|
|
2278
|
-
- No functional changes.
|
|
2279
|
-
|
|
2280
|
-
## 2.4.0
|
|
2281
|
-
|
|
2282
|
-
- **New: live room tracking for B01/Q7-series robots.** While the robot is actively cleaning, the plugin now fetches the robot's live position from the encrypted SCMap channel (`currentPose`, ~20s cadence, only during active cleaning states) and ray-casts it against the per-room boundary outlines (`roomChain`) to determine which room the robot is physically inside. The detected room is published as the Matter Service Area `currentArea`, so Apple Home's status pill can show "cleaning in \<room\>" with the actual room — including runs started from the robot button or the Roborock app, and full-home cleans, which previously had no room to name. This closes the gap noted in 2.3.1 ("deriving the live room from the robot's map position, the way the vendor app does").
|
|
2283
|
-
- **Honest progress semantics.** The progress list only transitions rooms that are part of the announced run scope: a detected room becomes operating, and a previously operating room is marked completed only if the robot was actually detected inside it during this run — the old first-requested-room guess falls back to pending instead of claiming a clean that may never have happened. Rooms outside the announced scope update `currentArea` (a true statement about where the robot is) but never rewrite the scope, and stale progress lists from finished runs are never mutated.
|
|
2284
|
-
- **Protocol layer:** the minimal SCMap protobuf reader now decodes `mapHead` (grid geometry), `currentPose` and `roomChain` alongside the existing room list, following the wider CRL-200S family schema documented by ioBroker.roborock; wire-format parsing is covered by tests that encode payloads independently and run the production AES/zlib decode path end to end. Each live fetch also opportunistically refreshes the room-name cache, postponing the next scheduled 6-hour room refresh.
|
|
2285
|
-
- **Footprint and control:** map fetches ride a dedicated 20s attempt throttle with a single-flight guard, run only while the robot is in an actively-cleaning state, and stop the moment the run ends. The feature is on by default and can be disabled with the new **Enable Live Room Tracking** setting (`enableLiveRoomTracking: false`).
|
|
2286
|
-
- Full suite: 232 passing (14 new tests: protobuf parsing/geometry, API throttle/notify/caching behavior, and Matter progress semantics).
|
|
2287
|
-
|
|
2288
|
-
## 2.3.2
|
|
2289
|
-
|
|
2290
|
-
Security and dependency hygiene release (prompted by the Socket.dev scan of 2.3.1).
|
|
2291
|
-
|
|
2292
|
-
- **All 10 known vulnerabilities in the production dependency tree resolved** (5 high, 5 moderate — including ws memory disclosure/DoS via mqtt and the qs DoS via express) through lockfile upgrades.
|
|
2293
|
-
- **Nine unused dependencies removed entirely:** abstract-things, tinkerhub-discovery, yargs, chalk, deep-equal, rxjs, semver, debug, and express — all inherited from the upstream project's pre-Matter (miio) era and referenced by zero files in this fork. Removing express also eliminates the whole qs/body-parser/path-to-regexp advisory chain at the root instead of patching around it. Verified by full-tree usage analysis, the complete test suite, strict type checking, and a runtime load check.
|
|
2294
|
-
- npm audit (production): 0 vulnerabilities. Smaller install footprint, cleaner supply-chain surface.
|
|
2295
|
-
|
|
2296
|
-
## 2.3.1
|
|
2297
|
-
|
|
2298
|
-
- **Full-home cleans now publish the run's scope as Service Area progress.** Previously a full clean cleared the progress list entirely, leaving controllers with no per-run data — which Apple Home renders as a permanent "Preparing" pill for the whole run. Every supported area is now reported as pending at start and completed when the robot returns to the charger. No area is claimed as current and currentArea stays null: the robots do not report which room they are physically inside, and the plugin does not invent one. Whether Apple's pill label improves with real scope data is up to Apple's renderer — this ships the honest maximum of what the robots expose. (Deriving the live room from the robot's map position, the way the vendor app does, remains a possible future feature.)
|
|
2299
|
-
- Full suite: 217 passing.
|
|
2300
|
-
|
|
2301
|
-
## 2.3.0
|
|
2302
|
-
|
|
2303
|
-
Performance release: snappier state in Apple Home while robots are working, and a much quieter idle load.
|
|
2304
|
-
|
|
2305
|
-
- **Adaptive B01 poll cadence.** The dedicated B01/Q7 status loop still ticks every 15s, but the cloud-protecting attempt throttle is now state-aware: ~12s effective cadence while the robot is actively working (cleaning, spot/zone/segment runs, returning, docking, mop washing) and the conservative ~45s at rest. Phase transitions — started from the robot button or the Roborock app included — now reach Apple Home within seconds instead of up to ~45s, while a docked fleet keeps the gentle cloud footprint.
|
|
2306
|
-
- **Confirmed-publish diffing.** Cluster payloads byte-identical to the last CONFIRMED publish are no longer re-submitted on every poll and live message (previously 4-6 unchanged clusters per robot per cycle through the Homebridge/matter.js stack, around the clock). Three safety layers prevent the historical "Updating..." store-desync that made upstream remove its old change tracking: all publishes remain serialized, tracking entries are recorded per cluster only after the individual write succeeded (and dropped on failure so retries always go through), and the 60s heartbeat now performs a FORCED full publish as a self-healing safety net. Behavior on failure paths, registration, and the battery resync nudge is unchanged.
|
|
2307
|
-
- Test suite updated to the new contracts and extended with an adaptive-cadence test; the optimistic-state protection test is now stricter (any docked/charging leak during the start window fails it). Full suite: 216 passing.
|
|
2308
|
-
|
|
2309
|
-
## 2.2.1
|
|
2310
|
-
|
|
2311
|
-
- **Removed: the HomeKit battery companion accessories introduced in 2.2.0.** This fork stays Matter-only; a HAP side-channel is not the right answer. Any companions created by 2.2.0 are no longer registered by the plugin and can be removed from the Homebridge cache via the Homebridge UI (Settings -> Remove single cached accessory) if they linger.
|
|
2312
|
-
- Retained from 2.2.0: Service Area progress persistence across restarts, the accessory-context mutation fix, the README documentation of the controller-side battery reporting limitation, and the ready-to-file upstream report in `docs/matter-battery-issue-draft.md` — filing that issue with Homebridge is the correct, Matter-native path to a permanent battery fix.
|
|
2313
|
-
- Full suite: 215 passing.
|
|
2314
|
-
|
|
2315
|
-
## 2.2.0
|
|
2316
|
-
|
|
2317
|
-
- **New: HomeKit battery companion accessories (enabled by default).** The Matter battery percentage freezes in Apple Home because the attribute carries the Matter spec "changes omitted" reporting quality — changes are never pushed to subscribed controllers, matter.js implements this faithfully, and Apple never re-reads (matter.js' own controller compensates by always reading such attributes; Apple's does not). Since no bridge-side write can force the attribute to report, the plugin now publishes a small HomeKit Battery accessory per vacuum through the regular Homebridge child bridge, mirroring the exact values of every Matter publish: live percentage, charging state, and a low-battery flag at 20%. Pair the plugin's child bridge with Apple Home to see them; opt out with `disableBatteryCompanion` in the plugin config (removes existing companions cleanly).
|
|
2318
|
-
- **New: Service Area progress survives restarts.** The active room and per-area progress are persisted in the accessory context and restored on startup, so a Homebridge restart mid-clean no longer drops Apple Home back to a generic label.
|
|
2319
|
-
- **Fixed a context-replacement bug:** metadata updates replaced the accessory `context` object instead of mutating it, which could orphan persisted state held by Homebridge under the old reference. Found by the new persistence test.
|
|
2320
|
-
- Documentation: README section on the Apple Home battery limitation with the full evidence chain, and `docs/matter-battery-issue-draft.md` — a ready-to-file upstream report for Homebridge/matter.js.
|
|
2321
|
-
- Full suite: 217 passing, including companion mirroring in the three-robot end-to-end simulation.
|
|
2322
|
-
|
|
2323
|
-
## 2.1.3
|
|
2324
|
-
|
|
2325
|
-
- **Service Area progress feature is now announced at commissioning.** Homebridge derives Matter cluster features from which attributes are present when the accessory registers (the same mechanism as its own PowerSource Rechargeable fix, homebridge#3914). The `progress` list was previously only included while a room clean was running — never at registration — so the progress feature was likely never announced to controllers, leaving Apple Home unable to render "cleaning in <room>" and stuck on "heading to the room"/"Preparing" instead. `progress` (empty when idle) and `estimatedEndTime` (null; the robots provide no ETA data) are now always present in the cluster state. NOTE: Matter locks cluster features at commissioning, so this improvement requires re-pairing the robot once.
|
|
2326
|
-
- **Battery investigation concluded (evidence in README):** the full chain robot → plugin → Homebridge → matter.js store is verified correct end-to-end (store values match the Roborock app in real time), while Apple Home renders the percentage from pairing time. The charge state on the same cluster updates live; the percentage attribute has the Matter "changes omitted" reporting quality, so value changes are not pushed to subscribed controllers by design and Apple never re-reads it. No plugin-side write can force this attribute to report; the resync nudge from 2.1.1 remains as a best-effort priming aid. Verified paths to a fresh value: re-establishing the controller subscription (hub restart) or re-pairing.
|
|
2327
|
-
- Code cleanup: removed unused parameters; the codebase now compiles clean with noUnusedLocals + noUnusedParameters.
|
|
2328
|
-
- Full suite: 214 passing.
|
|
2329
|
-
|
|
2330
|
-
## 2.1.2
|
|
2331
|
-
|
|
2332
|
-
- **Apple Home's status pill now shows real cleaning progress instead of a permanent "Preparing".** The Service Area cluster previously exposed rooms but never populated the progress attributes, so controllers that render a progress pill had nothing to show for the entire run. Room cleans started from Apple Home now publish `currentArea` (the room being cleaned — Apple displays its name) and a per-area `progress` list: the requested room is marked operating, additional requested rooms pending, and everything flips to completed when the robot returns to the charger. Honest limitations: with multiple rooms selected the first is shown as current (the robot does not report which room it is inside), and full-home cleans have no room to name.
|
|
2333
|
-
- **Battery publish diagnostics on every change:** the "Matter publish for <duid>: battery=…%" info line now also logs whenever the published battery value changes (not only on the first publish after boot), making the exact value handed to the Matter layer permanently visible in normal logs.
|
|
2334
|
-
- The end-to-end simulation now runs with a realistic stale cloud snapshot (pairing-day battery in HomeData) and proves the live channel wins in every publish, plus a full room-clean progress scenario (start → operating → completed).
|
|
2335
|
-
- Full suite: 214 passing.
|
|
2336
|
-
|
|
2337
|
-
## 2.1.1
|
|
2338
|
-
|
|
2339
|
-
- **Fixed Apple Home showing a frozen, hours-old battery percentage even though the plugin publishes the correct value.** Root cause: Matter controllers filter attribute reports by cluster data version, and matter.js suppresses no-op attribute writes — so a battery that sits at the same value forever never generates a new report for a controller whose cache missed one (observed in the field as a Q7 stuck on its pairing-day percentage across full server restarts, while frequently-changing attributes like the operational state kept updating fine). The plugin now performs a one-time battery resync per boot: the battery attributes are published as briefly unknown and then with their real values, forcing two genuine store changes that bump the cluster data version so every subscribed controller receives a fresh report — no hub restart or re-pairing required. The resync covers both publish paths (live messages and periodic refreshes), runs exactly once per boot, and logs an info line ("Battery resync for <duid>: ... battery=100%") for verification.
|
|
2340
|
-
- Full suite: 211 passing, including nudge-ordering assertions in the three-robot end-to-end simulation.
|
|
2341
|
-
|
|
2342
|
-
## 2.1.0 (first public fork release as homebridge-roborock-matter)
|
|
2343
|
-
|
|
2344
|
-
This is the first release under the fork name **homebridge-roborock-matter**, maintained by Mathias Hornbek. It is a Matter-only fork of `homebridge-roborock-vacuum2` by Joshua Appleman (originally adapted from ioBroker.roborock by copystring), published under the MIT license with all original copyright preserved.
|
|
2345
|
-
|
|
2346
|
-
The 2.0.0-matter.x pre-release series is consolidated into this release. Highlights versus upstream:
|
|
2347
|
-
|
|
2348
|
-
- Matter-only: HomeKit accessories removed; each robot is a single native Matter vacuum.
|
|
2349
|
-
- Full B01/Q7-series (roborock.vacuum.sc05) support: commands, status, battery, charging, mop/vacuum mode switching, and room selection via the encrypted B01 map channel, built against the python-roborock reference.
|
|
2350
|
-
- Robustness: startup guards, a dedicated self-healing B01 status loop, per-cluster Matter publish isolation, interval-lifecycle fixes, request-id and throttling fixes.
|
|
2351
|
-
- UI: light, WCAG-AA settings theme with per-device enable/disable and a Charging/Docked tile option with a configurable battery threshold.
|
|
2352
|
-
- 210 passing tests, including fixture-driven B01 protocol and map-decode verification and a full three-robot end-to-end simulation.
|
|
2353
|
-
|
|
2354
|
-
## 2.0.0-matter.10 (Matter-only edition, unofficial)
|
|
2355
|
-
|
|
2356
|
-
Boot responsiveness and publish evidence, following field verification that the plugin chain is now fully correct (robots report state=8, battery=100%, charging=yes across restarts):
|
|
2357
|
-
|
|
2358
|
-
- **The dedicated B01 status loop now polls immediately at start** instead of waiting for the first 15-second tick: after a restart the Matter store briefly holds the registration snapshot, and landing the real values right away both shortens that window and generates a genuine attribute-change report for controllers as early as possible.
|
|
2359
|
-
- **One-time publish evidence at info level:** the first successful Matter publish per accessory logs the exact values handed to the Matter layer ("Matter publish for <duid>: battery=100%, operationalState=66"), closing the last observability gap between the robot and Apple Home — any remaining discrepancy is now provably on the controller side (hub cache/subscription), where a Matter-hub restart or a re-pair of the affected accessory resolves it.
|
|
2360
|
-
- Full suite: 210 passing.
|
|
2361
|
-
|
|
2362
|
-
## 2.0.0-matter.9 (Matter-only edition, unofficial)
|
|
2363
|
-
|
|
2364
|
-
The frozen-battery mystery, solved with field evidence:
|
|
2365
|
-
|
|
2366
|
-
- **Root cause found via the new first-success log lines:** both Q7 robots reported `battery=100%` correctly through the B01 channel — but with `fault=407`, and the adapter treated any non-zero fault as an error state. Q7 fault code 407 is the informational "Cleaning in progress. Scheduled cleanup ignored." message, which lingers after harmless events; the reference implementation treats the fault field as a separate diagnostic channel that never overrides the work status. The adapter now does the same: work status is the sole source of the robot state, informational codes (0, 407) are normalized out of error_code, and real fault codes still surface as diagnostics without disturbing the state.
|
|
2367
|
-
- **Fixed the freezing mechanism itself — per-cluster Matter publish isolation.** Cluster publishes ran in one all-or-nothing batch, so a single misbehaving cluster (here: the erroneous operational-state publish) could block every other attribute, leaving Apple Home stuck on pairing-day values (74%, not charging, Ready). Each cluster now publishes independently: one failure can never again freeze the battery. A totally failed batch keeps its previous semantics, and an "endpoint still initializing" failure still schedules the retry even when other clusters landed.
|
|
2368
|
-
- **The full-chain simulation now replays the exact field payloads** (fault 407 on healthy, charging robots) and asserts the complete user-visible outcome: correct battery, Charging below the threshold, Docked at 100%.
|
|
2369
|
-
- Full suite: 210 passing.
|
|
2370
|
-
|
|
2371
|
-
## 2.0.0-matter.8 (Matter-only edition, unofficial)
|
|
2372
|
-
|
|
2373
|
-
Deep verification and cleanup pass, anchored by a new full-chain simulation:
|
|
2374
|
-
|
|
2375
|
-
- **Fixed a sequencing flaw in the dedicated B01 status loop start:** the loop was started from inside the device-creation loop but gated on a set that is only populated later, so whether it started at boot depended on device ordering (with a single Q7 it would not start until the 3-minute supervisor). It now starts deterministically after all devices are created.
|
|
2376
|
-
- **Verification without debug mode:** the loop start is logged at info level, and each Q7 logs a one-time "B01 status online for <duid>: state=…, battery=…%, charging=yes/no" info line on its first successful status — the raw values straight from the robot, making frozen-battery reports diagnosable at a glance.
|
|
2377
|
-
- **New full-chain simulation test** replicating the exact three-robot setup (two Q7s + one classic): real createDevices + initializeDeviceUpdates, real dedicated loop under fake timers, real map decode against the reference fixture, real Matter accessories — only the cloud transport is scripted. It asserts battery following the robot (74% → 100%) and the tile switching Charging (65) → Docked (66) across the 90% threshold.
|
|
2378
|
-
- **The startup warning for sc05/Q7 models is gone:** B01/Q7-series robots are first-class citizens of this fork (debug note instead), and the v1 feature probes (get_timer, carpet, water box) are skipped for them entirely — faster startup, clean log.
|
|
2379
|
-
- **Dead-weight removal:** the HomeKit-era scenes machinery is deleted (this also removes a pointless cloud API call every 3 minutes), consumable state churn is dropped from the HomeData poller, the per-device 1-second status tick is skipped for B01 robots (the dedicated loop owns their cadence), room refreshes run in the background when a persisted cache exists (faster boot), and unused water tables plus a dead variable are removed.
|
|
2380
|
-
- Full suite: 209 passing.
|
|
2381
|
-
|
|
2382
|
-
## 2.0.0-matter.7 (Matter-only edition, unofficial)
|
|
2383
|
-
|
|
2384
|
-
Deep interval-lifecycle surgery — the actual root cause behind frozen battery/status readings:
|
|
2385
|
-
|
|
2386
|
-
- **Found and fixed an upstream architectural bug: the per-device interval properties held STARTER FUNCTIONS, not interval handles.** Every `clearInterval(vacuum.getStatusIntervall)` call was a silent no-op, and the "restart when missing" check (`!vacuum.mainUpdateInterval`) could never fire because a function is always truthy. Consequence: whichever flow stopped polling first (offline flap, reconnect, shutdown-restart races) killed it permanently, and every supervision layer — including matter.6's — faithfully called a restart mechanism that was structurally incapable of restarting anything. The starters now store real handles (self-clearing on restart), offline clears the handles and nulls the properties, and coming back online genuinely restarts both intervals. This benefits classic robots too.
|
|
2387
|
-
- **B01/Q7 robots get a dedicated, self-managed status loop** completely independent of the v1 per-device machinery: one adapter-level interval ticks every 15 seconds and refreshes every initialized B01 robot (the attempt throttle keeps the effective cloud cadence at ~45s). It is cleared properly on shutdown and revived by the HomeData supervisor within 3 minutes if anything ever kills it. A Q7 battery reading can now be at most about a minute old whenever the cloud answers.
|
|
2388
|
-
- Four new lifecycle tests, including the historically impossible restart branch and a full kill-and-revive cycle of the B01 loop. Full suite: 208 passing.
|
|
2389
|
-
|
|
2390
|
-
## 2.0.0-matter.6 (Matter-only edition, unofficial)
|
|
2391
|
-
|
|
2392
|
-
Room cleaning fix plus a status self-healing package, both driven by field logs:
|
|
2393
|
-
|
|
2394
|
-
- **Fixed Q7 room cleaning aborting with "Method load_multi_map is not supported".** The Matter room-clean flow compares the area's map id with the device's current map id and switches maps on mismatch. For B01 robots the current-map lookup returned null (v1 structure), so every room command attempted a map switch that has no Q7 equivalent — and aborted before the segment command was ever sent. B01 rooms are always fetched from the robot's current map (the `cur` flag), so the current map id now reports the canonical 0 and no switch is attempted. Full-home cleaning was unaffected; per-room cleaning now sends `service.set_room_clean` with the selected room ids directly.
|
|
2395
|
-
- **Fixed stale battery/status freezing (Home app showing an hours-old percentage):**
|
|
2396
|
-
- B01 status refreshes now throttle on attempts, not successes — a robot or cloud that stops answering no longer turns the poll tick into a per-second retry storm that can perpetuate rate limiting.
|
|
2397
|
-
- Consecutive failures are counted: every 10th logs a warning with the last error, and recovery logs an info line, so silent outages become visible.
|
|
2398
|
-
- The HomeData poller now supervises B01 device intervals: an online flap used to kill Q7 status polling permanently (the v1 restart path never runs for B01); intervals now restart automatically when the robot is back online.
|
|
2399
|
-
- Live status values older than 15 minutes fall back to the periodically refreshed HomeData snapshot (which translates Q7-native codes), so the Matter tile self-heals even if the request path is down.
|
|
2400
|
-
- Note: Q7 room names are refreshed from the map at most every 6 hours; after renaming rooms in the Roborock app, restart the Roborock bridge to pick the new names up immediately.
|
|
2401
|
-
- Nine new tests (attempt throttling, failure escalation and recovery, staleness fallback, interval supervision, canonical B01 map id, and a no-map-switch room-clean regression). Full suite: 204 passing.
|
|
2402
|
-
|
|
2403
|
-
## 2.0.0-matter.5 (Matter-only edition, unofficial)
|
|
2404
|
-
|
|
2405
|
-
- **Fixed the Apple Home tile showing "Ready" instead of "Charging" on Q7 robots.** Root cause: when the Matter layer falls back to the cloud HomeData snapshot (cold start, or before the first live refresh), Q7 devices store their NATIVE work-status codes there — charging is 4, which reads as the v1 "remote control" state and never maps to the Charging tile. The fallback now translates Q7 codes to v1 states for B01 robots, and the live status mapping additionally carries `charge_status` (charging and dock air-drying) so the PowerSource cluster and the Charging/Docked threshold logic see the charger in every path. Verified by three new tests including an end-to-end accessory publish asserting Matter operational state 65 (Charging) for a charging Q7 at 74% with the 90% threshold.
|
|
2406
|
-
|
|
2407
|
-
## 2.0.0-matter.4 (Matter-only edition, unofficial)
|
|
2408
|
-
|
|
2409
|
-
- Removed the "Enable Matter vacuum" option from the settings UI, config schema, and code. In a Matter-only plugin the toggle was meaningless (off would mean the plugin does nothing). Matter publication is now unconditional; availability depends solely on the Homebridge Matter API. Legacy configs still carrying `"enableMatter": false` are ignored with a friendly one-line note in the log. The Matter feature toggles (Service Area, Power Source, Clean Mode, Charging/Docked status, threshold) are unchanged.
|
|
2410
|
-
|
|
2411
|
-
## 2.0.0-matter.4 (Matter-only edition, unofficial)
|
|
2412
|
-
|
|
2413
|
-
The two missing Q7 pieces, built against the python-roborock reference:
|
|
2414
|
-
|
|
2415
|
-
- **Mop/Vacuum mode switching for Q7.** The Matter clean-mode selection (Vacuum / Mop / Vacuum + Mop) now maps to the Q7 native `mode` property via `prop.set` — including the crossed enum values (Matter Mop=1 is Q7 mode 2; Matter combo=2 is Q7 mode 1). The v1-era "fan power off" workaround for mop-only is never sent to Q7 robots; suction levels still apply through the wind mapping. Water remains fully unexposed (manual tank).
|
|
2416
|
-
- **Room selection (Matter Service Area) for Q7.** Implemented the B01 map channel end to end: `service.get_map_list` -> current map id (`cur` flag) -> `service.upload_by_mapid` -> protocol-301 payload -> base64 + AES-128-ECB (key derived from serial+model exactly as the reference) + zlib inflate -> minimal SCMap protobuf reader extracting room ids and names. Rooms are cached, persisted across restarts, refreshed at most every 6 hours, and fed to the Matter Service Area cluster in the standard shape — so per-room cleaning uses the same `service.set_room_clean` room ids the robot expects.
|
|
2417
|
-
- Verified against a wire fixture generated with the reference implementation's own protobuf gencode and crypto: map-key derivation matches character for character, and the full decode chain reproduces the reference rooms (including UTF-8 names). Full suite: 195 passing.
|
|
2418
|
-
- Note: robots already paired before rooms were available must be removed from Apple Home and re-paired once for the Service Area cluster to appear (Matter locks the cluster set at commissioning).
|
|
2419
|
-
|
|
2420
|
-
## 2.0.0-matter.3 (Matter-only edition, unofficial)
|
|
2421
|
-
|
|
2422
|
-
Deep Q7/B01 hardening pass:
|
|
2423
|
-
|
|
2424
|
-
- **Fixed a serious polling bug: B01 status refreshes bypassed the v1 throttle**, turning the 1-second poll tick into roughly one cloud request per second per Q7 robot. B01 refreshes are now throttled (periodic at most every 45s, forced/post-command at most every 1.5s) with concurrent callers sharing a single in-flight request. Robot-initiated pushes trigger a forced refresh so Matter still converges within seconds of real changes.
|
|
2425
|
-
- **Q7 water is neither queried nor exposed.** Q7-series robots use a manually filled water tank with no electronic water control, so the `water` property is no longer polled, water state is never mapped, water-control commands are rejected, and — most importantly — Matter clean-mode capabilities for B01 robots are now pinned to vacuum-only (`canMop: false`) regardless of what the generic cloud schema claims. No mop modes ever appear in Apple Home for Q7 robots.
|
|
2426
|
-
- **Fixed Matter room cleaning for Q7**: the adapter translated `app_segment_clean`, but the API layer's actual wire method is `app_segment_clean_by_ids` with a `{segments, repeat}` object. Both names now translate to `service.set_room_clean` with the correct room ids (ready for when the B01 map channel lands).
|
|
2427
|
-
- **B01 robots are marked remote at creation**, so the transport layer never attempts local TCP connections to them (they are cloud/MQTT-only by design).
|
|
2428
|
-
- **Fixed a request-id wraparound collision** affecting all protocols: the id generator handed out 0 twice in a row every 10,000 requests, colliding two pending requests.
|
|
2429
|
-
- Six new tests: throttle cadence and forced-gap behavior, in-flight deduplication, B01 capability pinning against a mop-advertising schema, the segment wire-method translation, water exclusion, and wraparound id uniqueness. Full suite: 186 passing.
|
|
2430
|
-
|
|
2431
|
-
## 2.0.0-matter.2 (Matter-only edition, unofficial)
|
|
2432
|
-
|
|
2433
|
-
Fixes from the first field test of B01/Q7 support:
|
|
2434
|
-
|
|
2435
|
-
- **Fixed Apple Home commissioning failure for room-less robots.** The Service Area cluster was published with an empty supportedAreas list for robots without room data (all B01/Q7 robots until the map channel lands), which violates Matter conformance and makes Apple Home abort pairing. The cluster is now omitted entirely when no rooms are available; robots with rooms (classic models) are unchanged. Covered by tests for both cases.
|
|
2436
|
-
- **Fixed a TypeError in the Service Area room refresh on B01 devices** ("Cannot read properties of undefined (reading 'map_status')"): the classic get_room_mapping flow reads a v1-shaped status array, but B01 status responses are Q7 dictionaries. The room refresh is now skipped for B01 robots (their room data requires the protobuf map channel), and the map_status read is defensively guarded regardless.
|
|
2437
|
-
- **B01-unsupported methods now log at debug level** instead of red errors. get_timer, get_carpet_clean_mode, and similar feature probes simply have no Q7 equivalent yet; startup logs stay calm.
|
|
2438
|
-
|
|
2439
|
-
## 2.0.0-matter.1 (Matter-only edition, unofficial)
|
|
2440
|
-
|
|
2441
|
-
**Breaking: HomeKit (HAP) accessories removed.** The plugin now publishes each robot exclusively as a native Matter vacuum for Apple Home. On first start, all legacy HomeKit accessories (the fan tile and helper switches, including scene and schedule switches) are unregistered automatically, so every robot appears exactly once. This removes ~1,500 lines of accessory code, the scene/schedule polling loops, and the consumables/clean-summary refreshers — fewer moving parts, less MQTT traffic, fewer failure modes.
|
|
2442
|
-
|
|
2443
|
-
**New: B01/Q7-series protocol support (Q7 M5 `roborock.vacuum.sc05`, Q7 M5+ `ss07`, ...).** These 2025 robots speak a different RPC dialect; the plugin previously sent classic v1 methods they ignore, and dropped their responses (correlated by `msgId`, not `id`) — hence the endless command timeouts. Implemented against the actively maintained python-roborock reference and its recorded protocol fixtures:
|
|
2444
|
-
|
|
2445
|
-
- A translation layer (`b01Q7Adapter`) maps the plugin's v1 command surface to the Q7 dialect: start/stop/pause via `service.set_room_clean`, dock via `service.start_recharge`, locate via `service.find_device`, segment cleaning with Q7 room ids, fan power and water level via `prop.set`, and status via `prop.get` — with Q7 work states, battery, faults, and modes mapped back to the universal v1 fields the Matter layer already understands (including the Charging/Docked tile logic).
|
|
2446
|
-
- Correct B01 request payloads (single object on dps 10000 with `method`/`msgId`/`params`; no `t`, no numeric `id`) and response correlation by the 12-digit `msgId` on dps 10001, with `code != 0` surfaced as command errors. Robot-initiated B01 pushes trigger an immediate status refresh.
|
|
2447
|
-
- B01 devices are routed cloud/MQTT-only, and periodic v1 reads with no Q7 equivalent (network info, consumables, server timers, room mapping) return quiet neutral responses — ending the `get_network_info` timeout noise permanently.
|
|
2448
|
-
- Known limitation: Matter Service Area (room selection) is not yet available for Q7-series robots; it requires the B01 protobuf map channel and will follow. Classic robots are unaffected.
|
|
2449
|
-
- 20 new tests, including byte-level encryption round-trips and correlation against a real recorded Q7 response fixture. Full suite: 175 passing.
|
|
2450
|
-
|
|
2451
|
-
## 1.4.67-hardened.6 (unofficial hardening build)
|
|
2452
|
-
|
|
2453
|
-
- Redesigned the plugin settings UI as a light, readable theme: white panels on a soft neutral background, a calm teal accent, and dark headings/text. All key color pairs verified at WCAG AA contrast (headings 16-17:1, muted text and pills 5+:1).
|
|
2454
|
-
- Headings now use explicit colors instead of inheritance. Homebridge UI injects its own theme stylesheet into custom-UI iframes, which could previously render section headings nearly invisible depending on the selected Homebridge theme.
|
|
2455
|
-
- Fixed the Devices section layout: the list container borrowed the pairing-list grid class, misaligning checkbox rows. Devices now have their own styled rows with hover states and a "Disabled" chip on skipped robots.
|
|
2456
|
-
- Accessibility and polish: keyboard focus rings on buttons/inputs/links, input focus glow, accent-colored checkboxes, toast notifications with colored edge indicators, and consistent button hover/active states.
|
|
2457
|
-
|
|
2458
|
-
## 1.4.67-hardened.5 (unofficial hardening build)
|
|
2459
|
-
|
|
2460
|
-
- Fixed Matter pairing entries never matching their robots: the commissioning serial (the robot's SN for vacuum nodes) was looked up in a DUID-keyed map, so every node fell back to the generic "Matter Roborock Bridge" label. Devices are now indexed by both DUID and serial, so vacuum pairing cards show the robot's name.
|
|
2461
|
-
- Pairing records belonging to disabled (skipped) robots are now hidden behind a one-line note with a "Show anyway" toggle. These records are inert leftovers in Homebridge's Matter storage from when the robots were managed; the accessories themselves are no longer registered. The list updates live when robots are enabled/disabled in the Devices section.
|
|
2462
|
-
- The platform now logs each stale Matter accessory it unregisters ("Unregistering stale Matter accessory ..."), making skip-list cleanup visible in the Homebridge log.
|
|
2463
|
-
- Polished the Devices section row layout (alignment/spacing) introduced in hardened.3.
|
|
2464
|
-
|
|
2465
|
-
## 1.4.67-hardened.4 (unofficial hardening build)
|
|
2466
|
-
|
|
2467
|
-
- The Charging/Docked tile opt-in now uses the battery percentage as the discriminator between the two states, with a configurable "Charged Battery Threshold (%)" (default 100). While docked below the threshold the Apple Home tile shows Charging — even if the robot already claims fully charged — and at or above it the tile shows Docked, even if the robot still reports a charging flag. Worn batteries commonly report "fully charged" early; lowering the threshold (e.g. 90) keeps the tile honest. Falls back to the state-based value when no battery reading is available. Exposed in both the config schema and the settings UI; covered by four new tests.
|
|
2468
|
-
|
|
2469
|
-
## 1.4.67-hardened.3 (unofficial hardening build)
|
|
2470
|
-
|
|
2471
|
-
- Fixed skip-list enforcement: `skipDevices` was only applied to the login-time runtime list, so skipped robots still had HomeKit and Matter accessories published for them with no runtime behind them. The skip list is now enforced at the source (`getAllHomeDevices`), covering discovery, Matter publication, read paths, and local-key refresh consistently; existing accessories for skipped robots are unregistered by the stale-accessory cleanup on the next bridge restart. Covered by a regression test matching both DUID and serial number.
|
|
2472
|
-
- Added a Devices section to the plugin settings UI listing every robot from cached HomeData (name, model, DUID, serial, online state) with a per-robot checkbox. Unchecking a robot writes it to Skip Devices and saves automatically; skipped robots stay visible so they can be re-enabled. The section is fed by the existing diagnostics endpoint, so it works even for robots the plugin no longer manages.
|
|
2473
|
-
- Exposed the "Show Charging/Docked on the Apple Home tile" option in the settings UI (previously only reachable through the JSON config editor, since the custom UI replaces the schema-generated form).
|
|
2474
|
-
- Performance: `getStoredHomeData` now memoizes the parsed HomeData per distinct payload. Previously every Matter attribute read and cluster build re-parsed the full multi-kilobyte HomeData JSON; steady-state CPU/GC pressure drops accordingly. The ignored-device set is also cached per config identity (including a fix for a fresh-array fallback that defeated identity comparison).
|
|
2475
|
-
- Regression suite extended to 19 tests, including parse-memoization reference stability and source-level skip enforcement.
|
|
2476
|
-
|
|
2477
|
-
## 1.4.67-hardened.2 (unofficial hardening build)
|
|
2478
|
-
|
|
2479
|
-
- Added an opt-in "Enable Matter Charging/Docked Status" setting. When enabled, the plugin publishes the standard RVC Charging (0x41) and Docked (0x42) operational states — and advertises them in the operational state list for Matter conformance — so the Apple Home tile shows "Charging"/"Docked" instead of always "Ready" while on the dock. Default remains off, preserving the upstream Ready-on-dock behavior for older iOS versions. Covered by three new conformance tests (charging, fully-charged/docked, and default-off).
|
|
2480
|
-
|
|
2481
|
-
## 1.4.67-hardened.1 (unofficial hardening build)
|
|
2482
|
-
|
|
2483
|
-
All robustness changes from the 1.4.64-hardened.1 build, re-ported onto upstream 1.4.66 (none had been independently fixed upstream), plus two new fixes:
|
|
2484
|
-
|
|
2485
|
-
- `catchError` no longer renders "Failed to execute undefined on robot undefined (unknown model)" when a caller only passes a message; the message is logged as-is. Contextual calls keep the existing format.
|
|
2486
|
-
- The unmapped-model notice (e.g. `roborock.vacuum.sc05` / Q7 M5) is now an informative warning explaining that generic defaults are applied and that core controls and Matter still work, instead of a scary "not fully supported / contact the dev" error with broken formatting.
|
|
2487
|
-
- The Matter device-not-ready classifier now also recognizes the upstream "Vacuum <duid> is not initialized." phrasing used by the new schedule endpoints, so those failures log calmly during startup races too.
|
|
2488
|
-
|
|
2489
|
-
Re-ported hardening (see 1.4.64-hardened.1 notes for details): startup-race command guards with rollback, no silent success on unbuildable messages, self-healing 60s Matter heartbeat, throw-proof status reads, extended endpoint-init backoff (1s–60s), dispose() lifecycle on shutdown/unregister, unref'ed timers, clean-mode capability fallback, and lazy HomeData debug serialization. Regression suite extended to 13 tests covering all of the above.
|
|
2490
|
-
|
|
2491
|
-
## 1.4.66
|
|
2492
|
-
|
|
2493
|
-
- Exposed each Roborock app schedule as a persistent HomeKit switch, with live enable/disable state backed by `get_server_timer` and `upd_server_timer`. Addresses issue #6.
|
|
2494
|
-
- Added Matter Service Area current-room reporting for active room cleaning, including resets that prevent stale room status during whole-home, spot, or zone cleaning. Addresses issue #7.
|
|
2495
|
-
|
|
2496
|
-
## 1.4.65
|
|
2497
|
-
|
|
2498
|
-
- Internal cleanup pass across the whole codebase: removed duplicated logic (shared crypto helpers, shared live-message parsing, consolidated device-model tables), deleted dead code, and simplified several hot paths (parallelized independent requests, reduced redundant JSON parsing/buffer reads) with no intended behavior changes. Verified against a live Roborock S6 Pure over Matter (start, pause, dock).
|
|
2499
|
-
- Fixed a display bug in the Homebridge UI's Matter pairing card where a real pairing/setup code could be mistaken for "not available" if it happened to match the literal placeholder text used for missing codes.
|
|
2500
|
-
- Fixed plugin config local test failing after first successful run within the same config session. The TCP socket probe was not properly managing socket lifecycle, which could cause resource exhaustion on subsequent test runs. Added `socket.unref()` to prevent sockets from keeping the Node process alive and improved error handling during socket cleanup. Addresses issue #13.
|
|
2501
|
-
|
|
2502
|
-
## 1.4.63
|
|
2503
|
-
|
|
2504
|
-
- Matter Pause and Return to Dock are now always forwarded to the robot instead of being dropped when the plugin's cached state looks idle. The cache can lag or be overridden by a stale HomeData refresh while the robot is really cleaning, which previously made the plugin silently reject real pause/dock commands as "not cleaning" / "already docked" (seen on a Roborock S7 `roborock.vacuum.a15` that was room-cleaning while HomeData reported it as charging). A redundant pause/dock on an already-docked robot is a harmless no-op. Addresses issue #12.
|
|
2505
|
-
- Fixed the Matter Cleaning tile collapsing back to Docked/Ready in Apple Home almost immediately after Start on models that sync slowly through the cloud (e.g. S8 / `roborock.vacuum.a51`). The optimistic Cleaning state is now held through the lagging "still docked/charging" reports during the recent-command window after a Start/Resume/area-clean, instead of being abandoned after two contradicting reports, so the tile stays on Cleaning — and Return to Dock stays available — until the robot actually reports Cleaning. It still falls back to the real state once that window passes, so a start the robot never acted on (e.g. a full bin) does not stay stuck on Cleaning. Follow-up to the 1.4.60 command-forwarding fix for issue #4.
|
|
2506
|
-
|
|
2507
|
-
## 1.4.62
|
|
2508
|
-
|
|
2509
|
-
- Added explicit package author metadata so npm identifies Joshua Appleman as the package author while keeping trusted GitHub Actions publishing intact.
|
|
2510
|
-
|
|
2511
|
-
## 1.4.61
|
|
2512
|
-
|
|
2513
|
-
- Kept Matter RVC state publishes as serialized full snapshots for all refresh paths, including live updates and Service Area selection changes, so Apple Home is not left depending on partial cluster writes after controller refreshes.
|
|
2514
|
-
- Removed the plugin's explicit RVC Operational State `operationalError` write and added tests pinning the Matter RVC mode clusters without unsupported `startUpMode`/`onMode` attributes.
|
|
2515
|
-
- Added rechargeable battery metadata to the optional Matter Power Source cluster, including nullable charging-current and time-to-full-charge values.
|
|
2516
|
-
- Improved the Homebridge UI Matter Pairing lookup to search common Docker/Homebridge Matter storage paths and keep loading pairing data even when plugin config is unavailable.
|
|
2517
|
-
- Updated Matter RVC `Updating...` documentation after the live Homebridge 2.1.1-beta reset/re-pair test rendered the full RVC endpoint correctly in Apple Home.
|
|
2518
|
-
|
|
2519
|
-
## 1.4.60
|
|
2520
|
-
|
|
2521
|
-
- Fixed Matter Pause and Return to Dock being silently dropped on models that sync slowly (e.g. Roborock S8 / `roborock.vacuum.a51`, which fall back to the cloud). After a Matter Start, these robots can keep reporting "docked/charging" for tens of seconds before they report "Cleaning"; during that lag the plugin's cached state was stale, so a follow-up pause/dock was rejected as "not cleaning" / "already docked." An explicit Matter pause/dock issued within 60s of a start/resume/area-clean is now forwarded to the robot even when the cached snapshot still reads docked (a redundant pause/dock on an already-docked robot is a harmless no-op). The Pause control also gained the same in-flight-command allowance that Return to Dock already had. Addresses issue #4.
|
|
2522
|
-
|
|
2523
|
-
## 1.4.59
|
|
2524
|
-
|
|
2525
|
-
- Made the HomeKit Pause Cleaning and Return to Dock switches wait for Roborock acknowledgement and log command timing, matching the fan Start/Stop path. Previously these were fire-and-forget, so a pause/dock that the robot did not acknowledge (e.g. once it is already cleaning) failed silently with no log; they now surface the acknowledgement time or a clear timeout/error to aid diagnosis.
|
|
2526
|
-
|
|
2527
|
-
## 1.4.58
|
|
2528
|
-
|
|
2529
|
-
- Fixed the root cause of Apple Home getting stuck on "Updating..." until Play Sound to Locate was pressed: Matter publishes are now serialized full snapshots with no plugin-side change tracking, so racing state updates can no longer leave the Matter store holding a stale value that the plugin refused to re-send. Verified at the Matter protocol level against a live Homebridge 2.1.1-beta container.
|
|
2530
|
-
- Restored spec-conformant RVC Operational State phase attributes (`phaseList`/`currentPhase` are null again) and removed the synthetic identify pulses and phase flapping that were broadcast to every Apple Home hub as refresh signals. The nulls are written on every publish so upgraded installs repair their Matter store without re-pairing.
|
|
2531
|
-
- Replaced the 5-second active-state heartbeat with a quiet 60-second full-snapshot safety net; matter.js suppresses unchanged writes, so steady-state Matter traffic drops to normal keep-alives.
|
|
2532
|
-
- Kept Play Sound to Locate (Identify) working as a manual full-state resync, and added regression tests pinning publish serialization, null phase attributes, full-snapshot republishes, and the no-synthetic-identify rule.
|
|
2533
|
-
|
|
2534
|
-
## 1.4.57
|
|
2535
|
-
|
|
2536
|
-
- Hardened Roborock MQTT protocol 300/301 parsing so short cloud payloads are skipped cleanly instead of throwing `RangeError` during inbound message handling.
|
|
2537
|
-
- Made legacy HomeKit fan Start/Stop commands wait for Roborock acknowledgement and log command timing, improving diagnostics for models where switches appear to do nothing.
|
|
2538
|
-
- Propagated Matter command errors/timeouts reliably and added one bounded Matter Return to Dock retry when Roborock still reports active cleaning after an ambiguous `app_charge` timeout.
|
|
2539
|
-
|
|
2540
|
-
## 1.4.56
|
|
2541
|
-
|
|
2542
|
-
- Hardened Roborock live cloud/local status routing so device-scoped updates are delivered only to the matching vacuum, and unscoped live arrays are ignored when multiple vacuums are configured.
|
|
2543
|
-
- Added normal Homebridge log entries when the legacy HomeKit fan accessory receives Start/Stop writes, making it easier to tell whether a failed command reached the plugin.
|
|
2544
|
-
- Added regression coverage for multi-vacuum live-message routing and unscoped live payload handling.
|
|
2545
|
-
|
|
2546
|
-
## 1.4.55
|
|
2547
|
-
|
|
2548
|
-
- Kept Matter optimistic state after Roborock cloud or local command acknowledgement timeouts and started an immediate fast follow-up refresh cadence so Apple Home can converge once live `get_status` catches up.
|
|
2549
|
-
- Allowed Matter Return to Dock to send `app_charge` after a recently timed-out Start even when the cached Roborock snapshot still says docked or charging.
|
|
2550
|
-
- Added regression coverage for timed-out Matter commands, fast status refreshes, and stale docked snapshots during follow-up dock requests.
|
|
2551
|
-
|
|
2552
|
-
## 1.4.54
|
|
2553
|
-
|
|
2554
|
-
- Bounded Matter clean-mode preparation so slow Roborock cloud acknowledgements for fan or mop settings no longer delay the actual Start command for 30-40 seconds.
|
|
2555
|
-
- Limited Matter clean-mode prep commands to a short request timeout and kept Start moving with optimistic state when prep is slow or ambiguous.
|
|
2556
|
-
- Stopped trying alternate Roborock water-mode commands after timeout errors, while still falling back for unsupported or unknown command responses.
|
|
2557
|
-
|
|
2558
|
-
## 1.4.53
|
|
2559
|
-
|
|
2560
|
-
- Improved Matter state reads so Apple Home can receive cached/live vacuum state quickly while the plugin refreshes Roborock in the background, reducing long `Updating...` stalls after reopening Home.
|
|
2561
|
-
- Added a Matter Pairing section to the Config UI that reads Homebridge commissioning data and shows the Roborock child/daughter bridge QR code plus each vacuum's 11-digit setup code after restart.
|
|
2562
|
-
- Improved the Config UI local connection test to recognize an already-active or recently-used local Roborock connection and show the source of the diagnostic result.
|
|
2563
|
-
- Moved debug logging and Roborock cloud fallback toggles into an Advanced troubleshooting section so the normal setup flow stays focused on account, Matter, and pairing.
|
|
2564
|
-
- Quieted repeated `get_status` warnings for known Roborock status fields when Homebridge has not created a matching diagnostic state object, while keeping warnings for genuinely new fields.
|
|
2565
|
-
|
|
2566
|
-
## 1.4.52
|
|
2567
|
-
|
|
2568
|
-
- Delayed and retried Matter state refreshes while Homebridge reports a freshly registered endpoint is still initializing, reducing startup AccessControl warnings after bridge or child-bridge restarts.
|
|
2569
|
-
- Added compact Roborock status diagnostics to copied Config UI reports, including recent `get_status` and live cloud/local payloads for troubleshooting incorrect current-state or room-status reports.
|
|
2570
|
-
- Captured compact `get_server_timer` and `get_timer` responses while debug logging is enabled so schedule-switch feature requests can be investigated without exposing credentials.
|
|
2571
|
-
|
|
2572
|
-
## 1.4.51
|
|
2573
|
-
|
|
2574
|
-
- Scoped live Roborock cloud/local status updates to the source vacuum so one robot's push messages no longer update every configured HomeKit or Matter vacuum.
|
|
2575
|
-
- Kept Matter optimistic state after Roborock command acknowledgement timeouts, avoiding stale Idle/Charging rollbacks when the robot accepted the command but the cloud acknowledgement arrived late or not at all.
|
|
2576
|
-
- Made the Config UI local connection test recover from stalled requests and skip LAN probing when **Use Roborock cloud only** is enabled.
|
|
2577
|
-
|
|
2578
|
-
## 1.4.50
|
|
2579
|
-
|
|
2580
|
-
- Fixed the Node current CI test failure by isolating Matter timer cleanup in tests and adding a safe timer fallback for deferred Matter state updates when the test runtime removes the global timer.
|
|
2581
|
-
|
|
2582
|
-
## 1.4.49
|
|
2583
|
-
|
|
2584
|
-
- Added **Use Roborock cloud only** to disable local LAN discovery and local TCP commands for installations where local sockets appear connected but repeatedly time out; commands and status polling now route through Roborock cloud when available.
|
|
2585
|
-
- Updated diagnostics and copied reports to show cloud-only mode clearly instead of stale local connection state.
|
|
2586
|
-
- Graduated Matter Service Area room selection from a separate beta checkbox so it is included automatically whenever the Matter vacuum is enabled.
|
|
2587
|
-
|
|
2588
|
-
## 1.4.48
|
|
2589
|
-
|
|
2590
|
-
- Applied **Prefer Roborock cloud for Matter commands** to Matter follow-up status refreshes as well as commands, so S8-style local status timeouts do not leave Apple Home stuck on Cleaning after the robot returns to dock.
|
|
2591
|
-
- Passed the Matter cloud preference through the Roborock status polling stack down to the underlying `get_prop/get_status` request.
|
|
2592
|
-
|
|
2593
|
-
## 1.4.47
|
|
2594
|
-
|
|
2595
|
-
- Kept the Matter vacuum run mode active while Roborock is returning to dock, avoiding an inconsistent Idle/Returning state combination that could make Apple Home show "No Response" during the charging transition.
|
|
2596
|
-
|
|
2597
|
-
## 1.4.46
|
|
2598
|
-
|
|
2599
|
-
- Preferred Roborock cloud acknowledgements for Matter saved-map switches before selected-area cleaning, avoiding local `load_multi_map` acknowledgement timeouts that could leave Apple Home stuck on "Updating...".
|
|
2600
|
-
- Continued Matter selected-area cleaning when Roborock has already switched to the requested saved map even if the map-load acknowledgement reports a timeout.
|
|
2601
|
-
|
|
2602
|
-
## 1.4.45
|
|
2603
|
-
|
|
2604
|
-
- Added an optional **Prefer Roborock cloud for Matter commands** setting so Matter vacuum commands can bypass local LAN command timeouts on models such as the S8 while leaving the existing HomeKit accessories on their normal transport path.
|
|
2605
|
-
- Forced short follow-up status refreshes after Matter commands are acknowledged so Apple Home can move out of optimistic states such as Returning once Roborock reports the real charging/docked status.
|
|
2606
|
-
- Ignored empty Roborock cloud push results so `CloudMessage data: undefined` packets no longer get forwarded as accessory updates.
|
|
2607
|
-
|
|
2608
|
-
## 1.4.44
|
|
2609
|
-
|
|
2610
|
-
- Treated unsupported Roborock clean-mode setting responses such as `unknown_method` as best-effort during Matter starts, so models that reject water-box commands can still continue to the actual start command and remember the unsupported setting path.
|
|
2611
|
-
|
|
2612
|
-
## 1.4.43
|
|
2613
|
-
|
|
2614
|
-
- Cleared stale remote-fallback markers when a vacuum reconnects over local TCP, so polling can return to local transport instead of staying pinned to Roborock cloud after a temporary connect failure.
|
|
2615
|
-
|
|
2616
|
-
## 1.4.42
|
|
2617
|
-
|
|
2618
|
-
- Fixed Apple Home getting stuck on "Connecting" when commissioning the Matter vacuum by reverting the operational state list to bare state IDs without labels. The manufacturer-range operational states with labels introduced in 1.4.40 were not tolerated by Apple Home during commissioning; this restores the known-good advertisement that paired successfully.
|
|
2619
|
-
|
|
2620
|
-
## 1.4.41
|
|
2621
|
-
|
|
2622
|
-
- Built the Matter cluster snapshot from the freshest live Roborock status (state, battery, charge) instead of the slower periodic HomeData snapshot, so registration snapshots and Apple Home attribute reads reflect changes sooner.
|
|
2623
|
-
- Allowed slow saved-map switches (`load_multi_map`) up to 30 seconds before timing out, because older models such as the S6 Pure can take longer than the default 10 seconds to switch maps, and kept transient timeout warnings classified correctly regardless of the configured duration.
|
|
2624
|
-
- Internal hardening with no behavior change: introduced a typed Roborock API surface for the Matter accessory and consolidated duplicated Matter name normalization to reduce drift.
|
|
2625
|
-
|
|
2626
|
-
## 1.4.40
|
|
2627
|
-
|
|
2628
|
-
- Restored the original Roborock map after Matter Service Area room refreshes, even when another saved-map load times out, and retried empty saved maps periodically so newly segmented rooms can appear without restarting Homebridge.
|
|
2629
|
-
- Hardened Matter RVC conformance by using standard Vacuum and Mop clean-mode tags for Vacuum + Mop, moving Roborock-specific operational states into the labeled manufacturer range, and returning INVALID_SET for multi-map room selections.
|
|
2630
|
-
- Cleared optimistic Matter state after repeated contradicting Roborock updates so Apple Home does not stay on a wrong state until the timeout when a command is acknowledged but has no effect.
|
|
2631
|
-
- Built only the requested Matter cluster for single-attribute reads and mirrored the Roborock name onto the accessory `name` to reduce generic "Matter Accessory" labels during pairing.
|
|
2632
|
-
|
|
2633
|
-
## 1.4.38
|
|
2634
|
-
|
|
2635
|
-
- Ensured every Matter Service Area room advertises a matching saved-map entry, using Roborock map names when available and a generated label otherwise, so Apple Home no longer risks getting stuck on Updating when a room references a map without a reported name.
|
|
2636
|
-
- Cached persisted Roborock state (HomeData, room mappings, transport diagnostics) in memory after the first read to cut repeated disk reads on every status lookup and command while preserving the on-disk file format and legacy migration.
|
|
2637
|
-
- Removed an unreachable internal command branch and a duplicate status helper, and ignored local tooling files during lint.
|
|
2638
|
-
|
|
2639
|
-
## 1.4.37
|
|
2640
|
-
|
|
2641
|
-
- Kept unresolved Roborock maps out of Matter Service Area metadata until they have matching room segment IDs, avoiding Apple Home getting stuck on Updating with incomplete map data.
|
|
2642
|
-
- Avoided reloading the Roborock map that is already active while refreshing Matter room mappings, preventing startup timeouts on models that reject that reload.
|
|
2643
|
-
|
|
2644
|
-
## 1.4.36
|
|
2645
|
-
|
|
2646
|
-
- Reloaded saved Roborock maps during Matter Service Area refresh even when Roborock reports the map is already active, giving multi-floor rooms another chance to expose segment IDs.
|
|
2647
|
-
- Published saved Matter Service Area map names as soon as Roborock reports them, even while rooms for a map are still being resolved.
|
|
2648
|
-
- Documented Matter pairing-name behavior and why Apple Home may ask to add the external vacuum accessory after the bridge is commissioned.
|
|
2649
|
-
|
|
2650
|
-
## 1.4.35
|
|
2651
|
-
|
|
2652
|
-
- Added capability-gated Matter clean modes for Vacuum, Mop, and Vacuum + Mop on Roborock models that report mop or water support.
|
|
2653
|
-
- Applied selected Matter clean modes before Matter start/resume commands by updating Roborock suction and water settings where the model exposes those controls.
|
|
2654
|
-
- Refreshed Matter Service Area room mappings across saved Roborock maps while idle, then restored the original map so multi-floor room lists can populate automatically.
|
|
2655
|
-
- Applied cached Roborock identity metadata earlier for restored Matter accessories so re-pairing is less likely to show a generic Matter Accessory name.
|
|
2656
|
-
|
|
2657
|
-
## 1.4.34
|
|
2658
|
-
|
|
2659
|
-
- Prefixed Matter Service Area room labels with the Roborock map name when multiple saved maps are available, so controllers that flatten maps still show floor context.
|
|
2660
|
-
- Documented the map-name label fallback for Apple Home and other Matter clients that do not expose a separate map picker yet.
|
|
2661
|
-
|
|
2662
|
-
## 1.4.33
|
|
2663
|
-
|
|
2664
|
-
- Added multi-map Matter Service Area metadata so supported clients can group rooms by saved Roborock maps.
|
|
2665
|
-
- Cached room mappings per Roborock map and preserved saved map names for upper/lower floor setups.
|
|
2666
|
-
- Loaded the selected Roborock map before starting Matter room cleaning when a selected area is on another map.
|
|
2667
|
-
|
|
2668
|
-
## 1.4.32
|
|
2669
|
-
|
|
2670
|
-
- Deferred Matter state pushes until after command handlers return to reduce HomeKit command timeouts.
|
|
2671
|
-
- Added Matter Service Area map metadata and clearer Matter command/room-selection diagnostics.
|
|
2672
|
-
- Documented re-pairing the Matter vacuum after changing the Service Area beta setting because controllers can cache the cluster list.
|
|
2673
|
-
|
|
2674
|
-
## 1.4.31
|
|
2675
|
-
|
|
2676
|
-
- Added an opt-in beta Matter Service Area path that exposes cached Roborock rooms to Matter clients and uses selected rooms for Matter-initiated cleaning.
|
|
2677
|
-
- Documented the Service Area beta as work in progress and kept it behind a separate setting from the main experimental Matter vacuum.
|
|
2678
|
-
|
|
2679
|
-
## 1.4.30
|
|
2680
|
-
|
|
2681
|
-
- Moved local/cloud transport transition diagnostics behind debug logging to keep normal Homebridge logs quieter.
|
|
2682
|
-
- Updated Matter vacuum commands to report the requested state immediately and log Roborock acknowledgment timing.
|
|
2683
|
-
- Expanded Matter battery power-source state and linked the regular HomeKit battery service to the main accessory.
|
|
2684
|
-
- Sanitized Roborock scene switch names so generated HomeKit names avoid unsupported characters.
|
|
2685
|
-
|
|
2686
|
-
## 1.4.29
|
|
2687
|
-
|
|
2688
|
-
- Kept Matter vacuum state optimistic after commands so Apple Home does not fall back to stale ready/idle status while Roborock reports the transition.
|
|
2689
|
-
|
|
2690
|
-
## 1.4.28
|
|
2691
|
-
|
|
2692
|
-
- Added a Matter RVC clean-mode cluster so Apple Home can complete the native vacuum accessory setup.
|
|
2693
|
-
- Clarified Matter vacuum setup instructions for child bridge Matter enablement and log-based pairing codes.
|
|
2694
|
-
|
|
2695
|
-
## 1.4.27
|
|
2696
|
-
|
|
2697
|
-
- Removed the unsupported Matter run-mode startup attribute from experimental vacuum state updates.
|
|
2698
|
-
|
|
2699
|
-
## 1.4.26
|
|
2700
|
-
|
|
2701
|
-
- Fixed experimental Matter vacuum registration by omitting standard operational-state labels that Matter rejects during conformance validation.
|
|
2702
|
-
|
|
2703
|
-
## 1.4.25
|
|
2704
|
-
|
|
2705
|
-
- Added optional experimental Matter robotic vacuum exposure for Homebridge 2 with Matter enabled.
|
|
2706
|
-
- Kept the existing HomeKit fan/switch accessory path active for backwards compatibility.
|
|
2707
|
-
- Documented the Matter setting and Phase 1 command mapping in the README, roadmap, and admin UI.
|
|
2708
|
-
|
|
2709
|
-
## 1.4.24
|
|
2710
|
-
|
|
2711
|
-
- Changed transient timeout warning throttling to group repeated polling failures per vacuum instead of per command.
|
|
2712
|
-
- Increased the default transient warning interval to 6 hours and added a configurable Homebridge/UI setting.
|
|
2713
|
-
- Added support for setting the transient warning interval to 0 so recurring transient warnings only appear when debug logging is enabled.
|
|
2714
|
-
|
|
2715
|
-
## 1.4.23
|
|
2716
|
-
|
|
2717
|
-
- Throttled repeated transient command warnings so recurring Roborock polling timeouts are logged periodically instead of every refresh cycle.
|
|
2718
|
-
|
|
2719
|
-
## 1.4.22
|
|
2720
|
-
|
|
2721
|
-
- Added dedicated HomeKit momentary switches for Pause Cleaning and Return to Dock.
|
|
2722
|
-
- Changed the main HomeKit off action to stop cleaning only instead of also sending a dock command.
|
|
2723
|
-
- Clarified cloud-only transport logs so expected Roborock cloud calls are not described as fallback from local control.
|
|
2724
|
-
|
|
2725
|
-
## 1.4.21
|
|
2726
|
-
|
|
2727
|
-
- Added plain-English transport transition logs for local TCP connections, cloud fallback, local recovery, remote/shared devices, offline state, missing local credentials, and missing local IP discovery.
|
|
2728
|
-
- Reduced duplicate fallback logging and stopped printing local keys in debug discovery logs.
|
|
2729
|
-
|
|
2730
|
-
## 1.4.20
|
|
2731
|
-
|
|
2732
|
-
- Added a "Test Local Connection" action in the admin UI that performs a live LAN TCP probe for each cached vacuum.
|
|
2733
|
-
- Included local test results in copied diagnostic reports with DUIDs and local IPs still redacted.
|
|
2734
|
-
|
|
2735
|
-
## 1.4.19
|
|
2736
|
-
|
|
2737
|
-
- Added a short diagnostics auto-refresh after admin UI startup when the first snapshot is not locally connected.
|
|
2738
|
-
- Added transport freshness timestamps to diagnostic cards and copied diagnostic reports.
|
|
2739
|
-
|
|
2740
|
-
## 1.4.18
|
|
2741
|
-
|
|
2742
|
-
- Updated the roadmap to reflect completed diagnostics, Homebridge compatibility, CI, release automation, and security work.
|
|
2743
|
-
- Improved diagnostics wording so local credentials, local TCP connectivity, cloud fallback, and offline states are easier to understand.
|
|
2744
|
-
- Added a redacted "Copy Diagnostic Report" action for future GitHub Issues.
|
|
2745
|
-
- Added GitHub Issue templates for bug reports, feature requests, and model support reports.
|
|
2746
|
-
|
|
2747
|
-
## 1.4.17
|
|
2748
|
-
|
|
2749
|
-
- Maintenance release to verify the trusted publishing and GitHub release automation after the admin UI and diagnostics updates.
|
|
2750
|
-
- No runtime behavior changes from `1.4.16`.
|
|
2751
|
-
|
|
2752
|
-
## 1.4.16
|
|
2753
|
-
|
|
2754
|
-
- Improved the Homebridge admin UI for readability with clearer section layout, status messaging, help text, and explicit settings save behavior.
|
|
2755
|
-
- Documented all plugin settings in the Homebridge schema and README, including region selection, encrypted tokens, password fallback, debug logging, and skipped devices.
|
|
2756
|
-
- Added serial numbers to UI diagnostics so ignored device values are easier to copy from the admin panel.
|
|
2757
|
-
- Fixed `skipDevices` so Homebridge config values are passed into discovery and can match either Roborock serial numbers or DUIDs.
|
|
2758
|
-
|
|
2759
|
-
## 1.4.15
|
|
2760
|
-
|
|
2761
|
-
- Tightened obstacle photo handling in the map UI to accept only base64-encoded image data and render it through browser-generated blob URLs.
|
|
2762
|
-
- Added blob URL cleanup when closing or replacing obstacle photos to avoid leaking browser-side object URLs.
|
|
2763
|
-
|
|
2764
|
-
## 1.4.14
|
|
2765
|
-
|
|
2766
|
-
- Hardened region detection by parsing the configured Roborock host instead of using substring matches.
|
|
2767
|
-
- Sanitized map obstacle image URLs before assigning them in the browser UI to reduce XSS and client-side redirect risk.
|
|
2768
|
-
- Added explicit read-only permissions to the CI workflow, upgraded GitHub Actions versions, and moved Codecov uploads to a repository secret.
|
|
2769
|
-
|
|
2770
|
-
## 1.4.13
|
|
2771
|
-
|
|
2772
|
-
- Adjusted `package.json` repository metadata to match the fork URL exactly for npm Trusted Publishing compatibility.
|
|
2773
|
-
- Updated the npm publish workflow to use Node 24 and the latest npm CLI for Trusted Publishing compatibility.
|
|
2774
|
-
|
|
2775
|
-
## 1.4.12
|
|
2776
|
-
|
|
2777
|
-
- Improved model resolution and startup hardening for newer Roborock metadata layouts.
|
|
2778
|
-
- Added diagnostics in the Homebridge UI for model detection, local key availability, discovery state, local IP, TCP connection state, and last transport used.
|
|
2779
|
-
- Fixed updater payload crashes caused by malformed or partial cloud/local message payloads.
|
|
2780
|
-
- Improved room mapping behavior with clearer logging and fallback labels when Roborock room names are missing.
|
|
2781
|
-
- Replaced forced hourly MQTT reconnects with a health-check-based reconnect path.
|
|
2782
|
-
- Added guards against transient `0%` battery reports while the robot is docked or charging to reduce false HomeKit low-battery alerts.
|
|
2783
|
-
- Added regression tests around transport selection, room mapping, and model/diagnostics handling.
|
|
2784
|
-
- Added incremental TypeScript-style checking for the core transport queue and a `typecheck` script for ongoing migration work.
|
|
2785
|
-
- Added GitHub Actions automation for npm publishing on `master` using npm Trusted Publishing.
|
|
2786
|
-
|
|
2787
|
-
## 1.2.2
|
|
2788
|
-
|
|
2789
|
-
- **New Feature**: Dynamic Scene Switch Management
|
|
2790
|
-
- Automatically create HomeKit switch buttons for each device's available scenes
|
|
2791
|
-
- Scene switches named after scene names with momentary switch behavior
|
|
2792
|
-
- Automatically add/remove corresponding switch buttons when scenes change
|
|
2793
|
-
- Execute corresponding scenes when switches are pressed, with error handling and status feedback
|
|
2794
|
-
- Synchronize scene switches when HomeData is updated
|
|
2795
|
-
- **Improvement**: Refactored scene API methods, separated scene fetching and device filtering functionality
|
|
2796
|
-
- **Fix**: Resolved recursive call issue in scene methods
|
|
2797
|
-
|
|
2798
|
-
## 1.0.15
|
|
2799
|
-
|
|
2800
|
-
- Fix Roborock Saros 10R Status issue
|
|
2801
|
-
|
|
2802
|
-
## 1.0.6
|
|
2803
|
-
|
|
2804
|
-
- Support new model
|
|
609
|
+
- Faster restarts: device setup no longer waits for LAN discovery, and several robots start at once instead of one by one (the real time saving landed in 3.0.1).
|
|
610
|
+
- The periodic status refresh for S/Q-series robots, which had never run, now works, so a missed live update corrects itself within a minute; idle CPU use also drops.
|
|
611
|
+
- Security: resolved a high-severity advisory in the `ip-address` dependency; `npm audit` reports zero vulnerabilities.
|
|
2805
612
|
|
|
2806
|
-
##
|
|
613
|
+
## 2.9.9 and earlier
|
|
2807
614
|
|
|
2808
|
-
-
|
|
615
|
+
- **2.5.0–2.9.9:** suction levels in the mode list (2.6.0), shown with Apple's own names (2.8.1) and following the Roborock app (2.8.0); live room tracking for classic robots and Max+ on the Q7 (2.7.0); robots that refuse a request are no longer asked again (2.5.0); Verified by Homebridge, with a Donate button (2.9.3); faster startup and room lookups (2.9.1–2.9.5); the clean mode of a clean started outside Apple Home (2.9.9).
|
|
616
|
+
- **2.1.1–2.4.2:** live room tracking for the Q7 (2.4.0), room-by-room progress in Apple Home (2.1.2, 2.3.1), progress kept across restarts (2.2.0), all known dependency vulnerabilities resolved and unused dependencies removed (2.3.2), and a failed login can no longer crash Homebridge (2.4.2).
|
|
617
|
+
- **2.1.0:** the first public release as homebridge-roborock-matter. Matter only, one native Matter vacuum per robot, full Q7 (B01) support built against python-roborock, and a new settings page. The 2.0.0-matter pre-releases led up to it.
|
|
618
|
+
- **1.4.67-hardened.1–6:** unofficial hardening builds on upstream 1.4.66: startup guards, a device list with per-robot on/off in the settings, and the Charging/Docked option.
|
|
619
|
+
- **1.0.0–1.4.66:** [homebridge-roborock-vacuum2](https://github.com/applemanj/homebridge-roborock-vacuum2) by Joshua Appleman, which this plugin is forked from.
|