diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 8d077e19344..691fc58c609 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -1244,7 +1244,7 @@ def _first_statistic( table: type[StatisticsBase], metadata_id: int, ) -> datetime | None: - """Return the data of the oldest statistic row for a given metadata id.""" + """Return the date of the oldest statistic row for a given metadata id.""" stmt = lambda_stmt( lambda: select(table.start_ts) .filter(table.metadata_id == metadata_id) @@ -1256,6 +1256,23 @@ def _first_statistic( return None +def _last_statistic( + session: Session, + table: type[StatisticsBase], + metadata_id: int, +) -> datetime | None: + """Return the date of the newest statistic row for a given metadata id.""" + stmt = lambda_stmt( + lambda: select(table.start_ts) + .filter(table.metadata_id == metadata_id) + .order_by(table.start_ts.desc()) + .limit(1) + ) + if stats := cast(Sequence[Row], execute_stmt_lambda_element(session, stmt)): + return dt_util.utc_from_timestamp(stats[0].start_ts) + return None + + def _get_oldest_sum_statistic( session: Session, head_start_time: datetime | None, @@ -1486,7 +1503,11 @@ def statistic_during_period( tail_start_time: datetime | None = None tail_end_time: datetime | None = None if end_time is None: - tail_start_time = now.replace(minute=0, second=0, microsecond=0) + tail_start_time = _last_statistic(session, Statistics, metadata_id) + if tail_start_time: + tail_start_time += Statistics.duration + else: + tail_start_time = now.replace(minute=0, second=0, microsecond=0) elif tail_only: tail_start_time = start_time tail_end_time = end_time diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index 0dd9241776d..d915eeeeeb6 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -7,6 +7,7 @@ import threading from unittest.mock import ANY, patch from freezegun import freeze_time +from freezegun.api import FrozenDateTimeFactory import pytest from homeassistant.components import recorder @@ -794,17 +795,30 @@ async def test_statistic_during_period_hole( } -@pytest.mark.freeze_time(datetime.datetime(2022, 10, 21, 6, 31, tzinfo=datetime.UTC)) +@pytest.mark.parametrize( + "frozen_time", + [ + # This is the normal case, all statistics runs are available + datetime.datetime(2022, 10, 21, 6, 31, tzinfo=datetime.UTC), + # Statistic only available up until 6:25, this can happen if + # core has been shut down for an hour + datetime.datetime(2022, 10, 21, 7, 31, tzinfo=datetime.UTC), + ], +) async def test_statistic_during_period_partial_overlap( recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator, + freezer: FrozenDateTimeFactory, + frozen_time: datetime, ) -> None: """Test statistic_during_period.""" + client = await hass_ws_client() + + freezer.move_to(frozen_time) now = dt_util.utcnow() await async_recorder_block_till_done(hass) - client = await hass_ws_client() zero = now start = zero.replace(hour=0, minute=0, second=0, microsecond=0)