Improve error handling and logging on MQTT update entity state updates when template rederings fails (#141960)

This commit is contained in:
Jan Bouwhuis 2025-04-01 19:22:32 +02:00 committed by GitHub
parent d9cd62bf65
commit faac51d219
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 79 additions and 2 deletions

View File

@ -26,7 +26,7 @@ from . import subscription
from .config import DEFAULT_RETAIN, MQTT_RO_SCHEMA
from .const import CONF_COMMAND_TOPIC, CONF_RETAIN, CONF_STATE_TOPIC, PAYLOAD_EMPTY_JSON
from .entity import MqttEntity, async_setup_entity_entry_helper
from .models import MqttValueTemplate, ReceiveMessage
from .models import MqttValueTemplate, PayloadSentinel, ReceiveMessage
from .schemas import MQTT_ENTITY_COMMON_SCHEMA
from .util import valid_publish_topic, valid_subscribe_topic
@ -136,7 +136,18 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity):
@callback
def _handle_state_message_received(self, msg: ReceiveMessage) -> None:
"""Handle receiving state message via MQTT."""
payload = self._templates[CONF_VALUE_TEMPLATE](msg.payload)
payload = self._templates[CONF_VALUE_TEMPLATE](
msg.payload, PayloadSentinel.DEFAULT
)
if payload is PayloadSentinel.DEFAULT:
_LOGGER.warning(
"Unable to process payload '%s' for topic %s, with value template '%s'",
msg.payload,
msg.topic,
self._config.get(CONF_VALUE_TEMPLATE),
)
return
if not payload or payload == PAYLOAD_EMPTY_JSON:
_LOGGER.debug(

View File

@ -1,6 +1,7 @@
"""The tests for mqtt update component."""
import json
from typing import Any
from unittest.mock import patch
import pytest
@ -225,6 +226,71 @@ async def test_value_template(
assert state.attributes.get("latest_version") == "2.0.0"
@pytest.mark.parametrize(
"hass_config",
[
{
mqtt.DOMAIN: {
update.DOMAIN: {
"state_topic": "test/update",
"value_template": (
"{\"latest_version\":\"{{ value_json['update']['latest_version'] }}\","
"\"installed_version\":\"{{ value_json['update']['installed_version'] }}\","
"\"update_percentage\":{{ value_json['update'].get('progress', 'null') }}}"
),
"name": "Test Update",
}
}
}
],
)
async def test_errornous_value_template(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that it fetches the given payload with a template or handles the exception."""
state_topic = "test/update"
await mqtt_mock_entry()
# Simulate a template redendering error with payload
# without "update" mapping
example_payload: dict[str, Any] = {
"child_lock": "UNLOCK",
"current": 0.02,
"energy": 212.92,
"indicator_mode": "off/on",
"linkquality": 65,
"power": 0,
"power_outage_memory": "off",
"state": "ON",
"voltage": 232,
}
async_fire_mqtt_message(hass, state_topic, json.dumps(example_payload))
await hass.async_block_till_done()
assert hass.states.get("update.test_update") is not None
assert "Unable to process payload '" in caplog.text
# Add update info
example_payload["update"] = {
"latest_version": "2.0.0",
"installed_version": "1.9.0",
"progress": 20,
}
async_fire_mqtt_message(hass, state_topic, json.dumps(example_payload))
await hass.async_block_till_done()
state = hass.states.get("update.test_update")
assert state is not None
assert state.state == STATE_ON
assert state.attributes.get("installed_version") == "1.9.0"
assert state.attributes.get("latest_version") == "2.0.0"
assert state.attributes.get("update_percentage") == 20
@pytest.mark.parametrize(
"hass_config",
[