V2 phase1 architecture - #232
Open
GreenGrassBlueOcean wants to merge 923 commits into
Open
GreenGrassBlueOcean wants to merge 923 commits into
GreenGrassBlueOcean wants to merge 923 commits into
Conversation
This was referenced Sep 8, 2026
Closed
Options flow crash on HA 2025.12+ + aggressive WHO=18 (energy) polling causing gateway overload
#221
Closed
Closed
Closed
Closed
Closed
Closed
Closed
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 ☂️ |
GreenGrassBlueOcean
added a commit
that referenced
this pull request
Sep 8, 2026
Member
|
Finally had time to spin up that VM. What does not work is:
With all that's been discussed today, I'm not sure what was supposed to make the cut at this stage though 😅 |
…nnotations-syntax-standards test(standards): enforce valid Python syntax and strict __future__ positioning
…dress-tolerance feat(cenplus): support wire WHERE address resolution and KW8011 lifecycle (#466)
…nion-cast-and-host-matching feat(media_player): add cast platform and host/name companion resolution
…room trace - Implement leader handover in async_unjoin_player via DecoderPool.transfer_leadership - Expand DecoderPool idle states to include paused and standby - Allow self-healing decoder claiming from inactive owners - Expand CONF_DECODER_PRE_GAIN up to 100% for line-level gain staging to eliminate F441 hiss - Forward volume and mute directly to streaming companion entities - Add anonymized authentic MH200 + F441M multiroom sound trace - Update unit tests, privacy tests, and sync trace availability documentation
…-sync-time fix(services): send DIMENSION 0 alongside DIMENSION 22 during sync_time (#520)
…layer - Add tests for decoder pool leadership transfer to last remaining member - Add test for leadership transfer when leader lacks active decoder - Add test for claiming idle decoder from powered-off owner zone - Streamline decoder volume synchronization to companion target - Guard async_unjoin_player turn-off command with _turning_off to dampen bus-OFF echoes
…e duplicate devices (#524) A gateway added next to a configured one was set up as a standalone primary: its startup sweep discovered every device the other gateway already had. Once it was made a follower, only the duplicate actuators were pruned, leaving their lock / unlock / calibrate buttons unavailable on devices without an actuator. - Config flow: when another IP gateway is configured, a bus_topology step asks standalone or shared before the entry exists. Shared creates the entry as secondary / standby of the chosen gateway (role and delegated WHOs suggested from both models) and promotes a standalone primary to shared primary. - Pruning a follower's duplicate now also removes its companion buttons and the device once empty; button setup cleans up orphans left by earlier versions (only when the primary has the actuator). - topology: split the follower delegation out of infer_shared_bus_topology; recommend_follower keeps the configured gateway as primary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iss auto-off A zone that was just dropped from a group can now reclaim its former decoder from the leader within a 30s handover window, turning the displaced leader off instead of leaving it silently holding the decoder. Decoders auto power off their zone shortly after the underlying stream goes idle, pauses, or stops, avoiding audible hiss. Also fixes a missing patch/AsyncMock import in test_decoder_pool.py and restores 100% test coverage on decoder_pool.py and media_player.py. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tics gap Ingests the physical F461 bus sweep contributed by @lyubomirtraykov in issue #466 (comment 5870342995): 200 authentic on-wire frames covering WHO 1 (lights), WHO 2 (automation), WHO 4 (climate, 7 zones), WHO 5 (alarm), WHO 13 (gateway diagnostics), WHO 16 (audio), WHO 18 (energy), and WHO 1013 (gateway object model). Adds a dedicated replay test plus targeted assertions for the WHO 13/ WHO 1013 gateway diagnostics, which the F461 had never answered in our corpus before, and regenerates the Hardware Trace Availability Matrix in README.md and docs/trace-availability.md via scripts/update_trace_matrix.py. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…oup-handover-and-gain-staging feat(media_player): add group handover, 100% pre-gain, and F441 multiroom trace
…traces test(466): add BTicino F461 gateway trace closing WHO 13/1013 diagnostics gap
…gateway anotherjulien found it on #525: after a follower's duplicate covers are pruned in favour of the primary, its device-level "Calibrate all covers" button stayed present and looked available even though this gateway has nothing left to calibrate. `available` now also requires at least one cover entity of this config entry, matching what `async_press` already checks. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The F461 physical bus sweep contributed by @lyubomirtraykov in issue #466 (comment 5870342995) answers the WHO=13 device type query with the shared code 200 and the WHO=1013 OBJECT_MODEL query with 134 - trailing metadata *15*5*0 (N_CONF/BRAND/LINE), identical to the three gateways already confirmed in PR #420 (F454, MH202, MyHomeServer1). Adds a golden plant fixture (tests/fixtures/plants/issue_466_f461/) with the real identity-exchange frames and parametrizes test_golden_who1013_exchange / test_real_who1013_reply_is_object_model_n_conf_brand_line to replay it, and updates the const.py commentary that previously listed F461 as "reported in issue #370, no diagnostics yet" for WHO=13 code 200 and left WHO=1013 code 134 untraced. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes a real audible-interruption bug reported against multi-room grouping: a member zone dropped from its group (own unjoin, or left out of a join snapshot) was switched off immediately, even when the same room was about to be reassigned elsewhere (e.g. reused as a new leader) a couple of seconds later. Captured trace, single-decoder install: Transferring leadership of badkamer to audio_zone_23 (1 remaining member(s)) Calling set_members on native player badkamer with add=[], remove=['audio_zone_23'] Clearing active output protocol on badkamer Setting active output protocol on Eetkamer to Native (+2.0s) Start Queue Flow stream for Queue Eetkamer (+1.3s) Note: deselecting the leader itself is already handled atomically by DecoderPool.transfer_leadership (async_unjoin_player's leader branch, merged separately) and needs no grace period — this change does not touch that path. What was still missing is protection for a plain member being dropped from its group without becoming a leader in the same call: its own unjoin, or a join-snapshot drop. A dropped member now keeps playing through a 5-second grace period instead of being switched off immediately: - Group bookkeeping (group_members) still updates at once. - Only the physical OFF frame is delayed, cancelled by anything that puts the zone back to work within the window: play_media, turn_on, or being joined into a different group (_async_wake_zone, the leader-join path, and a genuine bus ON report all cancel it). - Since the amplifier was never turned off, a follow-up play_media skips its own wake step — zero audible gap. - Nothing reclaiming the zone in time still switches it off, just delayed. Tests: a member surviving its own wake-sequence echo through a full departure-and-reclaim cycle, the grace-period fallback, a join-snapshot drop, and a departing member immediately joined into a different group. Updated test_unjoin_player_member, which pinned the old immediate-off behaviour. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ndover-grace fix(media_player): grace period before a member's group-leave OFF
…who1013-golden test(466): confirm F461 WHO1013 identity 134 on physical hardware
…rding fix(topology): ask for bus topology when adding a gateway, prune whole duplicate devices (#524)
async_dispatcher_connect and hass.bus.async_listen classify a plain lambda as an Executor-type HassJob (not a synchronous @callback), so homeassistant schedules the listener onto a background job instead of invoking it inline. The two CEN/CEN+ failover-bridging tests asserted on listener state immediately after await gw_sb._process_message(msg) without awaiting hass.async_block_till_done(), racing the scheduled job. This passed reliably under the pinned local test environment but flaked in the "Run Pytest Suite & Coverage" CI job, which installs an unpinned/newer homeassistant release: https://github.com/OpenWebNet-HA/MyHOME/actions/runs/36442320774/job/108995686149 Decorate the listeners with @callback, matching the convention already used elsewhere in this file, so delivery is synchronous and deterministic. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n-dispatcher-test-race fix(tests): eliminate dispatcher/event-bus race in CEN failover tests
…ice triggers (#445, #466) Adds native Home Assistant Device Trigger support for centralized shutter commands broadcast on WHO=2 General address 0 (*2*11#..., *2*12#..., *2*10#...). - Enriches myhome_general_automation_event, myhome_area_automation_event, and myhome_group_automation_event with where, gateway_mac, and entry_id payloads. - Dispatches gateway-scoped events via async_dispatcher_send for multi-gateway plant isolation. - Exposes centralized_shutter_open, centralized_shutter_close, and centralized_shutter_stop device triggers on MyHOME Gateway device entries. - Adds device_automation translation strings to strings.json and translations/en.json. - Adds comprehensive unit tests in test_device_trigger.py and updates assertions in test_gateway.py and test_issue_445_myhomeserver1_covers.py.
…rdware Incorporates two follow-up bus traces from @f18m answering the open questions in #445 (comment 5854237269) that had put this PR in draft (#445 comment 5868017035): - Trace 1 (stationary STOP/PRESET press): the LN-4660M2's STOP button sends the byte-identical *2*10#001#1*0## frame whether shutters are moving or already stationary - no separate preset-recall command or Dimension 11 write was observed. - Trace 2 (local room keypad): a LOCAL keypad on the same hardware family uses the plain *2*WHAT*WHERE## syntax at its own point address, never the centralized multi-parameter form on General address 0, and captures the first real on-wire Dimension 10 "closing" transitional state (*#2*02*10*12*100*001*0##). Adds both as golden corpus fixtures (tests/golden/frames/who02_automation.yaml) and as two new regression tests in test_issue_445_myhomeserver1_covers.py asserting exactly one myhome_general_automation_event fires for the stationary STOP and none fires for the local keypad frames. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ions Explains how to set decoder_1_pre_gain against the BTicino hardware's own feedback (Ingresso RCA / Controllo Stereo signal LED) instead of guessing a percentage, and warns against wiring a decoder's headphone/AUX output instead of its fixed line-out. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e constant (#523) SHARED_BUS_EVIDENCE_WINDOW_S is 600.0 (10 minutes), matching the #459 audit's intent, but the gateway.py comment and gateways.md both said 5 minutes / 300.0. Align both to the actual 10-minute window and drop the unused SHARED_BUS_RX_WINDOW_S leftover from the old RX/RX correlation logic. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nce-window-523 fix(gateway): align shared-bus evidence window docs with the 10-minute constant
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of Changes in v2 Phase 1 Architecture
This pull request delivers the complete Phase 1 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.0b8 client library integration, zero strict typing errors under
mypy --strict, and 100.0% statement test coverage (7,952/7,952 statements) across 2,336 passing automated tests (plus 5 snapshot test suites) spanning 812 commits and 103 merged sub-pull requests.🏆 Integration Quality Scale & Architectural Modernization
Home Assistant Core ≥ 2026.3 & Platinum Tier Quality Scale:
brandsapproved inhome-assistant/brands#2052, strict unique IDs, config entry uniqueness, removal instructions, setup tests, runtime-data mapping.PARALLEL_UPDATES = 0across all 9 platform files), config entry unloading, reconnect grace period logging, reauth flow, 100% test coverage.async-dependency), raw TCP OpenWebNet communication (inject-websessionexempt), and zero strict typing errors across all modules (strict-typing).mypy --strictwith zero errors across all 42 integration modules (chore: strict-typing ratchet, CI dry-run fixes; cover travel time copied_from #326, chore(typing): strict-typing ratchet phases 1-3 (507 -> 269 errors) #375, fix(typing): Resolve 64 new strict typing errors from OWNd 2.0.0b7 #393), backed by OWNd 2.0.0b8's PEP 561py.typedmarker.entry.runtime_dataMigration: Gateway state, frame routing, and platforms live entirely within typedMyHOMERuntimeData, completely retiring legacyhass.data[DOMAIN]storage (feat: quality-scale Bronze + Gold (runtime_data, exception translations, stale devices, entity audit, docs); min core 2026.3 #321).has_entity_name = True): Fully adopts Home Assistant standard naming conventions with translation keys instrings.json/translations/en.jsonand automatic entity ID generation (feat: Home Assistant device/entity naming (quality-scale has-entity-name, entity-translations) #325).HomeAssistantError,ServiceValidationError) are mapped to translation keys underexceptionsinstrings.jsonandtranslations/en.json(feat: quality-scale Bronze + Gold (runtime_data, exception translations, stale devices, entity audit, docs); min core 2026.3 #321).async_remove_config_entry_device(feat: quality-scale Bronze + Gold (runtime_data, exception translations, stale devices, entity audit, docs); min core 2026.3 #321).Four-Milestone Modular Refactoring (Phase 1 Deep Modularization):
legacy_yaml.py,migrate.py, refactor(init): decompose __init__.py into legacy_yaml and migrate sibling modules (Milestone 1) #467): Decomposed__init__.pyboot pipeline into dedicated legacy YAML parser and schema migration engine, isolating YAML ingest from modern config entry initialization.cover_calibration.py,cover_motion.py,cover_scope.py, refactor(cover): encapsulate calibration hub, motion math, and isolate multi-gateway state (Milestone 2) #468): Decomposedcover.py, isolating multi-gateway state, encapsulating motion physics math, and establishing the central calibration hub.gateway_sessions.py,gateway_events.py,gateway_resync.py, refactor: decompose init, cover, and gateway session/event runners (Milestones 1–3) #472): Decomposedgateway.pyinto dedicated session runners, event listener & watchdog supervisor, and debounced reactive resync coordinator.light_fade.py,light_dali.py,light_group.py, refactor(light): extract SoftwareFadeEngine and DaliFeatureLock subsystems (Milestone 4) #475, refactor(light): extract SoftwareFadeEngine and DaliFeatureLock subsystems (Milestone 4) #485): Decomposedlight.py, extracting theSoftwareFadeEngine,DaliFeatureLock, and light group management into dedicated, self-contained subsystems.Unified Platform Skeleton (
discovery.py) & Per-Gateway Frame Router (router.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):discovery.py): Implemented a unified lifecycle engine across all 9 entity platforms (light,switch,cover,climate,alarm_control_panel,sensor,binary_sensor,media_player,button). Standardizes the four lifecycle phases: restore (pre-loading saved entities from registry), configure (applyingmyhome.yaml), discover (instantaneous creation on first bus frame), and route (dispatching subsequent frames). Eliminates startup race conditions and frame loss during initial gateway status sweeps.router.py): Replaced legacymyhome_update_{mac}_{who}_{where}dispatcher signals (60+ string-interpolated signals) with a dedicated per-gatewayFrameRouterdelivering frames directly to subscribing entities by address.handle_eventtyping contract onMyHOMEEntityand establishes an architectural checklist in the pull request template to prevent legacy dispatcher regressions (fix(light): complete FrameRouter migration for MyHOMELightGroup #391).🚀 Key Subsystems & Feature Capabilities
Transports, Gateway Profiles, Identity Resolution & 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):
F452,F453AV,F455(reclassified as single-bus Basic Gateway, docs(gateway): reclassify F455 as single-bus Basic Gateway (#466) #471, Community Call for Real-World Gateway Traces: Help Us Eliminate Hardware Blind Spots! 📡 #466),3578(USB/Serial).F454,MH200,MH200N,MH202,MH201,MyHomeServer1,HL4684, and the4890family (AM4890,H4890,LN4890,LN4890Areinstated with authentic H4890 traces, Community Call for Real-World Gateway Traces: Help Us Eliminate Hardware Blind Spots! 📡 #466, 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).gateway_identity_mismatch,gateway_identity_corrected, and unconfigured timezone (999) repair issues (feat(gateway): generate repair issue for unconfigured timezone (999) #387, feat(repairs): add repair issue for unknown gateway models #388).*1*0*0##), maintaining continuous entity availability.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):
light_fade.py) with transition-only software-stepped curves (<=1% granularity) avoiding bus congestion while maintaining silky dimming transitions.lock_features: true,light_dali.py) ([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, refactor(light): extract SoftwareFadeEngine and DaliFeatureLock subsystems (Milestone 4) #475, refactor(light): extract SoftwareFadeEngine and DaliFeatureLock subsystems (Milestone 4) #485):myhome.yaml/ options flow preventing DALI gateways (F429 / F461) from polluting relays or standard dimmers with remembered Dimension 12 (HSV) or Dimension 14 (tunable white) values from previous sweeps.light_group.py, 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):myhome.yamlinstantiate assumed-state light entities with member inference, polling, and intact APL address handling.gateway_resync.py, P7: lighting groups & general sync — declared groups + reactive re-sync for group/general frames #368, feat: debounced resync for group area general frames (#368 PR A) #377):RESYNC_LEADING_WINDOW_S) / 0.5 s debounce (RESYNC_DEBOUNCE_S) silence window synchronizing light entities upon physical wall switch or scene group/area/general commands.lightandswitch) by area instead of sending banned*#1*0##.myhome.turn_on_timed) leveraging native SCS hardware timers.Cover Subsystem (WHO=2) & Virtual Positioning Calibration (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):
cover_calibration.py,cover_motion.py,cover_scope.py, refactor(cover): encapsulate calibration hub, motion math, and isolate multi-gateway state (Milestone 2) #468):cover_scope.py, Mh201 and General cover button behaviour #433, fix(cover): a general / area / group cover follows its shutters (#433) #436):myhome.calibrate_cover):Calibrate UpandCalibrate Downconfig buttons (EntityCategory.CONFIG).myhome.set_cover_travel_time,myhome.reset_cover_travel_time,myhome.stop_cover_calibration.copied_from) across identical shutters.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, feat(climate): follow WHO 4 dimension 7 zone state on Home+Control plants (#429) #457):
WHERE 0#N) from zone N (Pump actuator incorrectly interpreted as zone actuator #431, fix(climate): a pump status (WHERE 0#N) is not zone N (#431) #432), and physical actuator frames (str(raw_where) in ("0", ""), 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), eliminating phantom climate zones.hvac_actionidle state ([BUG] Standalone Climate Zones are not updating hvac_action. #404, fix(climate): resolve hvac_action idle and preserve auto fan mode (#404, #303) #405).climate.py.#0) and 4695 (#0#1) coordinate seasonal heating/cooling modes across slave zones.Audio & Multi-Room Sound System 2.0 (WHO=16) (fix(media_player): make F441M source selection work — routing address used the wrong digits #422, 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):
media_playerentities for BTicino F441/F441M matrix zones.*#16*0*5##, test(sound): expect the dimension-5 WHO 16 status request #435, fix(gateway): discover WHO 16 with the dimension-5 status request *#16*0*5## #438): Automatic bus discovery via dimension 5 status requests.DecoderPoolwith anti-hiss gain staging.Scenario Controls (WHO=15 CEN & WHO=25 CEN+) (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, fix(cen+): long_press once per hold, repeats as long_press_repeat #439):
long_pressevent on initial hold, and subsequent periodic bursts aslong_press_repeat."0001") andvia_device_idlinking to gateway device (Deprecated via_device argument in gateway.py will stop working in Home Assistant 2027.8 #310).Sensors, Binary Sensors & Energy Counters ([Feature / Bug]: Dynamic discovery for WHO 1 light/motion sensors (Legrand 048834) & ghost light prevention #244, fix(sensor): dynamic discovery for WHO 1 light/motion sensors and ghost light prevention (#244) #245, fix: Resolve dry contact ghost devices and zero-padded WHERE sensor updates (WHO 1/25) #247, [Bug] F454 + 3455/L4577: recurring NACKs from temperature polling; readings continue when the request is disabled #308, Discovered temperature probe 105 is recreated and polled with *#4*105*15##, causing retry warnings #441, fix(sensor): one unique id per temperature probe, however it was created (#441) #452):
WHERE >= 100) with fallback polling ([Bug] F454 + 3455/L4577: recurring NACKs from temperature polling; readings continue when the request is disabled #308).WHAT = 34) and illuminance sensors (DIMENSION = 6), with ghost light elimination ([Feature / Bug]: Dynamic discovery for WHO 1 light/motion sensors (Legrand 048834) & ghost light prevention #244, fix(sensor): dynamic discovery for WHO 1 light/motion sensors and ghost light prevention (#244) #245).OWNDryContactEvent), and inverted logic support (fix: Resolve dry contact ghost devices and zero-padded WHERE sensor updates (WHO 1/25) #247).Burglar Alarm Platform (WHO=5) (Need help configuring Alarm feature (F454) #311, feat(sweep): add WHO 5 alarm and WHO 16 sound to bus sweep with MH200 golden samples #470):
alarm_control_panelsupportingARM_AWAY,ARM_HOME,DISARM, and panic trigger commands.Lovelace Bus Monitor Card v2 (
<myhome-bus-card>) (The Custom card does not have the Who=5 filter applicable #275, fix(frontend): add WHO=5 filter and generalize card field filtering (#275) #276, [Bug]: Custom element does not exist (myhome-openwebnet-bus-monitor / myhome-bus-card) and duplicate card registration in v2.0.0b5 #277, fix(frontend): deduplicate bus card in picker and eliminate event loop warnings (#277) #278, fix(frontend): resolve static path import coverage and restore 100% test coverage #279, timezone issue #305, fix(frontend): render bus-monitor timestamps in the browser's local time (#305) #343, fix(bus-monitor): backfill the card's whole buffer and export the full ring #449, feat(sweep): add WHO 5 alarm and WHO 16 sound to bus sweep with MH200 golden samples #470):RECindicator.node:test+ TAP reporter).Internationalization & Documentation (docs(mkdocs): render GitHub-style callouts as admonitions #415, chore(translations): set up Crowdin localization pipeline and anti-drift tooling #463, docs: add dynamic hardware trace availability matrix #464, feat(ci): dynamically calibrate supported entity domains table in README #483):
docs/configuration/(Supported Functions, Known Limitations, Troubleshooting, Use Cases, Runtime Behaviour, Gateways, Services, Bus Monitor, Sound System, CEN/CEN+, Lovelace Recipes).Golden Corpus Conformance & CI Verification:
tests/golden/corpus.json) covering 11 OpenWebNet subsystems against multi-authority real-world captures (feat(testing): add multi-authority OpenWebNet golden corpus conformance suite across 11 subsystems #281).scripts/anonymize_plant_fixture.py, fix: synthetic plant fixtures, privacy at the source, CI on the core users run (Python 3.14 / 2026.9) #316).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 #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 #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
Addresses #466