homebridge-roborock-matter 3.0.0 → 3.0.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 CHANGED
@@ -1,10 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.0.2
4
+
5
+ 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.
6
+
7
+ - **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.
8
+ - **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.
9
+ - **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.
10
+ - **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.
11
+ - **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.
12
+ - **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.
13
+ - **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.
14
+ - **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.
15
+ - 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.
16
+ - Full suite: 315 tests, 36 new. Each new test was checked to fail against the previous code and pass against this one.
17
+
18
+ ## 3.0.1
19
+
20
+ Follow-up to 3.0.0 after measuring a real restart instead of trusting the reasoning. Two things needed fixing, one of them mine.
21
+
22
+ - **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.
23
+ - **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.
24
+ - The diagnostics export is no longer headed `homebridge-roborock-vacuum2 diagnostic report` — a leftover from the fork that made reports confusing to read.
25
+
26
+ 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.
27
+
3
28
  ## 3.0.0
4
29
 
5
30
  Startup and refresh pass: Homebridge restarts are noticeably faster, and a status refresh that had never actually run now does. Nothing here requires re-pairing — existing setups keep working exactly as they are.
6
31
 
7
- - **Restarts are roughly 5 seconds faster.** LAN discovery listens for robot broadcasts for a fixed 5-second window, and startup used to sit and wait for it with nothing else happening. It now runs alongside the home-data refresh, device setup and network probes, which all fit inside that same window. Measured on a three-robot setup: ~8 s from "Starting adapter" to "Lets go!!!!!!!" before, ~3 s after.
32
+ - **LAN discovery moved off the critical path.** It listens for robot broadcasts for a fixed 5-second window, and startup used to sit and wait for it before even creating the devices. Device setup and network probes now run inside that window instead. (The wall-clock win landed in 3.0.1 see above; this release only reordered the work.)
8
33
  - **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.
9
34
  - **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.
10
35
  - **~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.
package/dist/platform.js CHANGED
@@ -402,8 +402,29 @@ class RoborockPlatform {
402
402
  if (!matter || typeof matter.unregisterPlatformAccessories !== "function") {
403
403
  return;
404
404
  }
405
- const currentMatterUuids = new Set(this.roborockAPI
406
- .getVacuumList()
405
+ // A failed startup surfaces here as "the account has no robots": when
406
+ // getHomeDetail() throws (Roborock maintenance, a rate-limited response,
407
+ // or plain DNS failure — EAI_AGAIN hit this log twice in three weeks) the
408
+ // error is caught and logged, but the discovery callback still runs, so
409
+ // getVacuumList() returns an empty array. Treating that as "everything is
410
+ // stale" unregisters every Matter accessory, and because Matter locks the
411
+ // mode list at commissioning, the user then has to re-pair every single
412
+ // robot — a destructive, non-recoverable outcome triggered by one bad
413
+ // cloud response. Leaving a genuinely removed robot in place until the
414
+ // next successful discovery is by far the cheaper mistake.
415
+ if (!this.roborockAPI.isInited()) {
416
+ this.log.debug("Skipping stale-accessory cleanup: the Roborock API is not initialised yet, so the device list cannot be trusted.");
417
+ return;
418
+ }
419
+ const knownDevices = this.roborockAPI.getVacuumList();
420
+ if (!Array.isArray(knownDevices) || knownDevices.length === 0) {
421
+ this.log.warn("Skipping stale-accessory cleanup: the Roborock account reported no robots. " +
422
+ "This is almost always a temporary cloud or network failure, and unregistering " +
423
+ "the Matter accessories here would force you to re-pair every robot. If you have " +
424
+ "genuinely removed all robots from your account, remove the accessories manually.");
425
+ return;
426
+ }
427
+ const currentMatterUuids = new Set(knownDevices
407
428
  .filter((device) => this.isSupportedDevice(this.roborockAPI.getProductAttribute(device.duid, "model")))
408
429
  .map((device) => this.generateMatterUuid(device.duid)));
409
430
  const staleAccessories = this.matterAccessories.filter((accessory) => !currentMatterUuids.has(accessory.UUID));
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
@@ -982,7 +982,7 @@ async function copyDiagnosticsReport() {
982
982
  function buildDiagnosticsReport(result) {
983
983
  const hasToken = Boolean(result.hasEncryptedToken || state.hasEncryptedToken);
984
984
  const lines = [
985
- "homebridge-roborock-vacuum2 diagnostic report",
985
+ "homebridge-roborock-matter diagnostic report",
986
986
  `generatedAt: ${result.generatedAt || "unknown"}`,
987
987
  `pluginVersion: ${result.pluginVersion || "unknown"}`,
988
988
  `nodeVersion: ${result.nodeVersion || "unknown"}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-roborock-matter",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "The most complete Roborock plugin for Apple Home. Supports the entire Roborock lineup — from the classic S-series to the new 2025 Q7 series that no other plugin can control. Sign in with your Roborock account and get native start/stop, room cleaning, suction levels, battery, and live 'cleaning in the kitchen' room tracking. Verified by Homebridge.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -811,10 +811,22 @@ class deviceFeatures {
811
811
  ].includes(robotModel),
812
812
  // this isn't the correct way to use this. This code must be from a different robot
813
813
  // isVoiceControlSupported: !!(parseInt(`0x${this.featuresStr || "0"}`.slice(-10, -9)) & 2),
814
+ // These two were written as bare arrays while every other model list in
815
+ // this object ends in `.includes(robotModel)`. An array is truthy in JS
816
+ // — an EMPTY array included — and the gate below is
817
+ // `if (featureList[feature])`, so both were unconditionally true for
818
+ // every robot. isElectronicWaterBoxSupported feeds isSupportedWaterMode,
819
+ // which is what makes canMop and canControlWater true, so Apple Home
820
+ // offered Mop and Vacuum + Mop plus a water-level control on dry-only
821
+ // robots (S4, S4 Max, S5, S6 Pure) and fired set_water_box_custom_mode
822
+ // at hardware with no tank. The three model allowlists above exist
823
+ // precisely to prevent that.
814
824
  isVoiceControlSupported: [
815
825
  "roborock.vacuum.a27", // S7 MaxV (Ultra)
816
- ],
817
- isElectronicWaterBoxSupported: [], // nothing for now. If this is needed, add the models here
826
+ ].includes(robotModel),
827
+ // No model is known to need this yet. Add models to the array and keep
828
+ // the .includes(robotModel) call if that changes.
829
+ isElectronicWaterBoxSupported: [].includes(robotModel),
818
830
  isCleanRouteFastModeSupported:
819
831
  this.featuresStr &&
820
832
  !!(256 & parseInt("0x" + this.featuresStr.slice(-8))),
@@ -103,9 +103,22 @@ class localConnector {
103
103
  scheduleReconnect(duid, ip, delayMs = LOCAL_RECONNECT_DELAY_MS) {
104
104
  this.clearReconnectTimer(duid);
105
105
  const { setTimer } = getTimerFns(this.adapter);
106
- const timer = setTimer(async () => {
106
+ // createClient can reject before its own try/catch takes over (the
107
+ // awaited diagnostics write happens first, and the failure handler awaits
108
+ // more of them). A bare timer callback that rejects is an unhandled
109
+ // rejection, which Node terminates the process for by default — so a full
110
+ // SD card at the moment a reconnect fires would take Homebridge down.
111
+ // Reconnect failures are expected and already logged inside createClient;
112
+ // swallowing them here only prevents the crash.
113
+ const timer = setTimer(() => {
107
114
  this.reconnectTimers.delete(duid);
108
- await this.createClient(duid, ip);
115
+ Promise.resolve()
116
+ .then(() => this.createClient(duid, ip))
117
+ .catch((error) => {
118
+ this.adapter.log.debug(
119
+ `Local reconnect attempt for ${duid} failed: ${error?.message || error}`
120
+ );
121
+ });
109
122
  }, delayMs);
110
123
  this.reconnectTimers.set(duid, timer);
111
124
  }
@@ -508,19 +521,37 @@ class localConnector {
508
521
  return new Promise((resolve, reject) => {
509
522
  const devices = {};
510
523
 
524
+ // The discovery socket is bound to 0.0.0.0 and receives whatever any
525
+ // host on the LAN broadcasts to this port — the Roborock phone app doing
526
+ // its own discovery, a port scanner, a malformed retransmit. Neither
527
+ // `localMessageParser.parse` (binary-parser throws RangeError on a short
528
+ // or over-declared buffer) nor `JSON.parse` is total, and a synchronous
529
+ // throw inside a dgram handler is an uncaught exception that takes
530
+ // Homebridge down with it. One stray datagram must never do that, so
531
+ // every unparseable packet is skipped instead.
511
532
  server.on("message", (msg) => {
512
- const parsedMessage = localMessageParser.parse(msg);
513
- const decodedMessage = this.decryptECB(
514
- parsedMessage.payload,
515
- BROADCAST_TOKEN
516
- ); // this might be decryptCBC for A01. Haven't checked this yet
517
-
518
- if (decodedMessage == null) {
519
- this.adapter.log.debug(`getLocalDevices: decodedMessage is null`);
533
+ let parsedDecodedMessage;
534
+
535
+ try {
536
+ const parsedMessage = localMessageParser.parse(msg);
537
+ const decodedMessage = this.decryptECB(
538
+ parsedMessage.payload,
539
+ BROADCAST_TOKEN
540
+ ); // this might be decryptCBC for A01. Haven't checked this yet
541
+
542
+ if (decodedMessage == null) {
543
+ this.adapter.log.debug(`getLocalDevices: decodedMessage is null`);
544
+ return;
545
+ }
546
+
547
+ parsedDecodedMessage = JSON.parse(decodedMessage);
548
+ } catch (error) {
549
+ this.adapter.log.debug(
550
+ `getLocalDevices: ignoring a malformed discovery datagram (${msg?.length ?? 0} bytes): ${error?.message || error}`
551
+ );
520
552
  return;
521
553
  }
522
554
 
523
- const parsedDecodedMessage = JSON.parse(decodedMessage);
524
555
  this.adapter.log.debug(
525
556
  `getLocalDevices parsedDecodedMessage: ${JSON.stringify(parsedDecodedMessage)}`
526
557
  );
@@ -604,7 +635,7 @@ class localConnector {
604
635
  decipher.update(input),
605
636
  decipher.final(),
606
637
  ]);
607
- const unpadded = safeRemovePkcs7(decryptedBuf);
638
+ const unpadded = this.safeRemovePkcs7(decryptedBuf);
608
639
 
609
640
  // 若原協定內容是 UTF-8,這裡再轉字串;否則直接回傳 Buffer 讓上層處理
610
641
  return unpadded.toString("utf8");
@@ -119,7 +119,13 @@ class message {
119
119
 
120
120
  if (protocol == 1) {
121
121
  const msg = Buffer.alloc(23);
122
- msg.write(version);
122
+ // latin1, not the UTF-8 default: the protocol version is three raw
123
+ // bytes, and one of the versions this file handles explicitly is
124
+ // "\x81S\x19". UTF-8 encodes 0x81 as two bytes (c2 81), so write()
125
+ // returned 4 and produced `c2 81 53 19`; the writeUint32BE below then
126
+ // overwrote the fourth byte, leaving a corrupt version AND a wrong
127
+ // sequence number. _decodeMsg already reads it back with latin1.
128
+ msg.write(version, "latin1");
123
129
  msg.writeUint32BE(currentSeq, 3);
124
130
  msg.writeUint32BE(currentRandom, 7);
125
131
  msg.writeUint32BE(timestamp, 11);
@@ -185,7 +191,13 @@ class message {
185
191
 
186
192
  if (encrypted) {
187
193
  const msg = Buffer.alloc(23 + encrypted.length);
188
- msg.write(version);
194
+ // latin1, not the UTF-8 default: the protocol version is three raw
195
+ // bytes, and one of the versions this file handles explicitly is
196
+ // "\x81S\x19". UTF-8 encodes 0x81 as two bytes (c2 81), so write()
197
+ // returned 4 and produced `c2 81 53 19`; the writeUint32BE below then
198
+ // overwrote the fourth byte, leaving a corrupt version AND a wrong
199
+ // sequence number. _decodeMsg already reads it back with latin1.
200
+ msg.write(version, "latin1");
189
201
  msg.writeUint32BE(currentSeq, 3);
190
202
  msg.writeUint32BE(currentRandom, 7);
191
203
  msg.writeUint32BE(timestamp, 11);
@@ -31,6 +31,9 @@ function getRequestTimeout(method, requestTimeoutMs) {
31
31
  * @property {(value: unknown) => void} resolve
32
32
  * @property {(reason?: unknown) => void} reject
33
33
  * @property {ReturnType<typeof setTimeout>} timeout
34
+ * @property {boolean} [secure] True for requests whose protocol-102 reply is
35
+ * only an acknowledgement, with the real payload arriving on protocol 301.
36
+ * @property {string} [method] The Roborock method, kept for diagnostics.
34
37
  */
35
38
 
36
39
  /**
@@ -322,11 +325,18 @@ class messageQueueHandler {
322
325
  }
323
326
  }, requestTimeout);
324
327
 
325
- // Store request with resolve and reject functions
328
+ // Store request with resolve and reject functions.
329
+ // `secure` travels with the entry so the MQTT receiver can tell a
330
+ // secure request (whose protocol-102 reply is only an ack — the real
331
+ // payload arrives on protocol 301) from an ordinary one (whose 102
332
+ // reply IS the result). It used to guess by comparing the result to
333
+ // the string "ok", which silently never matched.
326
334
  this.adapter.pendingRequests.set(messageID, {
327
335
  resolve,
328
336
  reject,
329
337
  timeout,
338
+ secure,
339
+ method,
330
340
  });
331
341
 
332
342
  if (useCloudConnection) {
@@ -39,6 +39,39 @@ let rriot;
39
39
  let photoGzipChunks = [];
40
40
  let photoChunkID = 0;
41
41
 
42
+ /**
43
+ * True when a protocol-102 result is a bare "command accepted" acknowledgement
44
+ * rather than real data. Roborock sends this as the single-element array
45
+ * ["ok"]; some firmware answers with the bare string.
46
+ * @param {unknown} result
47
+ * @returns {boolean}
48
+ */
49
+ function isOkAcknowledgement(result) {
50
+ if (result === "ok") {
51
+ return true;
52
+ }
53
+ return Array.isArray(result) && result.length === 1 && result[0] === "ok";
54
+ }
55
+
56
+ /**
57
+ * Decide whether a protocol-102 reply completes a pending request.
58
+ *
59
+ * Only a secure request answered with a bare acknowledgement keeps waiting —
60
+ * its real payload arrives on protocol 301. Everything else resolves here,
61
+ * including a secure request that came back with an error, which would
62
+ * otherwise hang until the request timeout.
63
+ *
64
+ * @param {{secure?: boolean}|undefined} pending
65
+ * @param {unknown} result
66
+ * @returns {boolean}
67
+ */
68
+ function shouldResolveOn102(pending, result) {
69
+ if (!pending) {
70
+ return false;
71
+ }
72
+ return !(pending.secure === true && isOkAcknowledgement(result));
73
+ }
74
+
42
75
  function payloadStartsWith(payload, value) {
43
76
  return (
44
77
  Buffer.isBuffer(payload) &&
@@ -76,6 +109,7 @@ class roborock_mqtt_connector {
76
109
  this.adapter = adapter;
77
110
 
78
111
  this.connected = false;
112
+ this.initialConnectTimeout = null;
79
113
 
80
114
  // NOTE: this class previously generated its own RSA-2048 keypair here,
81
115
  // but nothing ever read it — the protocol keypair lives in message.js
@@ -101,9 +135,27 @@ class roborock_mqtt_connector {
101
135
  }
102
136
 
103
137
  async initMQTT_Subscribe() {
104
- const timeout = setTimeout(async () => {
105
- this.adapter.restart();
138
+ // This used to invoke a `restart` method on the adapter, which the
139
+ // Roborock class has never had — so on the one path it was written for
140
+ // (the broker
141
+ // neither connecting nor emitting `reconnect` within 30 s, e.g. a Pi that
142
+ // boots before the network is up, or a SYN black-holed by a firewall) it
143
+ // threw a TypeError inside an async timer. That is an unhandled rejection,
144
+ // which terminates the Homebridge process under Node's default policy —
145
+ // the exact opposite of the intended recovery. mqtt.js already retries on
146
+ // its own reconnectPeriod, so the correct behaviour here is to say so
147
+ // clearly and let it keep trying. The handle is stored and unref'd so
148
+ // shutdown can cancel it instead of firing into a torn-down adapter.
149
+ this.clearInitialConnectTimeout();
150
+ this.initialConnectTimeout = setTimeout(() => {
151
+ this.initialConnectTimeout = null;
152
+ this.logConnectionIssue(
153
+ `The Roborock MQTT broker has not answered within 30 seconds of startup. The client keeps reconnecting in the background; robots stay on cloud fallback until the session is up.`
154
+ );
106
155
  }, 30000);
156
+ if (typeof this.initialConnectTimeout?.unref === "function") {
157
+ this.initialConnectTimeout.unref();
158
+ }
107
159
 
108
160
  await client.on("connect", (result) => {
109
161
  if (typeof result != "undefined") {
@@ -114,7 +166,7 @@ class roborock_mqtt_connector {
114
166
  );
115
167
  }
116
168
  });
117
- clearTimeout(timeout);
169
+ this.clearInitialConnectTimeout();
118
170
 
119
171
  this.connected = true;
120
172
  if (this._connectionIssueActive) {
@@ -157,7 +209,7 @@ class roborock_mqtt_connector {
157
209
  );
158
210
  }
159
211
  });
160
- clearTimeout(timeout);
212
+ this.clearInitialConnectTimeout();
161
213
  this.adapter.log.debug(`MQTT connection reconnect attempt.`);
162
214
  });
163
215
 
@@ -169,6 +221,17 @@ class roborock_mqtt_connector {
169
221
  });
170
222
  }
171
223
 
224
+ /**
225
+ * Cancel the startup watchdog. Safe to call when it is not armed, and
226
+ * called from shutdown so the timer cannot outlive the adapter.
227
+ */
228
+ clearInitialConnectTimeout() {
229
+ if (this.initialConnectTimeout) {
230
+ clearTimeout(this.initialConnectTimeout);
231
+ this.initialConnectTimeout = null;
232
+ }
233
+ }
234
+
172
235
  /**
173
236
  * Warn about a connection problem at most once per 5 minutes per message,
174
237
  * and remember that an outage is in progress so the next successful
@@ -306,16 +369,23 @@ class roborock_mqtt_connector {
306
369
  }
307
370
  }
308
371
 
309
- // special check for secure request like get_map_v1 etc. Don't process if result is OK. Instead wait for the actual response for protocol 301
310
- if (dps.result != "ok") {
311
- if (this.adapter.pendingRequests.has(dps.id)) {
312
- const { resolve, timeout } = this.adapter.pendingRequests.get(
313
- dps.id
314
- );
315
- this.adapter.clearTimeout(timeout);
316
- this.adapter.pendingRequests.delete(dps.id);
317
- resolve(dps.result);
318
- }
372
+ // Secure requests (get_map_v1 and friends) answer protocol 102 with
373
+ // a bare acknowledgement and deliver the real payload on protocol
374
+ // 301, so those must stay pending. Everything else is resolved here.
375
+ //
376
+ // This used to be `if (dps.result != "ok")`, which is always true:
377
+ // the wire format is the ARRAY ["ok"], and `["ok"] != "ok"` is false
378
+ // only after ToPrimitive — so the guard never fired for the case it
379
+ // was written for, and instead swallowed the completion of every
380
+ // ordinary cloud command that answers ["ok"] (app_start, app_stop,
381
+ // app_pause, app_charge, set_custom_mode, app_segment_clean, ...).
382
+ // Those requests then sat until the 10 s timeout and failed in Apple
383
+ // Home even though the robot had already carried them out.
384
+ const pending = this.adapter.pendingRequests.get(dps.id);
385
+ if (shouldResolveOn102(pending, dps.result)) {
386
+ this.adapter.clearTimeout(pending.timeout);
387
+ this.adapter.pendingRequests.delete(dps.id);
388
+ pending.resolve(dps.result);
319
389
  }
320
390
  // protocol 300 seems to be for get_photo 0 only. get_photo 0 is for large images. 1 is for small images.
321
391
  } else if (data.protocol == 300) {
@@ -493,6 +563,38 @@ class roborock_mqtt_connector {
493
563
  return this.connected;
494
564
  }
495
565
 
566
+ /**
567
+ * Resolve once the MQTT session is usable, or after `timeoutMs` regardless.
568
+ *
569
+ * The broker handshake takes a few seconds, and cloud requests issued
570
+ * before it completes fail outright with "Cloud connection not available".
571
+ * Startup used to get away with this because an unrelated fixed delay
572
+ * happened to sit in front of the first requests; waiting on the real
573
+ * signal makes that timing explicit rather than accidental. Resolving on
574
+ * timeout (instead of rejecting) keeps a slow or offline broker from
575
+ * blocking startup — callers fall back to their own error handling.
576
+ *
577
+ * @param {number} [timeoutMs=10000]
578
+ * @returns {Promise<boolean>} whether the connection came up in time
579
+ */
580
+ async waitUntilConnected(timeoutMs = 10000) {
581
+ if (this.connected) {
582
+ return true;
583
+ }
584
+
585
+ const deadline = Date.now() + timeoutMs;
586
+ while (!this.connected && Date.now() < deadline) {
587
+ await new Promise((resolve) => {
588
+ const timer = setTimeout(resolve, 100);
589
+ if (typeof timer?.unref === "function") {
590
+ timer.unref();
591
+ }
592
+ });
593
+ }
594
+
595
+ return this.connected;
596
+ }
597
+
496
598
  async ensureConnected() {
497
599
  if (client && this.connected) {
498
600
  this.adapter.log.debug("MQTT health check passed. Reconnect skipped.");
@@ -574,4 +676,6 @@ module.exports = {
574
676
  parseProtocol301Header,
575
677
  parsePhotoPayload,
576
678
  payloadStartsWith,
679
+ isOkAcknowledgement,
680
+ shouldResolveOn102,
577
681
  };
@@ -1410,6 +1410,18 @@ class Roborock {
1410
1410
  const homedata = await this.api.get(`v2/user/homes/${homeId}`);
1411
1411
  const homedataResult = homedata.data.result;
1412
1412
 
1413
+ // Guard the persisted copy the same way updateHomeData already does.
1414
+ // Roborock occasionally answers 200 with an envelope that carries no
1415
+ // `result` (maintenance windows, rate limiting). Writing that through
1416
+ // stores `{"ack":true}` over the cached device list on disk, so the
1417
+ // next restart starts from a destroyed HomeData as well — turning a
1418
+ // transient cloud hiccup into a persistent failure.
1419
+ if (!homedataResult) {
1420
+ throw new Error(
1421
+ `The Roborock cloud returned no home data for home ${homeId}. Keeping the previously cached device list.`
1422
+ );
1423
+ }
1424
+
1413
1425
  await this.setStateAsync("HomeData", {
1414
1426
  val: JSON.stringify(homedataResult),
1415
1427
  ack: true,
@@ -1505,52 +1517,26 @@ class Roborock {
1505
1517
  await this.updateHomeData(homeId);
1506
1518
 
1507
1519
  await this.createDevices();
1508
- await this.getNetworkInfo();
1509
1520
 
1510
- const discoveredDevices = await localDiscovery;
1511
-
1512
- if (!this.isCloudOnlyModeEnabled()) {
1513
- // merge udp discovered devices with local devices found via mqtt
1514
- Object.entries(discoveredDevices).forEach(([duid, ip]) => {
1515
- if (
1516
- !Object.prototype.hasOwnProperty.call(this.localDevices, duid)
1517
- ) {
1518
- this.localDevices[duid] = ip;
1519
- }
1520
- });
1521
- this.log.debug(
1522
- `localDevices: ${JSON.stringify(this.localDevices)}`
1523
- );
1524
-
1525
- for (const device of localKeyDevices) {
1526
- if (
1527
- !Object.prototype.hasOwnProperty.call(
1528
- this.localDevices,
1529
- device.duid
1530
- )
1531
- ) {
1532
- await this.updateTransportDiagnostics(device.duid, {
1533
- localDiscoveryState: "not-discovered",
1534
- lastTransportReason: "missing-local-ip",
1535
- });
1536
- }
1537
- }
1538
-
1539
- for (const device in this.localDevices) {
1540
- const duid = device;
1541
- const ip = this.localDevices[device];
1542
-
1543
- await this.updateTransportDiagnostics(duid, {
1544
- localIp: ip,
1545
- localDiscoveryState: "discovered",
1546
- });
1547
- await this.localConnector.createClient(duid, ip);
1548
- }
1549
- }
1521
+ // Cloud requests fail outright until the MQTT session is up, and
1522
+ // the very first ones (the per-robot network probe, then each
1523
+ // robot's initial poll) used to be issued a second after the
1524
+ // broker handshake started. Wait for the real signal instead of
1525
+ // hoping an unrelated delay covers it; a broker that never comes
1526
+ // up releases the wait and the requests fail as they would have.
1527
+ await this.rr_mqtt_connector.waitUntilConnected();
1550
1528
 
1529
+ await this.getNetworkInfo();
1551
1530
  await this.initializeDeviceUpdates();
1531
+
1552
1532
  this.bInited = true;
1553
1533
  this.log.info(`Starting adapter finished. Lets go!!!!!!!`);
1534
+
1535
+ // LAN attach runs to completion in the background. Everything above
1536
+ // works over the cloud, and the transport layer already falls back
1537
+ // to it, so holding accessory registration hostage to a fixed-length
1538
+ // broadcast listen only delayed the Apple Home tiles.
1539
+ void this.attachLocalTransports(localDiscovery, localKeyDevices);
1554
1540
  } else {
1555
1541
  this.log.info(
1556
1542
  `Most likely failed to login. Deleting UserData to force new login!`
@@ -1567,6 +1553,59 @@ class Roborock {
1567
1553
  }
1568
1554
  }
1569
1555
 
1556
+ /**
1557
+ * Merge LAN-discovered robots and open their local TCP connections.
1558
+ *
1559
+ * Deliberately runs after startup has completed: local transport is an
1560
+ * optimisation over the cloud path, not a prerequisite for it.
1561
+ *
1562
+ * @param {Promise<Record<string, string>>} localDiscovery
1563
+ * @param {Array<{duid: string}>} localKeyDevices
1564
+ */
1565
+ async attachLocalTransports(localDiscovery, localKeyDevices) {
1566
+ if (this.isCloudOnlyModeEnabled()) {
1567
+ return;
1568
+ }
1569
+
1570
+ try {
1571
+ const discoveredDevices = await localDiscovery;
1572
+
1573
+ // merge udp discovered devices with local devices found via mqtt
1574
+ Object.entries(discoveredDevices).forEach(([duid, ip]) => {
1575
+ if (!Object.prototype.hasOwnProperty.call(this.localDevices, duid)) {
1576
+ this.localDevices[duid] = ip;
1577
+ }
1578
+ });
1579
+ this.log.debug(`localDevices: ${JSON.stringify(this.localDevices)}`);
1580
+
1581
+ for (const device of this.normalizeArray(localKeyDevices)) {
1582
+ if (
1583
+ !Object.prototype.hasOwnProperty.call(this.localDevices, device.duid)
1584
+ ) {
1585
+ await this.updateTransportDiagnostics(device.duid, {
1586
+ localDiscoveryState: "not-discovered",
1587
+ lastTransportReason: "missing-local-ip",
1588
+ });
1589
+ }
1590
+ }
1591
+
1592
+ for (const duid in this.localDevices) {
1593
+ const ip = this.localDevices[duid];
1594
+
1595
+ await this.updateTransportDiagnostics(duid, {
1596
+ localIp: ip,
1597
+ localDiscoveryState: "discovered",
1598
+ });
1599
+ await this.localConnector.createClient(duid, ip);
1600
+ }
1601
+ } catch (error) {
1602
+ // Local transport is best-effort; the cloud path stays available.
1603
+ this.log.debug(
1604
+ `Local transport attach failed; continuing on cloud transport: ${error?.message || error}`
1605
+ );
1606
+ }
1607
+ }
1608
+
1570
1609
  async stopService() {
1571
1610
  try {
1572
1611
  this.flushPendingPersistedStates();
@@ -2081,15 +2120,32 @@ class Roborock {
2081
2120
  if (
2082
2121
  this.getVacuumDeviceInfo(duid, "pv") === b01Q7Adapter.B01_PROTOCOL_VERSION
2083
2122
  ) {
2123
+ // These used to go through startCommand(), whose SIMPLE_VACUUM_COMMANDS
2124
+ // allow-list contains neither `set_clean_type` nor `set_custom_mode` —
2125
+ // so both fell through to `Command ... not found.` and nothing was ever
2126
+ // sent to the robot. Mathias' own Homebridge log has carried that line
2127
+ // since 2026-07-13. runMatterSettingCommand is the same path the classic
2128
+ // (non-B01) branch below already uses: it goes straight to
2129
+ // vacuum.command and reports unsupported results as errors.
2130
+ //
2131
+ // The value stays wrapped in an array on purpose. Matter clean mode 0 is
2132
+ // a valid selection (vacuum), and vacuum.command's default branch tests
2133
+ // `if (value && ...)` — a bare 0 is falsy and would silently be sent as
2134
+ // an empty parameter list, which b01Q7Adapter then drops entirely.
2084
2135
  if (Number.isInteger(settings?.cleanMode)) {
2085
2136
  try {
2086
- await this.startCommand(
2137
+ await this.runMatterSettingCommand(
2087
2138
  duid,
2088
2139
  "set_clean_type",
2089
2140
  [settings.cleanMode],
2090
2141
  commandOptions
2091
2142
  );
2092
2143
  } catch (error) {
2144
+ this.rememberUnsupportedMatterSettingCommand(
2145
+ duid,
2146
+ "set_clean_type",
2147
+ error
2148
+ );
2093
2149
  this.log.debug(
2094
2150
  `B01 clean-type command failed for ${duid}; continuing with the start command. ${error.message || error}`
2095
2151
  );
@@ -2099,13 +2155,18 @@ class Roborock {
2099
2155
  const fanPower = settings?.fanPower;
2100
2156
  if (Number.isInteger(fanPower) && fanPower !== 105) {
2101
2157
  try {
2102
- await this.startCommand(
2158
+ await this.runMatterSettingCommand(
2103
2159
  duid,
2104
2160
  "set_custom_mode",
2105
2161
  [fanPower],
2106
2162
  commandOptions
2107
2163
  );
2108
2164
  } catch (error) {
2165
+ this.rememberUnsupportedMatterSettingCommand(
2166
+ duid,
2167
+ "set_custom_mode",
2168
+ error
2169
+ );
2109
2170
  this.log.debug(
2110
2171
  `B01 suction command failed for ${duid}; continuing with the start command. ${error.message || error}`
2111
2172
  );
@@ -2743,6 +2804,10 @@ class Roborock {
2743
2804
 
2744
2805
  this.localConnector.clearLocalDevicedTimeout();
2745
2806
 
2807
+ // The MQTT startup watchdog is armed in initMQTT_Subscribe and would
2808
+ // otherwise survive shutdown and fire into a torn-down adapter.
2809
+ this.rr_mqtt_connector?.clearInitialConnectTimeout?.();
2810
+
2746
2811
  for (const duid in this.vacuums) {
2747
2812
  this.clearInterval(this.vacuums[duid].getStatusIntervalHandle);
2748
2813
  this.clearInterval(this.vacuums[duid].mainUpdateIntervalHandle);