Repository navigation
fix(thermo): follow OWNd's dimension 22 crash-form detector instead of blocking every split-control frame - #849
Merged
GreenGrassBlueOcean merged 2 commits intoNov 3, 2026
Conversation
…f blocking every split-control frame Every WHO 4 dimension 22 spelling whose WHERE carries no actuator token (zone, general 0, central unit #0) kills bt_termo on the Linux IP gateways; the published split forms *michnovka#4*3#Z#N*22## and *michnovka#4*3#Z#N*OpenWebNet-HA#22*MOD*SP*VEL*SWING## crashed none of the twelve emulated gateways (own-firmware-oracle thermo-dim22, OWNd#151). OWNd >= 2.0.0b12 exports that split as is_who4_dimension_22_crash_form and gates its own send paths with it; MyHOME still refused every dimension 22 frame with one regex and called the dimension "a status dimension", which the specification contradicts (read/write, WHO 4 pp. 66-68). The OpenWebNet-HA#805 review asked for the switch to OWNd's detector. services.is_refused_dimension_22() is now the one guard: it follows the OWNd detector when the installed OWNd has it and keeps the blanket regex on 2.0.0b11 (the pinned version), so nothing changes for a current install until the pin moves. send_message, the bus-monitor WebSocket and the thermo switch trigger validation use it; the refusal texts say which forms are blocked and why, and the zone-request refusal in gateway.py keeps refusing dimension 22 for a zone WHERE. Tests cover both OWNd generations (the detector path skips on 2.0.0b11). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-strict passes on OWNd 2.0.0b11 The try/except import of is_who4_dimension_22_crash_form fails the strict-typing ratchet on the pinned OWNd, which has no such attribute (attr-defined). A typed getattr on OWNd.message keeps the same behaviour on both OWNd generations. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
GreenGrassBlueOcean
marked this pull request as ready for review
November 3, 2026 20:39
GreenGrassBlueOcean
merged commit Nov 3, 2026
b5ef513
into
OpenWebNet-HA:v2-phase1-architecture
16 checks passed
This was referenced Nov 3, 2026
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.
Requested by A
What is the problem
Before:
services.py,websocket.pyandwhere_grammar.pyrefuse every WHO 4 dimension 22 frame with one regex (^\*#4\*[^*]*\*#?22(\*[^#]*)?##$), split form included, and the trigger validation calls dimension 22 "a status dimension, not an actuator write command". That was the right guard when the only knowledge was the two F461 crash frames of OWNd#136. Since OWNd#151 (2.0.0b12) the firmware oracle has replayed every dimension 22 spelling on the twelve emulated gateways: every WHERE without an actuator token (zone*#4*Z*22##, general*#4*0*22##, central unit*#4*#0*22##, read or write) killsbt_termo, while the published split forms*#4*3#Z#N*22##and*#4*3#Z#N*#22*MOD*SP*VEL*SWING##crashed none and are forwarded by ten of twelve. OWNd exports that split asis_who4_dimension_22_crash_formand gates its own send paths with it; MyHOME still blocks the forwarded form, and the "status dimension" label contradicts the specification (dimension 22 is read/write, WHO 4 pp. 66-68). The #805 review asked for exactly this follow-up ("the MyHOME-side guard must switch toOWNd.message.is_who4_dimension_22_crash_formconsistently").After:
services.is_refused_dimension_22(frame)is the one guard. With an OWNd that exports the detector (2.0.0b12 or newer) it refuses what OWNd refuses and lets the published split form through; with the pinned 2.0.0b11, which has no such detector, the blanket regex stays and every dimension 22 frame is still refused, so nothing changes for a current install until the OWNd pin moves (#848). The WebSocket error, the service log line, the trigger-validation error and the bus-monitor doc say what is blocked and why;gateway.request_zone_dimensionkeeps refusing dimension 22 for a zone WHERE (that is the crash form by construction).What is the solution
services.py:_is_dimension_22_crash_formimported fromOWNd.messagewhen available,is_refused_dimension_22()wraps it with the regex fallback;handle_send_messageuses it.websocket.py:ws_bus_monitor_senduses it; error text names the crash form.where_grammar.py:validate_thermo_trigger_commandaccepts the split-unit write and refuses the crash forms with a message that no longer calls dimension 22 a status dimension (lazy import ofservicesinside the function, so no import cycle at module load).gateway.py: comment and message of the zone-request refusal corrected.docs/configuration/bus_monitor.md: the safety note describes the two classes of frames.tests/test_dimension22_guard.py: crash forms refused and split forms passed with the detector; every dimension 22 frame refused without it (monkeypatched); WebSocket and trigger validation both ways; non-dimension-22 frames untouched.tests/test_websocket.pyexpects the new error text.State Diagram
stateDiagram-v2 [*] --> Frame: dimension 22 frame Frame --> Detector: OWNd exports is_who4_dimension_22_crash_form Frame --> Blanket: OWNd 2.0.0b11 Detector --> Refused: WHERE without actuator token (Z, 0, #0, #Z, Z#N) Detector --> Forwarded: 3#Z#N request or 4-field write Blanket --> Refused: every spellingHardware Verification
None sent to hardware. The split-form verdicts are the oracle's (
thermo-dim22suite,harness=full, OWNd#151 / own-firmware-oracle#68); the crash on the zone form is the live F461 2.0.11 of OWNd#136 (tests/fixtures/traces/f461_live_2026-10-15). No dimension 22 frame was sent to any gateway for this change.How is it tested
tests/test_dimension22_guard.py: 30 tests with the detector present (crash forms refused, split forms passed, WebSocket forwards the split form and blocks the zone form, trigger validation accepts the split write), 17 of them also run without the detector (monkeypatched) and 13 skip when the installed OWNd has none.PYTHONPATH, the way themyhome-conformancejob runs it: 5275 passed, 1 deselected (test_check_all_documentation_real_tree, the known pin-bump signal).ruff checkclean.Where are the traces
No new trace: the change is in the guard, not in bus traffic. The existing
f461_live_2026-10-15capture stays the evidence for the crash form.What does the Firmware Oracle / Encyclopedia say
Oracle
thermo-dim22(11 steps, 12 gateways, two byte-identical runs): the four WHERE forms without an actuator token crashbt_termoon nine gateways;*#4*3#Z#N*22##goes out on ten of twelve as aD1telegram to zone Z, actuator N; the prefix-lessZ#Nisnack, silenton eleven. Encyclopediafunctional/who-4-temperature-control/dimensions.md: dimension 22 is the split-control read/write dimension with theMOD*SP*VEL*SWINGpayload; the WHERE for it is3#Z#N.Checklist
ruff checkclean on the touched files (the repo does not enforceruff format; untouched)🤖 Generated with Claude Code