From e34853a82ad247f99b1059640fc99220cd056e74 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Mar 2023 17:05:48 -1000 Subject: [PATCH] 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 --- homeassistant/components/history_stats/data.py | 16 ++++++++-------- .../components/history_stats/helpers.py | 2 +- homeassistant/components/history_stats/sensor.py | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/history_stats/data.py b/homeassistant/components/history_stats/data.py index 33f32e72292..d9b331d82bb 100644 --- a/homeassistant/components/history_stats/data.py +++ b/homeassistant/components/history_stats/data.py @@ -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 diff --git a/homeassistant/components/history_stats/helpers.py b/homeassistant/components/history_stats/helpers.py index 23143984f48..0c914e1fd41 100644 --- a/homeassistant/components/history_stats/helpers.py +++ b/homeassistant/components/history_stats/helpers.py @@ -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) diff --git a/homeassistant/components/history_stats/sensor.py b/homeassistant/components/history_stats/sensor.py index fc3aedfde24..2b02be17e9a 100644 --- a/homeassistant/components/history_stats/sensor.py +++ b/homeassistant/components/history_stats/sensor.py @@ -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