diff --git a/homeassistant/helpers/debounce.py b/homeassistant/helpers/debounce.py index 2fe42e19a67..de8f5eb4d53 100644 --- a/homeassistant/helpers/debounce.py +++ b/homeassistant/helpers/debounce.py @@ -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: diff --git a/tests/helpers/test_debounce.py b/tests/helpers/test_debounce.py index 24f243086e5..84b3d19b6d7 100644 --- a/tests/helpers/test_debounce.py +++ b/tests/helpers/test_debounce.py @@ -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