homebridge-roborock-matter 2.9.1 → 2.9.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,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.9.2
4
+
5
+ - **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).
6
+ - 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.
7
+
3
8
  ## 2.9.1
4
9
 
5
10
  Deep performance pass over the live-room hot paths, with honest before/after measurements.
package/README.md CHANGED
@@ -96,7 +96,7 @@ Everything is configurable from the Homebridge UI. The essentials:
96
96
 
97
97
  ## Battery percentage in Apple Home
98
98
 
99
- Apple Home renders the battery percentage from pairing time and never re-reads it — the Matter attribute carries the spec's "changes omitted" reporting quality, so value changes are not pushed to controllers _by design_ (charging state on the very same cluster updates live). This is a controller-side limitation verified end-to-end, not a plugin bug.
99
+ Apple Home renders the battery percentage from pairing time and refreshes it only on a fresh read (commissioning, hub restart) while charging state on the very same cluster updates live. This is a controller-side limitation, not a plugin bug, and it is being investigated upstream with the Homebridge team ([homebridge#3958](https://github.com/homebridge/homebridge/issues/3958)). Current state of knowledge: as of Matter 1.4 the attribute carries the **"quieter" (Q)** reporting quality reports ARE sent over the subscription (rate-limited to one per 10 s), a Homebridge maintainer verified that a spec-compliant matter.js controller receives and applies them, yet Apple Home in steady state does not. The likely permanent fix is on Apple's side (Apple Feedback).
100
100
 
101
101
  <details>
102
102
  <summary>The full evidence chain and workarounds</summary>
@@ -0,0 +1,29 @@
1
+ # Svar til bwp91 på homebridge#3958 — klar til at poste
2
+
3
+ ```markdown
4
+ Thank you — this is exactly the kind of verification I was hoping for, and
5
+ the C → Q correction is a genuinely important update. You're right that my
6
+ analysis was written against the older changes-omitted reading of the
7
+ attribute; I've already corrected the plugin's documentation to reflect the
8
+ Matter 1.4 Q (quieter) quality and your subscription trace
9
+ (mathiashornbek/homebridge-roborock-matter@main, README + docs).
10
+
11
+ Your controller log also explains two things I had observed but couldn't
12
+ fully reconcile: the resync nudge's null → value transition "working" at the
13
+ wire level while Apple still never converged, and `batChargeState` updating
14
+ live throughout. Both are consistent with the reports leaving the bridge and
15
+ Apple simply not applying Q-quality percentage reports in steady state.
16
+
17
+ I'll run the verification you suggested on my production setup (three
18
+ robots: one V1, two B01) — Homebridge with matter.js debug logging through a
19
+ full charge cycle, watching for the subscription flushes carrying
20
+ `batPercentRemaining` — and post the log excerpts here. If I can get
21
+ chip-tool set up on the same network I'll add a subscription trace from that
22
+ side too.
23
+
24
+ Assuming both confirm what your trace already shows, I'll file the Apple
25
+ Feedback report about the controller's handling of Q-quality PowerSource
26
+ attributes and link it here so others can dupe it. Thanks again for digging
27
+ in — happy to test any builds if something changes in the Homebridge Matter
28
+ layer down the road.
29
+ ```
@@ -1,66 +1,57 @@
1
- # Matter PowerSource `batPercentRemaining` never refreshes in controllers (changes-omitted attribute; device-side store is fresh)
2
-
3
- Ready to file against `homebridge/homebridge` (Matter API). Title suggestion:
4
-
5
- > Matter bridged accessories: `batPercentRemaining` freezes at pairing-time value in Apple Home (changes-omitted attribute; store verifiably fresh)
6
-
7
- ## Summary
8
-
9
- A Matter Robotic Vacuum Cleaner (device type 0x74) exposed through Homebridge 2's
10
- Matter API publishes battery updates continuously via `updateAccessoryState`. The
11
- matter.js store demonstrably carries the live value. Controllers (Apple Home,
12
- multi-hub) keep rendering the value from pairing time indefinitely across
13
- plugin restarts, Homebridge restarts, and days of uptime while
14
- `batChargeState` on the very same cluster updates live.
15
-
16
- ## Environment
17
-
18
- - Homebridge 2.1.x (Matter API), plugin `homebridge-roborock-matter` publishing
19
- full-state snapshots via `updateAccessoryState`
20
- - Three robots (one V1-protocol, two B01-protocol) identical behavior on all
21
- - Controller: Apple Home (Apple TV + HomePod hubs, current iOS/tvOS)
22
-
23
- ## Reproduction
24
-
25
- 1. Pair a bridged Matter accessory that exposes PowerSource with a battery
26
- percentage that changes over time (any RVC works).
27
- 2. Let the battery drain or charge by 10+ points.
28
- 3. Compare the Apple Home tile with the matter.js store on disk.
29
-
30
- ## Evidence chain
31
-
32
- 1. Plugin log shows continuous publishes with live values (73% → 92% over an
33
- hour, all three robots).
34
- 2. `grep -ro "batPercentRemaining[^,}]*" <storage> --include="*.json"` shows the
35
- persisted matter.js store matching the vendor app in real time (e.g. 170 =
36
- 85% while Apple renders 74%).
37
- 3. `batChargeState` (same PowerSource cluster) propagates to Apple Home in
38
- near-real-time throughout.
39
- 4. The values Apple renders are exactly each robot's value from its
40
- commissioning moment; a re-pair refreshes once, then freezes again.
41
- 5. A device-side "resync nudge" (publishing the attribute as briefly unknown,
42
- then real, to force two genuine store changes and a data-version bump) helps
43
- only controllers that re-prime their subscription (e.g. after a hub
44
- restart); Apple in steady state still never converges.
45
-
46
- ## Analysis
47
-
48
- `batPercentRemaining` (and `batTimeToFullCharge`) carry the Matter spec
49
- reporting quality **"changes omitted" (C)**. matter.js honors this on the
50
- device side (changes are not delivered via subscription reports), and the
51
- matter.js controller compensates on its own side ("Always read attributes that
52
- do not report changes via subscriptions" matter.js changelog 0.10). Apple's
53
- controller evidently performs no such re-reads, so bridged accessories freeze
54
- on the pairing-time percentage. No plugin-side write can force a
55
- changes-omitted attribute to report.
56
-
57
- ## Suggestion
58
-
59
- Consider a device-side mitigation in the Homebridge Matter layer for bridged
60
- accessories — for example bumping the cluster data version or scheduling a
61
- periodic re-announce for changes-omitted attributes whose value has drifted —
62
- so controllers that rely purely on subscriptions eventually converge. This
63
- would fix frozen battery percentages for every bridged plugin at once, in one
64
- place.
65
-
66
- Happy to provide full logs, store dumps, and to test builds.
1
+ # Matter PowerSource `batPercentRemaining` investigation record
2
+
3
+ Filed upstream as
4
+ [homebridge/homebridge#3958](https://github.com/homebridge/homebridge/issues/3958)
5
+ on 2026-07-15. This document tracks the current state of knowledge; the
6
+ original report text lives in the issue.
7
+
8
+ ## Symptom
9
+
10
+ A Matter RVC bridged through Homebridge 2's Matter API publishes battery
11
+ updates continuously; the matter.js store verifiably carries the live value;
12
+ `batChargeState` on the same PowerSource cluster updates live in Apple Home
13
+ but the rendered battery **percentage** stays at its commissioning-time value
14
+ until a fresh read (re-pair or Matter hub restart).
15
+
16
+ ## Corrected analysis (per Homebridge maintainer verification, 2026-07-15)
17
+
18
+ The original analysis assumed the attribute carries the Matter reporting
19
+ quality **C (changes omitted)** — never reported via subscription, controllers
20
+ must poll. That was true of older spec revisions, **but as of Matter 1.4 the
21
+ attribute is quality Q (quieter)** , and matter.js 0.17.x (shipped with every
22
+ Homebridge 2.1.x release) models it accordingly:
23
+
24
+ - **Q (quieter):** reported via subscription, rate-limited to at most one
25
+ report per 10 seconds, plus an immediate report on any null ↔ value
26
+ transition.
27
+
28
+ A Homebridge maintainer (bwp91) commissioned a matter.js controller against a
29
+ bridge exposing `PowerSource(Battery, Rechargeable)` — the same setup
30
+ Homebridge builds — and logged the subscription: percentage changes propagate
31
+ exactly as Q prescribes (immediate first report, deferred follow-up inside the
32
+ 10 s window, correct application after an interleaved `batChargeState` bump).
33
+ The "stale cluster data version" theory does not hold on the controller side.
34
+
35
+ ## Where that leaves things
36
+
37
+ - The bridge **emits** the reports; a spec-compliant controller **applies**
38
+ them. Apple Home in steady state does not — consistent with Apple's
39
+ controller still treating the attribute under the older changes-omitted
40
+ rules and refreshing only on a fresh read.
41
+ - The plugin's boot-time resync nudge (null value transition) does hit the
42
+ wire immediately (maintainer-confirmed) and remains useful for controllers
43
+ that re-prime their subscriptions; Apple still does not converge.
44
+ - **No device-side fix exists**: bumping the data version or re-announcing
45
+ only produces more of the reports Apple already receives and ignores.
46
+
47
+ ## Next verification steps (requested upstream)
48
+
49
+ 1. Run Homebridge with matter.js debug logging during a battery change and
50
+ capture the subscription flushes carrying `batPercentRemaining` proves
51
+ the reports leave THIS bridge specifically.
52
+ 2. Optionally subscribe with `chip-tool` and confirm it sees (and applies)
53
+ the live values.
54
+
55
+ If both confirm reports going out, the permanent fix belongs with Apple
56
+ (Apple Feedback report about the controller's handling of Q-quality
57
+ PowerSource attributes). The upstream issue stays open in the meantime.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-roborock-matter",
3
- "version": "2.9.1",
3
+ "version": "2.9.2",
4
4
  "description": "Matter-only Homebridge plugin publishing Roborock robot vacuums (including 2025 B01/Q7-series) as native Matter accessories for Apple Home. Fork of homebridge-roborock-vacuum2.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1345,7 +1345,23 @@ class deviceFeatures {
1345
1345
  }
1346
1346
  }
1347
1347
 
1348
+ // Classic v1 models whose upstream-vetted per-model action list includes
1349
+ // `set_custom_mode_max_plus` — the verified signal that the robot's firmware
1350
+ // accepts the Max+ suction level (v1 fan power 108). Keep this in sync with
1351
+ // the modelConfig action lists in processSupportedFeatures above; models are
1352
+ // only added here once the upstream feature data (or a field report with a
1353
+ // diagnostics export) confirms the level.
1354
+ const MAX_PLUS_FAN_POWER_MODELS = new Set([
1355
+ "roborock.vacuum.a70", // S8 Pro Ultra
1356
+ ]);
1357
+
1358
+ /** @param {string} model */
1359
+ function supportsMaxPlusFanPower(model) {
1360
+ return MAX_PLUS_FAN_POWER_MODELS.has(model);
1361
+ }
1362
+
1348
1363
  module.exports = {
1349
1364
  deviceFeatures,
1350
1365
  stateCodes,
1366
+ supportsMaxPlusFanPower,
1351
1367
  };
@@ -14,6 +14,8 @@ const roborock_mqtt_connector =
14
14
  const rrMessage = require("./lib/message").message;
15
15
  const vacuum_class = require("./lib/vacuum").vacuum;
16
16
  const deviceFeatures = require("./lib/deviceFeatures").deviceFeatures;
17
+ const supportsMaxPlusFanPower =
18
+ require("./lib/deviceFeatures").supportsMaxPlusFanPower;
17
19
  const RRMapParser = require("./lib/RRMapParser");
18
20
  const messageQueueHandler =
19
21
  require("./lib/messageQueueHandler").messageQueueHandler;
@@ -1944,6 +1946,12 @@ class Roborock {
1944
1946
  canVacuum: true,
1945
1947
  canMop: hasWaterModeSchema || hasMopSchema || hasMopFeature,
1946
1948
  canControlFanPower,
1949
+ // Max+ (fan power 108) only where the upstream-vetted per-model
1950
+ // feature data confirms the level (e.g. S8 Pro Ultra) — field
1951
+ // reports with diagnostics exports extend the list.
1952
+ canMaxPlusFanPower: supportsMaxPlusFanPower(
1953
+ this.getProductAttribute(duid, "model")
1954
+ ),
1947
1955
  canControlWater:
1948
1956
  hasWaterModeSchema ||
1949
1957
  this.hasVacuumFeature(duid, [