diff --git a/homeassistant/components/history_stats/data.py b/homeassistant/components/history_stats/data.py index 544e1772b01..40cf351fd9e 100644 --- a/homeassistant/components/history_stats/data.py +++ b/homeassistant/components/history_stats/data.py @@ -176,26 +176,27 @@ class HistoryStats: # state_changes_during_period is called with include_start_time_state=True # which is the default and always provides the state at the start # of the period - previous_state_matches = ( - self._history_current_period - and self._history_current_period[0].state in self._entity_states - ) - last_state_change_timestamp = start_timestamp + previous_state_matches = False + last_state_change_timestamp = 0.0 elapsed = 0.0 - match_count = 1 if previous_state_matches else 0 + match_count = 0 # Make calculations for history_state in self._history_current_period: current_state_matches = history_state.state in self._entity_states state_change_timestamp = history_state.last_changed + if state_change_timestamp > now_timestamp: + # Shouldn't count states that are in the future + continue + if previous_state_matches: elapsed += state_change_timestamp - last_state_change_timestamp elif current_state_matches: match_count += 1 previous_state_matches = current_state_matches - last_state_change_timestamp = state_change_timestamp + last_state_change_timestamp = max(start_timestamp, state_change_timestamp) # Count time elapsed between last history state and end of measure if previous_state_matches: diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index f86c04b3e5b..694c5c20707 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -437,10 +437,10 @@ async def test_measure(recorder_mock: Recorder, hass: HomeAssistant) -> None: await async_update_entity(hass, f"sensor.sensor{i}") await hass.async_block_till_done() - assert hass.states.get("sensor.sensor1").state == "0.83" - assert hass.states.get("sensor.sensor2").state == "0.833333333333333" + assert hass.states.get("sensor.sensor1").state == "0.5" + assert 0.499 < float(hass.states.get("sensor.sensor2").state) < 0.501 assert hass.states.get("sensor.sensor3").state == "2" - assert hass.states.get("sensor.sensor4").state == "83.3" + assert hass.states.get("sensor.sensor4").state == "50.0" async def test_async_on_entire_period( @@ -1254,10 +1254,10 @@ async def test_measure_sliding_window( await async_update_entity(hass, f"sensor.sensor{i}") await hass.async_block_till_done() - assert hass.states.get("sensor.sensor1").state == "0.83" - assert hass.states.get("sensor.sensor2").state == "0.833333333333333" - assert hass.states.get("sensor.sensor3").state == "2" - assert hass.states.get("sensor.sensor4").state == "41.7" + assert hass.states.get("sensor.sensor1").state == "0.0" + assert float(hass.states.get("sensor.sensor2").state) == 0 + assert hass.states.get("sensor.sensor3").state == "0" + assert hass.states.get("sensor.sensor4").state == "0.0" past_next_update = start_time + timedelta(minutes=30) with ( @@ -1268,12 +1268,12 @@ async def test_measure_sliding_window( freeze_time(past_next_update), ): async_fire_time_changed(hass, past_next_update) - await hass.async_block_till_done() + await hass.async_block_till_done(wait_background_tasks=True) - assert hass.states.get("sensor.sensor1").state == "0.83" - assert hass.states.get("sensor.sensor2").state == "0.833333333333333" - assert hass.states.get("sensor.sensor3").state == "2" - assert hass.states.get("sensor.sensor4").state == "41.7" + assert hass.states.get("sensor.sensor1").state == "0.17" + assert 0.166 < float(hass.states.get("sensor.sensor2").state) < 0.167 + assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor4").state == "8.3" async def test_measure_from_end_going_backwards( @@ -1355,10 +1355,10 @@ async def test_measure_from_end_going_backwards( await async_update_entity(hass, f"sensor.sensor{i}") await hass.async_block_till_done() - assert hass.states.get("sensor.sensor1").state == "0.83" - assert hass.states.get("sensor.sensor2").state == "0.833333333333333" - assert hass.states.get("sensor.sensor3").state == "2" - assert hass.states.get("sensor.sensor4").state == "83.3" + assert hass.states.get("sensor.sensor1").state == "0.0" + assert float(hass.states.get("sensor.sensor2").state) == 0 + assert hass.states.get("sensor.sensor3").state == "0" + assert hass.states.get("sensor.sensor4").state == "0.0" past_next_update = start_time + timedelta(minutes=30) with ( @@ -1369,12 +1369,12 @@ async def test_measure_from_end_going_backwards( freeze_time(past_next_update), ): async_fire_time_changed(hass, past_next_update) - await hass.async_block_till_done() + await hass.async_block_till_done(wait_background_tasks=True) - assert hass.states.get("sensor.sensor1").state == "0.83" - assert hass.states.get("sensor.sensor2").state == "0.833333333333333" - assert hass.states.get("sensor.sensor3").state == "2" - assert hass.states.get("sensor.sensor4").state == "83.3" + assert hass.states.get("sensor.sensor1").state == "0.17" + assert 0.166 < float(hass.states.get("sensor.sensor2").state) < 0.167 + assert hass.states.get("sensor.sensor3").state == "1" + assert 16.6 <= float(hass.states.get("sensor.sensor4").state) <= 16.7 async def test_measure_cet(recorder_mock: Recorder, hass: HomeAssistant) -> None: @@ -1403,7 +1403,7 @@ async def test_measure_cet(recorder_mock: Recorder, hass: HomeAssistant) -> None "homeassistant.components.recorder.history.state_changes_during_period", _fake_states, ), - freeze_time(start_time), + freeze_time(start_time + timedelta(minutes=60)), ): await async_setup_component( hass, @@ -1455,10 +1455,10 @@ async def test_measure_cet(recorder_mock: Recorder, hass: HomeAssistant) -> None await async_update_entity(hass, f"sensor.sensor{i}") await hass.async_block_till_done() - assert hass.states.get("sensor.sensor1").state == "0.83" - assert hass.states.get("sensor.sensor2").state == "0.833333333333333" + assert hass.states.get("sensor.sensor1").state == "0.5" + assert 0.499 < float(hass.states.get("sensor.sensor2").state) < 0.501 assert hass.states.get("sensor.sensor3").state == "2" - assert hass.states.get("sensor.sensor4").state == "83.3" + assert hass.states.get("sensor.sensor4").state == "50.0" @pytest.mark.parametrize("time_zone", ["Europe/Berlin", "America/Chicago", "US/Hawaii"]) @@ -1537,18 +1537,19 @@ async def test_end_time_with_microseconds_zeroed( await hass.async_block_till_done() await async_update_entity(hass, "sensor.heatpump_compressor_today") await hass.async_block_till_done() - assert hass.states.get("sensor.heatpump_compressor_today").state == "1.83" + assert hass.states.get("sensor.heatpump_compressor_today").state == "0.5" assert ( - hass.states.get("sensor.heatpump_compressor_today2").state - == "1.83333333333333" + 0.499 + < float(hass.states.get("sensor.heatpump_compressor_today2").state) + < 0.501 ) async_fire_time_changed(hass, time_200) - await hass.async_block_till_done(wait_background_tasks=True) - assert hass.states.get("sensor.heatpump_compressor_today").state == "1.83" + assert hass.states.get("sensor.heatpump_compressor_today").state == "0.5" assert ( - hass.states.get("sensor.heatpump_compressor_today2").state - == "1.83333333333333" + 0.499 + < float(hass.states.get("sensor.heatpump_compressor_today2").state) + < 0.501 ) hass.states.async_set("binary_sensor.heatpump_compressor_state", "off") await hass.async_block_till_done() @@ -1557,10 +1558,11 @@ async def test_end_time_with_microseconds_zeroed( with freeze_time(time_400): async_fire_time_changed(hass, time_400) await hass.async_block_till_done(wait_background_tasks=True) - assert hass.states.get("sensor.heatpump_compressor_today").state == "1.83" + assert hass.states.get("sensor.heatpump_compressor_today").state == "0.5" assert ( - hass.states.get("sensor.heatpump_compressor_today2").state - == "1.83333333333333" + 0.499 + < float(hass.states.get("sensor.heatpump_compressor_today2").state) + < 0.501 ) hass.states.async_set("binary_sensor.heatpump_compressor_state", "on") await async_wait_recording_done(hass) @@ -1568,10 +1570,11 @@ async def test_end_time_with_microseconds_zeroed( with freeze_time(time_600): async_fire_time_changed(hass, time_600) await hass.async_block_till_done(wait_background_tasks=True) - assert hass.states.get("sensor.heatpump_compressor_today").state == "3.83" + assert hass.states.get("sensor.heatpump_compressor_today").state == "2.5" assert ( - hass.states.get("sensor.heatpump_compressor_today2").state - == "3.83333333333333" + 2.499 + < float(hass.states.get("sensor.heatpump_compressor_today2").state) + < 2.501 ) rolled_to_next_day = start_of_today + timedelta(days=1)