mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 08:17:08 +00:00
Fixes the sensor.filter outlier filter (handle step-changes correctly) (#21332)
* Fix outlier filter median return, Add/update filter outlier tests * Switch outlier filter to store raw vals (handles step-changes correctly) * Filter store_raw as attribute instead of filter_state parameter * Fix linting issues
This commit is contained in:
parent
954bd4e13b
commit
616c7628d7
@ -313,6 +313,7 @@ class Filter:
|
||||
self._entity = entity
|
||||
self._skip_processing = False
|
||||
self._window_size = window_size
|
||||
self._store_raw = False
|
||||
|
||||
@property
|
||||
def window_size(self):
|
||||
@ -337,7 +338,10 @@ class Filter:
|
||||
"""Implement a common interface for filters."""
|
||||
filtered = self._filter_state(FilterState(new_state))
|
||||
filtered.set_precision(self.precision)
|
||||
self.states.append(copy(filtered))
|
||||
if self._store_raw:
|
||||
self.states.append(copy(FilterState(new_state)))
|
||||
else:
|
||||
self.states.append(copy(filtered))
|
||||
new_state.state = filtered.state
|
||||
return new_state
|
||||
|
||||
@ -402,12 +406,14 @@ class OutlierFilter(Filter):
|
||||
super().__init__(FILTER_NAME_OUTLIER, window_size, precision, entity)
|
||||
self._radius = radius
|
||||
self._stats_internal = Counter()
|
||||
self._store_raw = True
|
||||
|
||||
def _filter_state(self, new_state):
|
||||
"""Implement the outlier filter."""
|
||||
median = statistics.median([s.state for s in self.states]) \
|
||||
if self.states else 0
|
||||
if (len(self.states) == self.states.maxlen and
|
||||
abs(new_state.state -
|
||||
statistics.median([s.state for s in self.states])) >
|
||||
abs(new_state.state - median) >
|
||||
self._radius):
|
||||
|
||||
self._stats_internal['erasures'] += 1
|
||||
@ -415,7 +421,7 @@ class OutlierFilter(Filter):
|
||||
_LOGGER.debug("Outlier nr. %s in %s: %s",
|
||||
self._stats_internal['erasures'],
|
||||
self._entity, new_state)
|
||||
return self.states[-1]
|
||||
new_state.state = median
|
||||
return new_state
|
||||
|
||||
|
||||
|
@ -106,6 +106,23 @@ class TestFilterSensor(unittest.TestCase):
|
||||
precision=2,
|
||||
entity=None,
|
||||
radius=4.0)
|
||||
for state in self.values:
|
||||
filtered = filt.filter_state(state)
|
||||
assert 21 == filtered.state
|
||||
|
||||
def test_outlier_step(self):
|
||||
"""
|
||||
Test step-change handling in outlier.
|
||||
|
||||
Test if outlier filter handles long-running step-changes correctly.
|
||||
It should converge to no longer filter once just over half the
|
||||
window_size is occupied by the new post step-change values.
|
||||
"""
|
||||
filt = OutlierFilter(window_size=3,
|
||||
precision=2,
|
||||
entity=None,
|
||||
radius=1.1)
|
||||
self.values[-1].state = 22
|
||||
for state in self.values:
|
||||
filtered = filt.filter_state(state)
|
||||
assert 22 == filtered.state
|
||||
@ -119,7 +136,7 @@ class TestFilterSensor(unittest.TestCase):
|
||||
out = ha.State('sensor.test_monitored', 4000)
|
||||
for state in [out]+self.values:
|
||||
filtered = filt.filter_state(state)
|
||||
assert 22 == filtered.state
|
||||
assert 21 == filtered.state
|
||||
|
||||
def test_lowpass(self):
|
||||
"""Test if lowpass filter works."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user