#3958·homebridge

Matter bridged accessories: batPercentRemaining freezes at pairing-time value in Apple Home (changes-omitted attribute; device-side store verifiably fresh)

Author: mathiashornbekCreated Jul 15, 2026Updated Sep 13, 2026
Labelsbugstale

Analysis

A Matter Robotic Vacuum Cleaner (device type 0x74) exposed through Homebridge 2's Matter API publishes battery updates continuously via updateAccessoryState. The matter.js store demonstrably carries the live value. Controllers (Apple Home, multi-hub) keep rendering the value from pairing time indefinitely — across plugin restarts, Homebridge restarts, and days of uptime — while batChargeState on the very same PowerSource cluster updates live.

Environment

  • Homebridge 2.1.x (Matter API), plugin homebridge-roborock-matter publishing full-state snapshots via updateAccessoryState
  • Three robots (one V1-protocol, two B01-protocol) — identical behavior on all
  • Controller: Apple Home (Apple TV + HomePod hubs, current iOS/tvOS)

Reproduction

  1. Pair a bridged Matter accessory that exposes PowerSource with a battery percentage that changes over time (any RVC works).
  2. Let the battery drain or charge by 10+ points.
  3. Compare the Apple Home tile with the matter.js store on disk.

Evidence chain

  1. Plugin log shows continuous publishes with live values (73% → 92% over an hour, all three robots).
  2. grep -ro "batPercentRemaining[^,}]*" <storage> --include="*.json" shows the persisted matter.js store matching the vendor app in real time (e.g. 170 = 85% while Apple renders 74%).
  3. batChargeState (same PowerSource cluster) propagates to Apple Home in near-real-time throughout.
  4. The values Apple renders are exactly each robot's value from its commissioning moment; a re-pair refreshes once, then freezes again.
  5. A device-side "resync nudge" (publishing the attribute as briefly unknown, then real, to force two genuine store changes and a data-version bump) helps only controllers that re-prime their subscription (e.g. after a hub restart); Apple in steady state still never converges.

Analysis

batPercentRemaining (and batTimeToFullCharge) carry the Matter spec reporting quality "changes omitted" (C). matter.js honors this on the device side (changes are not delivered via subscription reports), and the matter.js controller compensates on its own side ("Always read attributes that do not report changes via subscriptions" — matter.js changelog 0.10). Apple's controller evidently performs no such re-reads, so bridged accessories freeze on the pairing-time percentage. No plugin-side write can force a changes-omitted attribute to report.

Suggestion

Consider a device-side mitigation in the Homebridge Matter layer for bridged accessories — for example bumping the cluster data version or scheduling a periodic re-announce for changes-omitted attributes whose value has drifted — so controllers that rely purely on subscriptions eventually converge. This would fix frozen battery percentages for every bridged plugin at once, in one place.

Happy to provide full logs, store dumps, and to test builds.

Expected Behavior

The battery percentage rendered by Matter controllers (Apple Home) should follow the value the bridge publishes and stores — like batChargeState on the same cluster already does — instead of remaining frozen at whatever the percentage was when the accessory was commissioned.

Steps To Reproduce

  1. Bridge any Matter accessory exposing the PowerSource cluster with a changing battery percentage through Homebridge 2's Matter API (a Robotic Vacuum Cleaner is a convenient example) and pair the bridge with Apple Home.
  2. Note the battery percentage Apple Home shows right after pairing.
  3. Let the battery drain or charge by 10+ points while the plugin keeps publishing fresh values via updateAccessoryState.
  4. Compare: the matter.js store on disk (grep -ro "batPercentRemaining[^,}]*" <storage> --include="*.json") carries the live value, batChargeState updates live in Apple Home — but the rendered percentage remains the pairing-time value indefinitely (verified across plugin restarts, Homebridge restarts, and days of uptime; only re-pairing or restarting the Matter hub refreshes it once).

Logs

Representative plugin log lines (battery values are handed to the Matter API on every change; duids shortened):

[7/15/2026, 1:38:20 PM] [Roborock Vacuum] Matter publish for 6Lfh...768U: battery=97%, operationalState=1.
[7/15/2026, 3:57:39 PM] [Roborock Vacuum] Battery resync for 6Lfh...768U: forced a fresh Matter attribute report (battery=100%).
[7/15/2026, 3:57:39 PM] [Roborock Vacuum] Matter publish for 6Lfh...768U: battery=100%, operationalState=0.
[7/15/2026, 3:57:39 PM] [Roborock Vacuum] Matter publish for 2xyU...BEqJ: battery=100%, operationalState=66.

Store check while Apple Home rendered a stale pairing-time percentage:

$ grep -ro "batPercentRemaining[^,}]*" <storage> --include="*.json"
batPercentRemaining":170     # = 85%, matching the vendor app in real time

Full DEBUG logs and store dumps available on request.

Configuration

{
  "platform": "RoborockVacuumPlatform",
  "name": "Roborock",
  "email": "<redacted>",
  "encryptedToken": "<redacted>",
  "enableMatterServiceArea": true,
  "enableLiveRoomTracking": true,
  "enableMatterPowerSource": true,
  "enableMatterCleanMode": true,
  "cloudOnlyMode": false
}

Environment

  • OS: Linux (Homebridge server)
  • Homebridge: 2.1.x with Matter enabled (behavior also verified against 2.1.2-beta.3 in test rigs)
  • Plugin: homebridge-roborock-matter 2.8.x (publishes via updateAccessoryState)
  • Node: v22 (LTS)
  • npm: 10.x
  • Controller: Apple Home — Apple TV + HomePod hubs, current iOS/tvOS

Process Supervisor

hb-service

Additional Context

A longer-form writeup of the investigation (including why a device-side resync nudge is insufficient in steady state) is maintained here: https://github.com/mathiashornbek/homebridge-roborock-matter/blob/main/docs/matter-battery-issue-draft.md

Related prior art: homebridge#3914 used attribute presence to fix PowerSource feature announcement — this report concerns reporting of the changes-omitted percentage attribute after commissioning.