From 2d131823ce3867de7df09c023182a804b8a29cb3 Mon Sep 17 00:00:00 2001 From: Diogo Gomes <diogogomes@gmail.com> Date: Thu, 24 Dec 2020 01:24:11 +0000 Subject: [PATCH] Fix filter sensor None state (#44439) Co-authored-by: Franck Nijhof <frenck@frenck.dev> --- homeassistant/components/filter/sensor.py | 18 +++++- tests/components/filter/test_sensor.py | 73 ++++++++++++++++++++++- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/filter/sensor.py b/homeassistant/components/filter/sensor.py index 72300c1621e..97d2b594cc3 100644 --- a/homeassistant/components/filter/sensor.py +++ b/homeassistant/components/filter/sensor.py @@ -197,7 +197,15 @@ class SensorFilter(Entity): @callback def _update_filter_sensor_state(self, new_state, update_ha=True): """Process device state changes.""" - if new_state is None or new_state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]: + if new_state is None: + _LOGGER.warning( + "While updating filter %s, the new_state is None", self._name + ) + self._state = None + self.async_write_ha_state() + return + + if new_state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]: self._state = new_state.state self.async_write_ha_state() return @@ -218,7 +226,11 @@ class SensorFilter(Entity): return temp_state = filtered_state except ValueError: - _LOGGER.error("Could not convert state: %s to number", self._state) + _LOGGER.error( + "Could not convert state: %s (%s) to number", + new_state.state, + type(new_state.state), + ) return self._state = temp_state.state @@ -425,7 +437,7 @@ class Filter: """Implement a common interface for filters.""" fstate = FilterState(new_state) if self._only_numbers and not isinstance(fstate.state, Number): - raise ValueError + raise ValueError(f"State <{fstate.state}> is not a Number") filtered = self._filter_state(fstate) filtered.set_precision(self.precision) diff --git a/tests/components/filter/test_sensor.py b/tests/components/filter/test_sensor.py index 8b779696a70..1c3b4b0d672 100644 --- a/tests/components/filter/test_sensor.py +++ b/tests/components/filter/test_sensor.py @@ -15,7 +15,7 @@ from homeassistant.components.filter.sensor import ( TimeThrottleFilter, ) from homeassistant.components.sensor import DEVICE_CLASS_TEMPERATURE -from homeassistant.const import SERVICE_RELOAD, STATE_UNAVAILABLE +from homeassistant.const import SERVICE_RELOAD, STATE_UNAVAILABLE, STATE_UNKNOWN import homeassistant.core as ha from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -136,6 +136,71 @@ async def test_chain_history(hass, values, missing=False): assert "17.05" == state.state +async def test_source_state_none(hass, values): + """Test is source sensor state is null and sets state to STATE_UNKNOWN.""" + await async_init_recorder_component(hass) + + config = { + "sensor": [ + { + "platform": "template", + "sensors": { + "template_test": { + "value_template": "{{ states.sensor.test_state.state }}" + } + }, + }, + { + "platform": "filter", + "name": "test", + "entity_id": "sensor.template_test", + "filters": [ + { + "filter": "time_simple_moving_average", + "window_size": "00:01", + "precision": "2", + } + ], + }, + ] + } + await async_setup_component(hass, "sensor", config) + await hass.async_block_till_done() + + hass.states.async_set("sensor.test_state", 0) + + await hass.async_block_till_done() + state = hass.states.get("sensor.template_test") + assert state.state == "0" + + await hass.async_block_till_done() + state = hass.states.get("sensor.test") + assert state.state == "0.0" + + # Force Template Reload + yaml_path = path.join( + _get_fixtures_base_path(), + "fixtures", + "template/sensor_configuration.yaml", + ) + with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path): + await hass.services.async_call( + "template", + SERVICE_RELOAD, + {}, + blocking=True, + ) + await hass.async_block_till_done() + + # Template state gets to None + state = hass.states.get("sensor.template_test") + assert state is None + + # Filter sensor ignores None state setting state to STATE_UNKNOWN + state = hass.states.get("sensor.test") + assert state.state == STATE_UNKNOWN + + async def test_chain_history_missing(hass, values): """Test if filter chaining works when recorder is enabled but the source is not recorded.""" await test_chain_history(hass, values, missing=True) @@ -239,6 +304,12 @@ async def test_invalid_state(hass): state = hass.states.get("sensor.test") assert state.state == STATE_UNAVAILABLE + hass.states.async_set("sensor.test_monitored", "invalid") + await hass.async_block_till_done() + + state = hass.states.get("sensor.test") + assert state.state == STATE_UNAVAILABLE + async def test_outlier(values): """Test if outlier filter works.""" -- GitLab