Optimize history_stats efficiency and database usage (#7858)
parent
2065426b16
commit
1b5f6aa1b9
|
@ -44,8 +44,6 @@ UNITS = {
|
|||
}
|
||||
ICON = 'mdi:chart-line'
|
||||
|
||||
ATTR_START = 'from'
|
||||
ATTR_END = 'to'
|
||||
ATTR_VALUE = 'value'
|
||||
|
||||
|
||||
|
@ -157,12 +155,9 @@ class HistoryStatsSensor(Entity):
|
|||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the sensor."""
|
||||
start, end = self._period
|
||||
hsh = HistoryStatsHelper
|
||||
return {
|
||||
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
|
||||
|
@ -172,13 +167,33 @@ class HistoryStatsSensor(Entity):
|
|||
|
||||
def update(self):
|
||||
"""Get the latest data and updates the states."""
|
||||
# Get previous values of start and end
|
||||
p_start, p_end = self._period
|
||||
|
||||
# Parse templates
|
||||
self.update_period()
|
||||
start, end = self._period
|
||||
|
||||
# Convert to UTC
|
||||
# Convert times to UTC
|
||||
start = dt_util.as_utc(start)
|
||||
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
|
||||
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 = (last_state is not None and
|
||||
last_state == self._entity_state)
|
||||
last_time = dt_util.as_timestamp(start)
|
||||
last_time = start_timestamp
|
||||
elapsed = 0
|
||||
count = 0
|
||||
|
||||
|
@ -210,8 +225,7 @@ class HistoryStatsSensor(Entity):
|
|||
|
||||
# Count time elapsed between last history state and end of measure
|
||||
if last_state:
|
||||
measure_end = min(dt_util.as_timestamp(end), dt_util.as_timestamp(
|
||||
datetime.datetime.now()))
|
||||
measure_end = min(end_timestamp, now_timestamp)
|
||||
elapsed += measure_end - last_time
|
||||
|
||||
# Save value in hours
|
||||
|
@ -279,13 +293,11 @@ class HistoryStatsHelper:
|
|||
hours, seconds = divmod(seconds, 3600)
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
if days > 0:
|
||||
return '%dd %dh %dm %ds' % (days, hours, minutes, seconds)
|
||||
return '%dd %dh %dm' % (days, hours, minutes)
|
||||
elif hours > 0:
|
||||
return '%dh %dm %ds' % (hours, minutes, seconds)
|
||||
elif minutes > 0:
|
||||
return '%dm %ds' % (minutes, seconds)
|
||||
return '%dh %dm' % (hours, minutes)
|
||||
else:
|
||||
return '%ds' % (seconds,)
|
||||
return '%dm' % minutes
|
||||
|
||||
@staticmethod
|
||||
def pretty_ratio(value, period):
|
||||
|
|
|
@ -58,16 +58,29 @@ class TestHistoryStatsSensor(unittest.TestCase):
|
|||
self.hass, 'test', 'on', None, today, duration, 'time', 'test')
|
||||
|
||||
sensor1.update_period()
|
||||
sensor1_start, sensor1_end = sensor1._period
|
||||
sensor2.update_period()
|
||||
sensor2_start, sensor2_end = sensor2._period
|
||||
|
||||
self.assertEqual(
|
||||
sensor1.device_state_attributes['from'][-8:], '00:00:00')
|
||||
self.assertEqual(
|
||||
sensor1.device_state_attributes['to'][-8:], '02:01:00')
|
||||
self.assertEqual(
|
||||
sensor2.device_state_attributes['from'][-8:], '21:59:00')
|
||||
self.assertEqual(
|
||||
sensor2.device_state_attributes['to'][-8:], '00:00:00')
|
||||
# Start = 00:00:00
|
||||
self.assertEqual(sensor1_start.hour, 0)
|
||||
self.assertEqual(sensor1_start.minute, 0)
|
||||
self.assertEqual(sensor1_start.second, 0)
|
||||
|
||||
# End = 02:01:00
|
||||
self.assertEqual(sensor1_end.hour, 2)
|
||||
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):
|
||||
"""Test the history statistics sensor measure."""
|
||||
|
|
Loading…
Reference in New Issue