Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update TimeSMAFilter based on time #53053

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions homeassistant/components/filter/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
CONF_FILTER_PRECISION = "precision"
CONF_FILTER_RADIUS = "radius"
CONF_FILTER_TIME_CONSTANT = "time_constant"
CONF_FILTER_UPDATE_BY_TIME = "update_by_time"
CONF_FILTER_LOWER_BOUND = "lower_bound"
CONF_FILTER_UPPER_BOUND = "upper_bound"
CONF_TIME_SMA_TYPE = "type"
Expand All @@ -72,6 +73,8 @@
NAME_TEMPLATE = "{} filter"
ICON = "mdi:chart-line-variant"

SCAN_INTERVAL = timedelta(minutes=3)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reasoning for choosing 3min ?


FILTER_SCHEMA = vol.Schema(
{vol.Optional(CONF_FILTER_PRECISION, default=DEFAULT_PRECISION): vol.Coerce(int)}
)
Expand Down Expand Up @@ -117,6 +120,7 @@
vol.Required(CONF_FILTER_WINDOW_SIZE): vol.All(
cv.time_period, cv.positive_timedelta
),
vol.Optional(CONF_FILTER_UPDATE_BY_TIME, default=False): cv.boolean,
}
)

Expand Down Expand Up @@ -192,12 +196,23 @@ def __init__(self, name, entity_id, filters):
self._icon = None
self._device_class = None

self._attr_should_poll = False
for filt in filters:
if getattr(filt, "update_by_time", False):
self._attr_should_poll = True
break

@callback
def _update_filter_sensor_state_event(self, event):
"""Handle device state changes."""
_LOGGER.debug("Update filter on event: %s", event)
self._update_filter_sensor_state(event.data.get("new_state"))

def update(self):
"""Update TimeSMAFilter value."""
_LOGGER.debug("Update filter: %s", self._state)
self._update_filter_sensor_state(self.hass.states.get(self._entity))

@callback
def _update_filter_sensor_state(self, new_state, update_ha=True):
"""Process device state changes."""
Expand Down Expand Up @@ -346,11 +361,6 @@ def unit_of_measurement(self):
"""Return the unit_of_measurement of the device."""
return self._unit_of_measurement

@property
def should_poll(self):
"""No polling needed."""
return False

@property
def extra_state_attributes(self):
"""Return the state attributes of the sensor."""
Expand Down Expand Up @@ -454,7 +464,7 @@ def filter_state(self, new_state):


@FILTERS.register(FILTER_NAME_RANGE)
class RangeFilter(Filter, SensorEntity):
class RangeFilter(Filter):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might have misunderstood something, but I see no reason for the filters to inherit SensorEntity

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I can recall :/

since tests didn't break and coverage is high, I'm ok with this

"""Range filter.

Determines if new state is in the range of upper_bound and lower_bound.
Expand Down Expand Up @@ -509,7 +519,7 @@ def _filter_state(self, new_state):


@FILTERS.register(FILTER_NAME_OUTLIER)
class OutlierFilter(Filter, SensorEntity):
class OutlierFilter(Filter):
"""BASIC outlier filter.

Determines if new state is in a band around the median.
Expand Down Expand Up @@ -547,7 +557,7 @@ def _filter_state(self, new_state):


@FILTERS.register(FILTER_NAME_LOWPASS)
class LowPassFilter(Filter, SensorEntity):
class LowPassFilter(Filter):
"""BASIC Low Pass Filter."""

def __init__(self, window_size, precision, entity, time_constant: int):
Expand All @@ -571,21 +581,27 @@ def _filter_state(self, new_state):


@FILTERS.register(FILTER_NAME_TIME_SMA)
class TimeSMAFilter(Filter, SensorEntity):
class TimeSMAFilter(Filter):
"""Simple Moving Average (SMA) Filter.

The window_size is determined by time, and SMA is time weighted.
"""

def __init__(
self, window_size, precision, entity, type
self,
window_size,
precision,
entity,
type,
update_by_time,
): # pylint: disable=redefined-builtin
"""Initialize Filter.

:param type: type of algorithm used to connect discrete values
"""
super().__init__(FILTER_NAME_TIME_SMA, window_size, precision, entity)
self._time_window = window_size
self.update_by_time = update_by_time
self.last_leak = None
self.queue = deque()

Expand Down Expand Up @@ -617,7 +633,7 @@ def _filter_state(self, new_state):


@FILTERS.register(FILTER_NAME_THROTTLE)
class ThrottleFilter(Filter, SensorEntity):
class ThrottleFilter(Filter):
"""Throttle Filter.

One sample per window.
Expand All @@ -640,7 +656,7 @@ def _filter_state(self, new_state):


@FILTERS.register(FILTER_NAME_TIME_THROTTLE)
class TimeThrottleFilter(Filter, SensorEntity):
class TimeThrottleFilter(Filter):
"""Time Throttle Filter.

One sample per time period.
Expand Down