From 1770ec29c756417049376f3dccef01fad19804ef Mon Sep 17 00:00:00 2001 From: BrawnyBravo <121905397+BrawnyBravo@users.noreply.github.com> Date: Mon, 26 Oct 2026 07:44:17 -0700 Subject: [PATCH] fix: cancel a running auto-reset timer when auto-reset is turned off Setting autoreset_control_seconds to 0 dropped the stored delay but left a timer that was already counting down. When it fired, it cleared the light's manual control and re-adapted it, overriding the user's change. Cancel that timer too, unless another profile for the light still uses auto-reset. --- custom_components/adaptive_lighting/switch.py | 4 +++ tests/test_switch.py | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 368e8826..a9356cc2 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -1130,8 +1130,12 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): and switch._auto_reset_manual_control_time ): still_reset.update(switch._resolve_lights()) + timers = self.manager.auto_reset_manual_control_timers for light in set(all_lights) - still_reset: self.manager.auto_reset_manual_control_times.pop(light, None) + # A timer started under the old delay must not reset the light. + if timer := timers.pop(light, None): + timer.cancel() async def _setup_listeners(self, _: Event[NoEventData] | None = None) -> None: _LOGGER.debug("%s: Called '_setup_listeners'", self._name) diff --git a/tests/test_switch.py b/tests/test_switch.py index c4aeb10e..0aadc46a 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -6414,6 +6414,31 @@ async def test_autoreset_control_seconds_zero_disables_existing_reset(hass): assert ENTITY_LIGHT_1 not in manager.auto_reset_manual_control_timers +async def test_autoreset_zero_cancels_running_reset_timer(hass): + """Disabling auto-reset must also stop a timer that is already running.""" + switch, _ = await setup_lights_and_switch(hass, {CONF_AUTORESET_CONTROL: 0.2}) + manager = switch.manager + manager.set_manual_control_attributes(ENTITY_LIGHT_1) + timer = manager.auto_reset_manual_control_timers[ENTITY_LIGHT_1] + assert timer.is_running() + + await hass.services.async_call( + DOMAIN, + SERVICE_CHANGE_SWITCH_SETTINGS, + {ATTR_ENTITY_ID: switch.entity_id, CONF_AUTORESET_CONTROL: 0}, + blocking=True, + ) + await hass.async_block_till_done() + + assert not timer.is_running() + assert ENTITY_LIGHT_1 not in manager.auto_reset_manual_control_timers + await asyncio.sleep(0.4) # longer than the old delay + await hass.async_block_till_done() + # The light stays manually controlled instead of being reset and re-adapted. + assert manager.get_manual_control_attributes(ENTITY_LIGHT_1) + assert switch.extra_state_attributes["autoreset_time_remaining"] == {} + + async def test_autoreset_zero_keeps_other_profiles_reset(hass): """A profile without auto-reset must not clear another profile's delay.""" switch, _ = await setup_lights_and_switch(hass, {CONF_AUTORESET_CONTROL: 3600})