From 5ef6c036102290042bfe9a3b87609c9f031a557f Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 18 Aug 2023 13:05:53 +0200 Subject: [PATCH] Log entity_id payload and template on MQTT value template error (#98353) * Log entity_id payload and template on error * Also handle cases with default values. * Do not log payload twice Co-authored-by: Erik Montnemery * Tweak test to assert without payload * black --------- Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/models.py | 39 ++++++++++++++++++++----- tests/components/mqtt/test_init.py | 32 ++++++++++++++++++++ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/mqtt/models.py b/homeassistant/components/mqtt/models.py index 9afa3de3f48..a936c9e420d 100644 --- a/homeassistant/components/mqtt/models.py +++ b/homeassistant/components/mqtt/models.py @@ -231,11 +231,21 @@ class MqttValueTemplate: values, self._value_template, ) - rendered_payload = ( - self._value_template.async_render_with_possible_json_value( - payload, variables=values + try: + rendered_payload = ( + self._value_template.async_render_with_possible_json_value( + payload, variables=values + ) ) - ) + except Exception as ex: # pylint: disable=broad-except + _LOGGER.error( + "%s: %s rendering template for entity '%s', template: '%s'", + type(ex).__name__, + ex, + self._entity.entity_id if self._entity else "n/a", + self._value_template.template, + ) + raise ex return rendered_payload _LOGGER.debug( @@ -248,9 +258,24 @@ class MqttValueTemplate: default, self._value_template, ) - rendered_payload = self._value_template.async_render_with_possible_json_value( - payload, default, variables=values - ) + try: + rendered_payload = ( + self._value_template.async_render_with_possible_json_value( + payload, default, variables=values + ) + ) + except Exception as ex: # pylint: disable=broad-except + _LOGGER.error( + "%s: %s rendering template for entity '%s', template: " + "'%s', default value: %s and payload: %s", + type(ex).__name__, + ex, + self._entity.entity_id if self._entity else "n/a", + self._value_template.template, + default, + payload, + ) + raise ex return rendered_payload diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index c0d7a94de5b..e3a12a2c24e 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -41,6 +41,7 @@ from .test_common import help_all_subscribe_calls from tests.common import ( MockConfigEntry, + MockEntity, async_fire_mqtt_message, async_fire_time_changed, mock_restore_cache, @@ -417,6 +418,37 @@ 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: + """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): + val_tpl.async_render_with_possible_json_value('{"some_var": null }') + await hass.async_block_till_done() + assert ( + "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): + val_tpl.async_render_with_possible_json_value( + '{"some_var": null }', default=100 + ) + assert ( + "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 + + async def test_service_call_without_topic_does_not_publish( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: