fix(bthome): emit measurement object ids in ascending order - #17
Open
jnimmo wants to merge 1 commit into
Open
Conversation
BTHome v2 requires object ids within a payload to be non-decreasing. build_advertisement_data_() wrote measurements in rotation-traversal order, so two things broke the ordering: - Sensors are not necessarily declared in object_id order, so even a full (unsplit) packet could be out of order. - The rotation cursor starts mid-list and wraps, placing a high object id before a low one whenever measurements are split across packets or a sensor is skipped (NaN / no state). Home Assistant's bthome_ble parser warns on this: "BTHome device is not sending object ids in numerical order" Separate the two concerns: rotation still decides *which* measurements go in each packet, but the selected set is now sorted by object_id before being encoded. Sensors and binary sensors are rotated and ordered through a single combined cursor, because their object_id ranges overlap (e.g. temperature 0x45 vs door 0x1A) and because a single cursor also prevents one category from starving the other when the payload is split across packets. Fixes dz0ny#15 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jnimmo
marked this pull request as draft
July 11, 2026 04:41
jnimmo
marked this pull request as ready for review
July 11, 2026 09:08
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.
Problem
BTHome v2 requires object ids within a single advertisement payload to be in non-decreasing numerical order.
build_advertisement_data_()wrote measurements in rotation-traversal order, which violated this. Home Assistant'sbthome_bleparser warns on every update:Values still decode correctly (each object is self-describing), but the warning is valid and some stricter receivers can mishandle it.
Root cause
Two effects combined:
object_idorder. In the reported config the ids are0x01, 0x45, 0x45, 0x2E, 0x4A, so even a single unsplit packet came out unsorted.(start + i) % count), placing a high id before a low one whenever measurements are split across packets or a sensor is skipped (NaN / no state). The reported payload…4a…01…45…45…2e…is exactly a wrap that started atvoltage.There was also a latent issue: sensors and binary sensors were selected with independent budgets, so when a payload overflows one advertisement, binary sensors could be starved indefinitely.
Fix
Separate selection from write order:
object_idbefore encoding.0x45vs door0x1A) and a single cursor also prevents either category from starving the other.Net effect for the reported config: the payload is now always
01 2e 45 45 4a(ascending), regardless of rotation state.Verification
No ESPHome toolchain was available locally, so the selection/sort/rotation logic was extracted into a standalone harness and checked to be non-decreasing across: every rotation start, forced packet-splitting, overlapping sensor+binary ids, NaN/absent sensors, and a mixed overflow case confirming binary sensors are no longer starved. A negative control reproduced the exact reported out-of-order payload from the pre-fix logic.
Fixes #15