mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Optimize history_stats efficiency and database usage (#7858)
This commit is contained in:
parent
2065426b16
commit
1b5f6aa1b9
@ -44,8 +44,6 @@ UNITS = {
|
|||||||
}
|
}
|
||||||
ICON = 'mdi:chart-line'
|
ICON = 'mdi:chart-line'
|
||||||
|
|
||||||
ATTR_START = 'from'
|
|
||||||
ATTR_END = 'to'
|
|
||||||
ATTR_VALUE = 'value'
|
ATTR_VALUE = 'value'
|
||||||
|
|
||||||
|
|
||||||
@ -157,12 +155,9 @@ class HistoryStatsSensor(Entity):
|
|||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes of the sensor."""
|
"""Return the state attributes of the sensor."""
|
||||||
start, end = self._period
|
|
||||||
hsh = HistoryStatsHelper
|
hsh = HistoryStatsHelper
|
||||||
return {
|
return {
|
||||||
ATTR_VALUE: hsh.pretty_duration(self.value),
|
ATTR_VALUE: hsh.pretty_duration(self.value),
|
||||||
ATTR_START: start.strftime('%Y-%m-%d %H:%M:%S'),
|
|
||||||
ATTR_END: end.strftime('%Y-%m-%d %H:%M:%S'),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -172,13 +167,33 @@ class HistoryStatsSensor(Entity):
|
|||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Get the latest data and updates the states."""
|
"""Get the latest data and updates the states."""
|
||||||
|
# Get previous values of start and end
|
||||||
|
p_start, p_end = self._period
|
||||||
|
|
||||||
# Parse templates
|
# Parse templates
|
||||||
self.update_period()
|
self.update_period()
|
||||||
start, end = self._period
|
start, end = self._period
|
||||||
|
|
||||||
# Convert to UTC
|
# Convert times to UTC
|
||||||
start = dt_util.as_utc(start)
|
start = dt_util.as_utc(start)
|
||||||
end = dt_util.as_utc(end)
|
end = dt_util.as_utc(end)
|
||||||
|
p_start = dt_util.as_utc(p_start)
|
||||||
|
p_end = dt_util.as_utc(p_end)
|
||||||
|
now = dt_util.as_utc(datetime.datetime.now())
|
||||||
|
|
||||||
|
# Compute integer timestamps
|
||||||
|
start_timestamp = math.floor(dt_util.as_timestamp(start))
|
||||||
|
end_timestamp = math.floor(dt_util.as_timestamp(end))
|
||||||
|
p_start_timestamp = math.floor(dt_util.as_timestamp(p_start))
|
||||||
|
p_end_timestamp = math.floor(dt_util.as_timestamp(p_end))
|
||||||
|
now_timestamp = math.floor(dt_util.as_timestamp(now))
|
||||||
|
|
||||||
|
# If period has not changed and current time after the period end...
|
||||||
|
if start_timestamp == p_start_timestamp and \
|
||||||
|
end_timestamp == p_end_timestamp and \
|
||||||
|
end_timestamp <= now_timestamp:
|
||||||
|
# Don't compute anything as the value cannot have changed
|
||||||
|
return
|
||||||
|
|
||||||
# Get history between start and end
|
# Get history between start and end
|
||||||
history_list = history.state_changes_during_period(
|
history_list = history.state_changes_during_period(
|
||||||
@ -191,7 +206,7 @@ class HistoryStatsSensor(Entity):
|
|||||||
last_state = history.get_state(self.hass, start, self._entity_id)
|
last_state = history.get_state(self.hass, start, self._entity_id)
|
||||||
last_state = (last_state is not None and
|
last_state = (last_state is not None and
|
||||||
last_state == self._entity_state)
|
last_state == self._entity_state)
|
||||||
last_time = dt_util.as_timestamp(start)
|
last_time = start_timestamp
|
||||||
elapsed = 0
|
elapsed = 0
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
@ -210,8 +225,7 @@ class HistoryStatsSensor(Entity):
|
|||||||
|
|
||||||
# Count time elapsed between last history state and end of measure
|
# Count time elapsed between last history state and end of measure
|
||||||
if last_state:
|
if last_state:
|
||||||
measure_end = min(dt_util.as_timestamp(end), dt_util.as_timestamp(
|
measure_end = min(end_timestamp, now_timestamp)
|
||||||
datetime.datetime.now()))
|
|
||||||
elapsed += measure_end - last_time
|
elapsed += measure_end - last_time
|
||||||
|
|
||||||
# Save value in hours
|
# Save value in hours
|
||||||
@ -279,13 +293,11 @@ class HistoryStatsHelper:
|
|||||||
hours, seconds = divmod(seconds, 3600)
|
hours, seconds = divmod(seconds, 3600)
|
||||||
minutes, seconds = divmod(seconds, 60)
|
minutes, seconds = divmod(seconds, 60)
|
||||||
if days > 0:
|
if days > 0:
|
||||||
return '%dd %dh %dm %ds' % (days, hours, minutes, seconds)
|
return '%dd %dh %dm' % (days, hours, minutes)
|
||||||
elif hours > 0:
|
elif hours > 0:
|
||||||
return '%dh %dm %ds' % (hours, minutes, seconds)
|
return '%dh %dm' % (hours, minutes)
|
||||||
elif minutes > 0:
|
|
||||||
return '%dm %ds' % (minutes, seconds)
|
|
||||||
else:
|
else:
|
||||||
return '%ds' % (seconds,)
|
return '%dm' % minutes
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pretty_ratio(value, period):
|
def pretty_ratio(value, period):
|
||||||
|
@ -58,16 +58,29 @@ class TestHistoryStatsSensor(unittest.TestCase):
|
|||||||
self.hass, 'test', 'on', None, today, duration, 'time', 'test')
|
self.hass, 'test', 'on', None, today, duration, 'time', 'test')
|
||||||
|
|
||||||
sensor1.update_period()
|
sensor1.update_period()
|
||||||
|
sensor1_start, sensor1_end = sensor1._period
|
||||||
sensor2.update_period()
|
sensor2.update_period()
|
||||||
|
sensor2_start, sensor2_end = sensor2._period
|
||||||
|
|
||||||
self.assertEqual(
|
# Start = 00:00:00
|
||||||
sensor1.device_state_attributes['from'][-8:], '00:00:00')
|
self.assertEqual(sensor1_start.hour, 0)
|
||||||
self.assertEqual(
|
self.assertEqual(sensor1_start.minute, 0)
|
||||||
sensor1.device_state_attributes['to'][-8:], '02:01:00')
|
self.assertEqual(sensor1_start.second, 0)
|
||||||
self.assertEqual(
|
|
||||||
sensor2.device_state_attributes['from'][-8:], '21:59:00')
|
# End = 02:01:00
|
||||||
self.assertEqual(
|
self.assertEqual(sensor1_end.hour, 2)
|
||||||
sensor2.device_state_attributes['to'][-8:], '00:00:00')
|
self.assertEqual(sensor1_end.minute, 1)
|
||||||
|
self.assertEqual(sensor1_end.second, 0)
|
||||||
|
|
||||||
|
# Start = 21:59:00
|
||||||
|
self.assertEqual(sensor2_start.hour, 21)
|
||||||
|
self.assertEqual(sensor2_start.minute, 59)
|
||||||
|
self.assertEqual(sensor2_start.second, 0)
|
||||||
|
|
||||||
|
# End = 00:00:00
|
||||||
|
self.assertEqual(sensor2_end.hour, 0)
|
||||||
|
self.assertEqual(sensor2_end.minute, 0)
|
||||||
|
self.assertEqual(sensor2_end.second, 0)
|
||||||
|
|
||||||
def test_measure(self):
|
def test_measure(self):
|
||||||
"""Test the history statistics sensor measure."""
|
"""Test the history statistics sensor measure."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user