mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-27 20:34:20 +02:00
Fix pending adaptations after light or profile removal (#1587)
This commit is contained in:
parent
34356a6b56
commit
2f37b6ea40
2 changed files with 261 additions and 3 deletions
|
|
@ -886,6 +886,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
assert hass is not None
|
||||
self.hass = hass
|
||||
self.manager = manager
|
||||
self._removed = False
|
||||
self.sleep_mode_switch = sleep_mode_switch
|
||||
self.adapt_color_switch = adapt_color_switch
|
||||
self.adapt_brightness_switch = adapt_brightness_switch
|
||||
|
|
@ -1078,6 +1079,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Remove the listeners upon removing the component."""
|
||||
self._removed = True
|
||||
self._remove_listeners()
|
||||
|
||||
def _resolve_lights(self, lights: list[str] | None = None) -> list[str]:
|
||||
|
|
@ -1433,6 +1435,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
if not is_first_call or data.initial_sleep:
|
||||
await asyncio.sleep(data.sleep_time)
|
||||
|
||||
if self._removed:
|
||||
return
|
||||
|
||||
# Instead of directly iterating the generator in the while-loop, we get
|
||||
# the next item here after the sleep to make sure it incorporates state
|
||||
# changes which happened during the sleep.
|
||||
|
|
@ -1488,6 +1493,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
Wraps the sequence of service calls in a task that can be cancelled from elsewhere, e.g.,
|
||||
to cancel an ongoing adaptation when a light is turned off.
|
||||
"""
|
||||
if self._removed:
|
||||
return
|
||||
|
||||
# Prevent overlap of multiple adaptation sequences
|
||||
self.manager.cancel_ongoing_adaptation_calls(data.entity_id)
|
||||
_LOGGER.debug(
|
||||
|
|
@ -1691,7 +1699,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
await asyncio.sleep(self._adapt_delay)
|
||||
|
||||
# Runtime settings may retire this profile's target while the event waits.
|
||||
if entity_id not in self.lights:
|
||||
if self._removed or entity_id not in self.lights:
|
||||
return
|
||||
|
||||
await self._update_attrs_and_maybe_adapt_lights(
|
||||
|
|
@ -1705,7 +1713,8 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
self,
|
||||
event: Event[EventStateChangedData],
|
||||
) -> None:
|
||||
if not _is_state_event(event, (STATE_ON, STATE_OFF)):
|
||||
new_state = event.data.get("new_state")
|
||||
if new_state is None or new_state.state not in (STATE_ON, STATE_OFF):
|
||||
_LOGGER.debug("%s: Ignoring sleep event %s", self._name, event)
|
||||
return
|
||||
_LOGGER.debug(
|
||||
|
|
@ -2730,7 +2739,7 @@ class AdaptiveLightingManager:
|
|||
elif state.state == STATE_OFF: # is turning on
|
||||
await on(eid, event)
|
||||
|
||||
async def state_changed_event_listener(
|
||||
async def state_changed_event_listener( # noqa: PLR0912
|
||||
self,
|
||||
event: Event[EventStateChangedData],
|
||||
) -> None:
|
||||
|
|
@ -2808,6 +2817,10 @@ class AdaptiveLightingManager:
|
|||
new_on.context.id,
|
||||
)
|
||||
|
||||
if old_on and not new_on:
|
||||
# Availability loss invalidates pending commands, not manual state.
|
||||
self.cancel_ongoing_adaptation_calls(entity_id)
|
||||
|
||||
if old_on and new_off:
|
||||
# Tracks 'on' → 'off' state changes
|
||||
self.on_to_off_event[entity_id] = event
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue