From 8d3f693907fa24a54544e3fd7e017c9ec5c2a652 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 13 Jan 2024 18:40:07 -1000 Subject: [PATCH] Avoid useless time fetch in DataUpdateCoordinator (#107999) * Avoid useless time fetch in DataUpdateCoordinator Since we used the async_call_at helper, it would always call dt_util.utcnow() to feed the _handle_refresh_interval which threw it away. This meant we had to fetch time twice as much as needed for each update * tweak * compat * adjust comment --- homeassistant/helpers/update_coordinator.py | 35 ++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/homeassistant/helpers/update_coordinator.py b/homeassistant/helpers/update_coordinator.py index 606b90e6005669..d8631398db76de 100644 --- a/homeassistant/helpers/update_coordinator.py +++ b/homeassistant/helpers/update_coordinator.py @@ -84,6 +84,7 @@ def __init__( self.logger = logger self.name = name self.update_method = update_method + self._update_interval_seconds: float | None = None self.update_interval = update_interval self._shutdown_requested = False self.config_entry = config_entries.current_entry.get() @@ -212,10 +213,21 @@ def _async_unsub_shutdown(self) -> None: self._unsub_shutdown() self._unsub_shutdown = None + @property + def update_interval(self) -> timedelta | None: + """Interval between updates.""" + return self._update_interval + + @update_interval.setter + def update_interval(self, value: timedelta | None) -> None: + """Set interval between updates.""" + self._update_interval = value + self._update_interval_seconds = value.total_seconds() if value else None + @callback def _schedule_refresh(self) -> None: """Schedule a refresh.""" - if self.update_interval is None: + if self._update_interval_seconds is None: return if self.config_entry and self.config_entry.pref_disable_polling: @@ -225,19 +237,20 @@ def _schedule_refresh(self) -> None: # than the debouncer cooldown, this would cause the debounce to never be called self._async_unsub_refresh() - # We use event.async_call_at because DataUpdateCoordinator does - # not need an exact update interval. - now = self.hass.loop.time() + # We use loop.call_at because DataUpdateCoordinator does + # not need an exact update interval which also avoids + # calling dt_util.utcnow() on every update. + hass = self.hass + loop = hass.loop - next_refresh = int(now) + self._microsecond - next_refresh += self.update_interval.total_seconds() - self._unsub_refresh = event.async_call_at( - self.hass, - self._job, - next_refresh, + next_refresh = ( + int(loop.time()) + self._microsecond + self._update_interval_seconds ) + self._unsub_refresh = loop.call_at( + next_refresh, hass.async_run_hass_job, self._job + ).cancel - async def _handle_refresh_interval(self, _now: datetime) -> None: + async def _handle_refresh_interval(self, _now: datetime | None = None) -> None: """Handle a refresh interval occurrence.""" self._unsub_refresh = None await self._async_refresh(log_failures=True, scheduled=True)