Optimize history_stats efficiency and database usage (#7858)

This commit is contained in:
Boris K 2017-06-02 07:52:55 +02:00 committed by Paulus Schoutsen
parent 2065426b16
commit 1b5f6aa1b9
2 changed files with 47 additions and 22 deletions

View File

@ -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):

View File

@ -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."""