Fix last_reported_timestamp not being updated when last_reported is changed (#118341)

* Reduce number of calls to last_reported_timestamp

When a state is created, last_update is always the same
as last_reported, and we only update it later if it changes
so we can pre-set the cached property to avoid it being
run when the recorder accesses it later.

* fix cache not being overridden

* coverage
This commit is contained in:
J. Nick Koston 2024-05-28 19:03:19 -10:00 committed by GitHub
parent f3fa843b9d
commit 76aa504e36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 26 additions and 5 deletions

View file

@ -1803,9 +1803,16 @@ class State:
# The recorder or the websocket_api will always call the timestamps,
# so we will set the timestamp values here to avoid the overhead of
# the function call in the property we know will always be called.
self.last_updated_timestamp = self.last_updated.timestamp()
if self.last_changed == self.last_updated:
self.__dict__["last_changed_timestamp"] = self.last_updated_timestamp
last_updated = self.last_updated
last_updated_timestamp = last_updated.timestamp()
self.last_updated_timestamp = last_updated_timestamp
if self.last_changed == last_updated:
self.__dict__["last_changed_timestamp"] = last_updated_timestamp
# If last_reported is the same as last_updated async_set will pass
# the same datetime object for both values so we can use an identity
# check here.
if self.last_reported is last_updated:
self.__dict__["last_reported_timestamp"] = last_updated_timestamp
@cached_property
def name(self) -> str:
@ -1822,8 +1829,6 @@ class State:
@cached_property
def last_reported_timestamp(self) -> float:
"""Timestamp of last report."""
if self.last_reported == self.last_updated:
return self.last_updated_timestamp
return self.last_reported.timestamp()
@cached_property
@ -2282,6 +2287,7 @@ class StateMachine:
# mypy does not understand this is only possible if old_state is not None
old_last_reported = old_state.last_reported # type: ignore[union-attr]
old_state.last_reported = now # type: ignore[union-attr]
old_state.last_reported_timestamp = timestamp # type: ignore[union-attr]
self._bus.async_fire_internal(
EVENT_STATE_REPORTED,
{

View file

@ -3524,3 +3524,18 @@ async def test_set_time_zone_deprecated(hass: HomeAssistant) -> None:
),
):
await hass.config.set_time_zone("America/New_York")
async def test_async_set_updates_last_reported(hass: HomeAssistant) -> None:
"""Test async_set method updates last_reported AND last_reported_timestamp."""
hass.states.async_set("light.bowl", "on", {})
state = hass.states.get("light.bowl")
last_reported = state.last_reported
last_reported_timestamp = state.last_reported_timestamp
for _ in range(2):
hass.states.async_set("light.bowl", "on", {})
assert state.last_reported != last_reported
assert state.last_reported_timestamp != last_reported_timestamp
last_reported = state.last_reported
last_reported_timestamp = state.last_reported_timestamp