Deprecate history integration's statistics API (#78056)
This commit is contained in:
parent
c528a2d2cd
commit
7937bfeedb
4 changed files with 535 additions and 440 deletions
|
@ -5,24 +5,24 @@ from http import HTTPStatus
|
|||
import json
|
||||
from unittest.mock import patch, sentinel
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
from pytest import approx
|
||||
|
||||
from homeassistant.components import history
|
||||
from homeassistant.components.recorder.history import get_significant_states
|
||||
from homeassistant.components.recorder.models import process_timestamp
|
||||
from homeassistant.components.recorder.websocket_api import (
|
||||
ws_handle_get_statistics_during_period,
|
||||
ws_handle_list_statistic_ids,
|
||||
)
|
||||
from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE
|
||||
import homeassistant.core as ha
|
||||
from homeassistant.helpers.json import JSONEncoder
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
|
||||
|
||||
from tests.components.recorder.common import (
|
||||
async_recorder_block_till_done,
|
||||
async_wait_recording_done,
|
||||
do_adhoc_statistics,
|
||||
wait_recording_done,
|
||||
)
|
||||
|
||||
|
@ -844,51 +844,13 @@ async def test_entity_ids_limit_via_api_with_skip_initial_state(
|
|||
assert response_json[1][0]["entity_id"] == "light.cow"
|
||||
|
||||
|
||||
POWER_SENSOR_ATTRIBUTES = {
|
||||
"device_class": "power",
|
||||
"state_class": "measurement",
|
||||
"unit_of_measurement": "kW",
|
||||
}
|
||||
PRESSURE_SENSOR_ATTRIBUTES = {
|
||||
"device_class": "pressure",
|
||||
"state_class": "measurement",
|
||||
"unit_of_measurement": "hPa",
|
||||
}
|
||||
TEMPERATURE_SENSOR_ATTRIBUTES = {
|
||||
"device_class": "temperature",
|
||||
"state_class": "measurement",
|
||||
"unit_of_measurement": "°C",
|
||||
}
|
||||
|
||||
|
||||
@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(
|
||||
hass, hass_ws_client, recorder_mock, units, attributes, state, value
|
||||
):
|
||||
"""Test statistics_during_period."""
|
||||
async def test_statistics_during_period(hass, hass_ws_client, recorder_mock, caplog):
|
||||
"""Test history/statistics_during_period forwards to recorder."""
|
||||
now = dt_util.utcnow()
|
||||
|
||||
hass.config.units = units
|
||||
await async_setup_component(hass, "history", {})
|
||||
await async_setup_component(hass, "sensor", {})
|
||||
await async_recorder_block_till_done(hass)
|
||||
hass.states.async_set("sensor.test", state, attributes=attributes)
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
do_adhoc_statistics(hass, start=now)
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
client = await hass_ws_client()
|
||||
|
||||
# Test the WS API works and issues a warning
|
||||
await client.send_json(
|
||||
{
|
||||
"id": 1,
|
||||
|
@ -903,322 +865,53 @@ async def test_statistics_during_period(
|
|||
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"] == {
|
||||
"sensor.test": [
|
||||
assert (
|
||||
"WS API 'history/statistics_during_period' is deprecated and will be removed in "
|
||||
"Home Assistant Core 2022.12. Use 'recorder/statistics_during_period' instead"
|
||||
) in caplog.text
|
||||
|
||||
# Test the WS API forwards to recorder
|
||||
with patch(
|
||||
"homeassistant.components.history.recorder_ws.ws_handle_get_statistics_during_period",
|
||||
wraps=ws_handle_get_statistics_during_period,
|
||||
) as ws_mock:
|
||||
await client.send_json(
|
||||
{
|
||||
"statistic_id": "sensor.test",
|
||||
"start": now.isoformat(),
|
||||
"end": (now + timedelta(minutes=5)).isoformat(),
|
||||
"mean": approx(value),
|
||||
"min": approx(value),
|
||||
"max": approx(value),
|
||||
"last_reset": None,
|
||||
"state": None,
|
||||
"sum": None,
|
||||
"id": 2,
|
||||
"type": "history/statistics_during_period",
|
||||
"start_time": now.isoformat(),
|
||||
"end_time": now.isoformat(),
|
||||
"statistic_ids": ["sensor.test"],
|
||||
"period": "hour",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
await client.receive_json()
|
||||
ws_mock.assert_awaited_once()
|
||||
|
||||
|
||||
@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
|
||||
async def test_list_statistic_ids(hass, hass_ws_client, recorder_mock, caplog):
|
||||
"""Test history/list_statistic_ids forwards to recorder."""
|
||||
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, hours=1)
|
||||
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
|
||||
):
|
||||
"""Test statistics_during_period."""
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"history",
|
||||
{"history": {}},
|
||||
)
|
||||
|
||||
client = await hass_ws_client()
|
||||
await client.send_json(
|
||||
{
|
||||
"id": 1,
|
||||
"type": "history/statistics_during_period",
|
||||
"start_time": "cats",
|
||||
"period": "5minute",
|
||||
}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert not response["success"]
|
||||
assert response["error"]["code"] == "invalid_start_time"
|
||||
|
||||
|
||||
async def test_statistics_during_period_bad_end_time(
|
||||
hass, hass_ws_client, recorder_mock
|
||||
):
|
||||
"""Test statistics_during_period."""
|
||||
now = dt_util.utcnow()
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"history",
|
||||
{"history": {}},
|
||||
)
|
||||
|
||||
client = await hass_ws_client()
|
||||
await client.send_json(
|
||||
{
|
||||
"id": 1,
|
||||
"type": "history/statistics_during_period",
|
||||
"start_time": now.isoformat(),
|
||||
"end_time": "dogs",
|
||||
"period": "5minute",
|
||||
}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert not response["success"]
|
||||
assert response["error"]["code"] == "invalid_end_time"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"units, attributes, display_unit, statistics_unit",
|
||||
[
|
||||
(IMPERIAL_SYSTEM, POWER_SENSOR_ATTRIBUTES, "W", "W"),
|
||||
(METRIC_SYSTEM, POWER_SENSOR_ATTRIBUTES, "W", "W"),
|
||||
(IMPERIAL_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, "°F", "°C"),
|
||||
(METRIC_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, "°C", "°C"),
|
||||
(IMPERIAL_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, "psi", "Pa"),
|
||||
(METRIC_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, "Pa", "Pa"),
|
||||
],
|
||||
)
|
||||
async def test_list_statistic_ids(
|
||||
hass,
|
||||
hass_ws_client,
|
||||
recorder_mock,
|
||||
units,
|
||||
attributes,
|
||||
display_unit,
|
||||
statistics_unit,
|
||||
):
|
||||
"""Test list_statistic_ids."""
|
||||
now = dt_util.utcnow()
|
||||
|
||||
hass.config.units = units
|
||||
await async_setup_component(hass, "history", {"history": {}})
|
||||
await async_setup_component(hass, "sensor", {})
|
||||
await async_recorder_block_till_done(hass)
|
||||
|
||||
client = await hass_ws_client()
|
||||
# Test the WS API works and issues a warning
|
||||
await client.send_json({"id": 1, "type": "history/list_statistic_ids"})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == []
|
||||
|
||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
||||
await async_wait_recording_done(hass)
|
||||
assert (
|
||||
"WS API 'history/list_statistic_ids' is deprecated and will be removed in "
|
||||
"Home Assistant Core 2022.12. Use 'recorder/list_statistic_ids' instead"
|
||||
) in caplog.text
|
||||
|
||||
await client.send_json({"id": 2, "type": "history/list_statistic_ids"})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == [
|
||||
{
|
||||
"statistic_id": "sensor.test",
|
||||
"has_mean": True,
|
||||
"has_sum": False,
|
||||
"name": None,
|
||||
"source": "recorder",
|
||||
"display_unit_of_measurement": display_unit,
|
||||
"statistics_unit_of_measurement": statistics_unit,
|
||||
}
|
||||
]
|
||||
|
||||
do_adhoc_statistics(hass, start=now)
|
||||
await async_recorder_block_till_done(hass)
|
||||
# Remove the state, statistics will now be fetched from the database
|
||||
hass.states.async_remove("sensor.test")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await client.send_json({"id": 3, "type": "history/list_statistic_ids"})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == [
|
||||
{
|
||||
"statistic_id": "sensor.test",
|
||||
"has_mean": True,
|
||||
"has_sum": False,
|
||||
"name": None,
|
||||
"source": "recorder",
|
||||
"display_unit_of_measurement": display_unit,
|
||||
"statistics_unit_of_measurement": statistics_unit,
|
||||
}
|
||||
]
|
||||
|
||||
await client.send_json(
|
||||
{"id": 4, "type": "history/list_statistic_ids", "statistic_type": "dogs"}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert not response["success"]
|
||||
|
||||
await client.send_json(
|
||||
{"id": 5, "type": "history/list_statistic_ids", "statistic_type": "mean"}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == [
|
||||
{
|
||||
"statistic_id": "sensor.test",
|
||||
"has_mean": True,
|
||||
"has_sum": False,
|
||||
"name": None,
|
||||
"source": "recorder",
|
||||
"display_unit_of_measurement": display_unit,
|
||||
"statistics_unit_of_measurement": statistics_unit,
|
||||
}
|
||||
]
|
||||
|
||||
await client.send_json(
|
||||
{"id": 6, "type": "history/list_statistic_ids", "statistic_type": "sum"}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == []
|
||||
with patch(
|
||||
"homeassistant.components.history.recorder_ws.ws_handle_list_statistic_ids",
|
||||
wraps=ws_handle_list_statistic_ids,
|
||||
) as ws_mock:
|
||||
await client.send_json({"id": 2, "type": "history/list_statistic_ids"})
|
||||
await client.receive_json()
|
||||
ws_mock.assert_called_once()
|
||||
|
||||
|
||||
async def test_history_during_period(hass, hass_ws_client, recorder_mock):
|
||||
|
@ -1239,7 +932,6 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock):
|
|||
hass.states.async_set("sensor.test", "on", attributes={"any": "attr"})
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
do_adhoc_statistics(hass, start=now)
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
client = await hass_ws_client()
|
||||
|
@ -1358,8 +1050,6 @@ async def test_history_during_period_impossible_conditions(
|
|||
hass, hass_ws_client, recorder_mock
|
||||
):
|
||||
"""Test history_during_period returns when condition cannot be true."""
|
||||
now = dt_util.utcnow()
|
||||
|
||||
await async_setup_component(hass, "history", {})
|
||||
await async_setup_component(hass, "sensor", {})
|
||||
await async_recorder_block_till_done(hass)
|
||||
|
@ -1374,7 +1064,6 @@ async def test_history_during_period_impossible_conditions(
|
|||
hass.states.async_set("sensor.test", "on", attributes={"any": "attr"})
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
do_adhoc_statistics(hass, start=now)
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
after = dt_util.utcnow()
|
||||
|
@ -1440,7 +1129,6 @@ async def test_history_during_period_significant_domain(
|
|||
hass.states.async_set("climate.test", "on", attributes={"temperature": "5"})
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
do_adhoc_statistics(hass, start=now)
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
client = await hass_ws_client()
|
||||
|
@ -1664,7 +1352,6 @@ async def test_history_during_period_with_use_include_order(
|
|||
hass.states.async_set("switch.excluded", "off", attributes={"any": "again"})
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
do_adhoc_statistics(hass, start=now)
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
client = await hass_ws_client()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue