Skip to content

fix: push polled values to HomeKit instead of discarding them - #1410

Merged
donavanbecker merged 1 commit into
OpenWonderLabs:latestfrom
JanzenMark:pr/push-polled-values
Sep 24, 2026
Merged

donavanbecker merged 1 commit into
OpenWonderLabs:latestfrom
JanzenMark:pr/push-polled-values

Conversation

@JanzenMark

Copy link
Copy Markdown
Contributor

Fixes #1409

The OpenAPI poll fetched each device and discarded the result, so HomeKit was
only ever told a value when a controller read one. The Home app shows the last
value it was notified of, so a stale reading never corrected itself.

What changed

  • src/SwitchBotHAPPlatform.ts — record each readable characteristic as it is
    registered, then push new values after every per-device and batched poll.
    Push once immediately after the accessories are registered as well, so the
    Home app shows a value without waiting for the first poll interval.

Two supporting changes make that safe and cheap:

  • getState() caches a fetched state briefly and shares one in-flight fetch.
    HomeKit reads every characteristic of an accessory together, so without this
    a push of a seven characteristic accessory costs seven API requests, and
    Homebridge warns that the read handler is slow.
  • A device that could not be read is marked, and a marked state pushes nothing.
    The getters report a fallback for a missing field, so pushing one would
    replace a good value in HomeKit with something that looks like a measurement,
    and nothing would correct it. A failed fetch is not cached either, or one
    transient error would hold every characteristic at its fallback for the whole
    window.

A genuine zero is still pushed. Only an unknown value is skipped.

Note on the test change

test/device/getstate-returns-status.spec.ts asserted the exact shape of the
minimal fallback, which now carries that marker, so two assertions there are
updated. That file came from #1404.

Tests

  • test/hap-push-updates.spec.ts — known values reach HomeKit, an unreadable
    device pushes nothing, a genuine zero is still pushed, an unknown value is
    skipped, one failing getter does not abandon the rest, the device state is
    read once for the whole accessory, and priming pushes each device once.
  • test/device/getstate-cache.spec.ts — a burst of concurrent reads costs one
    fetch, the value is reused inside the window and refetched after it, and a
    failure is marked and not cached.

Full suite, lint, typecheck and build pass.

Scope

Three linked changes rather than one. I kept them together because pushing
without the other two reintroduces the failure this is meant to fix: a
transient read error overwrites good values in HomeKit with zeros that look
real. Happy to split them if you would prefer.

The OpenAPI poll fetches each device and throws the result away:

    await client.getDevice(id)
    this.openApiRequestsToday++

Nothing calls updateValue, so HomeKit only learns a value when a controller
reads one. The Home app shows the last value it was notified of, so a reading
can stay stale indefinitely while a fresh read would return the right number.

Record each readable characteristic as it is registered, then push new values
after every per-device and batched poll. Push once immediately after the
accessories are registered as well, so the Home app shows a value without
waiting for the first poll interval.

Two supporting changes make that safe and cheap:

- getState() caches a fetched state briefly and shares one in-flight fetch.
  HomeKit reads every characteristic of an accessory together, so without this
  a push of a seven characteristic accessory costs seven API requests and
  Homebridge warns that the read handler is slow.
- A device that could not be read is marked, and a marked state pushes nothing.
  The getters report a fallback for a missing field, so pushing one would
  replace a good value in HomeKit with something that looks like a measurement.
  Nothing would then correct it. A failed fetch is not cached either, or one
  transient error would hold every characteristic at its fallback.

A genuine zero is still pushed. Only an unknown value is skipped.

test/device/getstate-returns-status.spec.ts asserted the exact shape of the
minimal fallback, which now carries that marker, so those two assertions are
updated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donavanbecker
donavanbecker merged commit f60c965 into OpenWonderLabs:latest Sep 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: polled values are fetched then discarded, so the Home app shows stale readings

2 participants