diff --git a/homeassistant/components/history/websocket_api.py b/homeassistant/components/history/websocket_api.py index 4be63f29c02..5bc14cd4c02 100644 --- a/homeassistant/components/history/websocket_api.py +++ b/homeassistant/components/history/websocket_api.py @@ -302,13 +302,9 @@ def _history_compressed_state(state: State, no_attributes: bool) -> dict[str, An comp_state: dict[str, Any] = {COMPRESSED_STATE_STATE: state.state} if not no_attributes or state.domain in history.NEED_ATTRIBUTE_DOMAINS: comp_state[COMPRESSED_STATE_ATTRIBUTES] = state.attributes - comp_state[COMPRESSED_STATE_LAST_UPDATED] = dt_util.utc_to_timestamp( - state.last_updated - ) + comp_state[COMPRESSED_STATE_LAST_UPDATED] = state.last_updated_timestamp if state.last_changed != state.last_updated: - comp_state[COMPRESSED_STATE_LAST_CHANGED] = dt_util.utc_to_timestamp( - state.last_changed - ) + comp_state[COMPRESSED_STATE_LAST_CHANGED] = state.last_changed_timestamp return comp_state diff --git a/homeassistant/components/logbook/models.py b/homeassistant/components/logbook/models.py index 04a2458237f..84ae84a3b70 100644 --- a/homeassistant/components/logbook/models.py +++ b/homeassistant/components/logbook/models.py @@ -16,7 +16,6 @@ from homeassistant.components.recorder.models import ( ) from homeassistant.const import ATTR_ICON, EVENT_STATE_CHANGED from homeassistant.core import Context, Event, State, callback -import homeassistant.util.dt as dt_util from homeassistant.util.json import json_loads from homeassistant.util.ulid import ulid_to_bytes @@ -131,7 +130,7 @@ def async_event_to_row(event: Event) -> EventAsRow: context_id_bin=ulid_to_bytes(context.id), context_user_id_bin=uuid_hex_to_bytes_or_none(context.user_id), context_parent_id_bin=ulid_to_bytes_or_none(context.parent_id), - time_fired_ts=dt_util.utc_to_timestamp(event.time_fired), + time_fired_ts=event.time_fired_timestamp, row_id=hash(event), ) # States are prefiltered so we never get states @@ -147,7 +146,7 @@ def async_event_to_row(event: Event) -> EventAsRow: context_id_bin=ulid_to_bytes(context.id), context_user_id_bin=uuid_hex_to_bytes_or_none(context.user_id), context_parent_id_bin=ulid_to_bytes_or_none(context.parent_id), - time_fired_ts=dt_util.utc_to_timestamp(new_state.last_updated), + time_fired_ts=new_state.last_updated_timestamp, row_id=hash(event), icon=new_state.attributes.get(ATTR_ICON), ) diff --git a/homeassistant/components/recorder/db_schema.py b/homeassistant/components/recorder/db_schema.py index b864e104ae6..dff26214d67 100644 --- a/homeassistant/components/recorder/db_schema.py +++ b/homeassistant/components/recorder/db_schema.py @@ -296,7 +296,7 @@ class Events(Base): event_data=None, origin_idx=EVENT_ORIGIN_TO_IDX.get(event.origin), time_fired=None, - time_fired_ts=dt_util.utc_to_timestamp(event.time_fired), + time_fired_ts=event.time_fired_timestamp, context_id=None, context_id_bin=ulid_to_bytes_or_none(event.context.id), context_user_id=None, @@ -495,16 +495,16 @@ class States(Base): # None state means the state was removed from the state machine if state is None: dbstate.state = "" - dbstate.last_updated_ts = dt_util.utc_to_timestamp(event.time_fired) + dbstate.last_updated_ts = event.time_fired_timestamp dbstate.last_changed_ts = None return dbstate dbstate.state = state.state - dbstate.last_updated_ts = dt_util.utc_to_timestamp(state.last_updated) + dbstate.last_updated_ts = state.last_updated_timestamp if state.last_updated == state.last_changed: dbstate.last_changed_ts = None else: - dbstate.last_changed_ts = dt_util.utc_to_timestamp(state.last_changed) + dbstate.last_changed_ts = state.last_changed_timestamp return dbstate diff --git a/homeassistant/components/websocket_api/messages.py b/homeassistant/components/websocket_api/messages.py index 1d3181fcf3a..55144217fdc 100644 --- a/homeassistant/components/websocket_api/messages.py +++ b/homeassistant/components/websocket_api/messages.py @@ -183,9 +183,9 @@ def _state_diff( if old_state.state != new_state.state: additions[COMPRESSED_STATE_STATE] = new_state.state if old_state.last_changed != new_state.last_changed: - additions[COMPRESSED_STATE_LAST_CHANGED] = new_state.last_changed.timestamp() + additions[COMPRESSED_STATE_LAST_CHANGED] = new_state.last_changed_timestamp elif old_state.last_updated != new_state.last_updated: - additions[COMPRESSED_STATE_LAST_UPDATED] = new_state.last_updated.timestamp() + additions[COMPRESSED_STATE_LAST_UPDATED] = new_state.last_updated_timestamp if old_state_context.parent_id != new_state_context.parent_id: additions[COMPRESSED_STATE_CONTEXT] = {"parent_id": new_state_context.parent_id} if old_state_context.user_id != new_state_context.user_id: diff --git a/homeassistant/core.py b/homeassistant/core.py index ebd40330d13..e65273538a8 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1077,6 +1077,11 @@ class Event: if not context.origin_event: context.origin_event = self + @cached_property + def time_fired_timestamp(self) -> float: + """Return time fired as a timestamp.""" + return self.time_fired.timestamp() + @cached_property def _as_dict(self) -> dict[str, Any]: """Create a dict representation of this Event. @@ -1445,6 +1450,16 @@ class State: "_", " " ) + @cached_property + def last_updated_timestamp(self) -> float: + """Timestamp of last update.""" + return self.last_updated.timestamp() + + @cached_property + def last_changed_timestamp(self) -> float: + """Timestamp of last change.""" + return self.last_changed.timestamp() + @cached_property def _as_dict(self) -> dict[str, Any]: """Return a dict representation of the State. @@ -1526,12 +1541,12 @@ class State: COMPRESSED_STATE_STATE: self.state, COMPRESSED_STATE_ATTRIBUTES: self.attributes, COMPRESSED_STATE_CONTEXT: context, - COMPRESSED_STATE_LAST_CHANGED: dt_util.utc_to_timestamp(self.last_changed), + COMPRESSED_STATE_LAST_CHANGED: self.last_changed_timestamp, } if self.last_changed != self.last_updated: - compressed_state[COMPRESSED_STATE_LAST_UPDATED] = dt_util.utc_to_timestamp( - self.last_updated - ) + compressed_state[ + COMPRESSED_STATE_LAST_UPDATED + ] = self.last_updated_timestamp return compressed_state @cached_property diff --git a/tests/test_core.py b/tests/test_core.py index 918f098eab7..c2a5a73e6ee 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -625,6 +625,14 @@ def test_event_eq() -> None: assert event1.as_dict() == event2.as_dict() +def test_event_time_fired_timestamp() -> None: + """Test time_fired_timestamp.""" + now = dt_util.utcnow() + event = ha.Event("some_type", {"some": "attr"}, time_fired=now) + assert event.time_fired_timestamp == now.timestamp() + assert event.time_fired_timestamp == now.timestamp() + + def test_event_json_fragment() -> None: """Test event JSON fragments.""" now = dt_util.utcnow() @@ -2453,6 +2461,23 @@ async def test_state_change_events_context_id_match_state_time( ) +def test_state_timestamps() -> None: + """Test timestamp functions for State.""" + now = dt_util.utcnow() + state = ha.State( + "light.bedroom", + "on", + {"brightness": 100}, + last_changed=now, + last_updated=now, + context=ha.Context(id="1234"), + ) + assert state.last_changed_timestamp == now.timestamp() + assert state.last_changed_timestamp == now.timestamp() + assert state.last_updated_timestamp == now.timestamp() + assert state.last_updated_timestamp == now.timestamp() + + async def test_state_firing_event_matches_context_id_ulid_time( hass: HomeAssistant, ) -> None: