Skip to content

Commit 2abb47e

Browse files
Florian HornerFlorian Horner
authored andcommitted
Merge PR basnijholt#1460: Fix transient Zigbee off-state cancelling adaptation
2 parents a387b6c + 644770d commit 2abb47e

2 files changed

Lines changed: 39 additions & 16 deletions

File tree

custom_components/adaptive_lighting/adaptation_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def _split_service_call_data(service_data: ServiceData) -> list[ServiceData]:
9898
split_data = {
9999
attribute: service_data[attribute]
100100
for attribute in attributes
101-
if service_data.get(attribute)
101+
if service_data.get(attribute) is not None
102102
}
103103
if split_data:
104104
service_datas.append(common_data | split_data)

custom_components/adaptive_lighting/switch.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2462,7 +2462,7 @@ async def on(eid: str, event: Event) -> None:
24622462
elif state.state == STATE_OFF: # is turning on
24632463
await on(eid, event)
24642464

2465-
async def state_changed_event_listener(
2465+
async def state_changed_event_listener( # noqa: PLR0912
24662466
self,
24672467
event: Event[EventStateChangedData],
24682468
) -> None:
@@ -2539,12 +2539,24 @@ async def state_changed_event_listener(
25392539
if old_on and new_off:
25402540
# Tracks 'on' → 'off' state changes
25412541
self.on_to_off_event[entity_id] = event
2542-
self.reset(entity_id)
2543-
_LOGGER.debug(
2544-
"Detected an 'on' → 'off' event for '%s' with context.id='%s'",
2545-
entity_id,
2546-
event.context.id,
2547-
)
2542+
if self.is_proactively_adapting(event.context.id):
2543+
# A transient 'off' state reported by the device during a
2544+
# proactive turn-on adaptation (e.g., Zigbee devices that
2545+
# briefly report 'off' mid-transition). Do not cancel the
2546+
# in-flight adaptation task — just record the event.
2547+
_LOGGER.debug(
2548+
"Detected an 'on' → 'off' event for '%s' with context.id='%s'"
2549+
" during proactive adaptation, skipping reset",
2550+
entity_id,
2551+
event.context.id,
2552+
)
2553+
else:
2554+
self.reset(entity_id)
2555+
_LOGGER.debug(
2556+
"Detected an 'on' → 'off' event for '%s' with context.id='%s'",
2557+
entity_id,
2558+
event.context.id,
2559+
)
25482560
elif old_off and new_on:
25492561
# Tracks 'off' → 'on' state changes
25502562
self.off_to_on_event[entity_id] = event
@@ -2761,14 +2773,6 @@ async def just_turned_off( # noqa: PLR0911
27612773
)
27622774
return False
27632775

2764-
if off_to_on_event.context.id == on_to_off_event.context.id:
2765-
_LOGGER.debug(
2766-
"just_turned_off: 'on' → 'off' state change has the same context.id as the"
2767-
" 'off' → 'on' state change for '%s'. This is probably a false positive.",
2768-
entity_id,
2769-
)
2770-
return True
2771-
27722776
id_on_to_off = on_to_off_event.context.id
27732777

27742778
turn_off_event = self.turn_off_event.get(entity_id)
@@ -2777,6 +2781,10 @@ async def just_turned_off( # noqa: PLR0911
27772781
else:
27782782
transition = None
27792783

2784+
# Check if the off→on was triggered by a real turn_on service call
2785+
# BEFORE the context-equality check. This prevents false positives
2786+
# where a Zigbee device briefly reports 'off' during a turn_on
2787+
# transition, causing both events to share the same context ID.
27802788
if self._off_to_on_state_event_is_from_turn_on(entity_id, off_to_on_event):
27812789
is_toggle = off_to_on_event == self.toggle_event.get(entity_id)
27822790
from_service = "light.toggle" if is_toggle else "light.turn_on"
@@ -2786,6 +2794,21 @@ async def just_turned_off( # noqa: PLR0911
27862794
)
27872795
return False
27882796

2797+
if (
2798+
off_to_on_event.context.id == on_to_off_event.context.id
2799+
and turn_off_event is not None
2800+
and turn_off_event.context.id == on_to_off_event.context.id
2801+
):
2802+
# Context IDs match AND a real turn_off service call was made with
2803+
# the same context. This is the legitimate case: a light still
2804+
# transitioning off that briefly polls as 'on'. Safe to cancel.
2805+
_LOGGER.debug(
2806+
"just_turned_off: 'on' → 'off' state change has the same context.id as the"
2807+
" 'off' → 'on' state change for '%s', confirmed by turn_off event.",
2808+
entity_id,
2809+
)
2810+
return True
2811+
27892812
if (
27902813
turn_off_event is not None
27912814
and id_on_to_off == turn_off_event.context.id

0 commit comments

Comments
 (0)