Switch underlying history stats calculation to use seconds (#77857)

* Switch history stats to report in seconds

Because hours were previously used, the data would always be off because
of the loss of resolution when the time being tracked was in a window
of more than 12s

* Apply suggestions from code review

* Update homeassistant/components/history_stats/sensor.py

* tweak
This commit is contained in:
J. Nick Koston 2023-03-12 17:05:48 -10:00 committed by GitHub
parent 4dcf7c6267
commit e34853a82a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 12 additions and 12 deletions

View File

@ -18,7 +18,7 @@ MIN_TIME_UTC = datetime.datetime.min.replace(tzinfo=dt_util.UTC)
class HistoryStatsState:
"""The current stats of the history stats."""
hours_matched: float | None
seconds_matched: float | None
match_count: int | None
period: tuple[datetime.datetime, datetime.datetime]
@ -125,12 +125,12 @@ class HistoryStats:
await self._async_history_from_db(current_period_start, current_period_end)
self._previous_run_before_start = False
hours_matched, match_count = self._async_compute_hours_and_changes(
seconds_matched, match_count = self._async_compute_seconds_and_changes(
now_timestamp,
current_period_start_timestamp,
current_period_end_timestamp,
)
self._state = HistoryStatsState(hours_matched, match_count, self._period)
self._state = HistoryStatsState(seconds_matched, match_count, self._period)
return self._state
async def _async_history_from_db(
@ -162,10 +162,10 @@ class HistoryStats:
no_attributes=True,
).get(self.entity_id, [])
def _async_compute_hours_and_changes(
def _async_compute_seconds_and_changes(
self, now_timestamp: float, start_timestamp: float, end_timestamp: float
) -> tuple[float, int]:
"""Compute the hours matched and changes from the history list and first state."""
"""Compute the seconds matched and changes from the history list and first state."""
# 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
@ -195,6 +195,6 @@ class HistoryStats:
measure_end = min(end_timestamp, now_timestamp)
elapsed += measure_end - last_state_change_timestamp
# Save value in hours
hours_matched = elapsed / 3600
return hours_matched, match_count
# Save value in seconds
seconds_matched = elapsed
return seconds_matched, match_count

View File

@ -79,7 +79,7 @@ def pretty_ratio(
if len(period) != 2 or period[0] == period[1]:
return 0.0
ratio = 100 * 3600 * value / (period[1] - period[0]).total_seconds()
ratio = 100 * value / (period[1] - period[0]).total_seconds()
return round(ratio, 1)

View File

@ -163,13 +163,13 @@ class HistoryStatsSensor(HistoryStatsSensorBase):
def _process_update(self) -> None:
"""Process an update from the coordinator."""
state = self.coordinator.data
if state is None or state.hours_matched is None:
if state is None or state.seconds_matched is None:
self._attr_native_value = None
return
if self._type == CONF_TYPE_TIME:
self._attr_native_value = round(state.hours_matched, 2)
self._attr_native_value = round(state.seconds_matched / 3600, 2)
elif self._type == CONF_TYPE_RATIO:
self._attr_native_value = pretty_ratio(state.hours_matched, state.period)
self._attr_native_value = pretty_ratio(state.seconds_matched, state.period)
elif self._type == CONF_TYPE_COUNT:
self._attr_native_value = state.match_count