From 65f44bd80b148c802b5ff21a911f13b77b0ab967 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 May 2022 01:03:56 -0500 Subject: [PATCH] Exclude last_changed when same as last_updated for history websocket api (#71886) --- homeassistant/components/recorder/history.py | 8 ++--- homeassistant/components/recorder/models.py | 32 +++++++++----------- tests/components/history/test_init.py | 31 +++++++++++-------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index f434c1d5fe2..7def35ce3ac 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -19,7 +19,7 @@ from sqlalchemy.sql.expression import literal from homeassistant.components import recorder from homeassistant.components.websocket_api.const import ( - COMPRESSED_STATE_LAST_CHANGED, + COMPRESSED_STATE_LAST_UPDATED, COMPRESSED_STATE_STATE, ) from homeassistant.core import HomeAssistant, State, split_entity_id @@ -662,12 +662,12 @@ def _sorted_states_to_dict( _process_timestamp: Callable[ [datetime], float | str ] = process_datetime_to_timestamp - attr_last_changed = COMPRESSED_STATE_LAST_CHANGED + attr_time = COMPRESSED_STATE_LAST_UPDATED attr_state = COMPRESSED_STATE_STATE else: state_class = LazyState # type: ignore[assignment] _process_timestamp = process_timestamp_to_utc_isoformat - attr_last_changed = LAST_CHANGED_KEY + attr_time = LAST_CHANGED_KEY attr_state = STATE_KEY result: dict[str, list[State | dict[str, Any]]] = defaultdict(list) @@ -742,7 +742,7 @@ def _sorted_states_to_dict( # # We use last_updated for for last_changed since its the same # - attr_last_changed: _process_timestamp(row.last_updated), + attr_time: _process_timestamp(row.last_updated), } ) prev_state = state diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 4c3832c4fc0..d64d85f3ce4 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -798,23 +798,21 @@ def row_to_compressed_state( start_time: datetime | None = None, ) -> dict[str, Any]: """Convert a database row to a compressed state.""" - if start_time: - last_changed = last_updated = start_time.timestamp() - else: - row_last_updated: datetime = row.last_updated - if ( - not (row_changed_changed := row.last_changed) - or row_last_updated == row_changed_changed - ): - last_changed = last_updated = process_datetime_to_timestamp( - row_last_updated - ) - else: - last_changed = process_datetime_to_timestamp(row_changed_changed) - last_updated = process_datetime_to_timestamp(row_last_updated) - return { + comp_state = { COMPRESSED_STATE_STATE: row.state, COMPRESSED_STATE_ATTRIBUTES: decode_attributes_from_row(row, attr_cache), - COMPRESSED_STATE_LAST_CHANGED: last_changed, - COMPRESSED_STATE_LAST_UPDATED: last_updated, } + if start_time: + comp_state[COMPRESSED_STATE_LAST_UPDATED] = start_time.timestamp() + else: + row_last_updated: datetime = row.last_updated + comp_state[COMPRESSED_STATE_LAST_UPDATED] = process_datetime_to_timestamp( + row_last_updated + ) + if ( + row_changed_changed := row.last_changed + ) and row_last_updated != row_changed_changed: + comp_state[COMPRESSED_STATE_LAST_CHANGED] = process_datetime_to_timestamp( + row_changed_changed + ) + return comp_state diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index bcbab1e21ca..23e0550d6aa 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -1133,11 +1133,12 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock): assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert "a" not in sensor_test_history[1] assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[2]["s"] == "on" assert sensor_test_history[2]["a"] == {} @@ -1163,10 +1164,11 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock): assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"any": "attr"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"any": "attr"} assert sensor_test_history[4]["s"] == "on" @@ -1193,10 +1195,11 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock): assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"any": "attr"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"any": "attr"} assert sensor_test_history[2]["s"] == "on" @@ -1331,11 +1334,11 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert "a" in sensor_test_history[1] assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[4]["s"] == "on" assert sensor_test_history[4]["a"] == {} @@ -1361,10 +1364,11 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"temperature": "1"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"temperature": "2"} assert sensor_test_history[4]["s"] == "on" @@ -1391,10 +1395,11 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"temperature": "1"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"temperature": "2"} assert sensor_test_history[2]["s"] == "off" @@ -1429,7 +1434,7 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"temperature": "5"} assert sensor_test_history[0]["lu"] == later.timestamp() - assert sensor_test_history[0]["lc"] == later.timestamp() + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) async def test_history_during_period_bad_start_time(