diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index edbb5fa7354..3ac715426e3 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -838,6 +838,10 @@ class TrackTemplateResultInfo: self._track_state_changes: _TrackStateChangeFiltered | None = None self._time_listeners: dict[Template, Callable[[], None]] = {} + def __repr__(self) -> str: + """Return the representation.""" + return f"" + def async_setup(self, raise_on_template_error: bool, strict: bool = False) -> None: """Activation of template tracking.""" block_render = False @@ -1651,12 +1655,6 @@ def _render_infos_needs_all_listener(render_infos: Iterable[RenderInfo]) -> bool if render_info.all_states or render_info.all_states_lifecycle: return True - # Previous call had an exception - # so we do not know which states - # to track - if render_info.exception: - return True - return False diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 2e112706fba..c923bd2d84a 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -274,6 +274,8 @@ class RenderInfo: f" entities={self.entities}" f" rate_limit={self.rate_limit}" f" has_time={self.has_time}" + f" exception={self.exception}" + f" is_static={self.is_static}" ">" ) diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index fb0925c15d2..066460c90d8 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -2209,7 +2209,7 @@ async def test_track_template_result_errors( hass.states.async_set("switch.not_exist", "on") await hass.async_block_till_done() - assert len(syntax_error_runs) == 1 + assert len(syntax_error_runs) == 0 assert len(not_exist_runs) == 2 assert not_exist_runs[1][0].data.get("entity_id") == "switch.not_exist" assert not_exist_runs[1][1] == template_not_exist @@ -2229,6 +2229,56 @@ async def test_track_template_result_errors( assert isinstance(not_exist_runs[2][3], TemplateError) +async def test_track_template_result_transient_errors( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test tracking template with transient errors in the template.""" + hass.states.async_set("sensor.error", "unknown") + template_that_raises_sometimes = Template( + "{{ states('sensor.error') | float }}", hass + ) + + sometimes_error_runs = [] + + @ha.callback + def sometimes_error_listener(event, updates): + track_result = updates.pop() + sometimes_error_runs.append( + ( + event, + track_result.template, + track_result.last_result, + track_result.result, + ) + ) + + info = async_track_template_result( + hass, + [TrackTemplate(template_that_raises_sometimes, None)], + sometimes_error_listener, + ) + await hass.async_block_till_done() + + assert sometimes_error_runs == [] + assert "ValueError" in caplog.text + assert "ValueError" in repr(info) + caplog.clear() + + hass.states.async_set("sensor.error", "unavailable") + await hass.async_block_till_done() + assert len(sometimes_error_runs) == 1 + assert isinstance(sometimes_error_runs[0][3], TemplateError) + sometimes_error_runs.clear() + assert "ValueError" in repr(info) + + hass.states.async_set("sensor.error", "4") + await hass.async_block_till_done() + assert len(sometimes_error_runs) == 1 + assert sometimes_error_runs[0][3] == 4.0 + sometimes_error_runs.clear() + assert "ValueError" not in repr(info) + + async def test_static_string(hass: HomeAssistant) -> None: """Test a static string.""" template_refresh = Template("{{ 'static' }}", hass) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 2434b2aed15..f97f0a4b9c5 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -4323,3 +4323,14 @@ def test_contains(hass: HomeAssistant, seq, value, expected) -> None: ) == expected ) + + +async def test_render_to_info_with_exception(hass: HomeAssistant) -> None: + """Test info is still available if the template has an exception.""" + hass.states.async_set("test_domain.object", "dog") + info = render_to_info(hass, '{{ states("test_domain.object") | float }}') + with pytest.raises(TemplateError, match="no default was specified"): + info.result() + + assert info.all_states is False + assert info.entities == {"test_domain.object"}