diff --git a/tests/components/recorder/common.py b/tests/components/recorder/common.py index 003b07ab80f..aee35fceb80 100644 --- a/tests/components/recorder/common.py +++ b/tests/components/recorder/common.py @@ -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: diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 993a4a5bcf8..074a98e5230 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -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) diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index bcdf07502b0..ed36f4dacbf 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -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( diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index a9fd7fbde9c..2bd751a553c 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -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: