Repository navigation
test(trace): add authentic MH201 FW 3.6.43 sweep & WHO 1013 identity trace (#466) - #824
Merged
GreenGrassBlueOcean merged 2 commits intoOct 27, 2026
Conversation
…trace (#466) Ingests the verbatim <myhome-bus-card> sweep export contributed by @emil-d on #466 (comment 6436427163) from a physical BTicino MH201 gateway (FW 3.6.43, WHO 13 device code 200, WHO 1013 OBJECT_MODEL 10 with trailing 15*0*5 metadata) and pins its identification, lighting replay, and single-slot command-session timeout/lockout timing in pytest. Co-authored-by: emil-d <50502252+emil-d@users.noreply.github.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…3 NACKs (#466) - Pin verbatim SHA-256 provenance (54717d7ef7b2...) and trailing-newline absence on the <myhome-bus-card> JSON.stringify export. - Distinguish fast ~10 ms command-session NACKs on unsupported WHO 13 dims 23/24 (+ 100 ms MH201Profile.command_queue_delay) from 10 s silent command-session hangs on *#2*0## / *#4*0##. - Replace empirical 0.74/0.73/0.64+0.05 offsets with exact protocol-constant derivation (removing the bogus 50 ms _send_query sleep since all 36 WHO 18 queries are already queued in FrameScheduler and failed connect() bypasses command_queue_delay), showing uniform 0.68-0.75 s 5-handshake TCP RTT across frames[16..21]. - Assert _handler_refuses_frame('*#4*0##') sweep suppression (#794), exact light WHERE set, and zero phantom sensor entities.
This was referenced Oct 28, 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.
What is the problem
Prior to this PR, none of the committed
MH201fixtures undertests/fixtures/carried a physicalWHO=1013Dimension 1 (OBJECT_MODEL = 10) diagnostic export orMH201firmware3.6.43(existing captures were firmware3.3.6on2.0.0b13and3.6.27on2.0.0b12, predatingWHO=1013identity export in<myhome-bus-card>).In #466 (comment 6436427163), @emil-d contributed a fresh
<myhome-bus-card>sweep export (myhome_sweep_MH201_all_2026-10-27T07-28-09.json) from a physical BTicino MH201 (3.6.43) connected to anINlighting/scenario bus (E46ADCN+F425+ 5 lighting/dimmer endpoints includingF414) coupled via an F422 in galvanic separation mode (MODempty,I4 = 2) to anOUTsound bus (F441Maudio matrix +F500 COAXpowered by346000). The export provides three pieces of field evidence not previously pinned in the repository:WHO=1013OBJECT_MODEL = 10(15*0*5) & FW3.6.43fixture:WHO=13shared device code200(*#13**15*200##) via startup*#1013**1*10*15*0*5##, recordingwho1013_code = "10",who1013_model = "MH201",who1013_n_conf = "15",who1013_brand = "0", andwho1013_line = "5"— confirming on a second firmware (3.6.43, alongside3.6.27inOpenWebNet-HA/OWNd#116) that the MH201 MCU emitsBRAND = 0/LINE = 5rather than theBRAND = 5/LINE = 0tuple emitted by Linux-based gateways (F454,MH202,MyHomeServer1,F461).WHO=13NACKs vs silent command-session hangs and single-slot TCP lockout onMH201:WHO=13dimensions23/24, the MH201 MCU emits zerorxevent frames and answers*99*0##with a fast*#*0##NACKin~9.8–9.9 ms(+109.9 ms/+109.8 msincludingMH201Profile.command_queue_delay = 100 ms).*#1*0##completes cleanly in283.8 ms(5lighting replies) +BUS_QUIET_PERIOD = 0.750 s,*#2*0##on a cover-less plant receives neither an event-session echo nor a command-sessionACK/NACK, hittingCOMMAND_TIMEOUT = 10.0 s+0.750 s(+10.753 s).1concurrent TCP command session (max_command_sessions = 1) and holds the unacknowledged server-side socket open after client close,*#4*0##(still sent on2.0.0b16before fix(gateway): skip the startup requests the firmware refuses, poll each zone's temperature on SCS-only gateways #794) backs off acrossMAX_CONNECT_ATTEMPTS = 5(1 + 2 + 4 + 6 = 13.0 s+ 5 TCP*99*0##handshakes =13.744 s), then hangs for another10.0 s+0.750 s(+24.494 s), and subsequent sweep frames (*#16*0*5##,*#18*51..52*51/1200##) exhaust allMAX_CONNECT_ATTEMPTS = 5(13.0 s+0.682–0.727 sfor 5 refused TCP*99*0##handshakes) on the locked command slot afterCommandWorkerPool.sending_looplogged theirtxintent inbus_monitor.MH201+WHO=16Sound System:*#16*0*5##on an idle*99*0##command session: the MH201 returned neitherACKnorNACK, echoed*#16*0*5##once on*99*1##, and forwarded zeroWHO=16frames even when physical audio controls were operated on theF441Msound bus.Refs #466
What is the solution
tests/fixtures/traces/issue_466/myhome_sweep_MH201_all_2026-10-27T07-28-09.json):@emil-d's untouched<myhome-bus-card>sweep export (22frames:13 tx/9 rx,SHA-256: 54717d7ef7b207d04a475d9c379d1b043f8e50a533cbfec400e248c315f5e2bb).tests/fixtures/traces/issue_466/README.md):MH2013.6.43hardware profile,E46ADCN+F422(I4 = 2) +F441M(S1=F500 COAX) plant topology,WHO=13/WHO=1013identification fields (10*15*0*5), fast~10 mscommand-sessionNACKs onWHO=13dims23/24,WHO=1lighting scan, exactCOMMAND_TIMEOUT+ single-slotMAX_CONNECT_ATTEMPTSlockout intervals, and idle-sessionWHO=16probe observations.tests/test_issue_466_mh201_3_6_43_sweep_trace.py):WHO=1013OBJECT_MODEL = 10(15*0*5) replay on both SSDPMH201and manualGenericentries, sub-300 msWHO=13replies +100 mscommand_queue_delay+ fast~10 msNACKs on Linux-only dimensions23/24, exact derived command-timeout and 5-attempt TCP handshake lockout intervals (+0.751 s,+10.753 s,+24.494 s,+14.477 s,+13.682 / 13.701 / 13.685 swith uniform0.68–0.75 s5-handshake RTT overhead) alongside#794's*#4*0##suppression in both_discovery_frames()and_handler_refuses_frame(), and full config-entry replay discovering the5OFFlight entities (31, 32, 71, 72, 73) with zero phantom cover/climate/audio/alarm/sensor entities.State Diagram
stateDiagram-v2 [*] --> WHO13Queries : myhome.sweep_bus (frames 0..7) WHO13Queries --> LightingScan : *#13**0/15/16## answered (<254 ms); 23/24 fast ~10 ms NACK (0 rx) LightingScan --> QuietAfterLights : tx *#1*0## -> rx echo (67 ms) + 5 lights (284 ms) QuietAfterLights --> CoverQueryHang : +0.751 s (BUS_QUIET_PERIOD) -> tx *#2*0## CoverQueryHang --> ClimateSlotWait : +10.00 s COMMAND_TIMEOUT + 0.75 s quiet (slot still held on MH201) ClimateSlotWait --> ClimateQueryHang : +13.744 s (1+2+4+6 s backoff + 5 TCP handshakes) -> tx *#4*0## (skipped since #794) ClimateQueryHang --> ExhaustedSlotLockout : +10.00 s COMMAND_TIMEOUT + 0.75 s quiet (+24.494 s total) ExhaustedSlotLockout --> [*] : frames 17..21 (*#16*0*5##, *#18*51..52##) abort after MAX_CONNECT_ATTEMPTS=5 (13.0 s + 0.68-0.73 s RTTs)Hardware Verification
firmware 3.6.43,WHO=13code200,WHO=101310*15*0*5,Home Assistant 2026.10.4,MyHOME 2.0.0b16,OWNd 2.0.0b11) by @emil-d on2026-10-27(#466 comment 6436427163).OWNd(master) and manifest-pinnedOWNd==2.0.0b11.How is it tested
tests/test_issue_466_mh201_3_6_43_sweep_trace.py(5 tests):test_mh201_3_6_43_sweep_schema_privacy_and_frame_counts: verifies verbatim SHA-256 (54717d7ef7b2…), zero PII findings (findings() == []), capture/telemetry/queue_pacingmetadata, andOWNMessage.parse()coverage across all 22 frames.test_mh201_3_6_43_first_physical_who1013_object_model_10_and_replay: pinsgateway.identification(who1013_code == "10",15*0*5,3.6.43) and replays*#13**15*200##,*#13**16*3*6*43##, and*#1013**1*10*15*0*5##throughMyHOMEGatewayHandleron both SSDPMH201and manualGenericentries.test_mh201_3_6_43_who13_queries_and_non_linux_mcu_silence: pins*#13**0##(253.9 ms),*#13**15##(18.6 ms), and*#13**16##(18.8 ms) latencies,100 msMH201Profile.command_queue_delay, and fast~9.8–9.9 mscommand-sessionNACKs (zerorxevent frames) on Linux-only dimensions23and24.test_mh201_3_6_43_command_timeout_and_single_slot_lockout_timings: verifies every inter-frame delta inframes[8..21]directly fromBUS_QUIET_PERIOD(0.75 s),COMMAND_TIMEOUT(10.0 s),MAX_CONNECT_ATTEMPTS = 5(1 + 2 + 4 + 6 = 13.0 sbackoff), and uniform0.68–0.75 s5-attempt TCP handshake RTT overhead, plus#794's*#4*0##suppression in both_discovery_frames()and_handler_refuses_frame().test_mh201_3_6_43_sweep_replay_discovers_five_lights_and_no_phantom_entities: replays all9rxframes through a liveMH201config entry and asserts the exact5OFFlight entities (31,32,71,72,73) and zero phantom cover, climate, media_player, alarm_control_panel, or sensor entities.Python 3.14.7,Home Assistant 2026.10.1, plus pinnedOWNd==2.0.0b11):pytest tests -q -o addopts="" --cov=custom_components/myhome --cov-report=term-missing: 5,028 passed, 5 snapshots passed, 100.0% statement coverage (17,455 / 17,455 statements, 0 missed) (includingtest_trace_oracle_validation.pyandtest_fixture_privacy.py).PYTHONPATH=/tmp/ownd_rel_b11 pytest tests/test_issue_466_mh201_3_6_43_sweep_trace.py: 5 passed.ruff check custom_components tests scripts: All checks passed (0 violations).scripts/typing_ratchet.py&mypy --strict: 0 error(s) in 0 module(s); baseline 0.scripts/verify_ha_standards.py: SUCCESS (54/54 Platinum rules satisfied).scripts/sync_documentation.py --check&scripts/manage_translations.py check: SUCCESS (0 broken links, Trace Matrix in sync, 100.0%en428/428 keys).Where are the traces
tests/fixtures/traces/issue_466/myhome_sweep_MH201_all_2026-10-27T07-28-09.json(22frames:13 tx/9 rx, physicalBTicino MH201firmware3.6.43, contributed by@emil-din #466 comment 6436427163).What does the Firmware Oracle / Encyclopedia say
OWN-DEV-0190,OWN-DEV-0078,WHO=1013Dimension 1):MH201(devices/definitions/own-dev-0190-mh201-hotel-room-scenario-manager.md): Catalogued inMHCatalogue.db(v3.5.38) asid_item = 1771(EN_DEVICE1814) underAccess control(id_system = 8) withmodobj = 10,brand_modobj = 5(Legrand BTicino), andline_modobj = 0(Undefined), hosting Objects98(Hotel room manager),150(Gateway Open SCS), and250(Gateway XOpen SCS). Note that physicalMH201firmware emits*#1013**1*10*15*0*5##(BRAND = 0,LINE = 5), confirming whyidentity.pyresolvesOBJECT_MODEL = 10directly without filtering onid_system = 26or trailingBRAND/LINE. Its manual (RA00122AB_S_EN) documents scenario families for lighting, automation, temperature, alarm, contacts, hotel, scenarios, and time — never sound (WHO=16/WHO=22), matchingMH201Profile(audio_measured=False).F422(devices/definitions/own-dev-0078-scs-scs-interface.md,MQ00280-f-ENp. 5): UnconfiguredMODwithI1..I3empty andI4 = 1..9selects Object76(Interface SCS / SCS galvanic), isolating power supplies (INautomation vsOUTsound powered by346000) while bridging digital SCS bus frames when no multiple automation systems share the sound bus.MH201uses an embedded non-Linux MCU firmware (confirming immediateNACK/ zerorxdata frames on*#13**23##and*#13**24##) and is not part of the 12 Linux-based QEMU images inown-firmware-oracle; all13deterministictxframes and9rxframes in this fixture passtests/test_trace_oracle_validation.pyTiers 1–4.Checklist
What is the problem,What is the solution,State Diagram,How is it tested,Where are the traces,What does the Firmware Oracle / Encyclopedia say).handle_event()and do not manually subscribe viaasync_dispatcher_connect.pytest,ruff, andscripts/verify_ha_standards.pypass.