Fix filter sensor None state (#44439)

Co-authored-by: Franck Nijhof <frenck@frenck.dev>
This commit is contained in:
Diogo Gomes 2020-12-24 01:24:11 +00:00 committed by GitHub
parent 6b743c3d16
commit 2d131823ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 4 deletions

View File

@ -197,7 +197,15 @@ class SensorFilter(Entity):
@callback @callback
def _update_filter_sensor_state(self, new_state, update_ha=True): def _update_filter_sensor_state(self, new_state, update_ha=True):
"""Process device state changes.""" """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._state = new_state.state
self.async_write_ha_state() self.async_write_ha_state()
return return
@ -218,7 +226,11 @@ class SensorFilter(Entity):
return return
temp_state = filtered_state temp_state = filtered_state
except ValueError: 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 return
self._state = temp_state.state self._state = temp_state.state
@ -425,7 +437,7 @@ class Filter:
"""Implement a common interface for filters.""" """Implement a common interface for filters."""
fstate = FilterState(new_state) fstate = FilterState(new_state)
if self._only_numbers and not isinstance(fstate.state, Number): 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 = self._filter_state(fstate)
filtered.set_precision(self.precision) filtered.set_precision(self.precision)

View File

@ -15,7 +15,7 @@ from homeassistant.components.filter.sensor import (
TimeThrottleFilter, TimeThrottleFilter,
) )
from homeassistant.components.sensor import DEVICE_CLASS_TEMPERATURE 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 import homeassistant.core as ha
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util 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 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): async def test_chain_history_missing(hass, values):
"""Test if filter chaining works when recorder is enabled but the source is not recorded.""" """Test if filter chaining works when recorder is enabled but the source is not recorded."""
await test_chain_history(hass, values, missing=True) await test_chain_history(hass, values, missing=True)
@ -239,6 +304,12 @@ async def test_invalid_state(hass):
state = hass.states.get("sensor.test") state = hass.states.get("sensor.test")
assert state.state == STATE_UNAVAILABLE 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): async def test_outlier(values):
"""Test if outlier filter works.""" """Test if outlier filter works."""