Skip to content

Commit

Permalink
perf: only write state when data changes (#954)
Browse files Browse the repository at this point in the history
* perf: avoid writing state when the controller did not actually update

#948 made me realize that the car data was being polled far less frequently than I thought as the underlying library was caching, but every time the coordinator fired, it would still callback all the listeners and write the state of all the entities which meant we ended up writing state every 10 seconds even if nothing has changed.

Keep track of when the controller was last updated in each entity and if it has not changed, we skip
the state write.

This reduced the number of calls to `async_write_ha_state` by 62% on my production HA instance!

* fix async_added_to_hass was being overridden and forgot to remove in pr
  • Loading branch information
bdraco authored Apr 26, 2024
1 parent 0784cae commit 048e16c
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
6 changes: 5 additions & 1 deletion custom_components/tesla_custom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ def __init__(
self.update_vehicles = update_vehicles
self._debounce_task = None
self._last_update_time = None
self.last_controller_update_time: float | None = None
self.assumed_state = True

update_interval = timedelta(seconds=MIN_SCAN_INTERVAL)
Expand Down Expand Up @@ -477,8 +478,11 @@ async def _async_update_data(self):
raise UpdateFailed(f"Error communicating with API: {err}") from err
else:
if vin := self.vin:
self.last_controller_update_time = controller.get_last_update_time(
vin=vin
)
self.assumed_state = not controller.is_car_online(vin=vin) and (
controller.get_last_update_time(vin=vin)
self.last_controller_update_time
- controller.get_last_wake_up_time(vin=vin)
> controller.update_interval
)
Expand Down
28 changes: 22 additions & 6 deletions custom_components/tesla_custom/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Support for Tesla cars and energy sites."""

from homeassistant.core import callback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import slugify
Expand Down Expand Up @@ -29,12 +30,6 @@ def __init__(
self._attr_name = self.type.capitalize()
self._attr_entity_registry_enabled_default = self._enabled_by_default

async def async_added_to_hass(self) -> None:
"""Register state update callback."""
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)


class TeslaCarEntity(TeslaBaseEntity):
"""Representation of a Tesla car device."""
Expand All @@ -61,6 +56,27 @@ def __init__(
model=car.car_type,
sw_version=car.car_version,
)
self._last_update_success: bool | None = None
self._last_controller_update_time: float | None = None

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
prev_last_update_success = self._last_update_success
prev_last_controller_update_time = self._last_controller_update_time
coordinator = self.coordinator
current_last_update_success = coordinator.last_update_success
current_last_controller_update_time = coordinator.last_controller_update_time
self._last_update_success = current_last_update_success
self._last_controller_update_time = current_last_controller_update_time
if (
prev_last_update_success == current_last_update_success
and prev_last_controller_update_time == current_last_controller_update_time
):
# If there was no change in the last update success or time,
# avoid writing state to prevent unnecessary entity updates.
return
super()._handle_coordinator_update()

async def update_controller(
self, *, wake_if_asleep: bool = False, force: bool = True, blocking: bool = True
Expand Down

0 comments on commit 048e16c

Please sign in to comment.