From 8e8fa0399e1d4b799c6978d381a2e187624deff4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Jun 2022 10:04:46 -1000 Subject: [PATCH] Fix statistics_during_period being incorrectly cached (#72947) --- .../components/recorder/statistics.py | 7 +- tests/components/history/test_init.py | 136 ++++++++++++++++++ 2 files changed, 138 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 4bed39fee4a..39fcb954ee9 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -984,7 +984,6 @@ def _reduce_statistics_per_month( def _statistics_during_period_stmt( start_time: datetime, end_time: datetime | None, - statistic_ids: list[str] | None, metadata_ids: list[int] | None, table: type[Statistics | StatisticsShortTerm], ) -> StatementLambdaElement: @@ -1002,7 +1001,7 @@ def _statistics_during_period_stmt( if end_time is not None: stmt += lambda q: q.filter(table.start < end_time) - if statistic_ids is not None: + if metadata_ids: stmt += lambda q: q.filter(table.metadata_id.in_(metadata_ids)) stmt += lambda q: q.order_by(table.metadata_id, table.start) @@ -1038,9 +1037,7 @@ def statistics_during_period( else: table = Statistics - stmt = _statistics_during_period_stmt( - start_time, end_time, statistic_ids, metadata_ids, table - ) + stmt = _statistics_during_period_stmt(start_time, end_time, metadata_ids, table) stats = execute_stmt_lambda_element(session, stmt) if not stats: diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 9dc7af59a38..8c0a80719a8 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -5,6 +5,7 @@ from http import HTTPStatus import json from unittest.mock import patch, sentinel +from freezegun import freeze_time import pytest from pytest import approx @@ -928,6 +929,141 @@ async def test_statistics_during_period( } +@pytest.mark.parametrize( + "units, attributes, state, value", + [ + (IMPERIAL_SYSTEM, POWER_SENSOR_ATTRIBUTES, 10, 10000), + (METRIC_SYSTEM, POWER_SENSOR_ATTRIBUTES, 10, 10000), + (IMPERIAL_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, 10, 50), + (METRIC_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, 10, 10), + (IMPERIAL_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, 1000, 14.503774389728312), + (METRIC_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, 1000, 100000), + ], +) +async def test_statistics_during_period_in_the_past( + hass, hass_ws_client, recorder_mock, units, attributes, state, value +): + """Test statistics_during_period in the past.""" + hass.config.set_time_zone("UTC") + now = dt_util.utcnow().replace() + + hass.config.units = units + await async_setup_component(hass, "history", {}) + await async_setup_component(hass, "sensor", {}) + await async_recorder_block_till_done(hass) + + past = now - timedelta(days=3) + + with freeze_time(past): + hass.states.async_set("sensor.test", state, attributes=attributes) + await async_wait_recording_done(hass) + + sensor_state = hass.states.get("sensor.test") + assert sensor_state.last_updated == past + + stats_top_of_hour = past.replace(minute=0, second=0, microsecond=0) + stats_start = past.replace(minute=55) + do_adhoc_statistics(hass, start=stats_start) + await async_wait_recording_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "history/statistics_during_period", + "start_time": now.isoformat(), + "end_time": now.isoformat(), + "statistic_ids": ["sensor.test"], + "period": "hour", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == {} + + await client.send_json( + { + "id": 2, + "type": "history/statistics_during_period", + "start_time": now.isoformat(), + "statistic_ids": ["sensor.test"], + "period": "5minute", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == {} + + past = now - timedelta(days=3) + await client.send_json( + { + "id": 3, + "type": "history/statistics_during_period", + "start_time": past.isoformat(), + "statistic_ids": ["sensor.test"], + "period": "5minute", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "sensor.test": [ + { + "statistic_id": "sensor.test", + "start": stats_start.isoformat(), + "end": (stats_start + timedelta(minutes=5)).isoformat(), + "mean": approx(value), + "min": approx(value), + "max": approx(value), + "last_reset": None, + "state": None, + "sum": None, + } + ] + } + + start_of_day = stats_top_of_hour.replace(hour=0, minute=0) + await client.send_json( + { + "id": 4, + "type": "history/statistics_during_period", + "start_time": stats_top_of_hour.isoformat(), + "statistic_ids": ["sensor.test"], + "period": "day", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "sensor.test": [ + { + "statistic_id": "sensor.test", + "start": start_of_day.isoformat(), + "end": (start_of_day + timedelta(days=1)).isoformat(), + "mean": approx(value), + "min": approx(value), + "max": approx(value), + "last_reset": None, + "state": None, + "sum": None, + } + ] + } + + await client.send_json( + { + "id": 5, + "type": "history/statistics_during_period", + "start_time": now.isoformat(), + "statistic_ids": ["sensor.test"], + "period": "5minute", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == {} + + async def test_statistics_during_period_bad_start_time( hass, hass_ws_client, recorder_mock ):