Skip to content

V2 phase1 architecture - #232

Open
GreenGrassBlueOcean wants to merge 1571 commits into
masterfrom
v2-phase1-architecture
Open

GreenGrassBlueOcean wants to merge 1571 commits into
masterfrom
v2-phase1-architecture

Conversation

@GreenGrassBlueOcean

@GreenGrassBlueOcean GreenGrassBlueOcean commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary of Changes in v2 Phase 1 Architecture

This pull request delivers the complete Phase 1 & Phase 2 Architecture Modernization for the OpenWebNet MyHOME integration. It establishes a modern, asynchronous foundation for Legrand & BTicino MyHOME SCS bus systems, achieving full Home Assistant Core ≥ 2026.3 compliance (Python 3.14), Integration Quality Scale 🏆 Platinum tier standards (54/54 rules satisfied/exempt), official OWNd 2.0.0b11 client library integration, zero strict typing errors under mypy --strict, and 100.0% statement test coverage (15,911/15,911 statements) across 4,393 passing automated tests (plus Syrupy snapshot test suites) spanning 1,519 commits and 297 merged sub-pull requests.

The current pre-release is 2.0.0b16 (#703). Everything below is what v2-phase1-architecture contains; the sections right after this one list what is new in 2.0.0b16 (since 2.0.0b15), 2.0.0b15 (since 2.0.0b14), and 2.0.0b14 (since 2.0.0b13).


🆕 What's new since 2.0.0b15 (2.0.0b16)

2.0.0b16 merges 86 pull requests since 2.0.0b15 on OWNd 2.0.0b11 (including the post-tag HA 2026.11 compatibility and group resync fixes #769, #771, #772). The headlines:

Area What changed PRs
Expose HA to the bus Declarative expose engine projecting Home Assistant entities and the clock onto free SCS addresses: virtual WHO 1 / WHO 2 actuators that physical wall controls can drive (mirroring HA state back to wall LEDs), WHO 4 virtual temperature probes, WHO 18 virtual energy meters, and the WHO 13 bus clock. Custom actions run as Home Assistant scripts. #750, #753
Covers (WHO 2 & WHO 1) & Locks (WHO 6) Motor start-up floor (min_motor_pulse, default 0.4 s, 0 disables) for timed covers and ≥2% minimum step when stationary for advanced covers; plain UP / DOWN (*2*1*WHERE## / *2*2*WHERE##) full runs at 0% and 100%; timed endpoint runs settle at travel_time + 2 s; timed runs end at the actuator's stop status (*2*0*WHERE##) and full-travel keypad runs report the endpoint; Venetian blind slat tilt (slat_tilt, slat_time); impulse garage/gate covers on WHO 1 relays with optional safety approval, native lock platform for WHO 6 door-entry strikes, and doorbell events; cover stop frames overtake queued commands; pluggable CoverKinematicsStrategy hierarchy; warning when advanced: true is set on standard F411 shutters. #508, #667, #679, #691, #694, #702, #707, #708, #739, #755, #760, #762
Climate (WHO 4) Season frames, generic setpoint mode, and central unit season tracking; holiday and vacation program attributes; zone humidity remembered and queried on Dimension 60 at startup; F461 zones polled, AUTO on standalone zones, fan coils per season, season persisted across restarts; hvac_action derived from season when actuator omits heating flag; standalone default follows the plant; F455 10 s command timeouts eliminated; migrated to native_* temperature API. #649, #657, #665, #675, #689, #698, #712, #714, #715, #718, #719, #721, #724, #725
Lighting (WHO 1) 2-digit Area 0 point-to-point addresses 01–09; ON dimmers on a fresh install probed on Dimension 1 for brightness support; timed turn-on migrated to OWNd builders; lights already reported by the startup sweep are not polled again; area and group re-syncs only skip/cancel once every registered member has echoed in the commanded polarity, and group members inherit the group's bus interface (#4#02). #659, #700, #701, #713, #740, #742, #748, #749, #763, #771
Audio (WHO 16 / 22) Tuner frequency, preset, and RDS read from WHO 22 with optional per-frequency pictures; sound source commands and physical wall transport steps; amplifiers no longer woken by Dimension 1 volume status reports (intercom ducking); centralized power-state decision engine; idle decoders can be claimed from a parked group. #651, #662, #670, #683, #695, #696, #723, #726
Scenarios (WHO 17 / 25) WHO 25 Scenario Plus commands and events; WHO 17 scene lifecycle events (myhome_scene_event). #656, #671, #673
Gateways, scheduling & clock Standalone FrameScheduler with per-target ordering and user commands prioritized ahead of status polls; H4684 / LGRH4684 and MH201 WHO 1013 identification; kernel and distribution queries in the diagnostic sweep; HA websession injected into OWNd discovery; myhome.sync_time rejects half-hour time zones with a translated error instead of truncating the offset. #652, #685, #711, #722, #738, #757, #764
Setup, migration & HA 2026.11+ Device settings in myhome.yaml copied into the config entry so they survive if the file is removed; blank-start discovery suppresses phantom entities; ignored_addresses can be cleared in the UI; restored sensors keep native ID regeneration; legacy entity friendly_name preserved as compat_name across HA 2026.11+ registry migration and availability updates; local brand assets bundled in custom_components/myhome/brand. #666, #672, #678, #686, #727, #741, #742, #769, #772
Architecture, Oracle & CI Monolithic config_flow, gateway, and decoder_pool split into focused subpackages; private OWNd access and runtime shims removed; mypy --strict verified against pinned OWNd 2.0.0b11; 4-tier trace-vs-firmware-oracle conformance gate and empty-bus NACK verification; HA deprecation guard in pytest and container smoke tests. #720, #728, #729, #737, #743, #744, #745, #747, #765, #766

📦 Previous Beta Summaries (2.0.0b15 & 2.0.0b14)

What shipped in 2.0.0b15 (since 2.0.0b14 — 41 PRs on OWNd 2.0.0b10)
Area What changed PRs
Device fault repairs & ignored addresses Actuator hardware anomalies surface as self-clearing Home Assistant repair issues (actuator_fault) and withdraw on recovery; ignored_addresses in the Options Flow silences faulty or unwanted devices and their repair issues. #593, #611, #621
Dimmers & lighting Dimmer no-load state (WHAT 19) handled gracefully without dropping light entities; brightness 0% routes to switch_off to avoid gateway firmware NACKs; dimension and motion frames no longer trigger broadcast resync sweeps; blank-start lighting discovery without myhome.yaml. #567, #580, #614, #622, #635
Thermoregulation (WHO 4) Central unit heating/cooling mode commands aligned with libqtdevices; 99-zone central unit polled with plain status request and exempt from Dimension 14 polling / PollHealth; invalid fan speeds outside 0–3 caught as friendly service errors; exact 0.0 °C WHO 4 readings preserved. #569, #590, #591, #630, #636, #641
Burglar alarm (WHO 5) Empty-WHERE system frames routed to the central alarm panel; read-only auxiliary panel mode with AUX arm/disarm guidance; MH202 and H4890 alarm profile and golden trace support. #567, #572, #576, #583, #608, #609, #618, #620
Scenario controls & sensors CEN and CEN+ triggers isolated so they never fire on each other's events; 4-digit physical CEN addresses and wire-address tolerance; event context forwarded for trigger IDs; broadcast WHERE 0 frames ignored by illuminance discovery; dusk curfew blueprint and hardware-coupled sensor guide. #597, #599, #603, #607, #633, #638, #639
Audio & gateways Physical wall button activation auto-joins the active streaming group and syncs source selection; streaming companions are never guessed; passive event session inactivity timeouts disabled on quiet plants; per-profile default command session counts; firmware-discriminated gateway profiles (F453AV); localized connection failure abort reasons with transient retry; reactive availability for CalibrateAllCoversButtonEntity. #568, #573, #574, #575, #581, #586, #589, #592, #605, #606, #623, #625, #640
What shipped in 2.0.0b14 (since 2.0.0b13 — 105 PRs on OWNd 2.0.0b9)
Area What changed PRs
Multiple gateways Several gateways per installation: separate buses, several gateways sharing one SCS bus, and a warm-standby gateway that takes over while the primary is offline. Topology is asked for in the initial config flow, and whole duplicate devices are pruned when a gateway turns out to share a bus. #459, #525, #531
Audio (WHO 16) Streaming companion discovery (DLNA / UPnP / Google Cast) so Music Assistant can stream to decoders whose own integration cannot play a URL; multi-room group handover, additive join, leader hand-over on OFF, groups that survive restarts and the anti-hiss switch-off; F500 / F500N tuner entities; F441M source selection actually works. Live-tested on an MH200 + F441M. #422, #427, #437, #487, #497, #519, #521, #528, #530, #532, #535, #538, #544, #554
Climate (WHO 4) Home+Control dimension 7 zone state; a pump (0#N) or a temperature probe (PZZ, WHERE ≥ 100) is no longer a zone; a running zone's dimension 12 write is its new setpoint; zones that never answer are no longer polled at startup; one WHO 4 WHERE grammar plus a CI guard against raw WHERE reads. #432, #457, #550, #552, #555, #556
Gateways & identity Gateway identity from WHO 1013 diagnostics and shown in the HA device page and diagnostics export; event-listener crash guard on a general *1*0*0##; event session closed on cancel; command workers capped at the profile's session limit; F461, H4890, MyHomeServer1 traces close the WHO 13 / 1013 gaps; myhome.sync_time also writes DIMENSION 0 for MH200N. #420, #423, #444, #446, #450, #455, #484, #522
Scenarios & sensors CEN+ long_press once per hold with long_press_repeat, CEN+ wire-WHERE addresses (Living Now KW8011); centralized shutter buttons as device triggers; F520 energy meter sweep and discovery; one unique ID per temperature probe. #430, #439, #452, #503, #506, #515
Lights & covers Unknown WHAT keeps the last state and no longer overrides a restored one; a general / area / group cover follows its shutters; adaptive-lighting compatibility; phantom alarm zones on MH200 / MH200N purged. #433 (#436), #456, #458, #462, #465, #490
Home Assistant 2026.12 readiness OptionsFlowWithReload, no entry update listener (fixes the options-flow reload race), and a verify_ha_standards.py rule that keeps it that way. #514, #543
Architecture & docs Four-milestone decomposition of __init__, cover, gateway and light (#467, #468, #472, #475, #485); OWNd timezone monkeypatch removed (#482); anti-drift sentinels, per-beta documentation snapshots with Delta Explorer, hardware trace matrix synced to #466 by CI, Crowdin translation pipeline. #463, #464, #483, #486, #495, #504, #507, #511

🏆 Integration Quality Scale & Architectural Modernization

  1. Home Assistant Core ≥ 2026.3 & Platinum Tier Quality Scale:

  2. Deep Modularization & Package Decomposition:

  3. Unified Platform Skeleton (discovery.py), Frame Router (router.py) & Declarative Expose Engine (expose.py) (refactor: one platform skeleton (restore / configure / discover / route) - all nine platforms #329, feat: quality-scale chain (#349, #321, #325, #326, #329) rebuilt on v2-phase1-architecture #363, fix(light): complete FrameRouter migration for MyHOMELightGroup #391, refactor: one WHO 4 WHERE grammar and a CI guard against raw WHERE reads #556, feat(discovery): resolve blank start lighting discovery and suppress polling avalanche (#578) #580, fix(discovery): improve blank-start discovery quality and suppress phantom entities (#681) #686, feat(discovery): reusable sweep poll gate, dimmer capability probe, and legacy YAML migration (#733) #742, feat(expose): add declarative HA-to-SCS bus expose engine and virtual actuator LED sync #750):


🚀 Key Subsystems & Feature Capabilities

  1. Transports, Gateway Profiles, Scheduling & Connection Resilience (Call to Action :: Gateway Traces :: #291 #292, Trace Builders with the 2.0.0b12 version #297, MH201: unexpected error logs with unclear reason #304, [v2-architecture] [F454 Gateway] No heartbeat received in 120 seconds #315, fix(gateway): identify gateways by precedence; gate startup discovery on the profile #345, [BUG] False gateway identity mismatch: confirmed F454 reports WHO=13 device type 200 #370, fix(gateway): treat WHO=13 device type 200 as ambiguous between F454 and MyHomeServer1 #373, Regression of #254: the first command after ~30 s of silence is silently lost (MyHomeServer1) #378, fix(gateway): close idle command sessions before the gateway drops them #381, feat(gateway): generate repair issue for unconfigured timezone (999) #387, feat(repairs): add repair issue for unknown gateway models #388, chore: bump OWNd to 2.0.0b7 for HMAC refactor #394, Spurious ERROR logged on startup discovery when subsystems are not present (*#4*0##, *#16*0##) #406, test(config_flow): prevent unmocked socket connects on reauth and options flow reload #413, fix(yaml): normalize single-digit MAC octets and harden secondary bus key indexing (#408) #414, Fix false gateway identity mismatch (#370) by using WHO=1013 diagnostics #420, Show the gateway's own model data in HA (device page, diagnostics) #423, fix(gateway): close the event session when the listener is cancelled #444, fix(identity): a same-family model with a code of its own is a contradiction #446, fix(gateway): cap command workers at the gateway profile's session limit #450, test(ownd): accept one or two sends for a NACKed status request #451, fix(gateway): a general "all lights off" (*1*0*0##) killed the event listener — entities unavailable until reload #455, Community Call for Real-World Gateway Traces: Help Us Eliminate Hardware Blind Spots! 📡 #466, docs(gateway): reclassify F455 as single-bus Basic Gateway (#466) #471, refactor(gateway): remove obsolete OWNd timezone monkeypatch #482, feat(gateway): reinstate 4890 family with authentic H4890 traces (#466) #484, feat(ci): auto-sync hardware trace matrix to issue #466 and normalize 4890 family naming #486, fix(services): send DIMENSION 0 alongside DIMENSION 22 during sync_time (#520) #522, refactor(repairs): track device faults per gateway and raise them as self-clearing repair issues #593, feat(config-flow): start new gateways at the profile's default command sessions #606, fix(gateway): support firmware-discriminated gateway profiles (OWNd#80) #623, fix(gateway): disable passive event inactivity timeout on quiet plants (#637) #640, feat(gateway): add H4684 and LGRH4684 models to WHO 1013 identification #652, feat(gateway_sessions): prioritize user commands over status polls in command queue (#680) #685, feat(gateway): include kernel and distribution queries in diagnostic sweep (#466, #709) #711, fix(gateway): identify MH201 via empty-WHERE WHO 1013 and include in device type 200 #722, refactor(protocol): eliminate private OWNd access and runtime shims (#736) #737, feat(scheduler): standalone FrameScheduler and per-target worker serialization (#731) #738, fix(config_flow): inject the HA websession into OWNd discovery and correct the OWNd licence #757, fix(services): reject half-hour time zones in sync_time with a translated error #764):

  2. Multiple Gateways, Shared Buses & Warm Standby (Two gateways in one installation? Help us support shared buses (MH200N + MH201, MHS1 + LN4890, …) #453, feat(gateway): multiple gateway and shared bus support (#453) #459, Adding a second gateway discovers the bus before its shared-bus role can be set, leaving cover devices with only unavailable buttons #524, fix(topology): ask for bus topology when adding a gateway, prune whole duplicate devices (#524) #525, fix(tests): eliminate dispatcher/event-bus race in CEN failover tests #529, fix(gateway): align shared-bus evidence window docs with the 10-minute constant #531):

    • Topologies: Standalone (own isolated bus, entities scoped {mac}-{who}-{where}) or Shared (several gateways on one physical bus) with an explicit role per gateway: Primary, Secondary (delegated WHOs), or Warm standby (automatic failover when primary drops > 60 s).
    • Topology at setup (fix(topology): ask for bus topology when adding a gateway, prune whole duplicate devices (#524) #525): When another IP gateway is already configured, a bus_topology step runs before entry creation, recommending roles and delegated WHOs from the gateway models and pruning duplicate devices cleanly.
  3. Lighting Subsystem (WHO=1) & Adaptive Lighting Compatibility ([FEATURE] DALI RGB / RGBW Color Support & Physical Bus Validation #288, DALI DT8 Light Temperature and RGB Support #307, feat(light): per-light feature lock for DALI DT8 fixtures (#288) — on v2-phase1 #364, P7: lighting groups & general sync — declared groups + reactive re-sync for group/general frames #368, feat: implement declared groups as assumed state lights with member inference (#368) #376, feat: debounced resync for group area general frames (#368 PR A) #377, Who= '1' Where ='10' Not available in the latest v2-phase1-architecture #402, fix(light): allow point-to-point WHERE '10' and correct Area 10 to '100' (#402) #403, fix(light): keep the last state when a light reports an unknown WHAT #456, fix(light): an unknown WHAT does not keep a state restored at startup #458, [RFC] Support & Compatibility for Adaptive Lighting (Circadian Lighting) with MyHOME Fixtures #461, feat(light): adaptive lighting compatibility and bus traffic optimization (#461) #462, fix(gateway): skip lighting scope events when is_on is None #465, refactor(light): extract SoftwareFadeEngine and DaliFeatureLock subsystems (Milestone 4) #475, refactor(light): extract SoftwareFadeEngine and DaliFeatureLock subsystems (Milestone 4) #485, fix(light): prevent dimension and motion frames from triggering broadcast resync sweeps (#612) #614, fix(light): handle dimmer no-load state WHAT 19 and promote to dimmable (#619) #622, fix(light): guard brightness 0 against firmware NACK and clamp 1% brightness #635, feat(lighting): migrate timed turn-on commands to OWNd b11 builders #659, fix(light): skip post-discovery poll for lights the startup sweep already reported (#612) #701, feat(light): support 2-digit Area 0 point-to-point addressing (01-09) (#684) #713, feat(resync,light_group): scope group echo cancellation and multi-entity area sweeps (#734) #740, feat(discovery): reusable sweep poll gate, dimmer capability probe, and legacy YAML migration (#733) #742, fix(resync): require all registered area entities to echo before skipping or cancelling area sweep (#746) #749, fix(resync,light_group): inherit group bus interface for members and seed leading group echoes (#734, #740) #771):

  4. Cover Subsystem (WHO=2 & WHO=1), Venetian Slat Tilt & Video Door Entry Locks (WHO=6) (RFC: timed covers, what I measured on a MyHOMEServer1 (latencies, frame order, non-linear position) #302, feat(frontend): bus-monitor trace/sweep workflow, self-describing exports, node test suite #319, feat(cover): travel-time calibration on the bus - calibrate_cover service, buttons, bus-measured runs (backend; card UI in #355) #349, A full open or close writes a stop at the end of the estimated time instead of letting the cover reach its end stop #379, A cover's position advances even when its direction frame never reached the bus #380, fix(cover): avoid artificial stop frames on full runs (#379) #384, fix(cover): abort motion and prevent position advance on undelivered frames (#380) #385, docs(covers): clarify automated travel time calibration sequence #411, docs(covers): the remaining points from the b13 field test #417, fix: cover level 255, per-family CEN triggers, corrected CEN golden frames #430, Mh201 and General cover button behaviour #433, fix(cover): a general / area / group cover follows its shutters (#433) #436, refactor(cover): encapsulate calibration hub, motion math, and isolate multi-gateway state (Milestone 2) #468, feat(device_trigger): add centralized shutter controls to gateway device triggers (#445, #466) #503, feat(cover): implement venetian blind slat tilt inching and clarify dimension 11 shutter level (#492) #508, feat(cover,lock): impulse garage/gate control, multi-factor safety approval, and intercom events #667, feat(cover): enable native broadcast for general covers behind an interface and pace fan-out #679, feat(gateway_sessions): let a cover stop overtake unrelated queued frames (#645) #694, feat(cover): motor start-up floor and full runs at 0/100% (#466) #702, fix(cover): end timed runs on stop status instead of stop write (#705) #707, feat(cover): per-cover motor start-up floor instead of single constant (#704) #708, feat(cover,light): pluggable CoverKinematicsStrategy hierarchy and low-priority fade step routing (#730) #739, fix(cover): confirm end_stop on full-travel bus runs and scope run timeouts #760, fix(cover,docs): warn and guide when F411 standard shutters set advanced: true (#761) #762):

  5. Climate & Thermoregulation Subsystem (WHO=4) ([BUG] Standalone Climate Zones not exposing FAN #303, Climate entity for Zone 1 incorrectly changes to heating when another zone is heating #333, fix(climate): an actuator status is not a second zone (#333) #336, fix(climate): enable fan mode dynamically for climate zones with fancoils (#303) #352, [BUG} Climate entities show 17 °C instead of the active 7 °C antifreeze target #383, fix(climate): preserve active antifreeze target temperature across status sweeps (#383) #386, [BUG] Standalone Climate Zones are not updating hvac_action. #404, fix(climate): resolve hvac_action idle and preserve auto fan mode (#404, #303) #405, [BUG] Climat Zones do not support Fan OFF #410, fix(climate): omit unsupported 'off' from fancoil fan modes (#410) #412, Climate: bus traces wanted to fix how manual, programmed, holiday and vacation modes are shown #429, Pump actuator incorrectly interpreted as zone actuator #431, fix(climate): a pump status (WHERE 0#N) is not zone N (#431) #432, climate: trailing '3' on WHO 4 dimension 12 is read as protection/off, so _target_temperature goes stale #454, feat(climate): follow WHO 4 dimension 7 zone state on Home+Control plants (#429) #457, Climate: a temperature probe (WHERE >= 100) is routed to zone WHERE-100 and can discover a phantom zone #549, fix(climate): a temperature probe is not zone WHERE-100 #550, fix(climate): a dimension 12 mode 3 of a running zone is its new setpoint (#454) #552, perf(climate): stop polling heating zones that never answer #555, refactor: one WHO 4 WHERE grammar and a CI guard against raw WHERE reads #556, fix(heating): exactly-zero WHO 4 readings are 0.0, freezing/probe/zero tests (#566) #569, fix(climate): exempt central units from Dimension 14 status queries and PollHealth (#582) #590, fix(climate): align central unit mode commands and fan mode with OWNd canonical protocol (OWNd#77) #630, fix(climate): poll 99-zone central unit with plain status request (#629) #636, fix(climate): turn OWNd's fan-speed ValueError into a service error (OWNd#77) #641, feat(climate): support WHO 4 season frames and generic setpoint mode (#649) #657, fix(climate): migrate to the native_* temperature API #665, fix(climate): eliminate 10s command session timeouts on F455 and add authentic trace fixture (#674) #675, feat(climate): support WHO 4 holiday plan and vacation program attributes #689, fix(climate): zones follow the plant for the standalone default (#697) #698, feat(climate): remember zone humidity support and query dimension 60 on startup (#710) #712, fix(climate,services): poll F461 zones, AUTO on standalone zones, fan coil per season, refuse WHO 4 dimension 22 (live F461 capture) #714, fix(climate): restore season and fan_coil_sides across restarts and bridge dim 19 is_active (#714) #715, fix(climate): determine hvac_action from season when active actuator carries no heating flag #721, feat(climate): keep the season of a central unit's mode frames (#699) #725):

  6. Audio & Multi-Room Sound System 2.0 (WHO=16 & WHO=22) (fix(media_player): make F441M source selection work — routing address used the wrong digits #422, feat(media_player): tuner entity for WHO=16 sound sources (F500) #427, test(sound): expect environment-addressed matrix routing #428, test(sound): expect the dimension-5 WHO 16 status request #435, fix(media_player): read sound addresses from where, not zone #437, fix(gateway): discover WHO 16 with the dimension-5 status request *#16*0*5## #438, feat(media_player): multi-room audio grouping and Music Assistant integration #460, feat(media_player): automatic companion decoder discovery & transparent DLNA streaming bridge #487, fix(media_player): parse real OWNSoundEvent dimensions and handle malformed RDS frames #497, feat(media_player): add cast platform and host/name companion resolution #519, feat(media_player): add group handover, 100% pre-gain, and F441 multiroom trace #521, fix(media_player): grace period before a member's group-leave OFF #528, fix(media_player): async_join_players is additive, not a snapshot #530, fix(media_player): switch off rooms that hear a decoder they do not own when it stops #532, fix(audio): keep the decoder books across restarts and close the #532 audit leftovers #535, fix(audio): keep the group when the anti-hiss timer switches its rooms off (live-tested) #538, perf(media_player): wake a parked group faster #544, fix(media_player): turning off a group's leading room no longer silences the whole group #554, feat(audio): never guess a streaming companion, guard decoders at runtime, pin the Music Assistant contract #568, feat(audio): auto-join active streaming group and sync wall button source selection (#579) #581, feat(sound): modernize sound source commands and intercept physical wall transport steps (blocked on OWNd 2.0.0b11) #651, fix(media_player): do not wake amplifiers on Dimension 1 volume status reports (#669) #670, fix(media_player): centralise power-state decision engine, document sound system state machine, and add intercom ducking trace replay (#682) #683, feat(sound): read tuner frequency, preset and RDS from WHO 22, with optional per-frequency pictures #695, fix(media_player): allow claiming idle decoder from parked group and reconcile displaced rooms #723):

  7. Scenario Controls (WHO=15 CEN, WHO=25 CEN+ / Scenario Plus & WHO=17 Scenes) (Deprecated via_device argument in gateway.py will stop working in Home Assistant 2027.8 #310, fix: cover level 255, per-family CEN triggers, corrected CEN golden frames #430, #439, #515, #548, #597, #603, #639, #656, #671):

    • CEN & CEN+ Isolation, 4-Digit Addressing & Wire Tolerance (#439, #515, #597, #603, #639): Strict per-family event isolation, 4-digit physical addresses, wire-address tolerance (Living Now KW8011), long_press deduplication with long_press_repeat, and forwarded event context for trigger IDs.
    • WHO 25 Scenario Plus & WHO 17 Scene Events (#656, #671): Full support for WHO 25 Scenario Plus commands/events and WHO 17 scenario programmer lifecycle events (myhome_scene_event).
  8. Sensors, Binary Sensors & Energy Counters (WHO=1, 4, 9, 18, 25) (#244, #245, #247, #308, #441, #452, #494, #506, #607, #633, #661, #678):

    • F520 Energy Meters & Dimension 1200 Lifecycle (#494, #506, #661): Proactive bus sweep and aligned active-power query / Dimension 1200 stream lifecycle across probe addresses 51–59 and 71–79.
    • Temperature Probes, PIR Motion, Illuminance & Dry Contacts: Deterministic probe unique IDs (#452) with exact 0.0 °C handling (#569); WHO 1 PIR (WHAT = 34) and illuminance (DIMENSION = 6) discovery with broadcast WHERE 0 phantom filtering (#607); WHO 25 dry contacts (3477 / F482) with dusk curfew blueprint (#633).
  9. Burglar Alarm Platform (WHO=5) (#311, #470, #490, #576, #608, #609, #618, #620):

    • Read-only central unit (3485/3486) and partition state tracking with empty-WHERE system frame routing (#618), zone 0 broadcast synchronization, AUX arm/disarm support (#609), MH202/H4890 profile support (#576), and MH200/MH200N phantom zone suppression (#490).
  10. Lovelace Bus Monitor Card v2 (<myhome-bus-card>) (#275, #276, #277, #278, #279, #305, #343, #449, #470, #747):

    • In-band circular ring buffer streaming live bus telegrams via WebSocket API with full buffer backfill, JSON/text trace export (#449), REC trace freeze, Sweep Bus workflow (#470), strict WebSocket frame validation (#747), and node:test frontend suite.
  11. Internationalization & Documentation (#415, #463, #464, #483, #495, #507, #511, #513, #577, #687, #768):

    • Crowdin Localization Pipeline & Anti-Drift Sentinels (#463, #464, #483, #495, #507, #577): Automated synchronization and CI verification across PLATFORMS, SUPPORTED_GATEWAY_MODELS, manifest.json SSDP, OWNd profiles, options-flow fields, device triggers, translations, and the firmware oracle (#743).
    • Complete documentation suite under docs/configuration/ and per-beta Delta Explorer (docs/roadmap/beta-differences.md) updated through v2.0.0b16 (#703, #768).
  12. Golden Corpus, Firmware Oracle & CI Verification:

    • 250+ Golden Corpus fixtures (tests/golden/corpus.json) and 4-tier Firmware Oracle Conformance Gate (tests/test_firmware_oracle_conformance.py, #642, #720, #743, #745, #766) validating gateway profiles and real plant traces against empirical firmware verdicts.
    • Production plant fixtures replayed deterministically in CI across MH200, MH200N, MH201, MH202, F453AV, F454, F455, F461, H4890, and MyHomeServer1 (#247, #280, #288, #292, #297, #303, #311, #333, #370, #377, #390, #434, #469, #473, #484, #486, #488, #489, #491, #496, #499, #502, #505, #512, #526, #527, #551, #576, #584, #596, #627, #634, #638, #646, #648, #655, #656, #660, #662, #664, #673, #675, #691, #692, #696, #700, #724, #748, #751, #752, #753, #755, #763).
    • 4,393 automated tests passing with 100.0% statement coverage across all 15,911 statements in 73 integration modules.

Resolved Issues & Merged Pull Requests

Closes #6
Closes #30
Closes #69
Closes #80
Closes #92
Closes #103
Closes #105
Closes #110
Closes #115
Closes #116
Closes #117
Closes #118
Closes #121
Closes #123
Closes #127
Closes #132
Closes #134
Closes #136
Closes #139
Closes #143
Closes #146
Closes #147
Closes #150
Closes #151
Closes #152
Closes #156
Closes #157
Closes #159
Closes #161
Closes #162
Closes #165
Closes #166
Closes #168
Closes #170
Closes #172
Closes #174
Closes #176
Closes #177
Closes #178
Closes #179
Closes #181
Closes #185
Closes #188
Closes #190
Closes #191
Closes #194
Closes #200
Closes #203
Closes #205
Closes #212
Closes #213
Closes #215
Closes #219
Closes #220
Closes #230
Closes #231
Closes #233
Closes #234
Closes #237
Closes #240
Closes #241
Closes #242
Closes #243
Closes #244
Closes #245
Closes #246
Closes #247
Closes #249
Closes #250
Closes #251
Closes #252
Closes #253
Closes #254
Closes #255
Closes #256
Closes #257
Closes #258
Closes #259
Closes #262
Closes #263
Closes #264
Closes #265
Closes #266
Closes #268
Closes #269
Closes #270
Closes #271
Closes #272
Closes #273
Closes #275
Closes #276
Closes #277
Closes #278
Closes #279
Closes #280
Closes #281
Closes #282
Closes #283
Closes #284
Closes #285
Closes #286
Closes #287
Closes #288
Closes #289
Closes #290
Closes #292
Closes #293
Closes #295
Closes #296
Closes #297
Closes #301
Closes #302
Closes #303
Closes #304
Closes #305
Closes #307
Closes #308
Closes #309
Closes #310
Closes #311
Closes #313
Closes #315
Closes #316
Closes #319
Closes #321
Closes #325
Closes #326
Closes #329
Closes #333
Closes #334
Closes #336
Closes #345
Closes #347
Closes #348
Closes #349
Closes #350
Closes #352
Closes #353
Closes #356
Closes #361
Closes #363
Closes #364
Closes #365
Closes #368
Closes #369
Closes #370
Closes #371
Closes #372
Closes #373
Closes #375
Closes #376
Closes #377
Closes #378
Closes #379
Closes #380
Closes #381
Closes #382
Closes #383
Closes #384
Closes #385
Closes #386
Closes #387
Closes #388
Closes #390
Closes #391
Closes #393
Closes #394
Closes #395
Closes #396
Closes #401
Closes #402
Closes #403
Closes #404
Closes #405
Closes #406
Closes #407
Closes #408
Closes #410
Closes #411
Closes #412
Closes #413
Closes #414
Closes #415
Closes #417
Closes #420
Closes #421
Closes #422
Closes #423
Closes #425
Closes #428
Closes #429
Closes #430
Closes #431
Closes #432
Closes #433
Closes #434
Closes #435
Closes #436
Closes #437
Closes #438
Closes #439
Closes #440
Closes #441
Closes #443
Closes #444
Closes #446
Closes #449
Closes #450
Closes #451
Closes #452
Closes #453
Closes #454
Closes #455
Closes #456
Closes #457
Closes #458
Closes #460
Closes #461
Closes #462
Closes #463
Closes #464
Closes #465
Closes #467
Closes #468
Closes #469
Closes #470
Closes #471
Closes #472
Closes #473
Closes #475
Closes #477
Closes #478
Closes #481
Closes #482
Closes #483
Closes #484
Closes #485
Closes #486
Closes #492
Closes #493
Closes #494
Closes #498
Closes #501
Closes #510
Closes #520
Closes #523
Closes #524
Closes #537
Closes #540
Closes #541
Closes #549
Closes #553
Closes #563
Closes #564
Closes #565
Closes #566
Closes #578
Closes #579
Closes #582
Closes #600
Closes #601
Closes #604
Closes #612
Closes #615
Closes #616
Closes #619
Closes #624
Closes #629
Closes #631
Closes #637
Closes #645
Closes #654
Closes #658
Closes #669
Closes #674
Closes #676
Closes #677
Closes #680
Closes #681
Closes #682
Closes #684
Closes #690
Closes #697
Closes #699
Closes #704
Closes #705
Closes #706
Closes #709
Closes #710
Closes #730
Closes #731
Closes #732
Closes #733
Closes #734
Closes #735
Closes #736
Closes #746
Closes #759
Closes #761
Addresses #445
Addresses #466


👥 Credits & Attribution

This integration is developed and maintained by the OpenWebNet-HA community.

Special thanks to:

This was referenced Sep 8, 2026
@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

@anotherjulien

Copy link
Copy Markdown
Member

Finally had time to spin up that VM.
First impression is that what works works 😄
The transition covered everything, also all device names reverted to <device type> <APL>.

What does not work is:

  • Advanced covers seem to be discovered as regular covers, even after seeing position aware OWN frames (I have Legrand 67557 everywhere)
  • Dry contacts (I have a Legrand 3477 coupled with a mechanical lever to monitor if my garage door is open/closed)
  • Lux Meters (I have a Legrand 048834 for motion/light sensing outdoors)
  • The mechanism to lock/unlock APL endpoints (relies with WHO 14, I've used it occasionally but I'm not sure many people do)

With all that's been discussed today, I'm not sure what was supposed to make the cut at this stage though 😅

GreenGrassBlueOcean and others added 30 commits October 23, 2026 19:42
…quests

fix(services): send status and dimension requests from send_message as requests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
test: collect #746 bus events on the event loop to keep their order
…-trace

test(trace): ingest #777 F454 + Legrand 067226 + F411 shared WHERE 52 trace fixture
feat(cover): per-cover min_position_delta for advanced covers
fix(ci): ignore custom_integration_behavior=IGNORE in HA deprecation guard
…e-gate

chore(github): enforce English + anonymous trace gates in issue & PR templates
-#142 and MyHOME #791-#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 *#4*2## reply order with dimension 12 before and different from 14,
*#4*2*20## followed by two actuator frames and *#4*#0*20## answering both,
*#1*0007## NACK vs *#1*07## ACK vs *#1*7## empty, *#13**0## echoed on the
event session, every WHO 16 request refused in under 10 ms, the dimension 27
model name frame, *#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
*#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: *#4*0## refused in
51 ms; *#4*#0## and *#4*#0*14## unanswered after 4.2 s (one OWNd retry);
*#4*35## reply order 0, 12, season, 14, 13; *#4*35*20## one actuator and
*#4*#0*20## 43 distinct actuator frames including zone 0; *#4*35*60## NACK
where no probe exists, *#4*0*60## answered by 17 zones, *#4*39*60## answered;
*#16*0*5## / *#16*101*5## / *#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; *#13**0## echoed on the event session;
*#13**12## answering the MAC; time zone 999.

The firmware oracle emulates MyHomeServer1 2.82.06 while this unit runs
3.87.13, and *#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>
…ng artefacts, README wording, loud pending-decoder skip)

Review 5638684839 on #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 (*#5*#1##
13 ms, *#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 *#4*2## because the patched
integration (#792) sent *#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>
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
…_time (#189)

Both services were registered without a schema, so a misspelt key such
as `gw_mac` was dropped and the frame went to the default gateway with
no error (#189), and `message: null` reached the handler. Register
closed vol.Schemas (gateway optional, message required), add text
selectors so the visual editor shows the fields, and align the
send_message docs (gateway was documented as required).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… reporting Generic

The profile block read `profile.name` and `profile.keepalive_interval`,
which no OWNd GatewayProfile has, so every diagnostics dump said
"Generic" / 90 s while the identification block named the real profile
(seen on a physical F461 2.0.11). Read `model_name`, `display_name` and
`event_keepalive_interval` (None = OS TCP keepalive only) and pin the
real F461 and MH201 profiles in a test.

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>
fix(diagnostics): read the OWNd profile's real field names instead of reporting Generic
…-same-address

fix(light): keep a configured light when a WHO 1 motion sensor shares its address (#777)
fix(services): closed schemas and selectors for send_message and sync_time (#189)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment