Add semantic class 0x2C percentage in whole percent #87

Merged
Claude merged 1 commit from feat/percentage-class into main 2026-09-25 06:16:51 +00:00
Collaborator

The firmware added semantic class 0x2C percentage to the measured band (CCS-LLA-26). Spec section 6: uint16, 1 % per step, 0 to 100 — unlike a level, which is 0.1 %.

Its first user is the lighting node's bathroom night brightness setting (PK_SETTING, range 1-100, default 30). It now shows as a config number in %, with a step of 1, derived from the new units.WHOLE_PERCENTAGE quantity so CMD_SETTING sends the raw percent unscaled.

Also adds the class to the frame bench's SC_ table, pinned against the protocol library by tests/test_bench_agrees_with_the_protocol.py.

Checked against https://git.robware.uk/rob/CampervanControlSystems raw docs/can-protocol.md sections 5.2 and 6. No shared vector for this class has been published from the firmware yet, so the new coverage is local to this repository.

Ticket: CCS-HA-53

Tested: full suite passes (1212 passed, 3 skipped), ruff check and format clean.

The firmware added semantic class 0x2C percentage to the measured band (CCS-LLA-26). Spec section 6: uint16, 1 % per step, 0 to 100 — unlike a level, which is 0.1 %. Its first user is the lighting node's bathroom night brightness setting (`PK_SETTING`, range 1-100, default 30). It now shows as a config number in `%`, with a step of 1, derived from the new `units.WHOLE_PERCENTAGE` quantity so `CMD_SETTING` sends the raw percent unscaled. Also adds the class to the frame bench's `SC_` table, pinned against the protocol library by `tests/test_bench_agrees_with_the_protocol.py`. Checked against https://git.robware.uk/rob/CampervanControlSystems raw docs/can-protocol.md sections 5.2 and 6. No shared vector for this class has been published from the firmware yet, so the new coverage is local to this repository. Ticket: CCS-HA-53 Tested: full suite passes (1212 passed, 3 skipped), ruff check and format clean.
Add semantic class 0x2C percentage in whole percent
All checks were successful
Lint, type check and test / hassfest (pull_request) Successful in 14s
Lint, type check and test / quality (pull_request) Successful in 1m53s
Lint, type check and test / release (pull_request) Has been skipped
1c1f744287
Spec section 6: 1 % per step, unlike a level's 0.1 %. First user is the
bathroom night brightness setting on the lighting node.
Claude left a comment

Traced the whole path: SemanticClass.PERCENTAGE (0x2C) → READOUTS[PERCENTAGE] with units.WHOLE_PERCENTAGE (factor=1) → _setting()'s generic replace() turns it into a NUMBER/CONFIG shape without any new branch → number.py's native_step (quantity.from_wire(1) = 1) and _range() both read endpoint.minimum/maximum unscaled → commands.setting_on_the_wire() multiplies by quantity.factor = 1, so 30 goes out as 30. All correct, and I couldn't find anywhere still reaching for the old units.PERCENTAGE (0.1 %) for this class — the two or units.PERCENTAGE fallbacks in commands.py/number.py are level-only defaults that a Shape with quantity=WHOLE_PERCENTAGE never hits.

The SC_PERCENTAGE addition to the bench table is pinned by test_the_presentation_vocabulary_agrees, which compares the full {name: value} dict, so a wrong value (not just a missing entry) would fail it. CMD_SETTING's value field carries no fixed quantity in the catalogue (a setting's unit is per-endpoint), so test_the_scaling_agrees has nothing to check here — correctly, nothing needed changing there.

New tests are genuinely discriminating: from_wire(30) == 30 and setting_on_the_wire(30, ...) == 30 would both fail against the old 0.1 %-scaled PERCENTAGE quantity, so they're not vacuous.

Ran the full suite, ruff check and ruff format --check — all clean, matches what's stated.

One thing to consider, not a blocker: other settings (the bathroom extractor's duration, the touch switch) each got a tools/frame_bench/examples/*.json when they landed, letting the read-only path be exercised against vcan0 for that specific case. This PR adds none for the new percentage setting on lighting endpoint 7. Worth a follow-up if you want the bench to be able to rehearse this one too.

Traced the whole path: `SemanticClass.PERCENTAGE` (0x2C) → `READOUTS[PERCENTAGE]` with `units.WHOLE_PERCENTAGE` (factor=1) → `_setting()`'s generic `replace()` turns it into a `NUMBER`/`CONFIG` shape without any new branch → `number.py`'s `native_step` (`quantity.from_wire(1)` = 1) and `_range()` both read `endpoint.minimum`/`maximum` unscaled → `commands.setting_on_the_wire()` multiplies by `quantity.factor` = 1, so 30 goes out as 30. All correct, and I couldn't find anywhere still reaching for the old `units.PERCENTAGE` (0.1 %) for this class — the two `or units.PERCENTAGE` fallbacks in `commands.py`/`number.py` are level-only defaults that a `Shape` with `quantity=WHOLE_PERCENTAGE` never hits. The `SC_PERCENTAGE` addition to the bench table is pinned by `test_the_presentation_vocabulary_agrees`, which compares the full `{name: value}` dict, so a wrong value (not just a missing entry) would fail it. `CMD_SETTING`'s `value` field carries no fixed `quantity` in the catalogue (a setting's unit is per-endpoint), so `test_the_scaling_agrees` has nothing to check here — correctly, nothing needed changing there. New tests are genuinely discriminating: `from_wire(30) == 30` and `setting_on_the_wire(30, ...) == 30` would both fail against the old 0.1 %-scaled `PERCENTAGE` quantity, so they're not vacuous. Ran the full suite, `ruff check` and `ruff format --check` — all clean, matches what's stated. One thing to consider, not a blocker: other settings (the bathroom extractor's duration, the touch switch) each got a `tools/frame_bench/examples/*.json` when they landed, letting the read-only path be exercised against `vcan0` for that specific case. This PR adds none for the new percentage setting on lighting endpoint 7. Worth a follow-up if you want the bench to be able to rehearse this one too.
Author
Collaborator

Thanks for the review. Leaving the frame_bench example out of scope for this PR — the ticket (CCS-HA-53) doesn't ask for one, and there's no repo rule requiring one per semantic class (several existing classes have none). Happy to add one in a follow-up if the read-only path for this setting needs rehearsing against vcan0.

Thanks for the review. Leaving the frame_bench example out of scope for this PR — the ticket (CCS-HA-53) doesn't ask for one, and there's no repo rule requiring one per semantic class (several existing classes have none). Happy to add one in a follow-up if the read-only path for this setting needs rehearsing against vcan0.
Claude merged commit 0ab0d6b9f9 into main 2026-09-25 06:16:51 +00:00
Claude deleted branch feat/percentage-class 2026-09-25 06:16:51 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/CampervanHomeAssistant!87
No description provided.