Improve mqtt value template error logging (#110492)

* Refactor mqtt value template error logging

* Remove import
This commit is contained in:
Jan Bouwhuis 2024-03-04 08:49:12 +01:00 committed by GitHub
parent 5227976aa2
commit c13231fc00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 81 additions and 45 deletions

View file

@ -29,7 +29,6 @@ from .const import (
CONF_STATE_TOPIC,
PAYLOAD_EMPTY_JSON,
PAYLOAD_NONE,
TEMPLATE_ERRORS,
)
from .debug_info import log_messages
from .mixins import (
@ -39,6 +38,7 @@ from .mixins import (
)
from .models import (
MqttValueTemplate,
MqttValueTemplateException,
PayloadSentinel,
ReceiveMessage,
ReceivePayloadType,
@ -134,7 +134,8 @@ class MqttEvent(MqttEntity, EventEntity):
event_type: str
try:
payload = self._template(msg.payload, PayloadSentinel.DEFAULT)
except TEMPLATE_ERRORS:
except MqttValueTemplateException as exc:
_LOGGER.warning(exc)
return
if (
not payload

View file

@ -24,14 +24,19 @@ from homeassistant.util import dt as dt_util
from . import subscription
from .config import MQTT_BASE_SCHEMA
from .const import CONF_ENCODING, CONF_QOS, TEMPLATE_ERRORS
from .const import CONF_ENCODING, CONF_QOS
from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_setup_entity_entry_helper,
)
from .models import MessageCallbackType, MqttValueTemplate, ReceiveMessage
from .models import (
MessageCallbackType,
MqttValueTemplate,
MqttValueTemplateException,
ReceiveMessage,
)
from .util import get_mqtt_data, valid_subscribe_topic
_LOGGER = logging.getLogger(__name__)
@ -191,7 +196,8 @@ class MqttImage(MqttEntity, ImageEntity):
try:
url = cv.url(self._url_template(msg.payload))
self._attr_image_url = url
except TEMPLATE_ERRORS:
except MqttValueTemplateException as exc:
_LOGGER.warning(exc)
return
except vol.Invalid:
_LOGGER.error(

View file

@ -94,7 +94,6 @@ from .const import (
DOMAIN,
MQTT_CONNECTED,
MQTT_DISCONNECTED,
TEMPLATE_ERRORS,
)
from .debug_info import log_message, log_messages
from .discovery import (
@ -109,6 +108,7 @@ from .discovery import (
from .models import (
MessageCallbackType,
MqttValueTemplate,
MqttValueTemplateException,
PublishPayloadType,
ReceiveMessage,
)
@ -482,7 +482,8 @@ def write_state_on_attr_change(
}
try:
msg_callback(msg)
except TEMPLATE_ERRORS:
except MqttValueTemplateException as exc:
_LOGGER.warning(exc)
return
if not _attrs_have_changed(tracked_attrs):
return

View file

@ -223,6 +223,36 @@ class MqttCommandTemplate:
) from exc
class MqttValueTemplateException(TemplateError):
"""Handle MqttValueTemplate exceptions."""
def __init__(
self,
*args: object,
base_exception: Exception,
value_template: str,
default: ReceivePayloadType | PayloadSentinel,
payload: ReceivePayloadType,
entity_id: str | None = None,
) -> None:
"""Initialize exception."""
super().__init__(base_exception, *args)
entity_id_log = "" if entity_id is None else f" for entity '{entity_id}'"
default_log = str(default)
default_payload_log = (
"" if default is PayloadSentinel.NONE else f", default value: {default_log}"
)
payload_log = str(payload)
self._message = (
f"{type(base_exception).__name__}: {base_exception} rendering template{entity_id_log}"
f", template: '{value_template}'{default_payload_log} and payload: {payload_log}"
)
def __str__(self) -> str:
"""Return exception message string."""
return self._message
class MqttValueTemplate:
"""Class for rendering MQTT value template with possible json values."""
@ -291,14 +321,13 @@ class MqttValueTemplate:
)
)
except TEMPLATE_ERRORS as exc:
_LOGGER.error(
"%s: %s rendering template for entity '%s', template: '%s'",
type(exc).__name__,
exc,
self._entity.entity_id if self._entity else "n/a",
self._value_template.template,
)
raise
raise MqttValueTemplateException(
base_exception=exc,
value_template=self._value_template.template,
default=default,
payload=payload,
entity_id=self._entity.entity_id if self._entity else None,
) from exc
return rendered_payload
_LOGGER.debug(
@ -318,17 +347,13 @@ class MqttValueTemplate:
)
)
except TEMPLATE_ERRORS as exc:
_LOGGER.error(
"%s: %s rendering template for entity '%s', template: "
"'%s', default value: %s and payload: %s",
type(exc).__name__,
exc,
self._entity.entity_id if self._entity else "n/a",
self._value_template.template,
default,
payload,
)
raise
raise MqttValueTemplateException(
base_exception=exc,
value_template=self._value_template.template,
default=default,
payload=payload,
entity_id=self._entity.entity_id if self._entity else None,
) from exc
return rendered_payload

View file

@ -3,6 +3,7 @@ from __future__ import annotations
from collections.abc import Callable
import functools
import logging
import voluptuous as vol
@ -15,7 +16,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import subscription
from .config import MQTT_BASE_SCHEMA
from .const import ATTR_DISCOVERY_HASH, CONF_QOS, CONF_TOPIC, TEMPLATE_ERRORS
from .const import ATTR_DISCOVERY_HASH, CONF_QOS, CONF_TOPIC
from .discovery import MQTTDiscoveryPayload
from .mixins import (
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
@ -25,10 +26,17 @@ from .mixins import (
send_discovery_done,
update_device,
)
from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType
from .models import (
MqttValueTemplate,
MqttValueTemplateException,
ReceiveMessage,
ReceivePayloadType,
)
from .subscription import EntitySubscription
from .util import get_mqtt_data, valid_subscribe_topic
_LOGGER = logging.getLogger(__name__)
LOG_NAME = "Tag"
TAG = "tag"
@ -138,7 +146,8 @@ class MQTTTagScanner(MqttDiscoveryDeviceUpdate):
async def tag_scanned(msg: ReceiveMessage) -> None:
try:
tag_id = str(self._value_template(msg.payload, "")).strip()
except TEMPLATE_ERRORS:
except MqttValueTemplateException as exc:
_LOGGER.warning(exc)
return
if not tag_id: # No output from template, ignore
return

View file

@ -19,6 +19,7 @@ from homeassistant.components.mqtt.mixins import MQTT_ENTITY_DEVICE_INFO_SCHEMA
from homeassistant.components.mqtt.models import (
MessageCallbackType,
MqttCommandTemplateException,
MqttValueTemplateException,
ReceiveMessage,
)
from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState
@ -433,37 +434,30 @@ async def test_value_template_value(hass: HomeAssistant) -> None:
assert template_state_calls.call_count == 1
async def test_value_template_fails(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
async def test_value_template_fails(hass: HomeAssistant) -> None:
"""Test the rendering of MQTT value template fails."""
# test rendering a value fails
entity = MockEntity(entity_id="sensor.test")
entity.hass = hass
tpl = template.Template("{{ value_json.some_var * 2 }}")
val_tpl = mqtt.MqttValueTemplate(tpl, hass=hass, entity=entity)
with pytest.raises(TypeError) as exc:
with pytest.raises(MqttValueTemplateException) as exc:
val_tpl.async_render_with_possible_json_value('{"some_var": null }')
assert str(exc.value) == "unsupported operand type(s) for *: 'NoneType' and 'int'"
assert (
assert str(exc.value) == (
"TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' "
"rendering template for entity 'sensor.test', "
"template: '{{ value_json.some_var * 2 }}'"
) in caplog.text
caplog.clear()
with pytest.raises(TypeError) as exc:
"template: '{{ value_json.some_var * 2 }}' "
'and payload: {"some_var": null }'
)
with pytest.raises(MqttValueTemplateException) as exc:
val_tpl.async_render_with_possible_json_value(
'{"some_var": null }', default=100
)
assert str(exc.value) == "unsupported operand type(s) for *: 'NoneType' and 'int'"
assert (
assert str(exc.value) == (
"TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' "
"rendering template for entity 'sensor.test', "
"template: '{{ value_json.some_var * 2 }}', default value: 100 and payload: "
'{"some_var": null }'
) in caplog.text
await hass.async_block_till_done()
)
async def test_service_call_without_topic_does_not_publish(