diff --git a/homeassistant/components/sensor/statistics.py b/homeassistant/components/sensor/statistics.py index 6181a4ae094..26253abd484 100644 --- a/homeassistant/components/sensor/statistics.py +++ b/homeassistant/components/sensor/statistics.py @@ -247,6 +247,9 @@ class StatisticsSensor(Entity): 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. + + If MaxAge is provided then query will restrict to entries younger then + current datetime - MaxAge. """ from homeassistant.components.recorder.models import States _LOGGER.debug("%s: initializing values from the database", @@ -254,7 +257,17 @@ class StatisticsSensor(Entity): with session_scope(hass=self._hass) as session: query = session.query(States)\ - .filter(States.entity_id == self._entity_id.lower())\ + .filter(States.entity_id == self._entity_id.lower()) + + if self._max_age is not None: + records_older_then = dt_util.utcnow() - self._max_age + _LOGGER.debug("%s: retrieve records not older then %s", + self.entity_id, records_older_then) + query = query.filter(States.last_updated >= records_older_then) + else: + _LOGGER.debug("%s: retrieving all records.", self.entity_id) + + query = query\ .order_by(States.last_updated.desc())\ .limit(self._sampling_size) states = execute(query) diff --git a/tests/components/sensor/test_statistics.py b/tests/components/sensor/test_statistics.py index 0bf9ecd8c6f..9188513b861 100644 --- a/tests/components/sensor/test_statistics.py +++ b/tests/components/sensor/test_statistics.py @@ -3,6 +3,7 @@ import unittest import statistics from homeassistant.setup import setup_component +from homeassistant.components.sensor.statistics import StatisticsSensor from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS, STATE_UNKNOWN) from homeassistant.util import dt as dt_util @@ -233,3 +234,61 @@ class TestStatisticsSensor(unittest.TestCase): # check if the result is as in test_sensor_source() state = self.hass.states.get('sensor.test_mean') assert str(self.mean) == state.state + + def test_initialize_from_database_with_maxage(self): + """Test initializing the statistics from the database.""" + mock_data = { + 'return_time': datetime(2017, 8, 2, 12, 23, 42, + tzinfo=dt_util.UTC), + } + + def mock_now(): + return mock_data['return_time'] + + # Testing correct retrieval from recorder, thus we do not + # want purging to occur within the class itself. + def mock_purge(self): + return + + # Set maximum age to 3 hours. + max_age = 3 + # Determine what our minimum age should be based on test values. + expected_min_age = mock_data['return_time'] + \ + timedelta(hours=len(self.values) - max_age) + + # enable the recorder + init_recorder_component(self.hass) + + with patch('homeassistant.components.sensor.statistics.dt_util.utcnow', + new=mock_now), \ + patch.object(StatisticsSensor, '_purge_old', mock_purge): + # 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() + # insert the next value 1 hour later + mock_data['return_time'] += timedelta(hours=1) + + # 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, + 'max_age': {'hours': max_age} + } + }) + + # check if the result is as in test_sensor_source() + state = self.hass.states.get('sensor.test_mean') + + assert expected_min_age == state.attributes.get('min_age') + # The max_age timestamp should be 1 hour before what we have right + # now in mock_data['return_time']. + assert mock_data['return_time'] == state.attributes.get('max_age') +\ + timedelta(hours=1)