Skip to content

Accept the optional timer byte in 0x1946 - #22

Open
smoke0leg wants to merge 1 commit into
caligo-mentis:masterfrom
smoke0leg:floor-heating-timer-byte
Open

Accept the optional timer byte in 0x1946#22
smoke0leg wants to merge 1 commit into
caligo-mentis:masterfrom
smoke0leg:floor-heating-timer-byte

Conversation

@smoke0leg

Copy link
Copy Markdown

What

0x1946 (Control Floor Heating Status) is encoded as a fixed 7-byte payload.
Some DLP thermostats reject that length and require an 8th byte — the timer —
which is the same field 0x1945 already handles as optional.

This makes 0x1946 symmetric with 0x1945: parse reads timer when the
payload is at least 8 bytes, encode appends it only when data.timer is set.
Payloads without a timer are byte-for-byte unchanged, so the change is
backwards compatible.

How it was found

Measured on a live Buspro installation (9 devices on subnet 1, three 0x23AB
DLP floor-heating thermostats). Controlling the floor heating failed
consistently until the payload length was changed:

Payload 0x1947 response
7 bytes F5 — failure
8 bytes, timer appended F8 — success

Same device, same field values; only the length differs. HDL's own
documentation is not public, so this is behaviour observed on the wire, not a
spec citation.

Tests

Added a second fixture for 0x1946 — one example with the timer and one
without — mirroring the pair that already exists for 0x1945.

The test was checked against the bug it claims to catch: with the new fixture
in place but lib/commands.js reverted, the suite fails with

AssertionError: expected Buffer [ 00, 01, 01, 16, 16, 14, 12 ]
      to equal Buffer [ 00, 01, 01, 16, 16, 14, 12, 01 ]
      (at length, A has 7 and B has 8)

With the fix applied: 207 passing.

npx eslint lib test reports the same 13 problems before and after this change
(all pre-existing and in unrelated lines), so I left them alone to keep the
diff focused.

Not included

0x1947, the control response, may carry the same optional byte. I have no
capture proving it, so I did not touch it.

🤖 Generated with Claude Code

0x1946 (Control Floor Heating Status) is encoded as a fixed 7-byte payload.
Some DLP thermostats reject that length and require an 8th byte — the timer —
which is the same field 0x1945 already handles as optional.

Measured on a live bus against three 0x23AB thermostats: with a 7-byte payload
the device answers 0x1947 with F5 (failure); appending the timer byte makes the
same command succeed with F8. Only the length differs between the two cases.

parse now reads timer when the payload is at least 8 bytes and encode appends
it only when data.timer is set, so payloads without a timer are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013snkkHrKRD5LX9e4xjDWez
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.

1 participant