Repository navigation
feat(resync,light_group): scope group echo cancellation and multi-entity area sweeps (#734) - #740
Merged
GreenGrassBlueOcean merged 4 commits intoOct 20, 2026
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
GreenGrassBlueOcean
force-pushed
the
arch/issue-730-native-fades-and-cover-strategies
branch
from
October 17, 2026 10:15
d919be7 to
c630e76
Compare
GreenGrassBlueOcean
force-pushed
the
arch/issue-734-bus-truthful-state-and-resync
branch
2 times, most recently
from
October 17, 2026 10:40
1aedd67 to
7eb1348
Compare
GreenGrassBlueOcean
force-pushed
the
arch/issue-730-native-fades-and-cover-strategies
branch
from
October 17, 2026 10:40
c630e76 to
37df2a8
Compare
GreenGrassBlueOcean
force-pushed
the
arch/issue-730-native-fades-and-cover-strategies
branch
4 times, most recently
from
October 19, 2026 09:41
2be0103 to
587efb1
Compare
GreenGrassBlueOcean
force-pushed
the
arch/issue-734-bus-truthful-state-and-resync
branch
from
October 19, 2026 09:49
7eb1348 to
348d40b
Compare
GreenGrassBlueOcean
force-pushed
the
arch/issue-730-native-fades-and-cover-strategies
branch
from
October 19, 2026 12:33
587efb1 to
58ffd33
Compare
GreenGrassBlueOcean
force-pushed
the
arch/issue-734-bus-truthful-state-and-resync
branch
from
October 19, 2026 12:37
348d40b to
e5ea6cd
Compare
Base automatically changed from
arch/issue-730-native-fades-and-cover-strategies
to
v2-phase1-architecture
October 20, 2026 13:28
…, #740) The replay fed all 28 frames back to back, so the whole trace sat in one 1.5 s leading window and the test passed on the pre-#740 logic as well. It now drives a virtual clock and a virtual async_call_later from the frame timestamps. On that timeline the *1*0*#1## OFF sweep is armed rather than skipped (06/07 echoed ON 0.55-0.66 s earlier, wrong polarity) and the member echoes cancel both group #1 sweeps. Both *#1*00## sweeps after the general commands go out: the F461 echoes 06 and 07 0.7 s and 1.5 s after *1*1*0## / *1*0*0##, past the 0.5 s debounce. The old assertion that they were cancelled only held because the replay ignored time. Also registers the members with the {mac}-1-{where} unique_id format light_group uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto arch/issue-734-bus-truthful-state-and-resync
GreenGrassBlueOcean
deleted the
arch/issue-734-bus-truthful-state-and-resync
branch
October 20, 2026 19:06
GreenGrassBlueOcean
added a commit
to GreenGrassBlueOcean/MyHOME
that referenced
this pull request
Oct 20, 2026
…seed leading group echoes (OpenWebNet-HA#734, OpenWebNet-HA#740)
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.
Closes #734
Builds on #746 (area sweeps wait for every registered entity in the area), which is already on
v2-phase1-architecture.What is the problem
#G) Echo Suppression inLightingResyncManager(custom_components/myhome/gateway_resync.py):LightingResyncManagerarms a debounced (RESYNC_DEBOUNCE_S = 0.5 s)*#1*#G##status poll after a group command (*1*1*#G##/*1*0*#G##), and skips or cancels it once the group's members have echoed their own point-to-point status within the1.5 sleading window (RESYNC_LEADING_WINDOW_S) or0.5 strailing debounce window.#4#<interface>) counted;membersdeclared on aMyHOMELightGroupinmyhome.yamlwere never passed toLightingResyncManager, so it could not verify whether all declared members had echoed.custom_components/myhome/gateway_resync.py):_leading_echoes_for_area(Switch entities not updating after Gen OFF command from wall switch #746) checked that leading echoes matched the command's ON/OFF state when entities were registered in the area (known_area_entities(area)), the trailinghandle_ptp_echopath did not check_resync_expected_on: an area ON (or general ON) followed by its registered light echoing OFF during the0.5 sdebounce window still cancelled*#1*<area>##.How is it solved
custom_components/myhome/gateway_resync.py,custom_components/myhome/light_group.py):LightingResyncManager.register_group_members(group_where, members)records the normalized point-to-point member WHERE addresses of a group (#Gor#G#4#<interface>→ set of point WHEREs via_normalize_group_keyand_normalize_ptp_where, preserving explicit#4#<interface>suffixes and inheriting the group's#4#<interface>when barePointToPointmembers like["11", "12"]are declared on a routed group inmyhome.yaml). Passing an empty list (register_group_members(group_where, [])) removes the entry.MyHOMELightGrouppreserves any#4#<interface>suffix fromdevice_idon_interfaceand_full_where, qualifies bare member WHEREs with#4#<interface>when resolving{mac}-1-{where}in the entity registry, registers its declaredmembersinasync_added_to_hass(), unregisters them viaasync_on_remove(lambda: register_fn(self._full_where, [])), and filters assumed-state group frames inhandle_event()by_interface.1.5 sleading window and0.5 strailing debounce window (with partial leading matches seeded into_resync_echoed_membersviagroup_seed_echoes, matchingarea_seed_echoes):_resync_expected_on); a trailing opposite-polarity echo discards that member from_resync_echoed_membersif it had echoed earlier in the window;_group_members[group_key](including matching#4#<interface>) and every declared member must echo before*#1*#G##is skipped or cancelled;members(assumed-state groups or bus-only groups not inmyhome.yaml), the fallback requires2distinct point-to-point addresses on the same bus interface (#4#<interface>vs local bus) echoing with matching ON/OFF state;_resync_echoed_members(mirrored to_resync_group_echoes).custom_components/myhome/gateway_resync.py):schedule_resync()now records_resync_expected_on[where]for area targets (from both area and general commands) as well as group targets.light/switchentities (known_area_entities(area)),handle_ptp_echo()requires trailing echoes to match_resync_expected_on[area]before adding them to_resync_area_echoes[area]and checking_area_fully_echoed(area, echoed). A trailing opposite-polarity echo discards any seeded echo for that address from_resync_area_echoes[area]._resync_timers,_resync_group_echoes,_resync_area_echoes,_resync_echoed_members,_resync_expected_on) are cleaned up on cancellation, inexecute_resync(), and incancel_all().Known limits (not changed here)
membersare not scoped to a room: two distinct same-interface echoes with matching ON/OFF state from any room still cancel the poll.06and07echo0.73 sand1.51 safter*1*1*0##/*1*0*0##, after the0.5 sRESYNC_DEBOUNCE_Stimer expires. That sends one*#1*00##per general command; changingRESYNC_DEBOUNCE_Sis out of scope for this PR.How is it tested
tests/test_issue_368_resync.py:test_group_resync_ignores_cross_room_and_opposite_polarity_echoes: with#6={11, 12}, neither leading OFF echoes of its members, nor leading/trailing ON echoes from21/22, nor a member echoing ON then flipping OFF before the other member echoes ON can skip or cancel*#1*#6##. Also verifies split leading + trailing member completion (11leading ON +12trailing ON) and that duplicate leading + trailing echoes of11count only once. Clearing members viaregister_group_members("6", [])removes the entry.test_opposite_polarity_trailing_echo_does_not_cancel_area_sweep: verifies that an Area 1 ON command (*1*1*1##) is not cancelled when its registered light ({MAC}-1-11) echoes OFF (*1*0*11##) during the debounce window, nor when a seeded leading ON echo on11flips OFF before12echoes ON.test_group_resync_preserves_bus_interface_on_members_and_group_key: verifies that a private-bus member11#4#01is not satisfied by a local-bus11echo (leading or trailing), that a routed group#6#4#01ignores local-bus echoes and polls*#1*#6#4#01##, and thatMyHOMELightGroupon#6#4#01preserves#4#01on_full_where, inherits#4#01for baremembers=["11", "12"]in bothgroup_membersand entity registry lookup ({mac}-1-11#4#01), and ignores local-bus group frames inhandle_event().test_replay_f461_live_2026_10_18_run1_lights_group_and_general: replays the28WHO=1 frames ofrun1_lights_group_and_generalfrom a physicalBTicino F461(2.0.11,BT-F411/4channels06and07registered as{mac}-1-06and{mac}-1-07) at their captured timestamps on a virtual clock and virtualasync_call_later:members=["06", "07"]and withmembers=[](unscoped fallback): when*1*0*#1##arrives att=9.447 s, the ON echoes of06(8.786 s) and07(8.898 s) inside the1.5 sleading window have opposite polarity, so the#1OFF timer is armed (whereas pre-feat(resync,light_group): scope group echo cancellation and multi-entity area sweeps (#734) #740 logic falsely skipped it) and then cancelled by the trailing OFF echoes at9.505 s/9.555 s; the two general commands (10.183 s,12.461 s) dispatch*#1*00##at+0.5 sbecause F461's general echoes arrive at+0.73 sand+1.51 s;members=["06", "07", "08"](where declared member08never echoes in the trace):06and07alone do not cancel#1, so*#1*#1##is dispatched at9.238 sand9.947 sin addition to the two*#1*00##sweeps.test_multi_entity_area_sweep_waits_for_all_known_entities: guards multi-entity area completion (Switch entities not updating after Gen OFF command from wall switch #746).gateway_resync.pyandlight_group.py(and 100.0% across the full integration, 4336 passed),mypy --strict,ruff check,verify_ha_standards.py, andsync_documentation.py --checkall green.State diagram
stateDiagram-v2 [*] --> Idle state "Group / Area command seen (*1*1*#G##, *1*0*#G##, *1*1*A##, *1*0*A##)" as Triggered state "Collecting member echoes (0.5 s RESYNC_DEBOUNCE_S)" as Debouncing state "Poll skipped / cancelled" as Cancelled state "Poll sent (*#1*#G## / *#1*A##)" as Dispatched Idle --> Triggered Triggered --> Cancelled : every required member echoed with matching state in the 1.5 s leading window Triggered --> Debouncing : otherwise, seed partial leading matches and arm the 0.5 s timer Debouncing --> Debouncing : echo from non-member / wrong bus interface, or opposite state (discards prior echo) Debouncing --> Debouncing : member echo with matching state (recorded) Debouncing --> Cancelled : last required member echoes with matching state Debouncing --> Dispatched : 0.5 s timer expires first Cancelled --> Idle Dispatched --> IdleWhere are the traces
tests/fixtures/traces/f461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.json, runrun1_lights_group_and_general: physicalBTicino F461fw2.0.11,BT-F411/4at06/07, group#1commands with their Dimension 15 group status (*#1*#1*15*1##/*#1*#1*15*0##), and general commands*1*1*0##/*1*0*0##.What do the Oracle / Encyclopedia say
*1*1*#1##and*#1*#1##have not yet been replayed intests/golden/firmware_oracle.json; gateway behaviour for group frames is taken from the live physicalF461capture.WHO=1): a group (#G) or area (A) WHERE carries no member list in the frame; actuator membership is stored in the physical actuators' configuration. Requiring all declaredmembers(or all registered area entities) to echo with matching ON/OFF state and bus interface ensuresMyHOMEonly suppresses*#1*#G##or*#1*A##when every known member has actually reported.