Improve mqtt value template error logging (#110492)
* Refactor mqtt value template error logging * Remove import
This commit is contained in:
parent
5227976aa2
commit
c13231fc00
6 changed files with 81 additions and 45 deletions
|
@ -29,7 +29,6 @@ from .const import (
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
PAYLOAD_EMPTY_JSON,
|
PAYLOAD_EMPTY_JSON,
|
||||||
PAYLOAD_NONE,
|
PAYLOAD_NONE,
|
||||||
TEMPLATE_ERRORS,
|
|
||||||
)
|
)
|
||||||
from .debug_info import log_messages
|
from .debug_info import log_messages
|
||||||
from .mixins import (
|
from .mixins import (
|
||||||
|
@ -39,6 +38,7 @@ from .mixins import (
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
MqttValueTemplate,
|
MqttValueTemplate,
|
||||||
|
MqttValueTemplateException,
|
||||||
PayloadSentinel,
|
PayloadSentinel,
|
||||||
ReceiveMessage,
|
ReceiveMessage,
|
||||||
ReceivePayloadType,
|
ReceivePayloadType,
|
||||||
|
@ -134,7 +134,8 @@ class MqttEvent(MqttEntity, EventEntity):
|
||||||
event_type: str
|
event_type: str
|
||||||
try:
|
try:
|
||||||
payload = self._template(msg.payload, PayloadSentinel.DEFAULT)
|
payload = self._template(msg.payload, PayloadSentinel.DEFAULT)
|
||||||
except TEMPLATE_ERRORS:
|
except MqttValueTemplateException as exc:
|
||||||
|
_LOGGER.warning(exc)
|
||||||
return
|
return
|
||||||
if (
|
if (
|
||||||
not payload
|
not payload
|
||||||
|
|
|
@ -24,14 +24,19 @@ from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from . import subscription
|
from . import subscription
|
||||||
from .config import MQTT_BASE_SCHEMA
|
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 .debug_info import log_messages
|
||||||
from .mixins import (
|
from .mixins import (
|
||||||
MQTT_ENTITY_COMMON_SCHEMA,
|
MQTT_ENTITY_COMMON_SCHEMA,
|
||||||
MqttEntity,
|
MqttEntity,
|
||||||
async_setup_entity_entry_helper,
|
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
|
from .util import get_mqtt_data, valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -191,7 +196,8 @@ class MqttImage(MqttEntity, ImageEntity):
|
||||||
try:
|
try:
|
||||||
url = cv.url(self._url_template(msg.payload))
|
url = cv.url(self._url_template(msg.payload))
|
||||||
self._attr_image_url = url
|
self._attr_image_url = url
|
||||||
except TEMPLATE_ERRORS:
|
except MqttValueTemplateException as exc:
|
||||||
|
_LOGGER.warning(exc)
|
||||||
return
|
return
|
||||||
except vol.Invalid:
|
except vol.Invalid:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
|
|
|
@ -94,7 +94,6 @@ from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MQTT_CONNECTED,
|
MQTT_CONNECTED,
|
||||||
MQTT_DISCONNECTED,
|
MQTT_DISCONNECTED,
|
||||||
TEMPLATE_ERRORS,
|
|
||||||
)
|
)
|
||||||
from .debug_info import log_message, log_messages
|
from .debug_info import log_message, log_messages
|
||||||
from .discovery import (
|
from .discovery import (
|
||||||
|
@ -109,6 +108,7 @@ from .discovery import (
|
||||||
from .models import (
|
from .models import (
|
||||||
MessageCallbackType,
|
MessageCallbackType,
|
||||||
MqttValueTemplate,
|
MqttValueTemplate,
|
||||||
|
MqttValueTemplateException,
|
||||||
PublishPayloadType,
|
PublishPayloadType,
|
||||||
ReceiveMessage,
|
ReceiveMessage,
|
||||||
)
|
)
|
||||||
|
@ -482,7 +482,8 @@ def write_state_on_attr_change(
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
msg_callback(msg)
|
msg_callback(msg)
|
||||||
except TEMPLATE_ERRORS:
|
except MqttValueTemplateException as exc:
|
||||||
|
_LOGGER.warning(exc)
|
||||||
return
|
return
|
||||||
if not _attrs_have_changed(tracked_attrs):
|
if not _attrs_have_changed(tracked_attrs):
|
||||||
return
|
return
|
||||||
|
|
|
@ -223,6 +223,36 @@ class MqttCommandTemplate:
|
||||||
) from exc
|
) 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 MqttValueTemplate:
|
||||||
"""Class for rendering MQTT value template with possible json values."""
|
"""Class for rendering MQTT value template with possible json values."""
|
||||||
|
|
||||||
|
@ -291,14 +321,13 @@ class MqttValueTemplate:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except TEMPLATE_ERRORS as exc:
|
except TEMPLATE_ERRORS as exc:
|
||||||
_LOGGER.error(
|
raise MqttValueTemplateException(
|
||||||
"%s: %s rendering template for entity '%s', template: '%s'",
|
base_exception=exc,
|
||||||
type(exc).__name__,
|
value_template=self._value_template.template,
|
||||||
exc,
|
default=default,
|
||||||
self._entity.entity_id if self._entity else "n/a",
|
payload=payload,
|
||||||
self._value_template.template,
|
entity_id=self._entity.entity_id if self._entity else None,
|
||||||
)
|
) from exc
|
||||||
raise
|
|
||||||
return rendered_payload
|
return rendered_payload
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
|
@ -318,17 +347,13 @@ class MqttValueTemplate:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except TEMPLATE_ERRORS as exc:
|
except TEMPLATE_ERRORS as exc:
|
||||||
_LOGGER.error(
|
raise MqttValueTemplateException(
|
||||||
"%s: %s rendering template for entity '%s', template: "
|
base_exception=exc,
|
||||||
"'%s', default value: %s and payload: %s",
|
value_template=self._value_template.template,
|
||||||
type(exc).__name__,
|
default=default,
|
||||||
exc,
|
payload=payload,
|
||||||
self._entity.entity_id if self._entity else "n/a",
|
entity_id=self._entity.entity_id if self._entity else None,
|
||||||
self._value_template.template,
|
) from exc
|
||||||
default,
|
|
||||||
payload,
|
|
||||||
)
|
|
||||||
raise
|
|
||||||
return rendered_payload
|
return rendered_payload
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
import functools
|
import functools
|
||||||
|
import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from . import subscription
|
from . import subscription
|
||||||
from .config import MQTT_BASE_SCHEMA
|
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 .discovery import MQTTDiscoveryPayload
|
||||||
from .mixins import (
|
from .mixins import (
|
||||||
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
@ -25,10 +26,17 @@ from .mixins import (
|
||||||
send_discovery_done,
|
send_discovery_done,
|
||||||
update_device,
|
update_device,
|
||||||
)
|
)
|
||||||
from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType
|
from .models import (
|
||||||
|
MqttValueTemplate,
|
||||||
|
MqttValueTemplateException,
|
||||||
|
ReceiveMessage,
|
||||||
|
ReceivePayloadType,
|
||||||
|
)
|
||||||
from .subscription import EntitySubscription
|
from .subscription import EntitySubscription
|
||||||
from .util import get_mqtt_data, valid_subscribe_topic
|
from .util import get_mqtt_data, valid_subscribe_topic
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
LOG_NAME = "Tag"
|
LOG_NAME = "Tag"
|
||||||
|
|
||||||
TAG = "tag"
|
TAG = "tag"
|
||||||
|
@ -138,7 +146,8 @@ class MQTTTagScanner(MqttDiscoveryDeviceUpdate):
|
||||||
async def tag_scanned(msg: ReceiveMessage) -> None:
|
async def tag_scanned(msg: ReceiveMessage) -> None:
|
||||||
try:
|
try:
|
||||||
tag_id = str(self._value_template(msg.payload, "")).strip()
|
tag_id = str(self._value_template(msg.payload, "")).strip()
|
||||||
except TEMPLATE_ERRORS:
|
except MqttValueTemplateException as exc:
|
||||||
|
_LOGGER.warning(exc)
|
||||||
return
|
return
|
||||||
if not tag_id: # No output from template, ignore
|
if not tag_id: # No output from template, ignore
|
||||||
return
|
return
|
||||||
|
|
|
@ -19,6 +19,7 @@ from homeassistant.components.mqtt.mixins import MQTT_ENTITY_DEVICE_INFO_SCHEMA
|
||||||
from homeassistant.components.mqtt.models import (
|
from homeassistant.components.mqtt.models import (
|
||||||
MessageCallbackType,
|
MessageCallbackType,
|
||||||
MqttCommandTemplateException,
|
MqttCommandTemplateException,
|
||||||
|
MqttValueTemplateException,
|
||||||
ReceiveMessage,
|
ReceiveMessage,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState
|
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
|
assert template_state_calls.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_value_template_fails(
|
async def test_value_template_fails(hass: HomeAssistant) -> None:
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
||||||
) -> None:
|
|
||||||
"""Test the rendering of MQTT value template fails."""
|
"""Test the rendering of MQTT value template fails."""
|
||||||
|
|
||||||
# test rendering a value fails
|
|
||||||
entity = MockEntity(entity_id="sensor.test")
|
entity = MockEntity(entity_id="sensor.test")
|
||||||
entity.hass = hass
|
entity.hass = hass
|
||||||
tpl = template.Template("{{ value_json.some_var * 2 }}")
|
tpl = template.Template("{{ value_json.some_var * 2 }}")
|
||||||
val_tpl = mqtt.MqttValueTemplate(tpl, hass=hass, entity=entity)
|
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 }')
|
val_tpl.async_render_with_possible_json_value('{"some_var": null }')
|
||||||
assert str(exc.value) == "unsupported operand type(s) for *: 'NoneType' and 'int'"
|
assert str(exc.value) == (
|
||||||
assert (
|
|
||||||
"TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' "
|
"TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' "
|
||||||
"rendering template for entity 'sensor.test', "
|
"rendering template for entity 'sensor.test', "
|
||||||
"template: '{{ value_json.some_var * 2 }}' "
|
"template: '{{ value_json.some_var * 2 }}' "
|
||||||
) in caplog.text
|
'and payload: {"some_var": null }'
|
||||||
caplog.clear()
|
)
|
||||||
with pytest.raises(TypeError) as exc:
|
with pytest.raises(MqttValueTemplateException) as exc:
|
||||||
val_tpl.async_render_with_possible_json_value(
|
val_tpl.async_render_with_possible_json_value(
|
||||||
'{"some_var": null }', default=100
|
'{"some_var": null }', default=100
|
||||||
)
|
)
|
||||||
assert str(exc.value) == "unsupported operand type(s) for *: 'NoneType' and 'int'"
|
assert str(exc.value) == (
|
||||||
assert (
|
|
||||||
"TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' "
|
"TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' "
|
||||||
"rendering template for entity 'sensor.test', "
|
"rendering template for entity 'sensor.test', "
|
||||||
"template: '{{ value_json.some_var * 2 }}', default value: 100 and payload: "
|
"template: '{{ value_json.some_var * 2 }}', default value: 100 and payload: "
|
||||||
'{"some_var": null }'
|
'{"some_var": null }'
|
||||||
) in caplog.text
|
)
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_service_call_without_topic_does_not_publish(
|
async def test_service_call_without_topic_does_not_publish(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue