mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Fix handling of missing period statistics in Anglian Water coordinator (#167427)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -92,6 +92,7 @@ class AnglianWaterUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
_LOGGER.debug("Updating statistics for the first time")
|
||||
usage_sum = 0.0
|
||||
last_stats_time = None
|
||||
allow_update_last_stored_hour = False
|
||||
else:
|
||||
if not meter.readings or len(meter.readings) == 0:
|
||||
_LOGGER.debug("No recent usage statistics found, skipping update")
|
||||
@@ -107,6 +108,7 @@ class AnglianWaterUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
continue
|
||||
start = dt_util.as_local(parsed_read_at) - timedelta(hours=1)
|
||||
_LOGGER.debug("Getting statistics at %s", start)
|
||||
stats: dict[str, list[Any]] = {}
|
||||
for end in (start + timedelta(seconds=1), None):
|
||||
stats = await get_instance(self.hass).async_add_executor_job(
|
||||
statistics_during_period,
|
||||
@@ -127,15 +129,28 @@ class AnglianWaterUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
"Not found, trying to find oldest statistic after %s",
|
||||
start,
|
||||
)
|
||||
assert stats
|
||||
|
||||
def _safe_get_sum(records: list[Any]) -> float:
|
||||
if records and "sum" in records[0]:
|
||||
return float(records[0]["sum"])
|
||||
return 0.0
|
||||
if not stats or not stats.get(usage_statistic_id):
|
||||
_LOGGER.debug(
|
||||
"Could not find existing statistics during period lookup for %s, "
|
||||
"falling back to last stored statistic",
|
||||
usage_statistic_id,
|
||||
)
|
||||
allow_update_last_stored_hour = True
|
||||
last_records = last_stat[usage_statistic_id]
|
||||
usage_sum = float(last_records[0].get("sum") or 0.0)
|
||||
last_stats_time = last_records[0]["start"]
|
||||
else:
|
||||
allow_update_last_stored_hour = False
|
||||
records = stats[usage_statistic_id]
|
||||
|
||||
usage_sum = _safe_get_sum(stats.get(usage_statistic_id, []))
|
||||
last_stats_time = stats[usage_statistic_id][0]["start"]
|
||||
def _safe_get_sum(records: list[Any]) -> float:
|
||||
if records and "sum" in records[0]:
|
||||
return float(records[0]["sum"])
|
||||
return 0.0
|
||||
|
||||
usage_sum = _safe_get_sum(records)
|
||||
last_stats_time = records[0]["start"]
|
||||
|
||||
usage_statistics = []
|
||||
|
||||
@@ -148,7 +163,13 @@ class AnglianWaterUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
)
|
||||
continue
|
||||
start = dt_util.as_local(parsed_read_at) - timedelta(hours=1)
|
||||
if last_stats_time is not None and start.timestamp() <= last_stats_time:
|
||||
if last_stats_time is not None and (
|
||||
start.timestamp() < last_stats_time
|
||||
or (
|
||||
start.timestamp() == last_stats_time
|
||||
and not allow_update_last_stored_hour
|
||||
)
|
||||
):
|
||||
continue
|
||||
usage_state = max(0, read["consumption"] / 1000)
|
||||
usage_sum = max(0, read["read"])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Tests for the Anglian Water coordinator."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
from datetime import timedelta
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from pyanglianwater.meter import SmartMeter
|
||||
import pytest
|
||||
@@ -162,3 +163,92 @@ async def test_coordinator_invalid_readings(
|
||||
"Could not parse read_at time also-invalid-date, skipping reading"
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
|
||||
async def test_coordinator_subsequent_run_missing_period_statistics(
|
||||
recorder_mock: Recorder,
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smart_meter: SmartMeter,
|
||||
mock_anglian_water_client: AsyncMock,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test the coordinator handles missing period lookup statistics."""
|
||||
coordinator = AnglianWaterUpdateCoordinator(
|
||||
hass, mock_anglian_water_client, mock_config_entry
|
||||
)
|
||||
await coordinator._async_update_data()
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
# Correct the latest already-stored reading. Fallback should still update
|
||||
# this hour instead of skipping it.
|
||||
mock_smart_meter.readings[-1] = {
|
||||
"read_at": "2024-06-01T14:00:00",
|
||||
"consumption": 35,
|
||||
"read": 70,
|
||||
}
|
||||
|
||||
# Add a new later reading to ensure fallback also accepts newer entries.
|
||||
mock_smart_meter.readings.append(
|
||||
{"read_at": "2024-06-01T15:00:00", "consumption": 20, "read": 90}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.anglian_water.coordinator.statistics_during_period",
|
||||
return_value={},
|
||||
):
|
||||
await coordinator._async_update_data()
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
assert "Could not find existing statistics during period lookup" in caplog.text
|
||||
|
||||
statistic_id = f"anglian_water:{ACCOUNT_NUMBER}_testsn_usage"
|
||||
stats = await hass.async_add_executor_job(
|
||||
get_last_statistics, hass, 1, statistic_id, True, {"sum"}
|
||||
)
|
||||
assert stats[statistic_id][0]["sum"] >= 70
|
||||
|
||||
parsed_read_at = dt_util.parse_datetime("2024-06-01T14:00:00")
|
||||
assert parsed_read_at is not None
|
||||
corrected_start = dt_util.as_local(parsed_read_at) - timedelta(hours=1)
|
||||
|
||||
corrected_stats = await hass.async_add_executor_job(
|
||||
statistics_during_period,
|
||||
hass,
|
||||
corrected_start,
|
||||
corrected_start + timedelta(seconds=1),
|
||||
{
|
||||
statistic_id,
|
||||
},
|
||||
"hour",
|
||||
None,
|
||||
{"sum"},
|
||||
)
|
||||
assert corrected_stats[statistic_id][0]["sum"] == 70
|
||||
|
||||
|
||||
async def test_coordinator_period_statistics_without_sum(
|
||||
recorder_mock: Recorder,
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_anglian_water_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test period lookup records without sum are handled safely."""
|
||||
coordinator = AnglianWaterUpdateCoordinator(
|
||||
hass, mock_anglian_water_client, mock_config_entry
|
||||
)
|
||||
await coordinator._async_update_data()
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
statistic_id = f"anglian_water:{ACCOUNT_NUMBER}_testsn_usage"
|
||||
with patch(
|
||||
"homeassistant.components.anglian_water.coordinator.statistics_during_period",
|
||||
return_value={statistic_id: [{"start": 0.0}]},
|
||||
):
|
||||
await coordinator._async_update_data()
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
stats = await hass.async_add_executor_job(
|
||||
get_last_statistics, hass, 1, statistic_id, True, {"sum"}
|
||||
)
|
||||
assert stats[statistic_id]
|
||||
|
||||
Reference in New Issue
Block a user