From d4c7515681c322e34c36686f77421b9135775a49 Mon Sep 17 00:00:00 2001 From: Andrey Kupreychik Date: Fri, 25 Jan 2019 17:07:45 +0700 Subject: [PATCH] Add time_throttle filter to sensor.filter (#20334) * Added time_throttle filter * Added time_throttle filter test * Updated comments for time_throttle filter --- homeassistant/components/sensor/filter.py | 36 ++++++++++++++++++++++- tests/components/sensor/test_filter.py | 14 ++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/filter.py b/homeassistant/components/sensor/filter.py index 42589d6bed3..3d05dd28e79 100644 --- a/homeassistant/components/sensor/filter.py +++ b/homeassistant/components/sensor/filter.py @@ -32,6 +32,7 @@ FILTER_NAME_RANGE = 'range' FILTER_NAME_LOWPASS = 'lowpass' FILTER_NAME_OUTLIER = 'outlier' FILTER_NAME_THROTTLE = 'throttle' +FILTER_NAME_TIME_THROTTLE = 'time_throttle' FILTER_NAME_TIME_SMA = 'time_simple_moving_average' FILTERS = Registry() @@ -101,6 +102,12 @@ FILTER_THROTTLE_SCHEMA = FILTER_SCHEMA.extend({ default=DEFAULT_WINDOW_SIZE): vol.Coerce(int), }) +FILTER_TIME_THROTTLE_SCHEMA = FILTER_SCHEMA.extend({ + vol.Required(CONF_FILTER_NAME): FILTER_NAME_TIME_THROTTLE, + vol.Required(CONF_FILTER_WINDOW_SIZE): vol.All(cv.time_period, + cv.positive_timedelta) +}) + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Optional(CONF_NAME): cv.string, @@ -109,6 +116,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ FILTER_LOWPASS_SCHEMA, FILTER_TIME_SMA_SCHEMA, FILTER_THROTTLE_SCHEMA, + FILTER_TIME_THROTTLE_SCHEMA, FILTER_RANGE_SCHEMA)]) }) @@ -444,7 +452,7 @@ class TimeSMAFilter(Filter): The window_size is determined by time, and SMA is time weighted. Args: - variant (enum): type of argorithm used to connect discrete values + type (enum): type of algorithm used to connect discrete values """ def __init__(self, window_size, precision, entity, @@ -502,3 +510,29 @@ class ThrottleFilter(Filter): self._skip_processing = True return new_state + + +@FILTERS.register(FILTER_NAME_TIME_THROTTLE) +class TimeThrottleFilter(Filter): + """Time Throttle Filter. + + One sample per time period. + """ + + def __init__(self, window_size, precision, entity): + """Initialize Filter.""" + super().__init__(FILTER_NAME_TIME_THROTTLE, + window_size, precision, entity) + self._time_window = window_size + self._last_emitted_at = None + + def _filter_state(self, new_state): + """Implement the filter.""" + window_start = new_state.timestamp - self._time_window + if not self._last_emitted_at or self._last_emitted_at <= window_start: + self._last_emitted_at = new_state.timestamp + self._skip_processing = False + else: + self._skip_processing = True + + return new_state diff --git a/tests/components/sensor/test_filter.py b/tests/components/sensor/test_filter.py index 3d44b7d131d..29718314ef4 100644 --- a/tests/components/sensor/test_filter.py +++ b/tests/components/sensor/test_filter.py @@ -5,7 +5,7 @@ from unittest.mock import patch from homeassistant.components.sensor.filter import ( LowPassFilter, OutlierFilter, ThrottleFilter, TimeSMAFilter, - RangeFilter) + RangeFilter, TimeThrottleFilter) import homeassistant.util.dt as dt_util from homeassistant.setup import setup_component import homeassistant.core as ha @@ -178,6 +178,18 @@ class TestFilterSensor(unittest.TestCase): filtered.append(new_state) assert [20, 21] == [f.state for f in filtered] + def test_time_throttle(self): + """Test if lowpass filter works.""" + filt = TimeThrottleFilter(window_size=timedelta(minutes=2), + precision=2, + entity=None) + filtered = [] + for state in self.values: + new_state = filt.filter_state(state) + if not filt.skip_processing: + filtered.append(new_state) + assert [20, 18, 22] == [f.state for f in filtered] + def test_time_sma(self): """Test if time_sma filter works.""" filt = TimeSMAFilter(window_size=timedelta(minutes=2),