Skip to content
Merged
Show file tree
Hide file tree
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
20 changes: 13 additions & 7 deletions homeassistant/components/mqtt/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

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,
Expand Down Expand Up @@ -272,13 +272,19 @@ def _update_state(msg: ReceiveMessage) -> None:
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
)
Expand Down
55 changes: 55 additions & 0 deletions tests/components/mqtt/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
):
Expand Down