Repository navigation
test(trace): ingest live F461 2.0.11 2026-10-23 capture and MyHomeServer1 3.87.13 reads behind OWNd #136-#142 and MyHOME #791-#798 - #799
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
GreenGrassBlueOcean
left a comment
There was a problem hiding this comment.
Thanks for splitting out the physical captures into a dedicated trace PR.
Below are the review findings that need to be addressed (particularly #1 and #5, plus the documentation/canonicalizer items):
1. Privacy Leak: Real Hardware MAC Suffixes Hardcoded in Both Test Files (High / Blocking)
While the JSON fixtures replace the gateway MACs with synthetic 00:03:50:00:10:23 (*#13**12*0*3*80*0*16*35##) and 00:03:50:00:20:24 (*#13**12*0*3*80*0*32*36##), the real 3-octet NIC suffixes (in both hex and decimal WHO 13 byte form) are hardcoded in the negative assertions of both test files:
tests/test_f461_live_2026_10_23_trace.py(line 235)tests/test_myhomeserver1_live_2026_10_24_trace.py(line 90)
Because every BTicino/Legrand gateway uses the public OUI 00:03:50 (0*3*80), committing the remaining 3 octets in assert "<nic_hex>" not in text and "<nic_dec>" not in text exposes the exact real MAC addresses of both physical gateways in the repository and git history.
- Why CI missed it:
findings()inscripts/anonymize_plant_fixture.pyonly checks full 6-octetMACregexes whendata_file=True(.json/.yaml), ignores.pyfiles for MAC/IP checks, and does not check decimal*#13**12*0*3*80*...##frames. - Action: Replace those hardcoded substring assertions in both test files (and force-push/amend so the real suffixes are not left in the PR commits) with a generic regex check verifying that every MAC in
textstarts with00:03:50:00:and every*#13**12*frame intextequalsMAC_FRAME.
2. Tier 1 Oracle Validation Blind Spot (frames vs rows Schema Split) (Medium)
In tests/test_trace_oracle_validation.py, _iter_all_trace_records() only inspects payload.get("frames").
- In
derived_trace_archivefiles,payload["frames"]only containsdirection: "rx"(session: "evt") entries; alltxframes live exclusively inpayload["rows"](dir: "tx"). - As a result,
test_tier1_trace_tx_canonical_oracle_coverage()never checks a singletxframe fromf461_live_2026-10-23ormyhomeserver1_live_2026-10-24. - If
rowstxframes are passed throughcanonicalize_trace_tx_frame(), 18 uniquetxframes inf461_live_2026-10-23and 5 inmyhomeserver1_live_2026-10-24are unmapped intests/golden/firmware_oracle.json:WHO 1:*#1*#1##,*#1*#1*15##,*#1*06*15##WHO 2:*#2*05*11##WHO 4:*#4*0*60##,*#4*2*60##,*#4*35*60##,*#4*39*60##,*#4*#0*0*0215##WHO 13:*#13**2##,*#13**3##,*#13**10##,*#13**11##,*#13**12##,*#13**19##,*#13**25##,*#13**26##,*#13**27##WHO 14:*#14*06##WHO 18:*#18*51*113*100##
3. Post-Processing Artifacts in derived_trace_archive (rows and frames) (Medium)
Downstream tests and reviewers using these fixtures should account for three structural artifacts introduced by the .jsonl -> derived_trace_archive conversion:
session: "cmd"rxrows have synthetic batch timestamps:- Because
OWNCommandSessionreturns(ack, replies)as a batch upon receiving*#*1##/*#*0##, everyrxrow withsession: "cmd"is stamped witht == result["t"]. - Consequently, in
rows, allsession: "evt"frames that arrived during a multi-frame command (such as the 5-frame*#4*2##reply over1.3sor the 100-frame*#1*0##reply over4.7s) appear chronologically before the correspondingsession: "cmd"frames, which are all dumped at the end att == result["t"].
- Because
result["events_after"]includes unrelated traffic from the post-command sleep window:- The converter populates
events_afterwith allevtframes betweentx[i]andtx[i+1], which includes the1.0s–6.0spause afterresult[i]has already received*#*1##/*#*0##. - For example, in
f461_live_2026-10-23(run1),*#8*1##NACKs locally in4 ms(t = 325.161s), yet itsevents_aftercontains['*#4*2*0*0239##', '*#4*2*12*0235*3##', '*4*1*2##', '*#4*2*14*0210*3##', '*#4*2*13*00##'](t = 325.229s–325.960s) from the background HA poll during the1.2ssleep before*#8*11##. - Similarly, in
myhomeserver1_live_2026-10-24(run1_identify), eachWHO 13result'sevents_afterincludes4–23unrelated backgroundWHO 4frames that arrived during the1.0sinter-command sleep.
- The converter populates
payload["frames"]has non-monotonictimestampacross runs:- Each run resets
t(and thereforeframes[i]["timestamp"]) to~0.0. Concatenating all runs intopayload["frames"]causestimestampto jump backwards between runs (382.6 -> 2.3 -> 1.2 -> 1.1 -> 1.1 -> 25.9 -> 1796.1 -> 2.9). - Corpus replayers like
_replay()intests/test_device_health_corpus.pypatchtime.monotonic()directly withframe["timestamp"], so monotonic time moves backwards at each run boundary.
- Each run resets
4. Inaccurate Prose Claims in f461_live_2026-10-23/README.md (Low)
- Claim (lines 72–73):
*#4*0##,*#5*0##,*#5*#1##,*#9*0##,*#17*0##,*#14*06##,*#25*21##,*#3*…##,*#8*…##and all 18 WHO 16 forms are refused by the gateway itself in under 10 ms.- Actual data in
run1_oracle_replay_readonly:*#25*21##took950 ms(0.95s) to NACK att = 240.843s(only inrun8andrun8bdid*#25*21##NACK in5–6 ms), and*#5*#1##took13 ms(0.013s).
- Actual data in
- Claim (lines 68–69):
While the integration polled the zone (run 6), the two actuator frames followed every
*#4*2##burst within 3 s.- Context: Please clarify in the bullet that those actuator frames (
*#4*2#1*20*0##,*#4*2#2*20*5##) followed every*#4*2##burst inrun6becauserun6ran the patched integration (#792) sending*#4*#0*20##after each poll, not because the F461 hardware emits dimension 20 in response to*#4*2##.
- Context: Please clarify in the bullet that those actuator frames (
5. pending_decoder_rx in tests/test_trace_oracle_validation.py Silently Skips Forever (Low)
In tests/test_trace_oracle_validation.py (lines 524–537):
elif frame in pending_decoder_rx:
continueUnlike known_unparseable_rx (which fails if parsed is not None), pending_decoder_rx uses continue, meaning once OWNd#139 lands and OWNMessage.parse decodes *#13**27*...##, the test will silently skip those frames forever instead of prompting cleanup. Please assert parsed is None (like known_unparseable_rx) until the OWNd pin is bumped so the test fails loudly and reminds us to remove pending_decoder_rx once the decoder is released.
| assert [r["frame"] for r in rows if r["dir"] == "rx" and r["session"] == "evt"] == [f["raw"] for f in frames] | ||
| # the real MAC and LAN address were rewritten in both their text and their WHO 13 byte forms | ||
| text = json.dumps(trace_payload) | ||
| assert "9f:3f:97" not in text and "159*63*151" not in text and "192*168" not in text |
There was a problem hiding this comment.
Blocking (Privacy Leak): Hardcoding the real 3-octet NIC suffix (both hex and decimal WHO 13 byte form) in this assertion reconstructs the physical gateway's exact MAC address when combined with the 00:03:50 (0*3*80) OUI. Please replace with a generic regex check (e.g. re.findall verifying every MAC starts with 00:03:50:00: and every *#13**12* frame equals MAC_FRAME) and amend the commit so the real octets are removed from git history.
| # only status reads were sent | ||
| assert all(r["frame"].startswith("*#") for r in rows if r["dir"] == "tx") | ||
| text = json.dumps(trace_payload) | ||
| assert "ca:33:85" not in text and "202*51*133" not in text and "192.168" not in text |
There was a problem hiding this comment.
Blocking (Privacy Leak): Same issue here — hardcoding the real 3-octet NIC suffix (hex and decimal) leaks the physical MyHomeServer1 MAC address. Please replace with a generic regex assertion against 00:03:50:00: / MAC_FRAME and amend the commit.
| * `*#4*0*60##` is answered by zone 2 (`*#4*2*60*39##`) after 2.1 s, `*#4*2*60##` in 0.14 s; the thermostat pushed | ||
| `*#4*2*60*38##` three times in 30 minutes on its own. | ||
| * `*#4*0##`, `*#5*0##`, `*#5*#1##`, `*#9*0##`, `*#17*0##`, `*#14*06##`, `*#25*21##`, `*#3*…##`, `*#8*…##` and all | ||
| 18 WHO 16 forms are refused by the gateway itself in under 10 ms. `*#4*#2##`, `*#4*#2*14##`, `*#4*#0##`, |
There was a problem hiding this comment.
In run1_oracle_replay_readonly, *#25*21## took 0.950s (950 ms, at t = 240.843s) to NACK (only in run8 / run8b did it NACK in 5–6 ms), and *#5*#1## took 13 ms (0.013s). Please adjust the wording here so it matches run1.
| 78 ms, dimension 12 follows at once, dimension 14 catches up 0.3 s later, the season frame stays `*4*1*2##`. | ||
| * `*#4*2*20##` is ACKed in 111 ms with `*#4*2#1*20*0##` on the command session, followed by `*#4*2#2*20*5##` on the | ||
| event session; `*#4*#0*20##` returns both on the command session after 2.1 s. While the integration polled the | ||
| zone (run 6), the two actuator frames followed every `*#4*2##` burst within 3 s. |
There was a problem hiding this comment.
Please clarify that in run6 the two actuator frames followed every *#4*2## burst because the patched integration under test (#792) explicitly sent *#4*#0*20## after each poll, not because the F461 emits dimension 20 on *#4*2##.
| if frame in known_unparseable_rx: | ||
| if parsed is not None: | ||
| failures.append(f"Expected None for malformed frame {frame}, got {parsed!r}") | ||
| elif frame in pending_decoder_rx: |
There was a problem hiding this comment.
Using continue here means this skip will remain silent even after OWNd#139 is released and pinned. Please assert parsed is None (or fail when parsed is not None with a message to remove pending_decoder_rx) so we are forced to clean this up when the OWNd pin advances.
…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>
…pture The facts of the F461 2026-10-23 capture read on a second gateway family: the user's home MyHomeServer1, firmware 3.87.13 (WHO 13 device type 200, WHO 1013 object model 67.15.5.0, dimension 27 "MyHomeServer1"), a large plant with WHO 4 zones 35-70 (dimension 7 setpoints, temperatures, humidity on 17 zones, actuators Z#1 / Z#2 and zone-0 actuators 0#1-0#4) and about 100 WHO 1 points including four-digit area-0 points 0010-0015. Status reads only. tests/fixtures/traces/myhomeserver1_live_2026-10-24/: the archive (376 event frames, 613 rows, three runs) and its README. The MAC is synthetic in text and in the WHO 13 dimension 12 bytes; the IP is a documentation address. tests/test_myhomeserver1_live_2026_10_24_trace.py pins: *michnovka#4*0## refused in 51 ms; *michnovka#4*#0## and *michnovka#4*#0*14## unanswered after 4.2 s (one OWNd retry); *michnovka#4*35## reply order 0, 12, season, 14, 13; *michnovka#4*35*20## one actuator and *michnovka#4*#0*20## 43 distinct actuator frames including zone 0; *michnovka#4*35*60## NACK where no probe exists, *michnovka#4*0*60## answered by 17 zones, *michnovka#4*39*60## answered; *OpenWebNet-HA#16*0*5## / *OpenWebNet-HA#16*101*5## / *michnovka#5*0## refused in 3-5 ms; *#1*0## with 100 point statuses; 0014 and 14 two devices, 0010 real and 10 absent, 0001 / 0004 refused at once vs 01 / 04 forwarded; *OpenWebNet-HA#13**0## echoed on the event session; *OpenWebNet-HA#13**12## answering the MAC; time zone 999. The firmware oracle emulates MyHomeServer1 2.82.06 while this unit runs 3.87.13, and *OpenWebNet-HA#13**12## returns a real MAC here where the emulated 2.82.06 returns zeros (oracle PR 59): stated in the fixture, the README and the tests. tests/test_trace_oracle_validation.py: the MyHomeServer1 dimension 27 reply joins the pending-decoder set (OWNd#139). The trace matrix is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
aecc092 to
fd16d98
Compare
… archive timing artefacts, README wording, loud pending-decoder skip) Review 5638684839 on OpenWebNet-HA#799, items 2-5 (item 1, the real MAC suffixes in the test modules, was fixed in the rewritten history before this commit). Tier 1: _iter_all_trace_records also reads a derived_trace_archive's rows (dir tx / rx), so the requests of the six archives are checked. The oracle index at 53f9514 has no stimulus, direct or canonical, for 115 of them (dimension 60 humidity, WHO 13 dimensions 10-12 / 19 / 27, *#1*#1##, WHO 14 status, report-form writes, WHO 6 / 8 on the F461, ...): they are listed in PHYSICAL_ONLY_TX_FRAMES with the capture and the live reply per frame, and a new test fails when an entry leaves the corpus or when the oracle grows a verdict for it. No other tier changed; rows add no rx frame to tier 4. Archives: both regenerated. Command-session rx rows sit right after their request at its t with t_estimated true (the session returns the replies as a batch with the ACK / NACK); result.events_after holds only the event-session frames that arrived while the request was pending; t and frames[].timestamp are one monotonic axis across runs (capture.runs[].t_offset). Said in capture.notes and the READMEs. The trace tests read the chronological rows for the frames that arrived after the ACK; the facts are unchanged. README f461_live_2026-10-23: the refusal sentence matches run 1 (*michnovka#5*#1## 13 ms, *OpenWebNet-HA#25*21## 950 ms in run 1 and 5-6 ms in runs 8 / 8b); the run 6 bullet says the actuator frames followed each *michnovka#4*2## because the patched integration (OpenWebNet-HA#792) sent *michnovka#4*#0*20## after every poll. pending_decoder_rx: assert parsed is None instead of continue, so the entries fail loudly once the OWNd pin carries the dimension 27 decoder (OWNd#139). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Thanks for the review. All five items are addressed; the branch was force-pushed once for item 1 and then advanced with one plain commit (3400664) for items 2–5.
Checks on the branch with the pinned OWNd: ruff, |
GreenGrassBlueOcean
left a comment
There was a problem hiding this comment.
Thanks for the quick turnaround — verified that all 5 review items are cleanly resolved:
- Privacy Leak Resolved (History Rewritten):
0occurrences of the real hex/decimal NIC suffixes remain in any commit on the branch (c246e15d,fd16d987,34006640), and both test suites now assert synthetic MAC /MAC_FRAMEinvariance via regex. - Tier 1 Oracle Validation Now Covers
rows:_iter_all_trace_records()readspayload["rows"], and the 115 physical-onlytxframes across the 6derived_trace_archivefixtures are documented inPHYSICAL_ONLY_TX_FRAMESand guarded bytest_tier1_physical_only_frames_are_in_the_corpus_and_not_in_the_oracle(). - Archive Post-Processing Artifacts Fixed:
session: "cmd"rxrows now sit right after theirtxrow with"t_estimated": true,result["events_after"]strictly bounds totx.t <= t <= result.t, andcapture.runs[].t_offsetmakesframes[].timestampandrows[].tmonotonic across runs. - README Prose Corrected:
f461_live_2026-10-23/README.mdaccurately reflects*#5*#1##(13 ms),*#25*21##(950 msinrun1,5–6 msinrun8/run8b), and therun6#792poll context. pending_decoder_rxFails Loudly: Assertsparsed is Noneso the temporary entry fails loudly onceOWNd#139is released and pinned.
LGTM!
ad8431f
into
OpenWebNet-HA:v2-phase1-architecture
MyHomeServer1 README: "OWnd" typo; the alarm gate, the config-flow identification and the liveness probe rows name MyHOME OpenWebNet-HA#803, OpenWebNet-HA#804, OpenWebNet-HA#806. F461 README: the "What this capture backs" table extends to MyHOME OpenWebNet-HA#800-OpenWebNet-HA#806 (OpenWebNet-HA#802 the dimension 27 wire form, OpenWebNet-HA#803 *michnovka#5*0##, OpenWebNet-HA#804 the WHO 13 / 1013 identification replies, OpenWebNet-HA#806 the *OpenWebNet-HA#13**0## echo; OpenWebNet-HA#800 / OpenWebNet-HA#801 / OpenWebNet-HA#805 have no frame in this capture); it states that f461_live_2026-10-15 holds the dimension 22 reboot behind OWNd OpenWebNet-HA#136, the *4*311*2## / *4*311*#2## pair behind OWNd OpenWebNet-HA#137 and probe_area0 behind OWNd OpenWebNet-HA#141 / MyHOME OpenWebNet-HA#798; and one sentence says reply_time_s is measured while tx.t is reconstructed from the probe log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
MyHomeServer1 README: "OWnd" typo; the alarm gate, the config-flow identification and the liveness probe rows name MyHOME #803, #804, #806. F461 README: the "What this capture backs" table extends to MyHOME #800-#806 (#802 the dimension 27 wire form, #803 *#5*0##, #804 the WHO 13 / 1013 identification replies, #806 the *#13**0## echo; #800 / #801 / #805 have no frame in this capture); it states that f461_live_2026-10-15 holds the dimension 22 reboot behind OWNd #136, the *4*311*2## / *4*311*#2## pair behind OWNd #137 and probe_area0 behind OWNd #141 / MyHOME #798; and one sentence says reply_time_s is measured while tx.t is reconstructed from the probe log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs(trace): README follow-ups on #799
…teway refuses it The model table ALARM_STATUS_REFUSED_MODELS turned emulator rows into a model-capability fact, and the physical traces committed in tests/fixtures disprove it: the OpenWebNet-HA#311 F454 (firmware 2.0) answers `*michnovka#5*#1##` with `*5*11*#1##` and reports `*5*1*##`, `*5*5*##`, `*5*7*##`, `*5*9*##`; the OpenWebNet-HA#629 F454 (1.0.34) and OpenWebNet-HA#619 F453AV (3.0.14) sweeps answer `*michnovka#5*0##` with `*5*0*##` / `*5*1*##`, `*5*9*##` and the zone frames. The own-firmware-oracle `nack` / `silent` rows (F450, F453AV, F454, F459, F460, F461, MyHomeServer1) are factory-blank emulations (tools/expected_divergences.yaml lists the F454 case under silent_bus_vs_populated_trace), and the live F461 2.0.11 and MyHomeServer1 3.87.13 NACKs (OpenWebNet-HA#799) were taken on plants without a central unit: they prove those plants NACKed, not that every plant with that gateway cannot answer. Silence is never converted into refusal. The table is removed. Only an explicit `supports_alarm_status_request = False` on the OWNd profile skips the poll; `None` or no flag polls. The flag belongs in OWNd (OpenWebNet-HA#381 pattern). A per-plant cache of a live NACK is not implemented here: the command session does not report a NACK back to the entity (the future resolves to the write time), so it would need gateway-session plumbing first. Tests: every formerly listed model with no flag is polled, explicit False skips, True / None poll; the three physical F454 / F453AV fixtures prove the request is answered and that `*5*9*##` confirms the restored state; the two live NACK traces prove nothing reaches the panel and the restored state is left alone. docs/configuration/alarm.md no longer contradicts its own point 1, and the release note describes the policy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rough the climate platform (OpenWebNet-HA#792 review, 2) The 90 cases of tests/test_climate_actuator_status.py fed inline frame tuples; the committed trace archives of OpenWebNet-HA#799 were only pinned as facts (tests/test_f461_live_2026_10_23_trace.py replays run4b, which has no dimension 20 frame). Two replays now run the real captures through the climate platform (async_setup_entry, bus discovery on, the frames delivered on myhome_message_<mac> as the gateway handler does): - f461_live_2026-10-23, run6_event_session_ha_fixed_integration: the 171 event-session frames of the 1770 s run while the fixed integration ran. hvac_action stays unknown through the five frames of the first *michnovka#4*2## reply, is idle with actuators {"1": "off"} on *michnovka#4*2#1*20*0## and {"1": "off", "2": "off"} on *michnovka#4*2#2*20*5## (the answer to *michnovka#4*#0*20##), and stays so through the six reconnect sweeps (seven polls, the fan frame repeated once and reversed once, the hashed *michnovka#4*2#1*OpenWebNet-HA#20*0## echo dropped as a command); 24.1 / 23.5 degrees, heating, humidity 38; the fan coil asked its side once and no *michnovka#4*2*11## went out in heating. - myhomeserver1_live_2026-10-24, run2_reads_thermo_sound_alarm_lights: the 43 actuator frames that follow *michnovka#4*#0*20## on the 34-zone plant. Every configured zone (35..70) ends idle with its actuators filled from the Z#1 / Z#2 frames, the five fan coils (47, 48, 57, 60, 70) ask dimension 19 once each in the order the plant answered, and the zone-0 pump frames 0#1..0#4 discover no zone, are watched by nothing and are asked for nothing. Both fail on v2-phase1-architecture (nothing watches the zones, no actuators attribute) and pass with this branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Requested by A
Summary
Ingests the physical capture behind the pull requests opened on 2026-10-24 as a trace fixture, as asked on OpenWebNet-HA/OWNd#137 (comment: "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?"). Same plant and format as
f461_live_2026-10-15(#714) andf461_live_2026-10-18(#753): a BTicino F461 2.0.11 (WHO 13 device type200, WHO 1013134.15.5.0), BT-F411/4 lights at06/07, an F422 with a light at09(its actuator lacked configurator "2" at capture time, so it reports in the area sweep but answers no point request), a BT-F411/2 5 s shutter at05, a KW4691 standalone thermostat at zone 2 with a BT-F430/2 valve and a BT-F430/4 fan coil; no central unit, no WHO 16 / 22 devices.It backs OWNd #136-#142 and MyHOME #791-#798; the README's last table maps each pull request to its frames.
A second fixture,
myhomeserver1_live_2026-10-24, adds the same facts read on a second gateway family: the user's home MyHomeServer1, firmware 3.87.13 (*#13**16*3*87*13##, WHO 13 device type200, WHO 1013 object model67.15.5.0,*#13**27##= "MyHomeServer1"), a large plant (WHO 4 zones 35-70 with dimension 7 setpoints, temperatures, humidity on 17 zones, actuatorsZ#1/Z#2and zone-0 actuators0#1-0#4; about 100 WHO 1 points including four-digit area-0 points0010-0015and four-digit0111-0515). Status reads only, no command was sent. Two caveats stated in the README, the fixture and the tests: the firmware oracle emulates MyHomeServer1 2.82.06 while this unit runs 3.87.13, and*#13**12##answers a real MAC here (synthetic in the fixture) where the emulated 2.82.06 returns zeros (oracle PR OpenWebNet-HA/own-firmware-oracle#59). The*4*311*2##NACK /*4*311*#2##ACK pair cited by OWNd #137 and the dimension 22 crash behind OWNd #136 are already upstream inf461_live_2026-10-15(run3_climatet=252,probe_followupt=7,probe_dim22_confirm); they are referenced, not duplicated.Changes
tests/fixtures/traces/f461_live_2026-10-23/myhome_trace_F461_live_2026-10-23.json: derived trace archive, 299 event-session frames inframes, 939 chronologicaltx/rx/resultrows inrows, ten runs (capture.runsnames each with its source, UTC window and purpose): the oracle replay of 165 read-only requests, reviewer reads, setpoint restore, final state (twice), report-form writes, the event session during a 30 min local Home Assistant run, the bus monitor of an earlier local run, and the two flicker re-tries.tests/fixtures/traces/f461_live_2026-10-23/README.md: plant, file format, runs, what the plant established, and the pull-request table.tests/test_f461_live_2026_10_23_trace.py(21 tests): schema and synthetic identity, every frame parses in the pinned OWNd, result rows consistent with theirtx/rxrows, and the facts the pull requests rely on (below); two entity replays (lights 06 / 07 and the shutter throughhandle_event, zone 2 through the climate entity) that do not depend on any of the open fixes.tests/fixtures/traces/myhomeserver1_live_2026-10-24/myhome_trace_MyHomeServer1_live_2026-10-24.json: same format, 376 event-session frames, 613 rows, three runs (run1_identify,run2_reads_thermo_sound_alarm_lights,run3_reads_lights_addresses_humidity), synthetic MAC00:03:50:00:20:24(*#13**12*0*3*80*0*32*36##) and IP192.0.2.9;gateway.oracle_notecarries the 2.82.06 / 3.87.13 caveat. README with plant, runs, findings and the pull-request table.tests/test_myhomeserver1_live_2026_10_24_trace.py(13 tests): schema / privacy / parsing / row consistency and the facts listed below.tests/test_trace_oracle_validation.py: tier 4 gets apending_decoder_rxset for the two authentic WHO 13 dimension 27 replies (*#13**27*70#52#54#49##= "F461",*#13**27*77#121#...#49##= "MyHomeServer1"): no decoder in the pinned OWNd, they parse toNonewithout raising; OWNd Data received is not a message #139 adds the decoder, after which they parse like any other frame and the entries can go.python scripts/update_trace_matrix.pywas run: the F461 and MyHomeServer1 rows already cover the WHOs seen, soREADME.md/docs/trace-availability.mdare unchanged.Review follow-up (review 5638684839)
*#13**12*frame equalsMAC_FRAME._iter_all_trace_recordsnow also reads aderived_trace_archive'srows(dirtx / rx), so tier 1 sees every request of the six archives that have one (f461_live_2026-10-15,-10-18,-10-23,f461_who14_live_2026-10-18,issue_667,myhomeserver1_live_2026-10-24). The oracle index at 53f9514 has no stimulus, canonical or otherwise, for 115 of those requests (56 / 17 / 18 / 5 / 14 / 5 per archive; dimension 60 humidity, WHO 13 dimensions 10-12 / 19 / 27, group status*#1*#1##, WHO 14 status, report-form writes, WHO 6 / 8, ...), so they are listed inPHYSICAL_ONLY_TX_FRAMESwith the capture and the live reply per frame. A new test keeps the list honest: every entry must still be in the corpus and must be removed as soon as the oracle grows a verdict for it (direct or canonical). Nothing else in tier 1 changed; tier 4 sees no new rx frame fromrows(the command-session replies are all also inframes).rxrows sit right after their request witht= the request'standt_estimated: true(the session hands the replies over as a batch);result.events_afterholds only the event-session frames withtx.t <= t <= result.t(the pause before the next request is in the chronological rows only);t/frames[].timestampare one monotonic axis across runs (each run starts 1 s after the previous one ended,capture.runs[].t_offsetrecords the offsets). Stated incapture.notesand both READMEs; the trace tests that readevents_afternow read the chronological rows for frames that arrived after the ACK (same facts).*#5*#1##13 ms;*#25*21##950 ms in run 1, 5-6 ms in runs 8 / 8b), and the run 6 bullet says the actuator frames followed each*#4*2##because the patched integration (feat(climate): request actuator status (dimension 20) on add and after reconnect, expose actuators, derive hvac_action #792) sent*#4*#0*20##after every poll, not because the F461 emits dimension 20 on*#4*2##.pending_decoder_rx:continuereplaced byassert parsed is None, "remove ... from pending_decoder_rx: the OWNd pin now decodes it", so the entries fail loudly once OWNd Data received is not a message #139 is pinned.*#5*0##; feat(config_flow): identify a manually added gateway after authentication #804: the WHO 13 / 1013 identification replies; feat(gateway_sessions): end-to-end event-session liveness probe with non-blocking polls (#123) #806: the*#13**0##echo; fix(diagnostics): read the OWNd profile's real field names instead of reporting Generic #800 / fix(legacy_yaml): warn when myhome.yaml has no section under this gateway's MAC (#106) #801 / fix(services): closed schemas and selectors for send_message and sync_time (#189) #805: no frame in the capture backs them) and points tof461_live_2026-10-15for the dimension 22 reboot (OWNd Thermostat sets to Automatic on startup #136), the*4*311*2##/*4*311*#2##pair (OWNd Change gateway #137) andprobe_area0(OWNd ClimateEntityFeature missing supported attributes #141 / MyHOME fix(validate): canonicalise 4-digit area-0 point addresses (0004→04) (#147) #798); one explicit sentence saysreply_time_sis measured whiletx.tis reconstructed from the probe log.Protocol & Bus Trace Evidence
*#13**16*2*0*11##), kernel 5.10.35, WHO 13 device type200, WHO 1013*#1013**1*134*15*5*0##, HMAC on both sessions. Recorded 2026-10-23 20:38-23:03 UTC with OWNd master (OWNCommandSession+OWNEventSession), one request at a time.*#4*2##->*#4*2*0*0239##,*#4*2*12*0235*3##,*4*1*2##,*#4*2*14*0210*3##,*#4*2*13*00##in 1.3 s: dimension 12 (setpoint in force) precedes and differs from dimension 14 (last commanded value);*#4*2*#14*0235*3##ACK in 78 ms, dimension 12 follows at once (fix(climate): show the setpoint in force (dimension 12), keep dimension 14 as the commanded value #791).*#4*2*20##->*#4*2#1*20*0##on cmd +*#4*2#2*20*5##on evt (111 ms);*#4*#0*20##-> both on cmd (2.1 s); the two actuator frames follow every*#4*2##burst while the integration ran (feat(climate): request actuator status (dimension 20) on add and after reconnect, expose actuators, derive hvac_action #792, OWNd Needs to add ClimateEntityFeature to custom_components.myhome.climate.MyHOMEClimate #140).*#4*0*60##->*#4*2*60*39##(2.1 s),*#4*2*60##(0.14 s), three spontaneous*#4*2*60*38##in 30 min (fix(climate): do not poll humidity (dimension 60) where the emulated firmware did not forward it #793).*#4*0##,*#5*0##,*#16*0*5##and the other 17 WHO 16 forms NACK in under 10 ms (fix(gateway): skip the startup requests the firmware refuses, poll each zone's temperature on SCS-only gateways #794, OWNd How to install this extension properly? #142);*#4*#2##,*#4*#2*14##,*#4*#0##,*#4*#0*14##NACK after the bus timeout, no central unit (OWNd Change gateway #137).*#1*0007##NACK in 7 ms,*#1*07##->*1*0*07##in 88 ms,*#1*7##ACK empty (area 7);*#1*0001##,*#1*0002##NACK (fix(validate): canonicalise 4-digit area-0 point addresses (0004→04) (#147) #798, OWNd ClimateEntityFeature missing supported attributes #141).*#13**0##on cmd is also delivered on evt (*#13**0*23*38*50*999##);*#13**27##->*#13**27*70#52#54#49##= "F461" (OWNd Data received is not a message #139);*#13**12##answers the MAC (synthetic in the fixture).*2*0*0##exactly once (one general stop from Home Assistant); light toggles without group / area frames (fix(events): fire scope events only for frames that carry a state (#367) #797). Nine full shutter runs end on the actuator's own*2*0*05##5.1-5.9 s after the start burst, twoset_cover_positionruns stopped by Home Assistant after 2.45 s and 3.65 s (fix(cover): a timed cover with no restored state starts unknown instead of "open 50 %" #795).*#18*51*113*100##ACK, not echoed;*#4*#0*0*0215##NACK after 4.1 s;*#18*51*51##/*#18*51*113##ACK with no data (no meter).*#4*0##NACK in 51 ms (refused at once);*#4*#0##and*#4*#0*14##NACK after 4.2 s (forwarded, unanswered, OWNd retried once) (OWNd Change gateway #137).*#4*35##->*#4*35*0*0231##,*#4*35*12*0200*3##,*4*1*35##,*#4*35*14*0200*3##,*#4*35*13*00##: the same reply order as the F461 (fix(climate): show the setpoint in force (dimension 12), keep dimension 14 as the commanded value #791).*#4*35*20##->*#4*35#1*20*0##;*#4*#0*20##ACK in 0.39 s with 43 distinct actuator frames on the event session (34Z#1, 5Z#2fan coils,*#4*0#1*20*0##..0#4), 44 in the run with the zone 35 reply (feat(climate): request actuator status (dimension 20) on add and after reconnect, expose actuators, derive hvac_action #792, OWNd Needs to add ClimateEntityFeature to custom_components.myhome.climate.MyHOMEClimate #140).*#4*35*60##NACK after 2.1 s (no probe in that zone);*#4*0*60##ACK answered by 17 zones;*#4*39*60##->*#4*39*60*51##(fix(climate): do not poll humidity (dimension 60) where the emulated firmware did not forward it #793).*#16*0*5##,*#16*101*5##NACK in 3 ms;*#5*0##NACK in 5 ms (fix(gateway): skip the startup requests the firmware refuses, poll each zone's temperature on SCS-only gateways #794, OWNd How to install this extension properly? #142, the alarm gate).*#1*0##ACK after 4.7 s with 100 point statuses (74 distinct points);*#1*0014##->*1*0*0014##and*#1*14##->*1*0*14##(two devices);*#1*0010##ACK while*#1*10##NACK 2.0 s;*#1*0001##NACK 8 ms and*#1*0004##NACK 14 ms (four-digit000Nrefused at once) vs*#1*01##/*#1*04##NACK 2.0 s (forwarded, no device) (fix(validate): canonicalise 4-digit area-0 point addresses (0004→04) (#147) #798, OWNd ClimateEntityFeature missing supported attributes #141).*#13**0##ACK and the reply echoed on the event session (the liveness probe);*#13**27##= "MyHomeServer1" (OWNd Data received is not a message #139);*#13**22##time zone999;*#13**12##answers the MAC.00:03:50:00:10:23and IP192.0.2.92, rewritten consistently in text (ownd_log) and in the WHO 13 byte forms (*#13**12*0*3*80*0*16*35##,*#13**10*192*0*2*92##); no password, no local paths, no diagnostics or logs. MyHomeServer1: MAC00:03:50:00:20:24in text and as*#13**12*0*3*80*0*32*36##, IP192.0.2.9; nothing else personal (status frames only).python scripts/anonymize_plant_fixture.py --checkon the new files: 0 with personal data;tests/test_fixture_privacy.pypasses.Testing
pytest tests/test_f461_live_2026_10_23_trace.py: 21 passed;pytest tests/test_myhomeserver1_live_2026_10_24_trace.py: 13 passed.pytest tests/test_trace_oracle_validation.py tests/test_device_health_corpus.py tests/test_fixture_privacy.py: 263 passed (tier 4 failed before thepending_decoder_rxentries:OWNMessage.parse returned None for trace rx frame: *#13**27*...; tier 1 failed with 115 unmapped requests oncerowswere included, beforePHYSICAL_ONLY_TX_FRAMES).master, f04d8aa, no fix PR applied):pytest tests/ -n 4 --cov=custom_components.myhome: 4452 passed, 1 skipped, 100 % statement coverage.ruff check .,scripts/typing_ratchet.py(0 strict errors),scripts/verify_ha_standards.py,scripts/sync_documentation.py --check,scripts/update_trace_matrix.py(no change): all pass.Checklist
handle_event()).pytest,ruff, andscripts/verify_ha_standards.pypass.🤖 Generated with Claude Code