Run statistics on 5-minute intervals in tests (#122592)

* Run statistics on 5-minute intervals in tests

* Fix test failing when mysql does not return rows in insert order
This commit is contained in:
Erik Montnemery 2024-07-25 17:32:49 +02:00 committed by GitHub
parent 08d7beb803
commit ec957e4a94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 226 additions and 104 deletions

View file

@ -79,10 +79,18 @@ async def async_block_recorder(hass: HomeAssistant, seconds: float) -> None:
await event.wait()
def get_start_time(start: datetime) -> datetime:
"""Calculate a valid start time for statistics."""
start_minutes = start.minute - start.minute % 5
return start.replace(minute=start_minutes, second=0, microsecond=0)
def do_adhoc_statistics(hass: HomeAssistant, **kwargs: Any) -> None:
"""Trigger an adhoc statistics run."""
if not (start := kwargs.get("start")):
start = statistics.get_start_time()
elif (start.minute % 5) != 0 or start.second != 0 or start.microsecond != 0:
raise ValueError(f"Statistics must start on 5 minute boundary got {start}")
get_instance(hass).queue_task(StatisticsTask(start, False))
@ -291,11 +299,11 @@ def record_states(hass):
wait_recording_done(hass)
return hass.states.get(entity_id)
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
one = zero + timedelta(seconds=1 * 5)
two = one + timedelta(seconds=15 * 5)
three = two + timedelta(seconds=30 * 5)
four = three + timedelta(seconds=15 * 5)
four = three + timedelta(seconds=14 * 5)
states = {mp: [], sns1: [], sns2: [], sns3: [], sns4: []}
with freeze_time(one) as freezer:

View file

@ -44,6 +44,7 @@ from .common import (
async_record_states,
async_wait_recording_done,
do_adhoc_statistics,
get_start_time,
statistics_during_period,
)
@ -342,7 +343,7 @@ async def test_compile_periodic_statistics_exception(
"""Test exception handling when compiling periodic statistics."""
await async_setup_component(hass, "sensor", {})
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
do_adhoc_statistics(hass, start=now)
do_adhoc_statistics(hass, start=now + timedelta(minutes=5))
await async_wait_recording_done(hass)

View file

@ -35,6 +35,7 @@ from .common import (
async_wait_recording_done,
create_engine_test,
do_adhoc_statistics,
get_start_time,
statistics_during_period,
)
from .conftest import InstrumentedMigration
@ -155,12 +156,17 @@ async def test_statistics_during_period(
recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test statistics_during_period."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = US_CUSTOMARY_SYSTEM
await async_setup_component(hass, "sensor", {})
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",
10,
attributes=POWER_SENSOR_KW_ATTRIBUTES,
timestamp=now.timestamp(),
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, start=now)
@ -608,7 +614,12 @@ async def test_statistic_during_period(
}
# Test we can automatically convert units
hass.states.async_set("sensor.test", None, attributes=ENERGY_SENSOR_WH_ATTRIBUTES)
hass.states.async_set(
"sensor.test",
None,
attributes=ENERGY_SENSOR_WH_ATTRIBUTES,
timestamp=now.timestamp(),
)
await client.send_json_auto_id(
{
"type": "recorder/statistic_during_period",
@ -1265,11 +1276,13 @@ async def test_statistics_during_period_unit_conversion(
converted_value,
) -> None:
"""Test statistics_during_period."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
hass.states.async_set("sensor.test", state, attributes=attributes)
hass.states.async_set(
"sensor.test", state, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, start=now)
@ -1350,12 +1363,16 @@ async def test_sum_statistics_during_period_unit_conversion(
converted_value,
) -> None:
"""Test statistics_during_period."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
hass.states.async_set("sensor.test", 0, attributes=attributes)
hass.states.async_set("sensor.test", state, attributes=attributes)
hass.states.async_set(
"sensor.test", 0, attributes=attributes, timestamp=now.timestamp()
)
hass.states.async_set(
"sensor.test", state, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, start=now)
@ -1471,7 +1488,7 @@ async def test_statistics_during_period_in_the_past(
) -> None:
"""Test statistics_during_period in the past."""
await hass.config.async_set_time_zone("UTC")
now = dt_util.utcnow().replace()
now = get_start_time(dt_util.utcnow())
hass.config.units = US_CUSTOMARY_SYSTEM
await async_setup_component(hass, "sensor", {})
@ -1726,7 +1743,7 @@ async def test_list_statistic_ids(
unit_class,
) -> None:
"""Test list_statistic_ids."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
has_mean = attributes["state_class"] == "measurement"
has_sum = not has_mean
@ -1740,7 +1757,9 @@ async def test_list_statistic_ids(
assert response["success"]
assert response["result"] == []
hass.states.async_set("sensor.test", 10, attributes=attributes)
hass.states.async_set(
"sensor.test", 10, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
await client.send_json_auto_id({"type": "recorder/list_statistic_ids"})
@ -1890,7 +1909,7 @@ async def test_list_statistic_ids_unit_change(
unit_class,
) -> None:
"""Test list_statistic_ids."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
has_mean = attributes["state_class"] == "measurement"
has_sum = not has_mean
@ -1903,7 +1922,9 @@ async def test_list_statistic_ids_unit_change(
assert response["success"]
assert response["result"] == []
hass.states.async_set("sensor.test", 10, attributes=attributes)
hass.states.async_set(
"sensor.test", 10, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, start=now)
@ -1926,7 +1947,9 @@ async def test_list_statistic_ids_unit_change(
]
# Change the state unit
hass.states.async_set("sensor.test", 10, attributes=attributes2)
hass.states.async_set(
"sensor.test", 10, attributes=attributes2, timestamp=now.timestamp()
)
await client.send_json_auto_id({"type": "recorder/list_statistic_ids"})
response = await client.receive_json()
@ -1965,7 +1988,7 @@ async def test_clear_statistics(
recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test removing statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
units = METRIC_SYSTEM
attributes = POWER_SENSOR_KW_ATTRIBUTES
@ -1975,9 +1998,15 @@ async def test_clear_statistics(
hass.config.units = units
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
hass.states.async_set("sensor.test1", state, attributes=attributes)
hass.states.async_set("sensor.test2", state * 2, attributes=attributes)
hass.states.async_set("sensor.test3", state * 3, attributes=attributes)
hass.states.async_set(
"sensor.test1", state, attributes=attributes, timestamp=now.timestamp()
)
hass.states.async_set(
"sensor.test2", state * 2, attributes=attributes, timestamp=now.timestamp()
)
hass.states.async_set(
"sensor.test3", state * 3, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, start=now)
@ -2088,7 +2117,7 @@ async def test_update_statistics_metadata(
new_display_unit,
) -> None:
"""Test removing statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
units = METRIC_SYSTEM
attributes = POWER_SENSOR_KW_ATTRIBUTES | {"device_class": None}
@ -2097,7 +2126,9 @@ async def test_update_statistics_metadata(
hass.config.units = units
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
hass.states.async_set("sensor.test", state, attributes=attributes)
hass.states.async_set(
"sensor.test", state, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, period="hourly", start=now)
@ -2177,7 +2208,7 @@ async def test_change_statistics_unit(
recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test change unit of recorded statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
units = METRIC_SYSTEM
attributes = POWER_SENSOR_KW_ATTRIBUTES | {"device_class": None}
@ -2186,7 +2217,9 @@ async def test_change_statistics_unit(
hass.config.units = units
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
hass.states.async_set("sensor.test", state, attributes=attributes)
hass.states.async_set(
"sensor.test", state, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, period="hourly", start=now)
@ -2322,7 +2355,7 @@ async def test_change_statistics_unit_errors(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test change unit of recorded statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
units = METRIC_SYSTEM
attributes = POWER_SENSOR_KW_ATTRIBUTES | {"device_class": None}
@ -2376,7 +2409,9 @@ async def test_change_statistics_unit_errors(
hass.config.units = units
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
hass.states.async_set("sensor.test", state, attributes=attributes)
hass.states.async_set(
"sensor.test", state, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
do_adhoc_statistics(hass, period="hourly", start=now)
@ -2599,7 +2634,7 @@ async def test_get_statistics_metadata(
unit_class,
) -> None:
"""Test get_statistics_metadata."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
has_mean = attributes["state_class"] == "measurement"
has_sum = not has_mean
@ -2678,10 +2713,14 @@ async def test_get_statistics_metadata(
}
]
hass.states.async_set("sensor.test", 10, attributes=attributes)
hass.states.async_set(
"sensor.test", 10, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
hass.states.async_set("sensor.test2", 10, attributes=attributes)
hass.states.async_set(
"sensor.test2", 10, attributes=attributes, timestamp=now.timestamp()
)
await async_wait_recording_done(hass)
await client.send_json_auto_id(

View file

@ -50,6 +50,7 @@ from tests.components.recorder.common import (
async_recorder_block_till_done,
async_wait_recording_done,
do_adhoc_statistics,
get_start_time,
statistics_during_period,
)
from tests.typing import (
@ -194,7 +195,7 @@ async def test_compile_hourly_statistics(
max,
) -> None:
"""Test compiling hourly statistics."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -278,7 +279,7 @@ async def test_compile_hourly_statistics_with_some_same_last_updated(
If the last updated value is the same we will have a zero duration.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -392,7 +393,7 @@ async def test_compile_hourly_statistics_with_all_same_last_updated(
If the last updated value is the same we will have a zero duration.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -498,7 +499,7 @@ async def test_compile_hourly_statistics_only_state_is_and_end_of_period(
max,
) -> None:
"""Test compiling hourly statistics when the only state at end of period."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -592,7 +593,7 @@ async def test_compile_hourly_statistics_purged_state_changes(
unit_class,
) -> None:
"""Test compiling hourly statistics."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -663,7 +664,7 @@ async def test_compile_hourly_statistics_wrong_unit(
attributes,
) -> None:
"""Test compiling hourly statistics for sensor with unit not matching device class."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -887,7 +888,7 @@ async def test_compile_hourly_sum_statistics_amount(
factor,
) -> None:
"""Test compiling hourly statistics."""
period0 = dt_util.utcnow()
period0 = get_start_time(dt_util.utcnow())
period0_end = period1 = period0 + timedelta(minutes=5)
period1_end = period2 = period0 + timedelta(minutes=10)
period2_end = period0 + timedelta(minutes=15)
@ -1071,7 +1072,7 @@ async def test_compile_hourly_sum_statistics_amount_reset_every_state_change(
factor,
) -> None:
"""Test compiling hourly statistics."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -1194,7 +1195,7 @@ async def test_compile_hourly_sum_statistics_amount_invalid_last_reset(
factor,
) -> None:
"""Test compiling hourly statistics."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -1294,7 +1295,7 @@ async def test_compile_hourly_sum_statistics_nan_inf_state(
factor,
) -> None:
"""Test compiling hourly statistics with nan and inf states."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -1429,7 +1430,7 @@ async def test_compile_hourly_sum_statistics_negative_state(
offset,
) -> None:
"""Test compiling hourly statistics with negative states."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
hass.data.pop(loader.DATA_CUSTOM_COMPONENTS)
mocksensor = MockSensor(name="custom_sensor")
@ -1437,10 +1438,11 @@ async def test_compile_hourly_sum_statistics_negative_state(
setup_test_component_platform(hass, DOMAIN, [mocksensor], built_in=False)
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(
hass, "sensor", {"sensor": [{"platform": "demo"}, {"platform": "test"}]}
)
await hass.async_block_till_done()
with freeze_time(zero) as freezer:
await async_setup_component(
hass, "sensor", {"sensor": [{"platform": "demo"}, {"platform": "test"}]}
)
await hass.async_block_till_done()
attributes = {
"device_class": device_class,
"state_class": state_class,
@ -1541,7 +1543,7 @@ async def test_compile_hourly_sum_statistics_total_no_reset(
factor,
) -> None:
"""Test compiling hourly statistics."""
period0 = dt_util.utcnow()
period0 = get_start_time(dt_util.utcnow())
period0_end = period1 = period0 + timedelta(minutes=5)
period1_end = period2 = period0 + timedelta(minutes=10)
period2_end = period0 + timedelta(minutes=15)
@ -1654,7 +1656,7 @@ async def test_compile_hourly_sum_statistics_total_increasing(
factor,
) -> None:
"""Test compiling hourly statistics."""
period0 = dt_util.utcnow()
period0 = get_start_time(dt_util.utcnow())
period0_end = period1 = period0 + timedelta(minutes=5)
period1_end = period2 = period0 + timedelta(minutes=10)
period2_end = period0 + timedelta(minutes=15)
@ -1767,7 +1769,7 @@ async def test_compile_hourly_sum_statistics_total_increasing_small_dip(
factor,
) -> None:
"""Test small dips in sensor readings do not trigger a reset."""
period0 = dt_util.utcnow()
period0 = get_start_time(dt_util.utcnow())
period0_end = period1 = period0 + timedelta(minutes=5)
period1_end = period2 = period0 + timedelta(minutes=10)
period2_end = period0 + timedelta(minutes=15)
@ -1869,7 +1871,7 @@ async def test_compile_hourly_energy_statistics_unsupported(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test compiling hourly statistics."""
period0 = dt_util.utcnow()
period0 = get_start_time(dt_util.utcnow())
period0_end = period1 = period0 + timedelta(minutes=5)
period1_end = period2 = period0 + timedelta(minutes=10)
period2_end = period0 + timedelta(minutes=15)
@ -1973,7 +1975,7 @@ async def test_compile_hourly_energy_statistics_multiple(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test compiling multiple hourly statistics."""
period0 = dt_util.utcnow()
period0 = get_start_time(dt_util.utcnow())
period0_end = period1 = period0 + timedelta(minutes=5)
period1_end = period2 = period0 + timedelta(minutes=10)
period2_end = period0 + timedelta(minutes=15)
@ -2187,7 +2189,7 @@ async def test_compile_hourly_statistics_unchanged(
value,
) -> None:
"""Test compiling hourly statistics, with no changes during the hour."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -2230,7 +2232,7 @@ async def test_compile_hourly_statistics_partially_unavailable(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test compiling hourly statistics, with the sensor being partially unavailable."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -2299,7 +2301,7 @@ async def test_compile_hourly_statistics_unavailable(
sensor.test1 is unavailable and should not have statistics generated
sensor.test2 should have statistics generated
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -2346,7 +2348,7 @@ async def test_compile_hourly_statistics_fails(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test compiling hourly statistics throws."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -2523,7 +2525,7 @@ async def test_compile_hourly_statistics_changing_units_1(
This tests the case where the recorder cannot convert between the units.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -2652,7 +2654,7 @@ async def test_compile_hourly_statistics_changing_units_2(
This tests the behaviour when the sensor units are note supported by any unit
converter.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow()) - timedelta(seconds=30 * 5)
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -2731,7 +2733,7 @@ async def test_compile_hourly_statistics_changing_units_3(
This tests the behaviour when the sensor units are note supported by any unit
converter.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -2852,7 +2854,7 @@ async def test_compile_hourly_statistics_convert_units_1(
This tests the case where the recorder can convert between the units.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -3011,7 +3013,7 @@ async def test_compile_hourly_statistics_equivalent_units_1(
max,
) -> None:
"""Test compiling hourly statistics where units change from one hour to the next."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -3136,7 +3138,7 @@ async def test_compile_hourly_statistics_equivalent_units_2(
max,
) -> None:
"""Test compiling hourly statistics where units change during an hour."""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -3160,7 +3162,7 @@ async def test_compile_hourly_statistics_equivalent_units_2(
)
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 * 10))
await async_wait_recording_done(hass)
assert "The unit of sensor.test1 is changing" not in caplog.text
assert "and matches the unit of already compiled statistics" not in caplog.text
@ -3182,9 +3184,9 @@ async def test_compile_hourly_statistics_equivalent_units_2(
"sensor.test1": [
{
"start": process_timestamp(
zero + timedelta(seconds=30 * 5)
zero + timedelta(seconds=30 * 10)
).timestamp(),
"end": process_timestamp(zero + timedelta(seconds=30 * 15)).timestamp(),
"end": process_timestamp(zero + timedelta(seconds=30 * 20)).timestamp(),
"mean": pytest.approx(mean),
"min": pytest.approx(min),
"max": pytest.approx(max),
@ -3229,7 +3231,7 @@ async def test_compile_hourly_statistics_changing_device_class_1(
Device class is ignored, meaning changing device class should not influence the statistics.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -3440,7 +3442,7 @@ async def test_compile_hourly_statistics_changing_device_class_2(
Device class is ignored, meaning changing device class should not influence the statistics.
"""
zero = dt_util.utcnow()
zero = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
# Wait for the sensor recorder platform to be added
await async_recorder_block_till_done(hass)
@ -3578,7 +3580,7 @@ async def test_compile_hourly_statistics_changing_state_class(
max,
) -> None:
"""Test compiling hourly statistics where state class changes."""
period0 = dt_util.utcnow()
period0 = get_start_time(dt_util.utcnow())
period0_end = period1 = period0 + timedelta(minutes=5)
period1_end = period0 + timedelta(minutes=10)
await async_setup_component(hass, "sensor", {})
@ -4148,7 +4150,7 @@ async def async_record_states(
one = zero + timedelta(seconds=1 * 5)
two = one + timedelta(seconds=10 * 5)
three = two + timedelta(seconds=40 * 5)
four = three + timedelta(seconds=10 * 5)
four = three + timedelta(seconds=9 * 5)
states = {entity_id: []}
freezer.move_to(one)
@ -4210,7 +4212,7 @@ async def test_validate_unit_change_convertible(
The test also asserts that the sensor's device class is ignored.
"""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = units
await async_setup_component(hass, "sensor", {})
@ -4222,14 +4224,20 @@ async def test_validate_unit_change_convertible(
# No statistics, unit in state matching device class - empty response
hass.states.async_set(
"sensor.test", 10, attributes={**attributes, "unit_of_measurement": unit}
"sensor.test",
10,
attributes={**attributes, "unit_of_measurement": unit},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
# No statistics, unit in state not matching device class - empty response
hass.states.async_set(
"sensor.test", 11, attributes={**attributes, "unit_of_measurement": "dogs"}
"sensor.test",
11,
attributes={**attributes, "unit_of_measurement": "dogs"},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
@ -4238,7 +4246,10 @@ async def test_validate_unit_change_convertible(
await async_recorder_block_till_done(hass)
do_adhoc_statistics(hass, start=now)
hass.states.async_set(
"sensor.test", 12, attributes={**attributes, "unit_of_measurement": "dogs"}
"sensor.test",
12,
attributes={**attributes, "unit_of_measurement": "dogs"},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
expected = {
@ -4258,7 +4269,10 @@ async def test_validate_unit_change_convertible(
# Valid state - empty response
hass.states.async_set(
"sensor.test", 13, attributes={**attributes, "unit_of_measurement": unit}
"sensor.test",
13,
attributes={**attributes, "unit_of_measurement": unit},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
@ -4270,7 +4284,10 @@ async def test_validate_unit_change_convertible(
# Valid state in compatible unit - empty response
hass.states.async_set(
"sensor.test", 13, attributes={**attributes, "unit_of_measurement": unit2}
"sensor.test",
13,
attributes={**attributes, "unit_of_measurement": unit2},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
@ -4309,7 +4326,7 @@ async def test_validate_statistics_unit_ignore_device_class(
The test asserts that the sensor's device class is ignored.
"""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = units
await async_setup_component(hass, "sensor", {})
@ -4321,7 +4338,9 @@ async def test_validate_statistics_unit_ignore_device_class(
# No statistics, no device class - empty response
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", 10, attributes=initial_attributes, timestamp=now.timestamp()
)
await hass.async_block_till_done()
await assert_validation_result(client, {})
@ -4329,7 +4348,10 @@ async def test_validate_statistics_unit_ignore_device_class(
do_adhoc_statistics(hass, start=now)
await async_recorder_block_till_done(hass)
hass.states.async_set(
"sensor.test", 12, attributes={**attributes, "unit_of_measurement": "dogs"}
"sensor.test",
12,
attributes={**attributes, "unit_of_measurement": "dogs"},
timestamp=now.timestamp(),
)
await hass.async_block_till_done()
await assert_validation_result(client, {})
@ -4389,7 +4411,7 @@ async def test_validate_statistics_unit_change_no_device_class(
attributes = dict(attributes)
attributes.pop("device_class")
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = units
await async_setup_component(hass, "sensor", {})
@ -4401,14 +4423,20 @@ async def test_validate_statistics_unit_change_no_device_class(
# No statistics, sensor state set - empty response
hass.states.async_set(
"sensor.test", 10, attributes={**attributes, "unit_of_measurement": unit}
"sensor.test",
10,
attributes={**attributes, "unit_of_measurement": unit},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
# No statistics, sensor state set to an incompatible unit - empty response
hass.states.async_set(
"sensor.test", 11, attributes={**attributes, "unit_of_measurement": "dogs"}
"sensor.test",
11,
attributes={**attributes, "unit_of_measurement": "dogs"},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
@ -4417,7 +4445,10 @@ async def test_validate_statistics_unit_change_no_device_class(
await async_recorder_block_till_done(hass)
do_adhoc_statistics(hass, start=now)
hass.states.async_set(
"sensor.test", 12, attributes={**attributes, "unit_of_measurement": "dogs"}
"sensor.test",
12,
attributes={**attributes, "unit_of_measurement": "dogs"},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
expected = {
@ -4437,7 +4468,10 @@ async def test_validate_statistics_unit_change_no_device_class(
# Valid state - empty response
hass.states.async_set(
"sensor.test", 13, attributes={**attributes, "unit_of_measurement": unit}
"sensor.test",
13,
attributes={**attributes, "unit_of_measurement": unit},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
@ -4449,7 +4483,10 @@ async def test_validate_statistics_unit_change_no_device_class(
# Valid state in compatible unit - empty response
hass.states.async_set(
"sensor.test", 13, attributes={**attributes, "unit_of_measurement": unit2}
"sensor.test",
13,
attributes={**attributes, "unit_of_measurement": unit2},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
@ -4486,7 +4523,7 @@ async def test_validate_statistics_unsupported_state_class(
unit,
) -> None:
"""Test validate_statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = units
await async_setup_component(hass, "sensor", {})
@ -4497,7 +4534,9 @@ async def test_validate_statistics_unsupported_state_class(
await assert_validation_result(client, {})
# No statistics, valid state - empty response
hass.states.async_set("sensor.test", 10, attributes=attributes)
hass.states.async_set(
"sensor.test", 10, attributes=attributes, timestamp=now.timestamp()
)
await hass.async_block_till_done()
await assert_validation_result(client, {})
@ -4509,7 +4548,9 @@ async def test_validate_statistics_unsupported_state_class(
# State update with invalid state class, expect error
_attributes = dict(attributes)
_attributes.pop("state_class")
hass.states.async_set("sensor.test", 12, attributes=_attributes)
hass.states.async_set(
"sensor.test", 12, attributes=_attributes, timestamp=now.timestamp()
)
await hass.async_block_till_done()
expected = {
"sensor.test": [
@ -4539,7 +4580,7 @@ async def test_validate_statistics_sensor_no_longer_recorded(
unit,
) -> None:
"""Test validate_statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = units
await async_setup_component(hass, "sensor", {})
@ -4550,7 +4591,9 @@ async def test_validate_statistics_sensor_no_longer_recorded(
await assert_validation_result(client, {})
# No statistics, valid state - empty response
hass.states.async_set("sensor.test", 10, attributes=attributes)
hass.states.async_set(
"sensor.test", 10, attributes=attributes, timestamp=now.timestamp()
)
await hass.async_block_till_done()
await assert_validation_result(client, {})
@ -4591,7 +4634,7 @@ async def test_validate_statistics_sensor_not_recorded(
unit,
) -> None:
"""Test validate_statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = units
await async_setup_component(hass, "sensor", {})
@ -4616,7 +4659,9 @@ async def test_validate_statistics_sensor_not_recorded(
"entity_filter",
return_value=False,
):
hass.states.async_set("sensor.test", 10, attributes=attributes)
hass.states.async_set(
"sensor.test", 10, attributes=attributes, timestamp=now.timestamp()
)
await hass.async_block_till_done()
await assert_validation_result(client, expected)
@ -4640,7 +4685,7 @@ async def test_validate_statistics_sensor_removed(
unit,
) -> None:
"""Test validate_statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
hass.config.units = units
await async_setup_component(hass, "sensor", {})
@ -4651,7 +4696,9 @@ async def test_validate_statistics_sensor_removed(
await assert_validation_result(client, {})
# No statistics, valid state - empty response
hass.states.async_set("sensor.test", 10, attributes=attributes)
hass.states.async_set(
"sensor.test", 10, attributes=attributes, timestamp=now.timestamp()
)
await hass.async_block_till_done()
await assert_validation_result(client, {})
@ -4688,7 +4735,7 @@ async def test_validate_statistics_unit_change_no_conversion(
unit2,
) -> None:
"""Test validate_statistics."""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
@ -4699,13 +4746,19 @@ async def test_validate_statistics_unit_change_no_conversion(
# No statistics, original unit - empty response
hass.states.async_set(
"sensor.test", 10, attributes={**attributes, "unit_of_measurement": unit1}
"sensor.test",
10,
attributes={**attributes, "unit_of_measurement": unit1},
timestamp=now.timestamp(),
)
await assert_validation_result(client, {})
# No statistics, changed unit - empty response
hass.states.async_set(
"sensor.test", 11, attributes={**attributes, "unit_of_measurement": unit2}
"sensor.test",
11,
attributes={**attributes, "unit_of_measurement": unit2},
timestamp=now.timestamp(),
)
await assert_validation_result(client, {})
@ -4717,7 +4770,10 @@ async def test_validate_statistics_unit_change_no_conversion(
# No statistics, original unit - empty response
hass.states.async_set(
"sensor.test", 12, attributes={**attributes, "unit_of_measurement": unit1}
"sensor.test",
12,
attributes={**attributes, "unit_of_measurement": unit1},
timestamp=now.timestamp(),
)
await assert_validation_result(client, {})
@ -4732,7 +4788,10 @@ async def test_validate_statistics_unit_change_no_conversion(
# Change unit - expect error
hass.states.async_set(
"sensor.test", 13, attributes={**attributes, "unit_of_measurement": unit2}
"sensor.test",
13,
attributes={**attributes, "unit_of_measurement": unit2},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
expected = {
@ -4752,7 +4811,10 @@ async def test_validate_statistics_unit_change_no_conversion(
# Original unit - empty response
hass.states.async_set(
"sensor.test", 14, attributes={**attributes, "unit_of_measurement": unit1}
"sensor.test",
14,
attributes={**attributes, "unit_of_measurement": unit1},
timestamp=now.timestamp(),
)
await async_recorder_block_till_done(hass)
await assert_validation_result(client, {})
@ -4796,7 +4858,7 @@ async def test_validate_statistics_unit_change_equivalent_units(
This tests no validation issue is created when a sensor's unit changes to an
equivalent unit.
"""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
@ -4807,7 +4869,10 @@ async def test_validate_statistics_unit_change_equivalent_units(
# No statistics, original unit - empty response
hass.states.async_set(
"sensor.test", 10, attributes={**attributes, "unit_of_measurement": unit1}
"sensor.test",
10,
attributes={**attributes, "unit_of_measurement": unit1},
timestamp=now.timestamp(),
)
await assert_validation_result(client, {})
@ -4821,7 +4886,10 @@ async def test_validate_statistics_unit_change_equivalent_units(
# Units changed to an equivalent unit - empty response
hass.states.async_set(
"sensor.test", 12, attributes={**attributes, "unit_of_measurement": unit2}
"sensor.test",
12,
attributes={**attributes, "unit_of_measurement": unit2},
timestamp=now.timestamp() + 1,
)
await assert_validation_result(client, {})
@ -4854,7 +4922,7 @@ async def test_validate_statistics_unit_change_equivalent_units_2(
This tests a validation issue is created when a sensor's unit changes to an
equivalent unit which is not known to the unit converters.
"""
now = dt_util.utcnow()
now = get_start_time(dt_util.utcnow())
await async_setup_component(hass, "sensor", {})
await async_recorder_block_till_done(hass)
@ -4865,7 +4933,10 @@ async def test_validate_statistics_unit_change_equivalent_units_2(
# No statistics, original unit - empty response
hass.states.async_set(
"sensor.test", 10, attributes={**attributes, "unit_of_measurement": unit1}
"sensor.test",
10,
attributes={**attributes, "unit_of_measurement": unit1},
timestamp=now.timestamp(),
)
await assert_validation_result(client, {})
@ -4879,7 +4950,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
hass.states.async_set(
"sensor.test", 12, attributes={**attributes, "unit_of_measurement": unit2}
"sensor.test",
12,
attributes={**attributes, "unit_of_measurement": unit2},
timestamp=now.timestamp(),
)
expected = {
"sensor.test": [
@ -5045,7 +5119,7 @@ async def async_record_states_partially_unavailable(hass, zero, entity_id, attri
one = zero + timedelta(seconds=1 * 5)
two = one + timedelta(seconds=15 * 5)
three = two + timedelta(seconds=30 * 5)
four = three + timedelta(seconds=15 * 5)
four = three + timedelta(seconds=14 * 5)
states = {entity_id: []}
with freeze_time(one) as freezer: