Skip to content

V2 phase1 architecture - #232

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

GreenGrassBlueOcean wants to merge 1822 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.0b13 client library integration, zero strict typing errors under mypy --strict, and 100.0% statement test coverage across 5,324 passing automated tests (plus Syrupy snapshot test suites) spanning 1,770 commits and 361 merged sub-pull requests.

The current pre-release is 2.0.0b17 (#855). Everything below is what v2-phase1-architecture contains; the sections right after this one list what is new in 2.0.0b17 (since 2.0.0b16), 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.0b16 (2.0.0b17)

2.0.0b17 merges 61 pull requests since 2.0.0b16 on OWNd 2.0.0b13. The headlines:

Area What changed PRs
Covers (WHO 2) & Calibration Timed covers full run settle on travel_time + 2 s from known positions; 0.5 s early cutoff tolerance recognizes actuator limits arriving just before travel time; configurable min_position_delta (1–5 %, default 2 %); translated stop frames (*2*1000#0*WHERE##) halt timed runs; calibration refuses whole-minute configurator cutoffs (1–10 min); unknown start handling and device class options. #778, #795, #796, #820, #834, #843, #847
Climate & Thermoregulation (WHO 4) Full BTicino 3550 99-zone central unit state hydration, active setpoint management, and diagnostic attributes (Discussion #819); new thermo actuator switch platform (switch) for manual solenoid control (Issue #785); interface routing, actuator guards, humidity poll gate, and pump ghost filtering. #791, #792, #793, #816, #817, #825, #845, #846
Lighting & Dimmers (WHO 1) Fine dimmer preset echo (WHAT 101–199) and autodiscovered dimmers; coarse preset brightness curve alignment; coexistence of lighting actuator and motion detector on the same bus address; Dimension 22 guard form crash fix; bus-truthful state updates and resync coordinator. #771, #777, #781, #828, #849, #851, #852
Gateways, Engine & Protocol Official OWNd 2.0.0b13 pin; Legrand 067292 gateway profile alias (Issue #832); per-firmware startup requests; manual gateway model identification in config flow; resolved flaky multi-gateway dispatcher callbacks; door-entry WHO 6 gateway boundary enforcement. #794, #801, #804, #835, #848, #850, #854
Audio (WHO 16 / 22) F500 FM tuner entities now support band, preset, and frequency controls with live HL4684 trace replay; stored station frequency range checks and Dimension 10 bounds; fixed-feed amplifier volume. #774, #839, #844
Burglar Alarm (WHO 5) HomeKit arm-away read-only mode; integrated live BTicino 3486 central unit alarm trace replay. #841, #842
Binary Sensors & UI Per-entity UI option to invert binary sensor states. #829
Architecture, CI & Traces Over 5,300 automated tests passing with 100% statement coverage; live plant trace fixtures added for F461, HL4684, Legrand 067292, F454, MH200N, and MH201; Home Assistant 2026.11/2026.12 readiness (compat_name, logger compat, deprecation guards). #769, #772, #776, #779, #783, #786, #788, #790, #799, #807, #810, #815, #818, #823, #824, #827, #841, #845, #853

📦 Previous Beta Summaries (2.0.0b16, 2.0.0b15 & 2.0.0b14)

What shipped in 2.0.0b16 (since 2.0.0b15 — 86 PRs on OWNd 2.0.0b11)

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

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) (#422, #427, #428, #435, #437, #438, #460, #487, #497, #519, #521, #528, #530, #532, #535, #538, #544, #554, #568, #581, #651, #670, #683, #695, #723):

    • Music Assistant Multi-Room Grouping & Wall Button Sync (#460, #521, #528, #530, #532, #535, #538, #544, #554, #581, #723): Additive async_join_players, leader handover on OFF, persistent .storage group/decoder books across restarts, anti-hiss group parking, automatic group join when a room is switched on at its physical wall button (#581), and claiming idle decoders from parked groups (#723).
    • WHO 22 Tuner RDS & Transport Controls (#427, #497, #651, #695): Reads tuner frequency, preset, and RDS from WHO 22 with optional per-frequency station pictures, modernizes sound source commands, and intercepts physical wall transport steps.
    • Centralized Power-State Engine & Intercom Ducking (#670, #683): Dimension 1 volume status reports sent during video intercom streams no longer wake sleeping amplifiers in Home Assistant.
    • Streaming Companion Bridge (decoder_companion.py, #487, #519, #568): Explicit companion matching (dlna_dmr, upnp, cast) without guessing, forwarding play_media while keeping hardware volume, pre-gain, and F441M routing on the matrix slot.
  7. Scenario Controls (WHO=15 CEN, WHO=25 CEN+ / Scenario Plus & WHO=17 Scenes) (#310, #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:

  • @anotherjulien for creating the original MyHOME integration and laying the protocol foundations.
  • @GreenGrassBlueOcean for the v2 modernized architecture, gateway profiles, streaming proxy, and test suite.
  • @xtimmy86x for crafting the frontend user experience, administration panel, cover calibration interfaces, and hardware diagnostics.
  • @gdluck for outstanding engineering, invaluable ideas, and rigorous physical F461 plant measurements.
  • @TheDarkWizard for crucial CEN+ wire address captures (KW8011), WHO 1001 diagnostics, and tireless real-world hardware testing across Living Now controls and MyHomeServer1 setups.
  • @wave68runner for the authentic HL4684 touchscreen captures, BTicino 3550 thermoregulation sweeps, and tuner diagnostics.
  • @Interstellar0verdrive for deep hardware actuator analysis, cover timing/concurrency mechanics, and platform extensions.
  • @fedem95, @mantovanellimatteo, @lyubomirtraykov, and Cedric Rohou for key bugfixes, platform extensions, and community testing.
  • @GianlucaCh for preserving and contributing the comprehensive 15-manual BTicino/Legrand specification archive (OWN DOC.zip), the official WHO_24.pdf Lighting Management specification, and CEN+ community automation references.
  • @nicolacavallo84 for the iconic 70+ device plant captures and H4890 touchscreen multi-gateway traces that form our foundational CI replay fixtures.
  • All community testers and trace providers whose real-world bus captures keep our test suite grounded in real hardware: "In traces we find the truth" — their traces prove again and again that we can only build reliable stuff when we get authentic user traces: @manfredgittmaier-afk, @eC0y0te, @simo72mast-lab, @lionelser, @caiosweet, @f18m, @marcob79, @xduss, @pakob81, @Depechie, @nce2704, @ricdijk, @imfreddie, @theveninjeremy, and @Yvan13120.

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 6 commits November 8, 2026 12:19
…-and-doorbell-callback

test(cover,door-entry): restore 100% coverage on cover.py and fix doorbell dispatcher race
…hl4684-traces

feat(identity,alarm): auto-detect WHO 13 firmware on connect and ingest H/L4684 6.0.11 WHO 5/6/9 traces (#466)
GreenGrassBlueOcean and others added 15 commits November 8, 2026 14:44
chore(git): merge master into v2-phase1-architecture to resolve PR #232 conflicts
…one-status-poll

fix(climate): poll *#4*Z## on F454 & MH202 and harvest partial NACK responses (#866)
…p sweep (#466)

A physical F455 1.0.76 answers the single-dimension reads *#4*Z*0##, *#4*Z*14## and
*#4*Z*12## in ~0.1 s and the actuator form *#4*Z#1*20## in 0.09 s, but ACKs *#4*Z##
only after ~9 s (the issue_674 timeouts) and returns nothing for *#4*0##
(tests/fixtures/traces/issue_466/F455_direct_requests_2026-11-06.jsonl); its standalone
zone 2 reported nothing on its own in four minutes of debug log. The F455 was on neither
the zone status poll nor the temperature poll, so a quiet zone had no temperature, setpoint
or mode after a restart.

Nothing the issue_674 fixes decided is undone: F455 zones still ask nothing on add, stay off
the *#4*Z## and *#4*Z*0## polls, and the startup sweep is unchanged. Added:

- SILENT_ZONE_POLL = {"F455"} and SILENT_ZONE_GRACE = 120 s (gateway.py, with
  silent_zone_poll_needed).
- MyHOMEClimate._poll_if_silent: 120 s after the startup sweep, a zone still without a
  temperature (a restored one does not count) or a setpoint of its own asks *#4*Z*0##,
  then *#4*Z*14##, *#4*Z*12## (its reply is followed by the zone's mode frame) and
  *#4*Z#1*20##, each only once the previous read was answered; a refused or unanswered read
  ends it, so a zone without a probe costs one request. The actuator is asked last because
  hvac_action is derived when an actuator report finds the mode known.

tests/test_f455_zone_polling.py ties the unchanged gates, the capture behind each read and
the poll itself together. Squash of the three development commits of #873 (the first two
added F455 to the poll tables and a ZONE_SETPOINT_POLL, the third reverted that in favour
of this mechanism), rebased on #867 and its ZONE_TEMPERATURE_POLL table.

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

CI on 6b8af43: the HA beta/dev lanes flagged `current_temperature` and
`target_temperature` as deprecated reads inside _poll_if_silent (use the
native attributes), and the strict coverage gate found the removal
callback that cancels a waiting poll never executed. The poll now reads
_attr_native_current_temperature / native_target_temperature, and the
scheduling test runs the entity's on-remove callbacks and asserts the
task is cancelled. 5407 tests, 100 % coverage, both OWNd lanes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… it for the silent poll

A third direct run on the physical F455 1.0.76 (20:55Z, same day, same probe
tool) against zone 3, an actuator channel with no probe:
*#4*3*0##, *#4*3*14##, *#4*3*12## and *#4*3*19## are each NACKed only after
8.96-9.01 s with no frame, *#4*3## ACKs after 8.99 s with none, *#4*3*20##
NACKs in 3 ms and *#4*3#1*20## answers *#4*3#1*20*0## in 0.09 s. The
populated zones answer *#4*Z*0## in 0.10-0.13 s in the same run.

This is the record behind the silent poll's "a refused read ends it" path:
a probe-less zone costs one read, and that read holds a command session for
~9 s, once per zone added. The SILENT_ZONE_POLL comment, the fixture README
and tests/test_f455_zone_polling.py say so and assert the timings; the trace
test pins the new file's SHA-256 and checks it carries no real address.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TwNCwPHjW6sW8g1sMd4zJW
…ecide it when it runs (#873 review)

Review 5817546241 on 195fd13, point by point:

1. A restored setpoint or mode is not a report. async_restore_last_state
   now flags _target_temperature_restored and _hvac_mode_restored like
   the restored temperature; dimension 14 / 12 frames, mode frames and a
   season frame that gives a mode clear them. _silent_zone_reads() asks
   only for what is missing: *0 when the temperature is not live, *14
   when the setpoint is not, *12 when the setpoint or the mode is not,
   #1*20 when no actuator state is known.
2. The gate is read inside the task, after wait_for_initial_discovery()
   and the grace sleep, as watch_zone_actuators does (2e647e7): the
   in-band identity may correct the model after the entities were added.
   The task is scheduled when the gateway wants the poll at add time or
   while the startup sweep is still pending.
3. The reads are built from zone_number(self._where) and the routing,
   actuator token first: *#4*2*0## for a "#2" zone, *#4*2#1#4#01*20##
   behind an interface. A transport error in the loop is logged (#593).
4. silent_zone_poll_needed goes through _who4_target() like
   humidity_request_refused (c889731) and is False for a follower
   without WHO 4 in delegated_whos.
5. HUMIDITY_REQUEST_REFUSED gains F455 (live 1.0.76: *#4*2*60## NACKed
   in 19 ms with nothing on the bus). ACTUATOR_REQUEST_REFUSED is left
   alone because it also gates the thermo actuator switches, whose
   *#4*Z#N*20## the F455 answers in 0.09 s; the new
   ZONE_ACTUATOR_REQUEST_REFUSED = {"F455"} drops only the zone form
   *#4*Z*20## (NACKed in 2-18 ms) in _send_zone_requests. Docs notes in
   climate.md and runtime_behaviour.md.

The six humidity-gate tests that used the F455 as their unlisted model
use the MH201; the #674 replay asserts zone 42's humidity keeps coming
from its own pushes. 44 tests in test_f455_zone_polling.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TwNCwPHjW6sW8g1sMd4zJW
…ed zone-form refusal (#873 review)

Two branches of gateway.py were not exercised: `_send_zone_requests` dropping a
dimension 20 batch that holds only zone-level addresses on an F455 (nothing left to
ask once the zone form is refused), and `zone_actuator_request_refused` read through
`_who4_target()` when another gateway serves WHO 4. Both are now asserted in
test_f455_zone_polling.py, as suggested in the review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fix(climate): ask silent F455 zones their state once after startup (#466)
…ator-4

fix(thermo): route WHO 4 actuator #4 (Z#4) events to actuator 4 instead of 1 (#863)
* fix(climate): take zone and central mode from status frames only

Live F461 2.0.11 + F455 on one bus with a BTicino 3550 central unit (#0),
zone 2 standalone and zone 4 a zone of the 3550 (2026-11-08): every
set_hvac_mode returned HTTP 200, but the mode shown was wrong.

- Standalone zones ignore mode / season frames in the #Z form (zone via
  the central unit, WHO 4 p. 6, 9, 65): the F455 emits *4*311*#2## after
  each status report of zone 2, which showed the heating zone as AUTO.
  Zones of a central unit keep every #Z state the 3550 reports.
- A standalone zone takes HEAT / COOL from its *4*1*Z## / *4*0*Z## answer
  to Home Assistant's own mode 1 / 2 setpoint write (*#4*2*#14*0265*2## ->
  *4*0*2##); unsolicited season frames stay season-only.
- Central 102 / 202 are antifreeze / thermal protection in their season
  (WHO 4 p. 5, 64), shown as HEAT / COOL with a central_operation
  attribute, not OFF. HEAT / COOL on #0 only change the season, so the
  mode shown waits for the 3550's answer instead of being set
  optimistically.
- The central drops the gateway's echo of Home Assistant's own command
  (the F455 returns *4*0*#0## 1 ms after sending it), which reset the
  3550's battery flag and turned the action OFF -> IDLE. The same echo
  object can arrive twice (climate and sensor platforms), so it is
  recognised by identity.
- HEAT in conditioning / COOL in heating on a zone of a central unit with
  a reported season, and AUTO on a standalone zone of a plant with a
  central unit, raise ServiceValidationError instead of a silent no-op.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DN9oUd4ipuDSAE8mb2BjSq

* fix(climate): a standalone zone's own season frame replaces a restored mode

A standalone thermostat reports its mode in the plain Z form only as season
(*4*1*Z## / *4*0*Z##), protection or off (WHO 4 p. 63), never as automatic.
While its mode is still the one Home Assistant restored, that season frame
is the mode it runs in.

Live F455, 2026-11-08: after a restart zone 2 came back as AUTO, saved
earlier from the F455's own *4*311*#2## (now ignored for standalone zones).
The zone then reported *4*1*2##, which was taken as season only, so the
stale AUTO stayed until a mode write. Zones of a central unit, and zones
that have reported since start-up, are unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DN9oUd4ipuDSAE8mb2BjSq

* fix(climate): a standalone zone's plain season frame is its mode

A standalone thermostat has no program: in the plain Z form it reports its
mode only as *4*1*Z## / *4*0*Z## (heating / conditioning), protection or
off (WHO 4 p. 63). So that frame now sets HEAT / COOL whoever changed the
zone, instead of only right after this entity's own setpoint write or while
its mode is the restored one. OFF and protection still come from their own
frames; zones of a central unit keep the season-only handling.

Live, 2026-11-08: the zone 2 thermostat switched to cool from the F455's
entity (*#4*2*#14*0265*2## -> *4*0*2##). The F461 on the same bus saw the
same *4*0*2## but, not having sent the write, kept HEAT.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DN9oUd4ipuDSAE8mb2BjSq

* test(climate): 102 / 202 on the central are protection in the trace replays

The F453AV (#619) and F454 (#629) trace replays asserted the old reading of
*4*102*#0## / *4*202*#0## as OFF. The central unit now shows the season's
mode with central_operation antifreeze / thermal_protection (WHO 4 p. 5,
p. 64). The #619 replays also check that a zone the central put in
protection is still OFF (#383).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DN9oUd4ipuDSAE8mb2BjSq

* test(climate): cover a central in protection after OFF

*4*303*#0## then *4*102*#0## (live 3550 answers): the central shows HEAT
with central_operation antifreeze, and its action leaves OFF. Restores the
100 % coverage of climate.py.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DN9oUd4ipuDSAE8mb2BjSq

* fix(climate): address the #880 review (central detection, echo, 4-zone, schedules)

- A central unit is known from #0 / #0#N frames, a #Z dimension, or a #Z
  zone status with its season (WHO 4 p. 65, p. 9: 110/210/111/211/103/203/
  102/202), in the dispatcher and in the climate entity. The generic
  *4*311*#Z## / *4*310*#Z## no longer count: the F455 makes up *4*311*#2##
  for a standalone thermostat, so a standalone-only plant was taken for one
  with a central unit. The live 3550 is still found from its *4*210*#4##.
- The central's echo drop applies only within 100 ms of the command (F455
  echo after 1 ms; the 3550's first answer after 121 ms), to every copy of
  the frame inside that window; a failed send disarms it.
- Central OFF / AUTO clear central_operation, OFF sets the action to off.
- HEAT / COOL wait for the 3550's answer only on #0; a 4-zone central
  (#0#1..#0#4), which is not polled, keeps the mode it was set to.
- A standalone zone's plain season frame no longer cancels an AUTO the zone
  itself reported (Dimension 7 automatic context); Dimension 7 also ends the
  restored-mode state.
- central_operation is restored after a restart; *4*302*#0## is the
  protection of the season the plant is in (WHO 4 p. 5).
- The season errors are two translation keys without a season placeholder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: gdluck <innowebdevs@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
…r1-thermo-actuator-trace

test(trace): add MyHomeServer1 actuator 30#4 trace fixture and replay test (#863)
…s, anchor the central echo on the write (#884)

Live F461 2.0.11 + F455 on one bus with a BTicino 3550 (no battery), 2026-11-09:

* The 3550 answers *#4*#0## with its mode, *4*21*#0## and *4*31*#0## within 4 ms.
  The mode / season handlers cleared the optional flags on the mode frame, so both
  central entities wrote battery_fault False and, 1.5 ms later, True on every reply.
  WHO 4 p. 24-25: the flags (22-24, 30, 31) are optional frames after the remote
  control frame (20 / 21), and no frame means "clear". The flag cycle now starts on
  20 / 21 and clears a flag only when the burst has settled without it; a mode frame
  alone no longer touches the flags.
* The F455 emits a generic *4*311*#4## 59-61 ms before the zone's seasonal status
  (*4*210*#4##, 18 of 18; the MH201 trace of #466 the same, before *4*110*#Z##), at
  every quarter-hour push of the 3550; the F461 never shows it. The F455 zone 4 entity
  went AUTO and back every 15 minutes. A generic 310 / 311 frame for a zone of the
  central is now held briefly: a seasonal status that follows replaces it, and only a
  generic frame with none after it is the zone's state (WHO 4 p. 9).
* The central echo window starts at the write time (the written future of
  MyHOMEGatewayHandler.send), not at the call, so a busy command pool does not push the
  echo outside it; a cancelled or failed write disarms it, and a repeated command is
  anchored on its own write.

Trace-replay tests from the captured frames:
tests/test_climate_central_flags_f455_poll_2026_11_09.py.

Co-authored-by: Mac Book <macbook@Macs-MacBook-Pro.local>

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