Repository navigation
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@GreenGrassBlueOcean all the PR's where tripple checked with oracle, test plant - tested with fable and co-verified with kimi3-pro. If anything, let me know |
|
Thanks @gdluck ,can you please split out the trace pr so that we can add the actual physical trace to the myhome repo? Like we did with all traces before? |
|
Done: the physical capture is split out as OpenWebNet-HA/MyHOME#799 ( |
…penWebNet-HA#136-OpenWebNet-HA#142 and MyHOME OpenWebNet-HA#791-OpenWebNet-HA#798 The maintainer asked on OpenWebNet-HA/OWNd#137 (comment 6384484511) for the physical trace behind the 2026-10-24 pull requests to be added to this repo like every capture before. Same plant and format as f461_live_2026-10-15 and f461_live_2026-10-18: a BTicino F461 2.0.11 (WHO 13 device type 200, WHO 1013 134.15.5.0) with BT-F411/4 lights at 06 / 07, an F422 light at 09 whose actuator lacked configurator 2 at capture time, a BT-F411/2 5 s shutter at 05 and a KW4691 standalone thermostat at zone 2 with a BT-F430/2 valve and a BT-F430/4 fan coil; no central unit. tests/fixtures/traces/f461_live_2026-10-23/: the derived trace archive (299 event-session frames, 939 tx / rx / result rows in ten runs: the oracle replay of 165 read-only requests, reviewer reads, setpoint restore, final state, report-form writes, the event session during a local Home Assistant run, the bus monitor of an earlier run, two flicker re-tries) and its README with the plant, the runs, what the plant established and which pull request each frame backs. MAC / IP are synthetic in both their text and WHO 13 byte forms. tests/test_f461_live_2026_10_23_trace.py pins the facts the pull requests rely on: the *michnovka#4*2## reply order with dimension 12 before and different from 14, *michnovka#4*2*20## followed by two actuator frames and *michnovka#4*#0*20## answering both, *#1*0007## NACK vs *#1*07## ACK vs *#1*7## empty, *OpenWebNet-HA#13**0## echoed on the event session, every WHO 16 request refused in under 10 ms, the dimension 27 model name frame, *michnovka#4*#2*14## and the other central-unit forms refused, one *2*0*0##, the shutter's own stop 5.1-5.9 s after each run; plus entity replays through handle_event that pass on the base branch with the pinned OWNd. tests/test_trace_oracle_validation.py: tier 4 tolerates the authentic *OpenWebNet-HA#13**27*70#52#54#49## reply until the pinned OWNd carries the dimension 27 decoder (OWNd#139); it parses to None without raising. The *4*311*2## NACK / *4*311*#2## ACK pair cited by OWNd#137 is already in f461_live_2026-10-15 and is referenced, not duplicated. scripts/update_trace_matrix.py leaves the matrix unchanged (the F461 row already covers WHO 1, 2, 4, 13, 1013). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
844f4fb to
a6c4cdc
Compare
Central-unit WHATs (311, 1, 303, ...) address the central unit by API contract (OWNd#77 fix 3): `where` must be `#0`, `#0#N` or `#Z`; `#Z#N` and plain zones are refused with ValueError. The plain zone form `*4*311*Z##` is nack/silent on all 12 emulated gateways (own-firmware- oracle thermo-zone-mode, ownd-pr82-heating) and a live F461 2.0.11 NACKs `*4*311*2##` while it ACKs `*4*311*michnovka#2##`. The default `#0` is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
@gdluck please rebase |
a6c4cdc to
0b29827
Compare
…dress
The guard refused `#Z#C` and its docstring said it is not a central-unit
form, but `set_mode("23#1")` in the same file builds exactly
`*4*311*OpenWebNet-HA#23#1##`, the frame libqtdevices sends, and the firmware oracle
`thermo-ts10` suite shows all 12 emulated gateways forward it. The regex
was also unbounded (`#999`, `#0#99`, `michnovka#1` built frames) while the sibling
`_WHO4_WHERE_REGEX` (OpenWebNet-HA#140) is bounded, and it did not strip whitespace
while `actuators_status` does.
`_WHO4_CENTRAL_WHERE_REGEX` is now `#(?:0(?:#[1-4])?|[1-9][0-9]?(?:#[1-9])?)`:
`#0`, `#0#1..#0#4`, `michnovka#1..OpenWebNet-HA#99`, `#Z#1..#Z#9`; `where` is stripped before
the match and quoted in the error. Tests: `OpenWebNet-HA#23#1`, `michnovka#1#2`, `#0#4`, `OpenWebNet-HA#99`,
padded `#0` accepted with their log line; `michnovka#1`, `OpenWebNet-HA#100`, `#999`, `#0#0`,
`#0#5`, `#0#99`, `michnovka#1#0`, `michnovka#1#10`, `michnovka#1#2#3`, `None`, `True`, `-1` refused;
`set_mode("23#1")` and `set_central_mode("OpenWebNet-HA#23#1")` build one frame.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Self-correction in e8e20fb (no review asked for it). @GreenGrassBlueOcean could you confirm the reading below, or say if you see it differently? Context. This PR made How it was found. An adversarial read of the PR against its neighbours in Live check (F461 2.0.11, 2026-10-26 16:45 UTC). What changed. |
…review of OpenWebNet-HA#148) The previous revision bounded `#0#C` to 1..4 and `#Z#C` to 1..9, reading `#0#N` as "zone N of a 4-zone central unit". `#0#C` is the 4-zone unit's configured zone selector, 1..99 (01..99): MyHOME_Suite OPEN.db `#0#[ZA][ZB]`, MHCatalogue 4695 `ZAZB_CENTRAL` 1..99, and the oracle `thermo-central-mode` rows (`*4*0*#0#2##` -> `D1 02 ...`, a two-nibble central address byte); `#Z#C` is zone Z controlled through 4-zone central unit C, also 1..99 (BtExperience parseControlUnit4 / parseZone4). Two-digit virtual spellings (`michnovka#1`, `#0#05`) are the same `[ZA][ZB]` convention as the plain `01` and are accepted. Only `#00`, `#0#0`, `#0#00` and three-digit values are refused. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Correction in 406d155, following @GreenGrassBlueOcean's #148 review: the previous revision bounded |
…l-mode-address # Conflicts: # OWNd/message/heating.py
Requested by A
Summary
OWNHeatingCommand.set_central_mode("1", CLIMATE_MODE_AUTO)built*4*311*1##: the builder accepted anywhere. The central-unit WHATs (311 automatic, 1 heating, 303 off, ...) address the central unit by API contract: #77 fix 3 ("*4*311*1##produces no bus frame;*4*311*#1##does") and the addressing of the central-unit commands: the Legrand WHO 4 document (openwebnet-who-4.pdf, WHERE table p. 6, commands pp. 8-12 and 24-27) has#0(99-zone central unit) and#1..#99(a zone set up by the central unit); the 4-zone forms#0#Cand#Z#Care not in that document and come from MyHOME_Suite OPEN.db / MHCatalogue 4695 and the firmware oracle (thermo-central-mode,thermo-ts10). The mode codes themselves are the ones BTicino's own client sends (libqtdevices): the PDF lists 303 and 102 on#0(pp. 24, 27) and 311 / 302 on#Zonly (pp. 9, 12), and has no*4*1*#0##/*4*0*#0##command, so this builder follows the client, not the document, for 1 / 0 / 311 / 302 on#0. The zone-level builder for a plain zone isset_mode(where, ...).The guard therefore applies to every WHAT this builder produces, by contract. The oracle evidence per WHAT is uneven and is listed honestly below: the plain form of 311 is
nack/silenton all 12 emulated gateways and NACKed by a live F461 2.0.11, while the plain forms of 1 and 303 are forwarded to the bus by some gateways (as a frame addressed to zone 1, which is not what the central-unit builder is for).Refs #77. The
central_statusdeprecation-text correction that this PR carried at first is now its own PR (see Companion).Changes
OWNd/message/heating.py:_WHO4_CENTRAL_WHERE_REGEX = #(?:0(?:#(?:0[1-9]|[1-9][0-9]?))?|(?:0[1-9]|[1-9][0-9]?)(?:#(?:0[1-9]|[1-9][0-9]?))?);set_central_modestripswhereand requires it tofullmatch:#0;#0#CwithCthe 4-zone unit's configured zone 1..99 or01..99(MyHOME_Suite OPEN.db#0#[ZA][ZB], MHCatalogue 4695ZAZB_CENTRAL, oraclethermo-central-mode*4*0*#0#2##→D1 02 …);#ZwithZ1..99 or01..99;#Z#Cwith central unitC1..99 (libqtdevices'*4*311*#23#1##, the frameset_mode("23#1")builds). ASCII digits, so#١is refused;#00,#0#0,#0#00and three-digit values (#999,#0#100) are refused. Revisions:e8e20fbstopped refusing#Z#Cand bounded the digits (adversarial scan; the firmware oraclethermo-ts10suite shows all 12 gateways forward*4*311*#23#1##);406d155widened the selectors from 1..4 / 1..9 to 1..99 after the docs(changelog): describe send_to_gateway's dimension 22 refusal as it is #148 review (the 1..4 reading of#0#Nwas wrong). Anything else raisesValueError("Central unit mode needs a central unit address such as '#0': ..."). The default#0is unchanged. Callers checked: MyHOMEclimate.pypassesself._where, which is#0or#0#1when_central; the golden corpus uses#0and#0#1.CHANGELOG.md: entry under[Unreleased]/Fixed.State Diagram
stateDiagram-v2 direction TB state "1. OWNHeatingCommand.set_central_mode(where, mode)" as Builder { [*] --> Validate Validate --> Built : where fullmatch #0 | #0#C | #Z | #Z#C, C and Z 1..99 or 01..99\n*4*311*#0##, *4*1*#0#1##, *4*311*#0#12##, *4*303*#23##, *4*311*#23#1## Validate --> Refused : plain zone 1 / 2 / 0, 23#1, #, ##0, #0*22, #a, #١, #00, #0#0, #999, #0#100\nValueError , before - *4*311*1## was built and sent } state "2. What the gateways do with the two forms of 311 (oracle thermo-zone-mode, ownd-pr82-heating; live F461 2.0.11)" as Gateway { [*] --> Plain Plain --> NackSilent : *4*311*1## nack/silent on 12 of 12\nlive *4*311*2## NACK [*] --> Central Central --> Forwarded : *4*311*#1## ack/out on 9, nack/out on F460 / F461 / MyHomeServer1\nlive *4*311*#2## ACK , no central unit - changes nothing }Where are the traces
#Zformtests/fixtures/traces/f461_live_2026-10-15(merged), runsrun3_climateandprobe_followup*4*311*2##NACKed;*4*311*#2##,*4*303*#2##,*4*302*#2##,*4*102*#2##,*4*202*#2##ACKed (they change nothing on a plant without a central unit)OpenWebNet-HA/MyHOME#799(open),f461_live_2026-10-23run 1 (run1_oracle_replay_readonly) andmyhomeserver1_live_2026-10-24run 2 (run2_reads_thermo_sound_alarm_lights)*#13**16*3*87*13##)*#4*#2##,*#4*#2*14##,*#4*#0##,*#4*#0*14##NACK after the bus timeout. MyHomeServer1:*#4*#0##and*#4*#0*14##NACK after ~4.2 s. No*4*311*...##was sent on the MyHomeServer1 (a real home).What does the Oracle say
Suites
thermo-zone-modeandownd-pr82-heating(results/<P>/<V>/oracle/full/<suite>.tsv; the rows are identical in both where both carry the frame). Twelve gateways: F450020010, F453010100, F453AV030014, F454020051, F459020105, F460020012, F461020011, H4684020054, MH200020102, MH200N010108, MH202010024, MyHomeServer1028206.ack/outnack/out(forwarded, no central unit on the emulated bus)nack/silent*4*311*1##(plain, this builder before the fix)*4*311*#1##(central form, this builder)*4*1*1##(plain heating)*4*0*1##(plain conditioning)*4*303*1##(plain off)So "every gateway refuses the plain form" holds for 311 only. For 1, 0 and 303 the plain form is a zone command that some gateways forward; this builder refuses it anyway because it is the central-unit builder (contract above), and
set_mode(where, ...)remains the zone-level builder. No published row exists for the plain forms of 302, 102 and 202 (the live F461 ACKs their#2forms, table above).Testing
tests/test_who4_central_mode_address.py(new, 49 cases):test_set_central_mode_requires_a_central_address(28:1,2,0,23#1, empty, blank,#,##0,#0*22,#a,#0\n1, an Arabic-Indic digit,#00#1,#0#,#00,#100,#999,#0#0,#0#00,#0#100,#0#999,#1#0,#1#00,#1#100,#1#2#3,None,True,-1),test_set_central_mode_refuses_a_plain_zone_for_every_mode(3: 311, 1, 303 onwhere="1"),test_set_central_mode_keeps_central_addresses(16, frame and log line:#0,#0#1,#0#4,#0#5,#0#05,#0#12,#0#99,#1,#01,#23,#99,#23#1,#23#12,#1#2, padded#0,#0with a trailing newline),test_set_central_mode_agrees_with_set_mode_on_probe_addresses(set_mode("23#1")==set_central_mode("#23#1")),test_set_central_mode_default_is_the_central_unit.master17 fail, 4 pass (set_central_mode('1', 'auto')returns*4*311*1##,#1#2is accepted); on this branch all 49 pass.master6c3930d after fix(connection): refuse WHO 4 dimension 22 in every send path #136 / fix(profiles): alias H/L4684 & HC4684 to H4684Profile and enable WHO 22 on FW >= 6.0.0 #147, head406d155):2088 passed, 1 skipped; 100 % statement coverage (scripts/verify_coverage.py),mypy OWNd tests/typingclean,ruff checkclean,scripts/sync_documentation.py --checkandscripts/verify_library_standards.pypass.Live retest (2026-10-24)
Not exercised live: neither plant has a central unit and
*4*311*...##was not sent (the plant's zone 2 is standalone;set_central_mode('2', 'auto')now raises without a gateway).Companion
MyHOME: none needed (MyHOME already passes a central-unit address). Related OWNd PR:
docs(heating): central_status deprecation text names the real reason(split out of this PR): #143.🤖 Generated with Claude Code