diff --git a/homeassistant/components/sensor/statistics.py b/homeassistant/components/sensor/statistics.py index 34d3cabf26b..a6932e2aebb 100644 --- a/homeassistant/components/sensor/statistics.py +++ b/homeassistant/components/sensor/statistics.py @@ -19,6 +19,7 @@ from homeassistant.core import callback from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_state_change from homeassistant.util import dt as dt_util +from homeassistant.components.recorder.util import session_scope, execute _LOGGER = logging.getLogger(__name__) @@ -88,6 +89,10 @@ class StatisticsSensor(Entity): self.min = self.max = self.total = self.count = 0 self.average_change = self.change = 0 + if 'recorder' in self._hass.config.components: + # only use the database if it's configured + hass.async_add_job(self._initzialize_from_database) + @callback # pylint: disable=invalid-name def async_stats_sensor_state_listener(entity, old_state, new_state): @@ -95,20 +100,23 @@ class StatisticsSensor(Entity): self._unit_of_measurement = new_state.attributes.get( ATTR_UNIT_OF_MEASUREMENT) - try: - self.states.append(float(new_state.state)) - if self._max_age is not None: - now = dt_util.utcnow() - self.ages.append(now) - self.count = self.count + 1 - except ValueError: - self.count = self.count + 1 + self._add_state_to_queue(new_state) hass.async_add_job(self.async_update_ha_state, True) async_track_state_change( hass, entity_id, async_stats_sensor_state_listener) + def _add_state_to_queue(self, new_state): + try: + self.states.append(float(new_state.state)) + if self._max_age is not None: + now = dt_util.utcnow() + self.ages.append(now) + self.count = self.count + 1 + except ValueError: + self.count = self.count + 1 + @property def name(self): """Return the name of the sensor.""" @@ -187,3 +195,27 @@ class StatisticsSensor(Entity): else: self.min = self.max = self.total = STATE_UNKNOWN self.average_change = self.change = STATE_UNKNOWN + + @asyncio.coroutine + def _initzialize_from_database(self): + """Initialize the list of states from the database. + + The query will get the list of states in DESCENDING order so that we + can limit the result to self._sample_size. Afterwards reverse the + list so that we get it in the right order again. + """ + from homeassistant.components.recorder.models import States + _LOGGER.debug("initializing values for %s from the database", + self.entity_id) + + with session_scope(hass=self._hass) as session: + query = session.query(States)\ + .filter(States.entity_id == self._entity_id.lower())\ + .order_by(States.last_updated.desc())\ + .limit(self._sampling_size) + states = execute(query) + + for state in reversed(states): + self._add_state_to_queue(state) + + _LOGGER.debug("initializing from database completed") diff --git a/tests/components/sensor/test_statistics.py b/tests/components/sensor/test_statistics.py index ba71c6e3993..bfb8fb61f9b 100644 --- a/tests/components/sensor/test_statistics.py +++ b/tests/components/sensor/test_statistics.py @@ -8,6 +8,8 @@ from homeassistant.util import dt as dt_util from tests.common import get_test_home_assistant from unittest.mock import patch from datetime import datetime, timedelta +from tests.common import init_recorder_component +from homeassistant.components import recorder class TestStatisticsSensor(unittest.TestCase): @@ -135,3 +137,28 @@ class TestStatisticsSensor(unittest.TestCase): self.assertEqual(6, state.attributes.get('min_value')) self.assertEqual(14, state.attributes.get('max_value')) + + def test_initialize_from_database(self): + """Test initializing the statistics from the database.""" + # enable the recorder + init_recorder_component(self.hass) + # store some values + for value in self.values: + self.hass.states.set('sensor.test_monitored', value, + {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + self.hass.block_till_done() + # wait for the recorder to really store the data + self.hass.data[recorder.DATA_INSTANCE].block_till_done() + # only now create the statistics component, so that it must read the + # data from the database + assert setup_component(self.hass, 'sensor', { + 'sensor': { + 'platform': 'statistics', + 'name': 'test', + 'entity_id': 'sensor.test_monitored', + 'sampling_size': 100, + } + }) + # check if the result is as in test_sensor_source() + state = self.hass.states.get('sensor.test_mean') + self.assertEqual(str(self.mean), state.state)