Skip duplicated data when calculating fossil energy consumption (#60599)

This commit is contained in:
Erik Montnemery 2021-12-08 19:59:26 +01:00 committed by GitHub
parent dbe0a801c6
commit 159506262a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 219 additions and 2 deletions

View File

@ -274,14 +274,16 @@ async def ws_get_fossil_energy_consumption(
) -> dict[datetime, float]:
"""Combine multiple statistics, returns a dict indexed by start time."""
result: defaultdict[datetime, float] = defaultdict(float)
seen: defaultdict[datetime, set[str]] = defaultdict(set)
for statistics_id, stat in stats.items():
if statistics_id not in statistic_ids:
continue
for period in stat:
if period["sum"] is None:
if period["sum"] is None or statistics_id in seen[period["start"]]:
continue
result[period["start"]] += period["sum"]
seen[period["start"]].add(statistics_id)
return {key: result[key] for key in sorted(result)}

View File

@ -1,9 +1,10 @@
"""Test the Energy websocket API."""
from unittest.mock import AsyncMock, Mock
from unittest.mock import AsyncMock, Mock, patch
import pytest
from homeassistant.components.energy import data, is_configured
from homeassistant.components.recorder import statistics
from homeassistant.components.recorder.statistics import async_add_external_statistics
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
@ -963,6 +964,220 @@ async def test_fossil_energy_consumption(hass, hass_ws_client):
}
@pytest.mark.freeze_time("2021-08-01 00:00:00+00:00")
async def test_fossil_energy_consumption_duplicate(hass, hass_ws_client):
"""Test fossil_energy_consumption with co2 sensor data."""
now = dt_util.utcnow()
later = dt_util.as_utc(dt_util.parse_datetime("2022-09-01 00:00:00"))
await hass.async_add_executor_job(init_recorder_component, hass)
await async_setup_component(hass, "history", {})
await async_setup_component(hass, "sensor", {})
period1 = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00"))
period2 = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 23:00:00"))
period2_day_start = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 00:00:00"))
period3 = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00"))
period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00"))
period4_day_start = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 00:00:00"))
external_energy_statistics_1 = (
{
"start": period1,
"last_reset": None,
"state": 0,
"sum": 2,
},
{
"start": period2,
"last_reset": None,
"state": 1,
"sum": 3,
},
{
"start": period3,
"last_reset": None,
"state": 2,
"sum": 4,
},
{
"start": period4,
"last_reset": None,
"state": 3,
"sum": 5,
},
{
"start": period4,
"last_reset": None,
"state": 3,
"sum": 5,
},
)
external_energy_metadata_1 = {
"has_mean": False,
"has_sum": True,
"name": "Total imported energy",
"source": "test",
"statistic_id": "test:total_energy_import_tariff_1",
"unit_of_measurement": "kWh",
}
external_energy_statistics_2 = (
{
"start": period1,
"last_reset": None,
"state": 0,
"sum": 20,
},
{
"start": period2,
"last_reset": None,
"state": 1,
"sum": 30,
},
{
"start": period3,
"last_reset": None,
"state": 2,
"sum": 40,
},
{
"start": period4,
"last_reset": None,
"state": 3,
"sum": 50,
},
{
"start": period4,
"last_reset": None,
"state": 3,
"sum": 50,
},
)
external_energy_metadata_2 = {
"has_mean": False,
"has_sum": True,
"name": "Total imported energy",
"source": "test",
"statistic_id": "test:total_energy_import_tariff_2",
"unit_of_measurement": "kWh",
}
external_co2_statistics = (
{
"start": period1,
"last_reset": None,
"mean": 10,
},
{
"start": period2,
"last_reset": None,
"mean": 30,
},
{
"start": period3,
"last_reset": None,
"mean": 60,
},
{
"start": period4,
"last_reset": None,
"mean": 90,
},
)
external_co2_metadata = {
"has_mean": True,
"has_sum": False,
"name": "Fossil percentage",
"source": "test",
"statistic_id": "test:fossil_percentage",
"unit_of_measurement": "%",
}
with patch.object(
statistics, "_statistics_exists", return_value=False
), patch.object(
statistics, "_insert_statistics", wraps=statistics._insert_statistics
) as insert_statistics_mock:
async_add_external_statistics(
hass, external_energy_metadata_1, external_energy_statistics_1
)
async_add_external_statistics(
hass, external_energy_metadata_2, external_energy_statistics_2
)
async_add_external_statistics(
hass, external_co2_metadata, external_co2_statistics
)
await async_wait_recording_done_without_instance(hass)
assert insert_statistics_mock.call_count == 14
client = await hass_ws_client()
await client.send_json(
{
"id": 1,
"type": "energy/fossil_energy_consumption",
"start_time": now.isoformat(),
"end_time": later.isoformat(),
"energy_statistic_ids": [
"test:total_energy_import_tariff_1",
"test:total_energy_import_tariff_2",
],
"co2_statistic_id": "test:fossil_percentage",
"period": "hour",
}
)
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
period2.isoformat(): pytest.approx((33.0 - 22.0) * 0.3),
period3.isoformat(): pytest.approx((44.0 - 33.0) * 0.6),
period4.isoformat(): pytest.approx((55.0 - 44.0) * 0.9),
}
await client.send_json(
{
"id": 2,
"type": "energy/fossil_energy_consumption",
"start_time": now.isoformat(),
"end_time": later.isoformat(),
"energy_statistic_ids": [
"test:total_energy_import_tariff_1",
"test:total_energy_import_tariff_2",
],
"co2_statistic_id": "test:fossil_percentage",
"period": "day",
}
)
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
period2_day_start.isoformat(): pytest.approx((33.0 - 22.0) * 0.3),
period3.isoformat(): pytest.approx((44.0 - 33.0) * 0.6),
period4_day_start.isoformat(): pytest.approx((55.0 - 44.0) * 0.9),
}
await client.send_json(
{
"id": 3,
"type": "energy/fossil_energy_consumption",
"start_time": now.isoformat(),
"end_time": later.isoformat(),
"energy_statistic_ids": [
"test:total_energy_import_tariff_1",
"test:total_energy_import_tariff_2",
],
"co2_statistic_id": "test:fossil_percentage",
"period": "month",
}
)
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
period1.isoformat(): pytest.approx((33.0 - 22.0) * 0.3),
period3.isoformat(): pytest.approx(
((44.0 - 33.0) * 0.6) + ((55.0 - 44.0) * 0.9)
),
}
async def test_fossil_energy_consumption_checks(hass, hass_ws_client):
"""Test fossil_energy_consumption parameter validation."""
client = await hass_ws_client(hass)