Fix handling None or empty value for numeric MQTT sensor (#87004)

* Allow `None` for numeric sensor, ignore empty val

* Add test case with omitting a value

* Use _numeric_state_expected property

* Only respect None if numeric state is expected
This commit is contained in:
Jan Bouwhuis 2023-02-07 11:23:23 +01:00 committed by GitHub
parent 42008c50f3
commit c78cae4483
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 7 deletions

View file

@ -38,7 +38,7 @@ from homeassistant.util import dt as dt_util
from . import subscription
from .config import MQTT_RO_SCHEMA
from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC
from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, PAYLOAD_NONE
from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
@ -272,13 +272,19 @@ class MqttSensor(MqttEntity, RestoreSensor):
payload = self._template(msg.payload, PayloadSentinel.DEFAULT)
if payload is PayloadSentinel.DEFAULT:
return
if self.device_class not in {
SensorDeviceClass.DATE,
SensorDeviceClass.TIMESTAMP,
}:
self._attr_native_value = str(payload)
new_value = str(payload)
if self._numeric_state_expected:
if new_value == "":
_LOGGER.debug("Ignore empty state from '%s'", msg.topic)
elif new_value == PAYLOAD_NONE:
self._attr_native_value = None
else:
self._attr_native_value = new_value
return
if (payload_datetime := dt_util.parse_datetime(str(payload))) is None:
if self.device_class is None:
self._attr_native_value = new_value
return
if (payload_datetime := dt_util.parse_datetime(new_value)) is None:
_LOGGER.warning(
"Invalid state message '%s' from '%s'", msg.payload, msg.topic
)

View file

@ -169,6 +169,61 @@ async def test_setting_sensor_native_value_handling_via_mqtt_message(
assert log == ("Invalid state message" in caplog.text)
async def test_setting_numeric_sensor_native_value_handling_via_mqtt_message(
hass: ha.HomeAssistant,
mqtt_mock_entry_with_yaml_config,
) -> None:
"""Test the setting of a numeric sensor value via MQTT."""
assert await async_setup_component(
hass,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"name": "test",
"state_topic": "test-topic",
"value_template": "{{ value_json.power }}",
"device_class": "power",
"unit_of_measurement": "W",
}
}
},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
# float value
async_fire_mqtt_message(hass, "test-topic", '{ "power": 45.3, "current": 5.24 }')
state = hass.states.get("sensor.test")
assert state.attributes.get("device_class") == "power"
assert state.state == "45.3"
# null value, native value should be None
async_fire_mqtt_message(hass, "test-topic", '{ "power": null, "current": 5.34 }')
state = hass.states.get("sensor.test")
assert state.state == "unknown"
# int value
async_fire_mqtt_message(hass, "test-topic", '{ "power": 20, "current": 5.34 }')
state = hass.states.get("sensor.test")
assert state.state == "20"
# int value
async_fire_mqtt_message(hass, "test-topic", '{ "power": "21", "current": 5.34 }')
state = hass.states.get("sensor.test")
assert state.state == "21"
# ignore empty value, native sensor value should not change
async_fire_mqtt_message(hass, "test-topic", '{ "power": "", "current": 5.34 }')
state = hass.states.get("sensor.test")
assert state.state == "21"
# omitting value, causing it to be ignored, native sensor value should not change (template warning will be logged though)
async_fire_mqtt_message(hass, "test-topic", '{ "current": 5.34 }')
state = hass.states.get("sensor.test")
assert state.state == "21"
async def test_setting_sensor_value_expires_availability_topic(
hass, mqtt_mock_entry_with_yaml_config, caplog
):