fix: push polled values to HomeKit instead of discarding them - #1410
Merged
donavanbecker merged 1 commit intoSep 24, 2026
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 isregistered, 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.
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.tsasserted the exact shape of theminimal 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 unreadabledevice 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 onefetch, 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.