Skip to content

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

Merged
GreenGrassBlueOcean merged 3 commits into
OpenWebNet-HA:v2-phase1-architecturefrom
gdluck:test/trace-f461-live-2026-10-23
Oct 24, 2026
Merged

GreenGrassBlueOcean merged 3 commits into
OpenWebNet-HA:v2-phase1-architecturefrom
gdluck:test/trace-f461-live-2026-10-23

Conversation

@gdluck

@gdluck gdluck commented Oct 24, 2026 •

Copy link
Copy Markdown

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) and f461_live_2026-10-18 (#753): a BTicino F461 2.0.11 (WHO 13 device type 200, WHO 1013 134.15.5.0), BT-F411/4 lights at 06 / 07, an F422 with a light at 09 (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 at 05, 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 type 200, WHO 1013 object model 67.15.5.0, *#13**27## = "MyHomeServer1"), a large plant (WHO 4 zones 35-70 with dimension 7 setpoints, temperatures, humidity on 17 zones, actuators Z#1 / Z#2 and zone-0 actuators 0#1-0#4; about 100 WHO 1 points including four-digit area-0 points 0010-0015 and four-digit 0111-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 in f461_live_2026-10-15 (run3_climate t=252, probe_followup t=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 in frames, 939 chronological tx / rx / result rows in rows, ten runs (capture.runs names 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 their tx / rx rows, and the facts the pull requests rely on (below); two entity replays (lights 06 / 07 and the shutter through handle_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 MAC 00:03:50:00:20:24 (*#13**12*0*3*80*0*32*36##) and IP 192.0.2.9; gateway.oracle_note carries 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 a pending_decoder_rx set 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 to None without 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.py was run: the F461 and MyHomeServer1 rows already cover the WHOs seen, so README.md / docs/trace-availability.md are unchanged.

Review follow-up (review 5638684839)

  1. Privacy: the real NIC suffixes are gone from both test modules and from the history (branch rewritten, force-pushed before this commit); the assertions now check that every MAC-like string equals the synthetic MAC and every *#13**12* frame equals MAC_FRAME.
  2. Tier 1 blind spot: _iter_all_trace_records now also reads a derived_trace_archive's rows (dir tx / 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 in PHYSICAL_ONLY_TX_FRAMES with 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 from rows (the command-session replies are all also in frames).
  3. Archive artefacts: both archives regenerated. Command-session rx rows sit right after their request with t = the request's t and t_estimated: true (the session hands the replies over as a batch); result.events_after holds only the event-session frames with tx.t <= t <= result.t (the pause before the next request is in the chronological rows only); t / frames[].timestamp are one monotonic axis across runs (each run starts 1 s after the previous one ended, capture.runs[].t_offset records the offsets). Stated in capture.notes and both READMEs; the trace tests that read events_after now read the chronological rows for frames that arrived after the ACK (same facts).
  4. README wording: the refusal sentence now matches run 1 (*#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##.
  5. pending_decoder_rx: continue replaced by assert 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.
  6. README follow-ups: typo fixed; the MyHomeServer1 table names fix(alarm): start unknown, restore the last state and poll *#5*0## unless the OWNd profile refuses it #803 / feat(config_flow): identify a manually added gateway after authentication #804 / feat(gateway_sessions): end-to-end event-session liveness probe with non-blocking polls (#123) #806; the F461 table extended to MyHOME fix(diagnostics): read the OWNd profile's real field names instead of reporting Generic #800-feat(gateway_sessions): end-to-end event-session liveness probe with non-blocking polls (#123) #806 (fix(gateway_events): warn once per frame shape for unparsed frames (#139) #802: the dimension 27 wire form; fix(alarm): start unknown, restore the last state and poll *#5*0## unless the OWNd profile refuses it #803: *#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 to f461_live_2026-10-15 for the dimension 22 reboot (OWNd Thermostat sets to Automatic on startup #136), the *4*311*2## / *4*311*#2## pair (OWNd Change gateway #137) and probe_area0 (OWNd ClimateEntityFeature missing supported attributes #141 / MyHOME fix(validate): canonicalise 4-digit area-0 point addresses (0004 → 04) (#147) #798); one explicit sentence says reply_time_s is measured while tx.t is reconstructed from the probe log.

Protocol & Bus Trace Evidence

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 the pending_decoder_rx entries: OWNMessage.parse returned None for trace rx frame: *#13**27*...; tier 1 failed with 115 unmapped requests once rows were included, before PHYSICAL_ONLY_TX_FRAMES).
  • Full suite against the PyPI-pinned OWNd behaviour (plain upstream OWNd 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

  • PR Description: This pull request has a clear title and summary explaining the change.
  • Trace-Backed (if protocol/device change): The fixture is the physical trace itself; every test pins a frame of it.
  • V2 Architecture: N/A (fixture and tests only; the entity replays go through handle_event()).
  • Tests & Linters: pytest, ruff, and scripts/verify_ha_standards.py pass.

🤖 Generated with Claude Code

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@gdluck gdluck changed the title test(trace): ingest live F461 2.0.11 2026-10-23 capture behind OWNd #136-#142 and MyHOME #791-#798 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 Oct 24, 2026
@gdluck
gdluck marked this pull request as draft October 24, 2026 13:39
@GreenGrassBlueOcean
GreenGrassBlueOcean marked this pull request as ready for review October 24, 2026 14:25
@GreenGrassBlueOcean GreenGrassBlueOcean self-assigned this Oct 24, 2026

@GreenGrassBlueOcean GreenGrassBlueOcean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() in scripts/anonymize_plant_fixture.py only checks full 6-octet MAC regexes when data_file=True (.json/.yaml), ignores .py files 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 text starts with 00:03:50:00: and every *#13**12* frame in text equals MAC_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_archive files, payload["frames"] only contains direction: "rx" (session: "evt") entries; all tx frames live exclusively in payload["rows"] (dir: "tx").
  • As a result, test_tier1_trace_tx_canonical_oracle_coverage() never checks a single tx frame from f461_live_2026-10-23 or myhomeserver1_live_2026-10-24.
  • If rows tx frames are passed through canonicalize_trace_tx_frame(), 18 unique tx frames in f461_live_2026-10-23 and 5 in myhomeserver1_live_2026-10-24 are unmapped in tests/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:

  1. session: "cmd" rx rows have synthetic batch timestamps:
    • Because OWNCommandSession returns (ack, replies) as a batch upon receiving *#*1## / *#*0##, every rx row with session: "cmd" is stamped with t == result["t"].
    • Consequently, in rows, all session: "evt" frames that arrived during a multi-frame command (such as the 5-frame *#4*2## reply over 1.3s or the 100-frame *#1*0## reply over 4.7s) appear chronologically before the corresponding session: "cmd" frames, which are all dumped at the end at t == result["t"].
  2. result["events_after"] includes unrelated traffic from the post-command sleep window:
    • The converter populates events_after with all evt frames between tx[i] and tx[i+1], which includes the 1.0s–6.0s pause after result[i] has already received *#*1## / *#*0##.
    • For example, in f461_live_2026-10-23 (run1), *#8*1## NACKs locally in 4 ms (t = 325.161s), yet its events_after contains ['*#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 the 1.2s sleep before *#8*11##.
    • Similarly, in myhomeserver1_live_2026-10-24 (run1_identify), each WHO 13 result's events_after includes 4–23 unrelated background WHO 4 frames that arrived during the 1.0s inter-command sleep.
  3. payload["frames"] has non-monotonic timestamp across runs:
    • Each run resets t (and therefore frames[i]["timestamp"]) to ~0.0. Concatenating all runs into payload["frames"] causes timestamp to jump backwards between runs (382.6 -> 2.3 -> 1.2 -> 1.1 -> 1.1 -> 25.9 -> 1796.1 -> 2.9).
    • Corpus replayers like _replay() in tests/test_device_health_corpus.py patch time.monotonic() directly with frame["timestamp"], so monotonic time moves backwards at each run boundary.

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## took 950 ms (0.95s) to NACK at t = 240.843s (only in run8 and run8b did *#25*21## NACK in 5–6 ms), and *#5*#1## took 13 ms (0.013s).
  • 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 in run6 because run6 ran the patched integration (#792) sending *#4*#0*20## after each poll, not because the F461 hardware emits dimension 20 in response to *#4*2##.

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:
                continue

Unlike 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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##`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

gdluck and others added 2 commits October 24, 2026 17:57
…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>
@gdluck
gdluck force-pushed the test/trace-f461-live-2026-10-23 branch from aecc092 to fd16d98 Compare October 24, 2026 14:58
… 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>
@gdluck

gdluck commented Oct 24, 2026

Copy link
Copy Markdown
Author

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.

  1. Privacy (blocking): both commits were recreated without the real NIC octets (history rewritten before this review landed: fd16d98). The assertions now check that every MAC-like string in the archive is the synthetic one and that every *#13**12* frame equals MAC_FRAME. grep for the former fragments over tests/ is empty on the branch.
  2. Tier 1 blind spot: _iter_all_trace_records now also reads a derived_trace_archive's rows (dir tx/rx). With rows included, 115 request frames across the six archives that carry rows (f461_live_2026-10-15 56, -10-18 17, -10-23 18, f461_who14 5, issue_667 14, myhomeserver1 5) have no stimulus in the oracle index at all (dimension 60, WHO 13 dims 2/3/10/11/12/19/25/26/27, *#1*#1##, WHO 14 status, report-form writes, …), so none can be canonicalised honestly. They are listed in PHYSICAL_ONLY_TX_FRAMES (frame → capture + live reply) and a new tier-1 test requires every entry to still be a corpus tx frame and to have no direct or canonical oracle verdict, so the list can neither rot nor hide a covered frame. The golden file is untouched.
  3. Archive artefacts: converters fixed and both archives regenerated. Command-session rx rows now sit right after their tx row with t = tx.t and "t_estimated": true (the probe only knows the batch time); result.events_after holds only the event frames between the request and its ACK/NACK; a cumulative per-run offset (capture.runs[].t_offset) makes frames[].timestamp and rows[].t monotonic across runs. Stated in capture.notes and both READMEs; counts unchanged (299 / 939 and 376 / 613).
  4. README wording: the refusal sentence now gives *#5*#1## 13 ms and *#25*21## 950 ms in run 1 (5–6 ms only in runs 8 / 8b); 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##.
  5. pending_decoder_rx: continue replaced by assert parsed is None, "remove … the OWNd pin now decodes it".

Checks on the branch with the pinned OWNd: ruff, verify_ha_standards.py, sync_documentation.py --check, anonymize_plant_fixture.py --check (0 files with personal data), the two trace modules (34 passed), oracle validation + health corpus + fixture privacy (252 passed), full suite 4452 passed / 1 skipped at 100 % statement coverage.

@GreenGrassBlueOcean GreenGrassBlueOcean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick turnaround — verified that all 5 review items are cleanly resolved:

  1. Privacy Leak Resolved (History Rewritten): 0 occurrences 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_FRAME invariance via regex.
  2. Tier 1 Oracle Validation Now Covers rows: _iter_all_trace_records() reads payload["rows"], and the 115 physical-only tx frames across the 6 derived_trace_archive fixtures are documented in PHYSICAL_ONLY_TX_FRAMES and guarded by test_tier1_physical_only_frames_are_in_the_corpus_and_not_in_the_oracle().
  3. Archive Post-Processing Artifacts Fixed: session: "cmd" rx rows now sit right after their tx row with "t_estimated": true, result["events_after"] strictly bounds to tx.t <= t <= result.t, and capture.runs[].t_offset makes frames[].timestamp and rows[].t monotonic across runs.
  4. README Prose Corrected: f461_live_2026-10-23/README.md accurately reflects *#5*#1## (13 ms), *#25*21## (950 ms in run1, 5–6 ms in run8/run8b), and the run6 #792 poll context.
  5. pending_decoder_rx Fails Loudly: Asserts parsed is None so the temporary entry fails loudly once OWNd#139 is released and pinned.

LGTM!

@GreenGrassBlueOcean
GreenGrassBlueOcean merged commit ad8431f into OpenWebNet-HA:v2-phase1-architecture Oct 24, 2026
16 checks passed
gdluck pushed a commit to gdluck/HomeAssistant-MyHOME that referenced this pull request Oct 24, 2026
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>
GreenGrassBlueOcean pushed a commit that referenced this pull request Oct 24, 2026
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>
GreenGrassBlueOcean added a commit that referenced this pull request Oct 24, 2026
gdluck pushed a commit to gdluck/HomeAssistant-MyHOME that referenced this pull request Oct 24, 2026
…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>
gdluck pushed a commit to gdluck/HomeAssistant-MyHOME that referenced this pull request Oct 25, 2026
…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>
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.

3 participants