Avoid firing update coordinator callbacks when nothing has changed (#97268)

This commit is contained in:
J. Nick Koston 2023-07-28 12:19:20 -05:00 committed by GitHub
parent 3a54448836
commit 13349e76ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 2 deletions

View file

@ -54,7 +54,12 @@ class BaseDataUpdateCoordinatorProtocol(Protocol):
class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
"""Class to manage fetching data from single endpoint."""
"""Class to manage fetching data from single endpoint.
Setting :attr:`always_update` to ``False`` will cause coordinator to only
callback listeners when data has changed. This requires that the data
implements ``__eq__`` or uses a python object that already does.
"""
def __init__(
self,
@ -65,6 +70,7 @@ class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
update_interval: timedelta | None = None,
update_method: Callable[[], Awaitable[_DataT]] | None = None,
request_refresh_debouncer: Debouncer[Coroutine[Any, Any, None]] | None = None,
always_update: bool = True,
) -> None:
"""Initialize global data updater."""
self.hass = hass
@ -74,6 +80,7 @@ class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
self.update_interval = update_interval
self._shutdown_requested = False
self.config_entry = config_entries.current_entry.get()
self.always_update = always_update
# It's None before the first successful update.
# Components should call async_config_entry_first_refresh
@ -277,7 +284,10 @@ class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
if log_timing := self.logger.isEnabledFor(logging.DEBUG):
start = monotonic()
auth_failed = False
previous_update_success = self.last_update_success
previous_data = self.data
try:
self.data = await self._async_update_data()
@ -371,7 +381,15 @@ class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
if not auth_failed and self._listeners and not self.hass.is_stopping:
self._schedule_refresh()
self.async_update_listeners()
if not self.last_update_success and not previous_update_success:
return
if (
self.always_update
or self.last_update_success != previous_update_success
or previous_data != self.data
):
self.async_update_listeners()
@callback
def async_set_update_error(self, err: Exception) -> None: