Use numerical_value when compiling statistics

This commit is contained in:
Erik 2023-02-01 14:03:33 +01:00
parent 65324431a4
commit 2f608d8650
4 changed files with 288 additions and 116 deletions

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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)