fix: re-adapt lights right after the auto-reset timer fires (#1506)

When the auto-reset timer fired, its callback called manager.reset(),
which pops the timer and calls timer.cancel() - cancelling the very task
that was running the callback. The manual_control flag was cleared, but
the re-adaptation that should follow was silently cancelled, so the light
only changed on the next interval pass (with the normal transition).

_AsyncSingleShotTimer.cancel() now never cancels the task that is
currently running its own callback. The trailing assert in the callback
is removed because it is reachable now and could trip when a new manual
change comes in while the re-adaptation is still running. The existing
auto-reset test also checks that a light.turn_on with the 'autoreset'
context is sent.

Fixes #1233

Co-authored-by: Dennis-Dekker <>
Co-authored-by: Bas Nijholt <bas@nijho.lt>
This commit is contained in:
Dennis Dekker 2026-09-06 09:01:50 +02:00 • committed by GitHub
commit c620d1480a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 26 additions and 3 deletions

View file

@ -914,11 +914,27 @@ async def test_auto_reset_manual_control(hass):
switch.extra_state_attributes["autoreset_time_remaining"][light.entity_id] > 0
)
await update()
# The auto reset must also re-adapt the light right away, not only clear the
# flag. Collect the 'light.turn_on' calls made with the 'autoreset' context.
autoreset_calls: list[Event] = []
async def _on_call_service(event: Event) -> None:
if (
event.data.get("domain") == LIGHT_DOMAIN
and event.data.get("service") == SERVICE_TURN_ON
and is_our_context(event.context, "autoreset")
):
autoreset_calls.append(event)
remove_listener = hass.bus.async_listen(EVENT_CALL_SERVICE, _on_call_service)
await asyncio.sleep(0.3) # Should be enough time for auto reset
await hass.async_block_till_done()
remove_listener()
assert not manual_control[light.entity_id], (light, manual_control)
assert (
light.entity_id not in switch.extra_state_attributes["autoreset_time_remaining"]
)
assert autoreset_calls, "auto reset did not re-adapt the light"
# Do a couple of quick changes and check that light is not reset
for i in range(3):