Add the ability to create Debouncer tasks as background tasks (#113128)

* Add the ability to Debouncer tasks in the background

This is a more general solution as a followup to
https://github.com/home-assistant/core/pull/112652#discussion_r1517159607

* Add the ability to Debouncer tasks in the background

This is a more general solution as a followup to
https://github.com/home-assistant/core/pull/112652#discussion_r1517159607

* fix
This commit is contained in:
J. Nick Koston 2024-03-12 02:41:12 -10:00 committed by GitHub
parent b3dedb3efb
commit 120525e94f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 50 additions and 7 deletions

View file

@ -23,6 +23,7 @@ class Debouncer(Generic[_R_co]):
cooldown: float,
immediate: bool,
function: Callable[[], _R_co] | None = None,
background: bool = False,
) -> None:
"""Initialize debounce.
@ -38,6 +39,7 @@ class Debouncer(Generic[_R_co]):
self._timer_task: asyncio.TimerHandle | None = None
self._execute_at_end_of_timer: bool = False
self._execute_lock = asyncio.Lock()
self._background = background
self._job: HassJob[[], _R_co] | None = (
None
if function is None
@ -109,7 +111,9 @@ class Debouncer(Generic[_R_co]):
assert self._job is not None
try:
if task := self.hass.async_run_hass_job(self._job):
if task := self.hass.async_run_hass_job(
self._job, background=self._background
):
await task
finally:
self._schedule_timer()
@ -130,7 +134,9 @@ class Debouncer(Generic[_R_co]):
return
try:
if task := self.hass.async_run_hass_job(self._job):
if task := self.hass.async_run_hass_job(
self._job, background=self._background
):
await task
except Exception: # pylint: disable=broad-except
self.logger.exception("Unexpected exception from %s", self.function)
@ -157,13 +163,18 @@ class Debouncer(Generic[_R_co]):
def _on_debounce(self) -> None:
"""Create job task, but only if pending."""
self._timer_task = None
if self._execute_at_end_of_timer:
self._execute_at_end_of_timer = False
if not self._execute_at_end_of_timer:
return
self._execute_at_end_of_timer = False
name = f"debouncer {self._job} finish cooldown={self.cooldown}, immediate={self.immediate}"
if not self._background:
self.hass.async_create_task(
self._handle_timer_finish(),
f"debouncer {self._job} finish cooldown={self.cooldown}, immediate={self.immediate}",
eager_start=True,
self._handle_timer_finish(), name, eager_start=True
)
return
self.hass.async_create_background_task(
self._handle_timer_finish(), name, eager_start=True
)
@callback
def _schedule_timer(self) -> None:

View file

@ -497,3 +497,35 @@ async def test_shutdown(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) -
assert len(calls) == 1
assert debouncer._timer_task is None
async def test_background(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test background tasks are created when background is True."""
calls = []
async def _func() -> None:
await asyncio.sleep(0.1)
calls.append(None)
debouncer = debounce.Debouncer(
hass, _LOGGER, cooldown=0.05, immediate=True, function=_func, background=True
)
await debouncer.async_call()
assert len(calls) == 1
debouncer.async_schedule_call()
assert len(calls) == 1
async_fire_time_changed(hass, utcnow() + timedelta(seconds=1))
await hass.async_block_till_done(wait_background_tasks=False)
assert len(calls) == 1
await hass.async_block_till_done(wait_background_tasks=True)
assert len(calls) == 2
async_fire_time_changed(hass, utcnow() + timedelta(seconds=1))
await hass.async_block_till_done(wait_background_tasks=False)
assert len(calls) == 2