mirror of
https://github.com/home-assistant/core.git
synced 2025-08-02 10:08:23 +00:00
Use numerical_value when compiling statistics
This commit is contained in:
parent
65324431a4
commit
2f608d8650
@ -6,7 +6,6 @@ from collections.abc import Iterable, MutableMapping
|
|||||||
import datetime
|
import datetime
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import math
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
@ -37,6 +36,7 @@ from homeassistant.util import dt as dt_util
|
|||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_LAST_RESET,
|
ATTR_LAST_RESET,
|
||||||
|
ATTR_NUMERICAL_VALUE,
|
||||||
ATTR_OPTIONS,
|
ATTR_OPTIONS,
|
||||||
ATTR_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -142,14 +142,6 @@ def _equivalent_units(units: set[str | None]) -> bool:
|
|||||||
return len(units) == 1
|
return len(units) == 1
|
||||||
|
|
||||||
|
|
||||||
def _parse_float(state: str) -> float:
|
|
||||||
"""Parse a float string, throw on inf or nan."""
|
|
||||||
fstate = float(state)
|
|
||||||
if math.isnan(fstate) or math.isinf(fstate):
|
|
||||||
raise ValueError
|
|
||||||
return fstate
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_states(
|
def _normalize_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
session: Session,
|
session: Session,
|
||||||
@ -163,9 +155,7 @@ def _normalize_states(
|
|||||||
|
|
||||||
fstates: list[tuple[float, State]] = []
|
fstates: list[tuple[float, State]] = []
|
||||||
for state in entity_history:
|
for state in entity_history:
|
||||||
try:
|
if (fstate := state.attributes.get(ATTR_NUMERICAL_VALUE)) is None:
|
||||||
fstate = _parse_float(state.state)
|
|
||||||
except (ValueError, TypeError): # TypeError to guard for NULL state in DB
|
|
||||||
continue
|
continue
|
||||||
fstates.append((fstate, state))
|
fstates.append((fstate, state))
|
||||||
|
|
||||||
@ -298,7 +288,7 @@ def warn_dip(
|
|||||||
),
|
),
|
||||||
entity_id,
|
entity_id,
|
||||||
f"from integration {domain} " if domain else "",
|
f"from integration {domain} " if domain else "",
|
||||||
state.state,
|
state.attributes[ATTR_NUMERICAL_VALUE],
|
||||||
previous_fstate,
|
previous_fstate,
|
||||||
state.last_updated.isoformat(),
|
state.last_updated.isoformat(),
|
||||||
_suggest_report_issue(hass, entity_id),
|
_suggest_report_issue(hass, entity_id),
|
||||||
@ -319,7 +309,7 @@ def warn_negative(hass: HomeAssistant, entity_id: str, state: State) -> None:
|
|||||||
),
|
),
|
||||||
entity_id,
|
entity_id,
|
||||||
f"from integration {domain} " if domain else "",
|
f"from integration {domain} " if domain else "",
|
||||||
state.state,
|
state.attributes[ATTR_NUMERICAL_VALUE],
|
||||||
state.last_updated.isoformat(),
|
state.last_updated.isoformat(),
|
||||||
_suggest_report_issue(hass, entity_id),
|
_suggest_report_issue(hass, entity_id),
|
||||||
)
|
)
|
||||||
@ -424,6 +414,7 @@ def _compile_statistics( # noqa: C901
|
|||||||
start - datetime.timedelta.resolution,
|
start - datetime.timedelta.resolution,
|
||||||
end,
|
end,
|
||||||
entity_ids=entities_significant_history,
|
entity_ids=entities_significant_history,
|
||||||
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
history_list = {**history_list, **_history_list}
|
history_list = {**history_list, **_history_list}
|
||||||
# If there are no recent state changes, the sensor's state may already be pruned
|
# If there are no recent state changes, the sensor's state may already be pruned
|
||||||
|
@ -55,7 +55,9 @@ def test_compile_hourly_statistics(hass_recorder):
|
|||||||
instance = recorder.get_instance(hass)
|
instance = recorder.get_instance(hass)
|
||||||
setup_component(hass, "sensor", {})
|
setup_component(hass, "sensor", {})
|
||||||
zero, four, states = record_states(hass)
|
zero, four, states = record_states(hass)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
# Should not fail if there is nothing there yet
|
# Should not fail if there is nothing there yet
|
||||||
@ -316,7 +318,9 @@ def test_rename_entity(hass_recorder):
|
|||||||
hass.block_till_done()
|
hass.block_till_done()
|
||||||
|
|
||||||
zero, four, states = record_states(hass)
|
zero, four, states = record_states(hass)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}):
|
for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}):
|
||||||
@ -382,7 +386,9 @@ def test_rename_entity_collision(hass_recorder, caplog):
|
|||||||
hass.block_till_done()
|
hass.block_till_done()
|
||||||
|
|
||||||
zero, four, states = record_states(hass)
|
zero, four, states = record_states(hass)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}):
|
for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}):
|
||||||
@ -447,7 +453,9 @@ def test_statistics_duplicated(hass_recorder, caplog):
|
|||||||
hass = hass_recorder()
|
hass = hass_recorder()
|
||||||
setup_component(hass, "sensor", {})
|
setup_component(hass, "sensor", {})
|
||||||
zero, four, states = record_states(hass)
|
zero, four, states = record_states(hass)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
@ -1709,6 +1717,14 @@ def record_states(hass):
|
|||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
return hass.states.get(entity_id)
|
return hass.states.get(entity_id)
|
||||||
|
|
||||||
|
def set_sensor_state(entity_id, numerical_value, attributes):
|
||||||
|
"""Set the state."""
|
||||||
|
hass.states.set(
|
||||||
|
entity_id, "", attributes={**attributes, "numerical_value": numerical_value}
|
||||||
|
)
|
||||||
|
wait_recording_done(hass)
|
||||||
|
return hass.states.get(entity_id)
|
||||||
|
|
||||||
zero = dt_util.utcnow()
|
zero = dt_util.utcnow()
|
||||||
one = zero + timedelta(seconds=1 * 5)
|
one = zero + timedelta(seconds=1 * 5)
|
||||||
two = one + timedelta(seconds=15 * 5)
|
two = one + timedelta(seconds=15 * 5)
|
||||||
@ -1725,25 +1741,25 @@ def record_states(hass):
|
|||||||
states[mp].append(
|
states[mp].append(
|
||||||
set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt2)})
|
set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt2)})
|
||||||
)
|
)
|
||||||
states[sns1].append(set_state(sns1, "10", attributes=sns1_attr))
|
states[sns1].append(set_sensor_state(sns1, 10, attributes=sns1_attr))
|
||||||
states[sns2].append(set_state(sns2, "10", attributes=sns2_attr))
|
states[sns2].append(set_sensor_state(sns2, 10, attributes=sns2_attr))
|
||||||
states[sns3].append(set_state(sns3, "10", attributes=sns3_attr))
|
states[sns3].append(set_sensor_state(sns3, 10, attributes=sns3_attr))
|
||||||
states[sns4].append(set_state(sns4, "10", attributes=sns4_attr))
|
states[sns4].append(set_sensor_state(sns4, 10, attributes=sns4_attr))
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=two
|
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=two
|
||||||
):
|
):
|
||||||
states[sns1].append(set_state(sns1, "15", attributes=sns1_attr))
|
states[sns1].append(set_sensor_state(sns1, 15, attributes=sns1_attr))
|
||||||
states[sns2].append(set_state(sns2, "15", attributes=sns2_attr))
|
states[sns2].append(set_sensor_state(sns2, 15, attributes=sns2_attr))
|
||||||
states[sns3].append(set_state(sns3, "15", attributes=sns3_attr))
|
states[sns3].append(set_sensor_state(sns3, 15, attributes=sns3_attr))
|
||||||
states[sns4].append(set_state(sns4, "15", attributes=sns4_attr))
|
states[sns4].append(set_sensor_state(sns4, 15, attributes=sns4_attr))
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=three
|
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=three
|
||||||
):
|
):
|
||||||
states[sns1].append(set_state(sns1, "20", attributes=sns1_attr))
|
states[sns1].append(set_sensor_state(sns1, 20, attributes=sns1_attr))
|
||||||
states[sns2].append(set_state(sns2, "20", attributes=sns2_attr))
|
states[sns2].append(set_sensor_state(sns2, 20, attributes=sns2_attr))
|
||||||
states[sns3].append(set_state(sns3, "20", attributes=sns3_attr))
|
states[sns3].append(set_sensor_state(sns3, 20, attributes=sns3_attr))
|
||||||
states[sns4].append(set_state(sns4, "20", attributes=sns4_attr))
|
states[sns4].append(set_sensor_state(sns4, 20, attributes=sns4_attr))
|
||||||
|
|
||||||
return zero, four, states
|
return zero, four, states
|
||||||
|
@ -17,6 +17,7 @@ from homeassistant.components.recorder.statistics import (
|
|||||||
get_metadata,
|
get_metadata,
|
||||||
list_statistic_ids,
|
list_statistic_ids,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.sensor.const import ATTR_NUMERICAL_VALUE
|
||||||
from homeassistant.helpers import recorder as recorder_helper
|
from homeassistant.helpers import recorder as recorder_helper
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
@ -131,7 +132,11 @@ async def test_statistics_during_period(recorder_mock, hass, hass_ws_client):
|
|||||||
hass.config.units = US_CUSTOMARY_SYSTEM
|
hass.config.units = US_CUSTOMARY_SYSTEM
|
||||||
await async_setup_component(hass, "sensor", {})
|
await async_setup_component(hass, "sensor", {})
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set("sensor.test", 10, attributes=POWER_SENSOR_KW_ATTRIBUTES)
|
hass.states.async_set(
|
||||||
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=POWER_SENSOR_KW_ATTRIBUTES | {ATTR_NUMERICAL_VALUE: 10},
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
@ -892,7 +897,9 @@ async def test_statistics_during_period_unit_conversion(
|
|||||||
|
|
||||||
await async_setup_component(hass, "sensor", {})
|
await async_setup_component(hass, "sensor", {})
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set("sensor.test", state, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
@ -983,8 +990,12 @@ async def test_sum_statistics_during_period_unit_conversion(
|
|||||||
|
|
||||||
await async_setup_component(hass, "sensor", {})
|
await async_setup_component(hass, "sensor", {})
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set("sensor.test", 0, attributes=attributes)
|
hass.states.async_set(
|
||||||
hass.states.async_set("sensor.test", state, attributes=attributes)
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 0}
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
@ -1112,7 +1123,11 @@ async def test_statistics_during_period_in_the_past(
|
|||||||
past = now - timedelta(days=3)
|
past = now - timedelta(days=3)
|
||||||
|
|
||||||
with freeze_time(past):
|
with freeze_time(past):
|
||||||
hass.states.async_set("sensor.test", 10, attributes=POWER_SENSOR_KW_ATTRIBUTES)
|
hass.states.async_set(
|
||||||
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=POWER_SENSOR_KW_ATTRIBUTES | {ATTR_NUMERICAL_VALUE: 10},
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
sensor_state = hass.states.get("sensor.test")
|
sensor_state = hass.states.get("sensor.test")
|
||||||
@ -1340,7 +1355,9 @@ async def test_list_statistic_ids(
|
|||||||
assert response["success"]
|
assert response["success"]
|
||||||
assert response["result"] == []
|
assert response["result"] == []
|
||||||
|
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
await client.send_json({"id": 2, "type": "recorder/list_statistic_ids"})
|
await client.send_json({"id": 2, "type": "recorder/list_statistic_ids"})
|
||||||
@ -1503,7 +1520,9 @@ async def test_list_statistic_ids_unit_change(
|
|||||||
assert response["success"]
|
assert response["success"]
|
||||||
assert response["result"] == []
|
assert response["result"] == []
|
||||||
|
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
@ -1526,7 +1545,9 @@ async def test_list_statistic_ids_unit_change(
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Change the state unit
|
# Change the state unit
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes2)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes2 | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
|
|
||||||
await client.send_json({"id": 3, "type": "recorder/list_statistic_ids"})
|
await client.send_json({"id": 3, "type": "recorder/list_statistic_ids"})
|
||||||
response = await client.receive_json()
|
response = await client.receive_json()
|
||||||
@ -1579,9 +1600,15 @@ async def test_clear_statistics(recorder_mock, hass, hass_ws_client):
|
|||||||
hass.config.units = units
|
hass.config.units = units
|
||||||
await async_setup_component(hass, "sensor", {})
|
await async_setup_component(hass, "sensor", {})
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set("sensor.test1", state, attributes=attributes)
|
hass.states.async_set(
|
||||||
hass.states.async_set("sensor.test2", state * 2, attributes=attributes)
|
"sensor.test1", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state}
|
||||||
hass.states.async_set("sensor.test3", state * 3, attributes=attributes)
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.test2", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state * 2}
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.test3", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state * 3}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
@ -1704,7 +1731,9 @@ async def test_update_statistics_metadata(
|
|||||||
hass.config.units = units
|
hass.config.units = units
|
||||||
await async_setup_component(hass, "sensor", {})
|
await async_setup_component(hass, "sensor", {})
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set("sensor.test", state, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, period="hourly", start=now)
|
do_adhoc_statistics(hass, period="hourly", start=now)
|
||||||
@ -1795,7 +1824,9 @@ async def test_change_statistics_unit(recorder_mock, hass, hass_ws_client):
|
|||||||
hass.config.units = units
|
hass.config.units = units
|
||||||
await async_setup_component(hass, "sensor", {})
|
await async_setup_component(hass, "sensor", {})
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set("sensor.test", state, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, period="hourly", start=now)
|
do_adhoc_statistics(hass, period="hourly", start=now)
|
||||||
@ -1968,7 +1999,9 @@ async def test_change_statistics_unit_errors(
|
|||||||
hass.config.units = units
|
hass.config.units = units
|
||||||
await async_setup_component(hass, "sensor", {})
|
await async_setup_component(hass, "sensor", {})
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set("sensor.test", state, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: state}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, period="hourly", start=now)
|
do_adhoc_statistics(hass, period="hourly", start=now)
|
||||||
@ -2311,10 +2344,14 @@ async def test_get_statistics_metadata(
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
hass.states.async_set("sensor.test2", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test2", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
await client.send_json(
|
await client.send_json(
|
||||||
|
@ -25,7 +25,11 @@ from homeassistant.components.recorder.statistics import (
|
|||||||
list_statistic_ids,
|
list_statistic_ids,
|
||||||
)
|
)
|
||||||
from homeassistant.components.recorder.util import get_instance, session_scope
|
from homeassistant.components.recorder.util import get_instance, session_scope
|
||||||
from homeassistant.components.sensor import ATTR_OPTIONS, DOMAIN
|
from homeassistant.components.sensor.const import (
|
||||||
|
ATTR_NUMERICAL_VALUE,
|
||||||
|
ATTR_OPTIONS,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_UNAVAILABLE
|
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_UNAVAILABLE
|
||||||
from homeassistant.core import HomeAssistant, State
|
from homeassistant.core import HomeAssistant, State
|
||||||
from homeassistant.setup import async_setup_component, setup_component
|
from homeassistant.setup import async_setup_component, setup_component
|
||||||
@ -140,7 +144,9 @@ def test_compile_hourly_statistics(
|
|||||||
"unit_of_measurement": state_unit,
|
"unit_of_measurement": state_unit,
|
||||||
}
|
}
|
||||||
four, states = record_states(hass, zero, "sensor.test1", attributes)
|
four, states = record_states(hass, zero, "sensor.test1", attributes)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero)
|
do_adhoc_statistics(hass, start=zero)
|
||||||
@ -202,10 +208,12 @@ def test_compile_hourly_statistics_purged_state_changes(
|
|||||||
"unit_of_measurement": state_unit,
|
"unit_of_measurement": state_unit,
|
||||||
}
|
}
|
||||||
four, states = record_states(hass, zero, "sensor.test1", attributes)
|
four, states = record_states(hass, zero, "sensor.test1", attributes)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
mean = min = max = float(hist["sensor.test1"][-1].state)
|
mean = min = max = hist["sensor.test1"][-1].attributes[ATTR_NUMERICAL_VALUE]
|
||||||
|
|
||||||
# Purge all states from the database
|
# Purge all states from the database
|
||||||
with patch(
|
with patch(
|
||||||
@ -214,7 +222,9 @@ def test_compile_hourly_statistics_purged_state_changes(
|
|||||||
hass.services.call("recorder", "purge", {"keep_days": 0})
|
hass.services.call("recorder", "purge", {"keep_days": 0})
|
||||||
hass.block_till_done()
|
hass.block_till_done()
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert not hist
|
assert not hist
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero)
|
do_adhoc_statistics(hass, start=zero)
|
||||||
@ -283,7 +293,9 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes)
|
|||||||
_, _states = record_states(hass, zero, "sensor.test7", attributes_tmp)
|
_, _states = record_states(hass, zero, "sensor.test7", attributes_tmp)
|
||||||
states = {**states, **_states}
|
states = {**states, **_states}
|
||||||
|
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero)
|
do_adhoc_statistics(hass, start=zero)
|
||||||
@ -474,7 +486,10 @@ async def test_compile_hourly_sum_statistics_amount(
|
|||||||
)
|
)
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
hist = history.get_significant_states(
|
hist = history.get_significant_states(
|
||||||
hass, period0 - timedelta.resolution, eight + timedelta.resolution
|
hass,
|
||||||
|
period0 - timedelta.resolution,
|
||||||
|
eight + timedelta.resolution,
|
||||||
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
assert_multiple_states_equal_without_context_and_last_changed(
|
assert_multiple_states_equal_without_context_and_last_changed(
|
||||||
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
||||||
@ -857,6 +872,11 @@ def test_compile_hourly_sum_statistics_nan_inf_state(
|
|||||||
one + timedelta.resolution,
|
one + timedelta.resolution,
|
||||||
significant_changes_only=False,
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
|
# Recorder will record state attributes with nan and inf replaced with null
|
||||||
|
for state in states["sensor.test1"]:
|
||||||
|
if not math.isfinite(state.attributes[ATTR_NUMERICAL_VALUE]):
|
||||||
|
state.attributes = state.attributes | {ATTR_NUMERICAL_VALUE: None}
|
||||||
|
|
||||||
assert_multiple_states_equal_without_context_and_last_changed(
|
assert_multiple_states_equal_without_context_and_last_changed(
|
||||||
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
||||||
)
|
)
|
||||||
@ -1019,7 +1039,7 @@ def test_compile_hourly_sum_statistics_negative_state(
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
assert "Error while processing event StatisticsTask" not in caplog.text
|
assert "Error while processing event StatisticsTask" not in caplog.text
|
||||||
state = states[entity_id][offending_state].state
|
state = states[entity_id][offending_state].attributes[ATTR_NUMERICAL_VALUE]
|
||||||
last_updated = states[entity_id][offending_state].last_updated.isoformat()
|
last_updated = states[entity_id][offending_state].last_updated.isoformat()
|
||||||
assert (
|
assert (
|
||||||
f"Entity {entity_id} {warning_1}has state class total_increasing, but its state "
|
f"Entity {entity_id} {warning_1}has state class total_increasing, but its state "
|
||||||
@ -1070,7 +1090,10 @@ def test_compile_hourly_sum_statistics_total_no_reset(
|
|||||||
)
|
)
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
hist = history.get_significant_states(
|
hist = history.get_significant_states(
|
||||||
hass, period0 - timedelta.resolution, eight + timedelta.resolution
|
hass,
|
||||||
|
period0 - timedelta.resolution,
|
||||||
|
eight + timedelta.resolution,
|
||||||
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
assert_multiple_states_equal_without_context_and_last_changed(
|
assert_multiple_states_equal_without_context_and_last_changed(
|
||||||
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
||||||
@ -1172,7 +1195,10 @@ def test_compile_hourly_sum_statistics_total_increasing(
|
|||||||
)
|
)
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
hist = history.get_significant_states(
|
hist = history.get_significant_states(
|
||||||
hass, period0 - timedelta.resolution, eight + timedelta.resolution
|
hass,
|
||||||
|
period0 - timedelta.resolution,
|
||||||
|
eight + timedelta.resolution,
|
||||||
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
assert_multiple_states_equal_without_context_and_last_changed(
|
assert_multiple_states_equal_without_context_and_last_changed(
|
||||||
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
||||||
@ -1272,7 +1298,10 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip(
|
|||||||
)
|
)
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
hist = history.get_significant_states(
|
hist = history.get_significant_states(
|
||||||
hass, period0 - timedelta.resolution, eight + timedelta.resolution
|
hass,
|
||||||
|
period0 - timedelta.resolution,
|
||||||
|
eight + timedelta.resolution,
|
||||||
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
assert_multiple_states_equal_without_context_and_last_changed(
|
assert_multiple_states_equal_without_context_and_last_changed(
|
||||||
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
||||||
@ -1288,8 +1317,8 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip(
|
|||||||
) not in caplog.text
|
) not in caplog.text
|
||||||
do_adhoc_statistics(hass, start=period2)
|
do_adhoc_statistics(hass, start=period2)
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
state = states["sensor.test1"][6].state
|
state = states["sensor.test1"][6].attributes[ATTR_NUMERICAL_VALUE]
|
||||||
previous_state = float(states["sensor.test1"][5].state)
|
previous_state = float(states["sensor.test1"][5].attributes[ATTR_NUMERICAL_VALUE])
|
||||||
last_updated = states["sensor.test1"][6].last_updated.isoformat()
|
last_updated = states["sensor.test1"][6].last_updated.isoformat()
|
||||||
assert (
|
assert (
|
||||||
"Entity sensor.test1 has state class total_increasing, but its state is not "
|
"Entity sensor.test1 has state class total_increasing, but its state is not "
|
||||||
@ -1379,7 +1408,10 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog):
|
|||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
|
|
||||||
hist = history.get_significant_states(
|
hist = history.get_significant_states(
|
||||||
hass, period0 - timedelta.resolution, eight + timedelta.resolution
|
hass,
|
||||||
|
period0 - timedelta.resolution,
|
||||||
|
eight + timedelta.resolution,
|
||||||
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
assert_multiple_states_equal_without_context_and_last_changed(
|
assert_multiple_states_equal_without_context_and_last_changed(
|
||||||
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
||||||
@ -1471,7 +1503,10 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
|||||||
states = {**states, **_states}
|
states = {**states, **_states}
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
hist = history.get_significant_states(
|
hist = history.get_significant_states(
|
||||||
hass, period0 - timedelta.resolution, eight + timedelta.resolution
|
hass,
|
||||||
|
period0 - timedelta.resolution,
|
||||||
|
eight + timedelta.resolution,
|
||||||
|
significant_changes_only=False,
|
||||||
)
|
)
|
||||||
assert_multiple_states_equal_without_context_and_last_changed(
|
assert_multiple_states_equal_without_context_and_last_changed(
|
||||||
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
dict(states)["sensor.test1"], dict(hist)["sensor.test1"]
|
||||||
@ -1656,7 +1691,9 @@ def test_compile_hourly_statistics_unchanged(
|
|||||||
"unit_of_measurement": state_unit,
|
"unit_of_measurement": state_unit,
|
||||||
}
|
}
|
||||||
four, states = record_states(hass, zero, "sensor.test1", attributes)
|
four, states = record_states(hass, zero, "sensor.test1", attributes)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=four)
|
do_adhoc_statistics(hass, start=four)
|
||||||
@ -1688,7 +1725,9 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog):
|
|||||||
four, states = record_states_partially_unavailable(
|
four, states = record_states_partially_unavailable(
|
||||||
hass, zero, "sensor.test1", TEMPERATURE_SENSOR_ATTRIBUTES
|
hass, zero, "sensor.test1", TEMPERATURE_SENSOR_ATTRIBUTES
|
||||||
)
|
)
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero)
|
do_adhoc_statistics(hass, start=zero)
|
||||||
@ -1757,7 +1796,9 @@ def test_compile_hourly_statistics_unavailable(
|
|||||||
)
|
)
|
||||||
_, _states = record_states(hass, zero, "sensor.test2", attributes)
|
_, _states = record_states(hass, zero, "sensor.test2", attributes)
|
||||||
states = {**states, **_states}
|
states = {**states, **_states}
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=four)
|
do_adhoc_statistics(hass, start=four)
|
||||||
@ -1965,7 +2006,9 @@ def test_compile_hourly_statistics_changing_units_1(
|
|||||||
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero)
|
do_adhoc_statistics(hass, start=zero)
|
||||||
@ -2075,7 +2118,9 @@ def test_compile_hourly_statistics_changing_units_2(
|
|||||||
hass, zero + timedelta(minutes=5), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=5), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero + timedelta(seconds=30 * 5))
|
do_adhoc_statistics(hass, start=zero + timedelta(seconds=30 * 5))
|
||||||
@ -2143,7 +2188,9 @@ def test_compile_hourly_statistics_changing_units_3(
|
|||||||
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero)
|
do_adhoc_statistics(hass, start=zero)
|
||||||
@ -2291,7 +2338,9 @@ def test_compile_hourly_statistics_convert_units_1(
|
|||||||
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
do_adhoc_statistics(hass, start=zero + timedelta(minutes=10))
|
do_adhoc_statistics(hass, start=zero + timedelta(minutes=10))
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
@ -2383,7 +2432,9 @@ def test_compile_hourly_statistics_equivalent_units_1(
|
|||||||
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero)
|
do_adhoc_statistics(hass, start=zero)
|
||||||
@ -2497,7 +2548,9 @@ def test_compile_hourly_statistics_equivalent_units_2(
|
|||||||
hass, zero + timedelta(minutes=5), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=5), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=zero + timedelta(seconds=30 * 5))
|
do_adhoc_statistics(hass, start=zero + timedelta(seconds=30 * 5))
|
||||||
@ -2612,7 +2665,9 @@ def test_compile_hourly_statistics_changing_device_class_1(
|
|||||||
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
# Run statistics again, additional statistics is generated
|
# Run statistics again, additional statistics is generated
|
||||||
@ -2667,7 +2722,9 @@ def test_compile_hourly_statistics_changing_device_class_1(
|
|||||||
hass, zero + timedelta(minutes=20), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=20), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
# Run statistics again, additional statistics is generated
|
# Run statistics again, additional statistics is generated
|
||||||
@ -2802,7 +2859,9 @@ def test_compile_hourly_statistics_changing_device_class_2(
|
|||||||
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
hass, zero + timedelta(minutes=10), "sensor.test1", attributes
|
||||||
)
|
)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, zero, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, zero, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
# Run statistics again, additional statistics is generated
|
# Run statistics again, additional statistics is generated
|
||||||
@ -2918,7 +2977,9 @@ def test_compile_hourly_statistics_changing_state_class(
|
|||||||
# Add more states, with changed state class
|
# Add more states, with changed state class
|
||||||
four, _states = record_states(hass, period1, "sensor.test1", attributes_2)
|
four, _states = record_states(hass, period1, "sensor.test1", attributes_2)
|
||||||
states["sensor.test1"] += _states["sensor.test1"]
|
states["sensor.test1"] += _states["sensor.test1"]
|
||||||
hist = history.get_significant_states(hass, period0, four)
|
hist = history.get_significant_states(
|
||||||
|
hass, period0, four, significant_changes_only=False
|
||||||
|
)
|
||||||
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
assert_dict_of_states_equal_without_context_and_last_changed(states, hist)
|
||||||
|
|
||||||
do_adhoc_statistics(hass, start=period1)
|
do_adhoc_statistics(hass, start=period1)
|
||||||
@ -3401,9 +3462,11 @@ def record_states(hass, zero, entity_id, attributes, seq=None):
|
|||||||
if seq is None:
|
if seq is None:
|
||||||
seq = [-10, 15, 30]
|
seq = [-10, 15, 30]
|
||||||
|
|
||||||
def set_state(entity_id, state, **kwargs):
|
def set_state(entity_id, numerical_value, attributes):
|
||||||
"""Set the state."""
|
"""Set the state."""
|
||||||
hass.states.set(entity_id, state, **kwargs)
|
hass.states.set(
|
||||||
|
entity_id, "", attributes={**attributes, "numerical_value": numerical_value}
|
||||||
|
)
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
return hass.states.get(entity_id)
|
return hass.states.get(entity_id)
|
||||||
|
|
||||||
@ -3416,23 +3479,17 @@ def record_states(hass, zero, entity_id, attributes, seq=None):
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=one
|
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=one
|
||||||
):
|
):
|
||||||
states[entity_id].append(
|
states[entity_id].append(set_state(entity_id, seq[0], attributes=attributes))
|
||||||
set_state(entity_id, str(seq[0]), attributes=attributes)
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=two
|
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=two
|
||||||
):
|
):
|
||||||
states[entity_id].append(
|
states[entity_id].append(set_state(entity_id, seq[1], attributes=attributes))
|
||||||
set_state(entity_id, str(seq[1]), attributes=attributes)
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=three
|
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=three
|
||||||
):
|
):
|
||||||
states[entity_id].append(
|
states[entity_id].append(set_state(entity_id, seq[2], attributes=attributes))
|
||||||
set_state(entity_id, str(seq[2]), attributes=attributes)
|
|
||||||
)
|
|
||||||
|
|
||||||
return four, states
|
return four, states
|
||||||
|
|
||||||
@ -3504,14 +3561,19 @@ async def test_validate_unit_change_convertible(
|
|||||||
|
|
||||||
# No statistics, unit in state matching device class - empty response
|
# No statistics, unit in state matching device class - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 10, attributes={**attributes, **{"unit_of_measurement": unit}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes | {"unit_of_measurement": unit, ATTR_NUMERICAL_VALUE: 10},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
# No statistics, unit in state not matching device class - empty response
|
# No statistics, unit in state not matching device class - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 11, attributes={**attributes, **{"unit_of_measurement": "dogs"}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": "dogs", ATTR_NUMERICAL_VALUE: 11},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
@ -3520,7 +3582,10 @@ async def test_validate_unit_change_convertible(
|
|||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 12, attributes={**attributes, **{"unit_of_measurement": "dogs"}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": "dogs", ATTR_NUMERICAL_VALUE: 12},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
expected = {
|
expected = {
|
||||||
@ -3540,7 +3605,9 @@ async def test_validate_unit_change_convertible(
|
|||||||
|
|
||||||
# Valid state - empty response
|
# Valid state - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 13, attributes={**attributes, **{"unit_of_measurement": unit}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes | {"unit_of_measurement": unit, ATTR_NUMERICAL_VALUE: 13},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
@ -3552,7 +3619,10 @@ async def test_validate_unit_change_convertible(
|
|||||||
|
|
||||||
# Valid state in compatible unit - empty response
|
# Valid state in compatible unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 13, attributes={**attributes, **{"unit_of_measurement": unit2}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit2, ATTR_NUMERICAL_VALUE: 14},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
@ -3615,7 +3685,9 @@ async def test_validate_statistics_unit_ignore_device_class(
|
|||||||
|
|
||||||
# No statistics, no device class - empty response
|
# No statistics, no device class - empty response
|
||||||
initial_attributes = {"state_class": "measurement", "unit_of_measurement": "dogs"}
|
initial_attributes = {"state_class": "measurement", "unit_of_measurement": "dogs"}
|
||||||
hass.states.async_set("sensor.test", 10, attributes=initial_attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=initial_attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -3623,7 +3695,10 @@ async def test_validate_statistics_unit_ignore_device_class(
|
|||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 12, attributes={**attributes, **{"unit_of_measurement": "dogs"}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=initial_attributes
|
||||||
|
| {"unit_of_measurement": "dogs", ATTR_NUMERICAL_VALUE: 12},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
@ -3703,14 +3778,19 @@ async def test_validate_statistics_unit_change_no_device_class(
|
|||||||
|
|
||||||
# No statistics, sensor state set - empty response
|
# No statistics, sensor state set - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 10, attributes={**attributes, **{"unit_of_measurement": unit}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes | {"unit_of_measurement": unit, ATTR_NUMERICAL_VALUE: 10},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
# No statistics, sensor state set to an incompatible unit - empty response
|
# No statistics, sensor state set to an incompatible unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 11, attributes={**attributes, **{"unit_of_measurement": "dogs"}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": "dogs", ATTR_NUMERICAL_VALUE: 11},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
@ -3719,7 +3799,10 @@ async def test_validate_statistics_unit_change_no_device_class(
|
|||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
do_adhoc_statistics(hass, start=now)
|
do_adhoc_statistics(hass, start=now)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 12, attributes={**attributes, **{"unit_of_measurement": "dogs"}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": "dogs", ATTR_NUMERICAL_VALUE: 12},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
expected = {
|
expected = {
|
||||||
@ -3739,7 +3822,9 @@ async def test_validate_statistics_unit_change_no_device_class(
|
|||||||
|
|
||||||
# Valid state - empty response
|
# Valid state - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 13, attributes={**attributes, **{"unit_of_measurement": unit}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes | {"unit_of_measurement": unit, ATTR_NUMERICAL_VALUE: 13},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
@ -3751,7 +3836,10 @@ async def test_validate_statistics_unit_change_no_device_class(
|
|||||||
|
|
||||||
# Valid state in compatible unit - empty response
|
# Valid state in compatible unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 13, attributes={**attributes, **{"unit_of_measurement": unit2}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit2, ATTR_NUMERICAL_VALUE: 14},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
@ -3810,7 +3898,9 @@ async def test_validate_statistics_unsupported_state_class(
|
|||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
# No statistics, valid state - empty response
|
# No statistics, valid state - empty response
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -3822,7 +3912,9 @@ async def test_validate_statistics_unsupported_state_class(
|
|||||||
# State update with invalid state class, expect error
|
# State update with invalid state class, expect error
|
||||||
_attributes = dict(attributes)
|
_attributes = dict(attributes)
|
||||||
_attributes.pop("state_class")
|
_attributes.pop("state_class")
|
||||||
hass.states.async_set("sensor.test", 12, attributes=_attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=_attributes | {ATTR_NUMERICAL_VALUE: 12}
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
expected = {
|
expected = {
|
||||||
"sensor.test": [
|
"sensor.test": [
|
||||||
@ -3874,7 +3966,9 @@ async def test_validate_statistics_sensor_no_longer_recorded(
|
|||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
# No statistics, valid state - empty response
|
# No statistics, valid state - empty response
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -3947,7 +4041,9 @@ async def test_validate_statistics_sensor_not_recorded(
|
|||||||
"homeassistant.components.sensor.recorder.is_entity_recorded",
|
"homeassistant.components.sensor.recorder.is_entity_recorded",
|
||||||
return_value=False,
|
return_value=False,
|
||||||
):
|
):
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await assert_validation_result(client, expected)
|
await assert_validation_result(client, expected)
|
||||||
|
|
||||||
@ -3993,7 +4089,9 @@ async def test_validate_statistics_sensor_removed(
|
|||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
# No statistics, valid state - empty response
|
# No statistics, valid state - empty response
|
||||||
hass.states.async_set("sensor.test", 10, attributes=attributes)
|
hass.states.async_set(
|
||||||
|
"sensor.test", "", attributes=attributes | {ATTR_NUMERICAL_VALUE: 10}
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -4063,13 +4161,19 @@ async def test_validate_statistics_unit_change_no_conversion(
|
|||||||
|
|
||||||
# No statistics, original unit - empty response
|
# No statistics, original unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 10, attributes={**attributes, **{"unit_of_measurement": unit1}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit1, ATTR_NUMERICAL_VALUE: 10},
|
||||||
)
|
)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
# No statistics, changed unit - empty response
|
# No statistics, changed unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 11, attributes={**attributes, **{"unit_of_measurement": unit2}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit2, ATTR_NUMERICAL_VALUE: 11},
|
||||||
)
|
)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -4081,7 +4185,10 @@ async def test_validate_statistics_unit_change_no_conversion(
|
|||||||
|
|
||||||
# No statistics, original unit - empty response
|
# No statistics, original unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 12, attributes={**attributes, **{"unit_of_measurement": unit1}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit1, ATTR_NUMERICAL_VALUE: 12},
|
||||||
)
|
)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -4096,7 +4203,10 @@ async def test_validate_statistics_unit_change_no_conversion(
|
|||||||
|
|
||||||
# Change unit - expect error
|
# Change unit - expect error
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 13, attributes={**attributes, **{"unit_of_measurement": unit2}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit2, ATTR_NUMERICAL_VALUE: 13},
|
||||||
)
|
)
|
||||||
await async_recorder_block_till_done(hass)
|
await async_recorder_block_till_done(hass)
|
||||||
expected = {
|
expected = {
|
||||||
@ -4193,7 +4303,10 @@ async def test_validate_statistics_unit_change_equivalent_units(
|
|||||||
|
|
||||||
# No statistics, original unit - empty response
|
# No statistics, original unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 10, attributes={**attributes, **{"unit_of_measurement": unit1}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit1, ATTR_NUMERICAL_VALUE: 10},
|
||||||
)
|
)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -4207,7 +4320,10 @@ async def test_validate_statistics_unit_change_equivalent_units(
|
|||||||
|
|
||||||
# Units changed to an equivalent unit - empty response
|
# Units changed to an equivalent unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 12, attributes={**attributes, **{"unit_of_measurement": unit2}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit2, ATTR_NUMERICAL_VALUE: 12},
|
||||||
)
|
)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -4273,7 +4389,10 @@ async def test_validate_statistics_unit_change_equivalent_units_2(
|
|||||||
|
|
||||||
# No statistics, original unit - empty response
|
# No statistics, original unit - empty response
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 10, attributes={**attributes, **{"unit_of_measurement": unit1}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit1, ATTR_NUMERICAL_VALUE: 10},
|
||||||
)
|
)
|
||||||
await assert_validation_result(client, {})
|
await assert_validation_result(client, {})
|
||||||
|
|
||||||
@ -4287,7 +4406,10 @@ async def test_validate_statistics_unit_change_equivalent_units_2(
|
|||||||
|
|
||||||
# Units changed to an equivalent unit which is not known by the unit converters
|
# Units changed to an equivalent unit which is not known by the unit converters
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.test", 12, attributes={**attributes, **{"unit_of_measurement": unit2}}
|
"sensor.test",
|
||||||
|
"",
|
||||||
|
attributes=attributes
|
||||||
|
| {"unit_of_measurement": unit2, ATTR_NUMERICAL_VALUE: 12},
|
||||||
)
|
)
|
||||||
expected = {
|
expected = {
|
||||||
"sensor.test": [
|
"sensor.test": [
|
||||||
@ -4366,9 +4488,11 @@ def record_meter_states(hass, zero, entity_id, _attributes, seq):
|
|||||||
We inject a bunch of state updates for meter sensors.
|
We inject a bunch of state updates for meter sensors.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def set_state(entity_id, state, **kwargs):
|
def set_state(entity_id, numerical_value, attributes):
|
||||||
"""Set the state."""
|
"""Set the state."""
|
||||||
hass.states.set(entity_id, state, **kwargs)
|
hass.states.set(
|
||||||
|
entity_id, "", attributes={**attributes, "numerical_value": numerical_value}
|
||||||
|
)
|
||||||
return hass.states.get(entity_id)
|
return hass.states.get(entity_id)
|
||||||
|
|
||||||
one = zero + timedelta(seconds=15 * 5) # 00:01:15
|
one = zero + timedelta(seconds=15 * 5) # 00:01:15
|
||||||
@ -4443,9 +4567,11 @@ def record_meter_state(hass, zero, entity_id, attributes, seq):
|
|||||||
We inject a state update for meter sensor.
|
We inject a state update for meter sensor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def set_state(entity_id, state, **kwargs):
|
def set_state(entity_id, numerical_value, attributes):
|
||||||
"""Set the state."""
|
"""Set the state."""
|
||||||
hass.states.set(entity_id, state, **kwargs)
|
hass.states.set(
|
||||||
|
entity_id, "", attributes={**attributes, "numerical_value": numerical_value}
|
||||||
|
)
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
return hass.states.get(entity_id)
|
return hass.states.get(entity_id)
|
||||||
|
|
||||||
@ -4464,9 +4590,11 @@ def record_states_partially_unavailable(hass, zero, entity_id, attributes):
|
|||||||
We inject a bunch of state updates temperature sensors.
|
We inject a bunch of state updates temperature sensors.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def set_state(entity_id, state, **kwargs):
|
def set_state(entity_id, numerical_value, attributes):
|
||||||
"""Set the state."""
|
"""Set the state."""
|
||||||
hass.states.set(entity_id, state, **kwargs)
|
hass.states.set(
|
||||||
|
entity_id, "", attributes={**attributes, "numerical_value": numerical_value}
|
||||||
|
)
|
||||||
wait_recording_done(hass)
|
wait_recording_done(hass)
|
||||||
return hass.states.get(entity_id)
|
return hass.states.get(entity_id)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user