Init statistics sensor upon HASS start (#18236)

* Update query to include maxAge

Updated the query from recorded to include MaxAge if set; reducing the amount of records retrieved that would otherwise be purged anyway for the sensor.

* Initialization upon HASS start

Register the state listener and read previous information from recorder once HASS is started.

* Updated test_statistics.py for HASS start

Updated test_statistics.py to start HASS and wait it is completed before running test.

* Added newline in docstring

Added newline in docstring.

* Added start of HASS to test_initialize_from_database_with_maxage

Added start of HASS to new test test_initialize_from_database_with_maxage.

* Updates based on review

Following updates based on review:
-) Removed self._hass and passing hass
-) Changed async_add_job to async_create_task
-) For state update, calling async_schedule_update_ha_state
This commit is contained in:
ehendrix23 2018-11-14 07:13:32 -07:00 committed by Martin Hjelmare
parent d2907b8e53
commit d2e102ee2f
2 changed files with 52 additions and 13 deletions

View File

@ -13,7 +13,8 @@ import voluptuous as vol
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import ( from homeassistant.const import (
CONF_NAME, CONF_ENTITY_ID, STATE_UNKNOWN, ATTR_UNIT_OF_MEASUREMENT) CONF_NAME, CONF_ENTITY_ID, EVENT_HOMEASSISTANT_START, STATE_UNKNOWN,
ATTR_UNIT_OF_MEASUREMENT)
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
@ -66,7 +67,7 @@ async def async_setup_platform(hass, config, async_add_entities,
max_age = config.get(CONF_MAX_AGE, None) max_age = config.get(CONF_MAX_AGE, None)
precision = config.get(CONF_PRECISION) precision = config.get(CONF_PRECISION)
async_add_entities([StatisticsSensor(hass, entity_id, name, sampling_size, async_add_entities([StatisticsSensor(entity_id, name, sampling_size,
max_age, precision)], True) max_age, precision)], True)
return True return True
@ -75,10 +76,9 @@ async def async_setup_platform(hass, config, async_add_entities,
class StatisticsSensor(Entity): class StatisticsSensor(Entity):
"""Representation of a Statistics sensor.""" """Representation of a Statistics sensor."""
def __init__(self, hass, entity_id, name, sampling_size, max_age, def __init__(self, entity_id, name, sampling_size, max_age,
precision): precision):
"""Initialize the Statistics sensor.""" """Initialize the Statistics sensor."""
self._hass = hass
self._entity_id = entity_id self._entity_id = entity_id
self.is_binary = True if self._entity_id.split('.')[0] == \ self.is_binary = True if self._entity_id.split('.')[0] == \
'binary_sensor' else False 'binary_sensor' else False
@ -99,10 +99,8 @@ class StatisticsSensor(Entity):
self.min_age = self.max_age = None self.min_age = self.max_age = None
self.change = self.average_change = self.change_rate = None self.change = self.average_change = self.change_rate = None
if 'recorder' in self._hass.config.components: async def async_added_to_hass(self):
# only use the database if it's configured """Register callbacks."""
hass.async_add_job(self._initialize_from_database)
@callback @callback
def async_stats_sensor_state_listener(entity, old_state, new_state): def async_stats_sensor_state_listener(entity, old_state, new_state):
"""Handle the sensor state changes.""" """Handle the sensor state changes."""
@ -111,10 +109,24 @@ class StatisticsSensor(Entity):
self._add_state_to_queue(new_state) self._add_state_to_queue(new_state)
hass.async_add_job(self.async_update_ha_state, True) self.async_schedule_update_ha_state(True)
async_track_state_change( @callback
hass, entity_id, async_stats_sensor_state_listener) def async_stats_sensor_startup(event):
"""Add listener and get recorded state."""
_LOGGER.debug("Startup for %s", self.entity_id)
async_track_state_change(
self.hass, self._entity_id, async_stats_sensor_state_listener)
if 'recorder' in self.hass.config.components:
# only use the database if it's configured
self.hass.async_create_task(
self._async_initialize_from_database()
)
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, async_stats_sensor_startup)
def _add_state_to_queue(self, new_state): def _add_state_to_queue(self, new_state):
try: try:
@ -241,7 +253,7 @@ class StatisticsSensor(Entity):
self.change = self.average_change = STATE_UNKNOWN self.change = self.average_change = STATE_UNKNOWN
self.change_rate = STATE_UNKNOWN self.change_rate = STATE_UNKNOWN
async def _initialize_from_database(self): async def _async_initialize_from_database(self):
"""Initialize the list of states from the database. """Initialize the list of states from the database.
The query will get the list of states in DESCENDING order so that we The query will get the list of states in DESCENDING order so that we
@ -255,7 +267,7 @@ class StatisticsSensor(Entity):
_LOGGER.debug("%s: initializing values from the database", _LOGGER.debug("%s: initializing values from the database",
self.entity_id) self.entity_id)
with session_scope(hass=self._hass) as session: with session_scope(hass=self.hass) as session:
query = session.query(States)\ query = session.query(States)\
.filter(States.entity_id == self._entity_id.lower()) .filter(States.entity_id == self._entity_id.lower())
@ -275,5 +287,7 @@ class StatisticsSensor(Entity):
for state in reversed(states): for state in reversed(states):
self._add_state_to_queue(state) self._add_state_to_queue(state)
self.async_schedule_update_ha_state(True)
_LOGGER.debug("%s: initializing from database completed", _LOGGER.debug("%s: initializing from database completed",
self.entity_id) self.entity_id)

View File

@ -49,6 +49,9 @@ class TestStatisticsSensor(unittest.TestCase):
} }
}) })
self.hass.start()
self.hass.block_till_done()
for value in values: for value in values:
self.hass.states.set('binary_sensor.test_monitored', value) self.hass.states.set('binary_sensor.test_monitored', value)
self.hass.block_till_done() self.hass.block_till_done()
@ -67,6 +70,9 @@ class TestStatisticsSensor(unittest.TestCase):
} }
}) })
self.hass.start()
self.hass.block_till_done()
for value in self.values: for value in self.values:
self.hass.states.set('sensor.test_monitored', value, self.hass.states.set('sensor.test_monitored', value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
@ -100,6 +106,9 @@ class TestStatisticsSensor(unittest.TestCase):
} }
}) })
self.hass.start()
self.hass.block_till_done()
for value in self.values: for value in self.values:
self.hass.states.set('sensor.test_monitored', value, self.hass.states.set('sensor.test_monitored', value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
@ -121,6 +130,9 @@ class TestStatisticsSensor(unittest.TestCase):
} }
}) })
self.hass.start()
self.hass.block_till_done()
for value in self.values[-3:]: # just the last 3 will do for value in self.values[-3:]: # just the last 3 will do
self.hass.states.set('sensor.test_monitored', value, self.hass.states.set('sensor.test_monitored', value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
@ -162,6 +174,9 @@ class TestStatisticsSensor(unittest.TestCase):
} }
}) })
self.hass.start()
self.hass.block_till_done()
for value in self.values: for value in self.values:
self.hass.states.set('sensor.test_monitored', value, self.hass.states.set('sensor.test_monitored', value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
@ -194,6 +209,9 @@ class TestStatisticsSensor(unittest.TestCase):
} }
}) })
self.hass.start()
self.hass.block_till_done()
for value in self.values: for value in self.values:
self.hass.states.set('sensor.test_monitored', value, self.hass.states.set('sensor.test_monitored', value,
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
@ -231,6 +249,10 @@ class TestStatisticsSensor(unittest.TestCase):
'sampling_size': 100, 'sampling_size': 100,
} }
}) })
self.hass.start()
self.hass.block_till_done()
# check if the result is as in test_sensor_source() # check if the result is as in test_sensor_source()
state = self.hass.states.get('sensor.test_mean') state = self.hass.states.get('sensor.test_mean')
assert str(self.mean) == state.state assert str(self.mean) == state.state
@ -284,6 +306,9 @@ class TestStatisticsSensor(unittest.TestCase):
} }
}) })
self.hass.start()
self.hass.block_till_done()
# check if the result is as in test_sensor_source() # check if the result is as in test_sensor_source()
state = self.hass.states.get('sensor.test_mean') state = self.hass.states.get('sensor.test_mean')