Dont count unrecorded time for history_stats (#126271)

This commit is contained in:
karwosts 2024-11-21 08:24:06 -08:00 committed by GitHub
parent 23acc31616
commit 3d499ab849
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 45 deletions

View File

@ -176,26 +176,27 @@ class HistoryStats:
# state_changes_during_period is called with include_start_time_state=True # state_changes_during_period is called with include_start_time_state=True
# which is the default and always provides the state at the start # which is the default and always provides the state at the start
# of the period # of the period
previous_state_matches = ( previous_state_matches = False
self._history_current_period last_state_change_timestamp = 0.0
and self._history_current_period[0].state in self._entity_states
)
last_state_change_timestamp = start_timestamp
elapsed = 0.0 elapsed = 0.0
match_count = 1 if previous_state_matches else 0 match_count = 0
# Make calculations # Make calculations
for history_state in self._history_current_period: for history_state in self._history_current_period:
current_state_matches = history_state.state in self._entity_states current_state_matches = history_state.state in self._entity_states
state_change_timestamp = history_state.last_changed 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: if previous_state_matches:
elapsed += state_change_timestamp - last_state_change_timestamp elapsed += state_change_timestamp - last_state_change_timestamp
elif current_state_matches: elif current_state_matches:
match_count += 1 match_count += 1
previous_state_matches = current_state_matches 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 # Count time elapsed between last history state and end of measure
if previous_state_matches: if previous_state_matches:

View File

@ -437,10 +437,10 @@ async def test_measure(recorder_mock: Recorder, hass: HomeAssistant) -> None:
await async_update_entity(hass, f"sensor.sensor{i}") await async_update_entity(hass, f"sensor.sensor{i}")
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor1").state == "0.5"
assert hass.states.get("sensor.sensor2").state == "0.833333333333333" 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.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( 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 async_update_entity(hass, f"sensor.sensor{i}")
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor1").state == "0.0"
assert hass.states.get("sensor.sensor2").state == "0.833333333333333" assert float(hass.states.get("sensor.sensor2").state) == 0
assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor3").state == "0"
assert hass.states.get("sensor.sensor4").state == "41.7" assert hass.states.get("sensor.sensor4").state == "0.0"
past_next_update = start_time + timedelta(minutes=30) past_next_update = start_time + timedelta(minutes=30)
with ( with (
@ -1268,12 +1268,12 @@ async def test_measure_sliding_window(
freeze_time(past_next_update), freeze_time(past_next_update),
): ):
async_fire_time_changed(hass, 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.sensor1").state == "0.17"
assert hass.states.get("sensor.sensor2").state == "0.833333333333333" assert 0.166 < float(hass.states.get("sensor.sensor2").state) < 0.167
assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor3").state == "1"
assert hass.states.get("sensor.sensor4").state == "41.7" assert hass.states.get("sensor.sensor4").state == "8.3"
async def test_measure_from_end_going_backwards( 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 async_update_entity(hass, f"sensor.sensor{i}")
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor1").state == "0.0"
assert hass.states.get("sensor.sensor2").state == "0.833333333333333" assert float(hass.states.get("sensor.sensor2").state) == 0
assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor3").state == "0"
assert hass.states.get("sensor.sensor4").state == "83.3" assert hass.states.get("sensor.sensor4").state == "0.0"
past_next_update = start_time + timedelta(minutes=30) past_next_update = start_time + timedelta(minutes=30)
with ( with (
@ -1369,12 +1369,12 @@ async def test_measure_from_end_going_backwards(
freeze_time(past_next_update), freeze_time(past_next_update),
): ):
async_fire_time_changed(hass, 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.sensor1").state == "0.17"
assert hass.states.get("sensor.sensor2").state == "0.833333333333333" assert 0.166 < float(hass.states.get("sensor.sensor2").state) < 0.167
assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor3").state == "1"
assert hass.states.get("sensor.sensor4").state == "83.3" assert 16.6 <= float(hass.states.get("sensor.sensor4").state) <= 16.7
async def test_measure_cet(recorder_mock: Recorder, hass: HomeAssistant) -> None: 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", "homeassistant.components.recorder.history.state_changes_during_period",
_fake_states, _fake_states,
), ),
freeze_time(start_time), freeze_time(start_time + timedelta(minutes=60)),
): ):
await async_setup_component( await async_setup_component(
hass, 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 async_update_entity(hass, f"sensor.sensor{i}")
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor1").state == "0.5"
assert hass.states.get("sensor.sensor2").state == "0.833333333333333" 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.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"]) @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 hass.async_block_till_done()
await async_update_entity(hass, "sensor.heatpump_compressor_today") await async_update_entity(hass, "sensor.heatpump_compressor_today")
await hass.async_block_till_done() 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 ( assert (
hass.states.get("sensor.heatpump_compressor_today2").state 0.499
== "1.83333333333333" < float(hass.states.get("sensor.heatpump_compressor_today2").state)
< 0.501
) )
async_fire_time_changed(hass, time_200) 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 == "0.5"
assert hass.states.get("sensor.heatpump_compressor_today").state == "1.83"
assert ( assert (
hass.states.get("sensor.heatpump_compressor_today2").state 0.499
== "1.83333333333333" < float(hass.states.get("sensor.heatpump_compressor_today2").state)
< 0.501
) )
hass.states.async_set("binary_sensor.heatpump_compressor_state", "off") hass.states.async_set("binary_sensor.heatpump_compressor_state", "off")
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1557,10 +1558,11 @@ async def test_end_time_with_microseconds_zeroed(
with freeze_time(time_400): with freeze_time(time_400):
async_fire_time_changed(hass, time_400) async_fire_time_changed(hass, time_400)
await hass.async_block_till_done(wait_background_tasks=True) 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 ( assert (
hass.states.get("sensor.heatpump_compressor_today2").state 0.499
== "1.83333333333333" < float(hass.states.get("sensor.heatpump_compressor_today2").state)
< 0.501
) )
hass.states.async_set("binary_sensor.heatpump_compressor_state", "on") hass.states.async_set("binary_sensor.heatpump_compressor_state", "on")
await async_wait_recording_done(hass) await async_wait_recording_done(hass)
@ -1568,10 +1570,11 @@ async def test_end_time_with_microseconds_zeroed(
with freeze_time(time_600): with freeze_time(time_600):
async_fire_time_changed(hass, time_600) async_fire_time_changed(hass, time_600)
await hass.async_block_till_done(wait_background_tasks=True) 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 ( assert (
hass.states.get("sensor.heatpump_compressor_today2").state 2.499
== "3.83333333333333" < float(hass.states.get("sensor.heatpump_compressor_today2").state)
< 2.501
) )
rolled_to_next_day = start_of_today + timedelta(days=1) rolled_to_next_day = start_of_today + timedelta(days=1)