Skip to content

Commit

Permalink
Avoid useless time fetch in DataUpdateCoordinator (#107999)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bdraco authored Jan 14, 2024
1 parent 9033f1f commit 8d3f693
Showing 1 changed file with 24 additions and 11 deletions.
35 changes: 24 additions & 11 deletions homeassistant/helpers/update_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand All @@ -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)
Expand Down

0 comments on commit 8d3f693

Please sign in to comment.