diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index 6adcb4d1fd9..18ef4c2082e 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -96,6 +96,18 @@ def condition_trace_set_result(result: bool, **kwargs: Any) -> None: node.set_result(result=result, **kwargs) +def condition_trace_update_result(result: bool, **kwargs: Any) -> None: + """Update the result of TraceElement at the top of the stack.""" + node = trace_stack_top(trace_stack_cv) + + # The condition function may be called directly, in which case tracing + # is not setup + if not node: + return + + node.update_result(result=result, **kwargs) + + @contextmanager def trace_condition(variables: TemplateVarsType) -> Generator: """Trace condition evaluation.""" @@ -118,7 +130,7 @@ def trace_condition_function(condition: ConditionCheckerType) -> ConditionChecke """Trace condition.""" with trace_condition(variables): result = condition(hass, variables) - condition_trace_set_result(result) + condition_trace_update_result(result) return result return wrapper @@ -644,15 +656,22 @@ def template( def async_template( - hass: HomeAssistant, value_template: Template, variables: TemplateVarsType = None + hass: HomeAssistant, + value_template: Template, + variables: TemplateVarsType = None, + trace_result: bool = True, ) -> bool: """Test if template condition matches.""" try: - value: str = value_template.async_render(variables, parse_result=False) + info = value_template.async_render_to_info(variables, parse_result=False) + value = info.result() except TemplateError as ex: raise ConditionErrorMessage("template", str(ex)) from ex - return value.lower() == "true" + result = value.lower() == "true" + if trace_result: + condition_trace_set_result(result, entities=list(info.entities)) + return result def async_template_from_config( diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index bf52fc81b6a..84e7b0639e5 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -456,7 +456,7 @@ class _ScriptRun: wait_template.hass = self._hass # check if condition already okay - if condition.async_template(self._hass, wait_template, self._variables): + if condition.async_template(self._hass, wait_template, self._variables, False): self._variables["wait"]["completed"] = True return diff --git a/homeassistant/helpers/trace.py b/homeassistant/helpers/trace.py index c92766036c6..32e387d972f 100644 --- a/homeassistant/helpers/trace.py +++ b/homeassistant/helpers/trace.py @@ -51,6 +51,11 @@ class TraceElement: """Set result.""" self._result = {**kwargs} + def update_result(self, **kwargs: Any) -> None: + """Set result.""" + old_result = self._result or {} + self._result = {**old_result, **kwargs} + def as_dict(self) -> dict[str, Any]: """Return dictionary version of this TraceElement.""" result: dict[str, Any] = {"path": self.path, "timestamp": self._timestamp} diff --git a/tests/components/trace/test_websocket_api.py b/tests/components/trace/test_websocket_api.py index 0b7b78b3f1a..8f8428dd517 100644 --- a/tests/components/trace/test_websocket_api.py +++ b/tests/components/trace/test_websocket_api.py @@ -223,7 +223,8 @@ async def test_get_trace( assert len(trace["trace"].get("condition/0", [])) == len(condition_results) for idx, condition_result in enumerate(condition_results): assert trace["trace"]["condition/0"][idx]["result"] == { - "result": condition_result + "result": condition_result, + "entities": [], } contexts[trace["context"]["id"]] = { "run_id": trace["run_id"], @@ -261,7 +262,10 @@ async def test_get_trace( trace = response["result"] assert set(trace["trace"]) == extra_trace_keys[2] assert len(trace["trace"]["condition/0"]) == 1 - assert trace["trace"]["condition/0"][0]["result"] == {"result": False} + assert trace["trace"]["condition/0"][0]["result"] == { + "result": False, + "entities": [], + } assert trace["config"] == moon_config assert trace["context"] assert "error" not in trace @@ -303,7 +307,10 @@ async def test_get_trace( assert "error" not in trace["trace"][f"{prefix}/0"][0] assert trace["trace"][f"{prefix}/0"][0]["result"] == moon_action assert len(trace["trace"]["condition/0"]) == 1 - assert trace["trace"]["condition/0"][0]["result"] == {"result": True} + assert trace["trace"]["condition/0"][0]["result"] == { + "result": True, + "entities": [], + } assert trace["config"] == moon_config assert trace["context"] assert "error" not in trace diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index 05f348ddfeb..d46c343dfb1 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -237,6 +237,14 @@ async def test_and_condition_with_template(hass): hass.states.async_set("sensor.temperature", 120) assert not test(hass) + assert_condition_trace( + { + "": [{"result": {"result": False}}], + "conditions/0": [ + {"result": {"entities": ["sensor.temperature"], "result": False}} + ], + } + ) hass.states.async_set("sensor.temperature", 105) assert not test(hass) diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index 7224dd70677..a0207edcbdd 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -1449,7 +1449,7 @@ async def test_condition_basic(hass, caplog): { "0": [{"result": {"event": "test_event", "event_data": {}}}], "1": [{"result": {"result": True}}], - "1/condition": [{"result": {"result": True}}], + "1/condition": [{"result": {"entities": ["test.entity"], "result": True}}], "2": [{"result": {"event": "test_event", "event_data": {}}}], } ) @@ -1466,7 +1466,7 @@ async def test_condition_basic(hass, caplog): { "0": [{"result": {"event": "test_event", "event_data": {}}}], "1": [{"error_type": script._StopScript, "result": {"result": False}}], - "1/condition": [{"result": {"result": False}}], + "1/condition": [{"result": {"entities": ["test.entity"], "result": False}}], }, expected_script_execution="aborted", ) @@ -1764,9 +1764,9 @@ async def test_repeat_var_in_condition(hass, condition): }, ], "0/repeat/while/0": [ - {"result": {"result": True}}, - {"result": {"result": True}}, - {"result": {"result": False}}, + {"result": {"entities": [], "result": True}}, + {"result": {"entities": [], "result": True}}, + {"result": {"entities": [], "result": False}}, ], "0/repeat/sequence/0": [ {"result": {"event": "test_event", "event_data": {}}} @@ -1797,8 +1797,8 @@ async def test_repeat_var_in_condition(hass, condition): }, ], "0/repeat/until/0": [ - {"result": {"result": False}}, - {"result": {"result": True}}, + {"result": {"entities": [], "result": False}}, + {"result": {"entities": [], "result": True}}, ], } assert_action_trace(expected_trace) @@ -2058,10 +2058,14 @@ async def test_choose(hass, caplog, var, result): expected_trace = {"0": [{"result": {"choice": expected_choice}}]} if var >= 1: expected_trace["0/choose/0"] = [{"result": {"result": var == 1}}] - expected_trace["0/choose/0/conditions/0"] = [{"result": {"result": var == 1}}] + expected_trace["0/choose/0/conditions/0"] = [ + {"result": {"entities": [], "result": var == 1}} + ] if var >= 2: expected_trace["0/choose/1"] = [{"result": {"result": var == 2}}] - expected_trace["0/choose/1/conditions/0"] = [{"result": {"result": var == 2}}] + expected_trace["0/choose/1/conditions/0"] = [ + {"result": {"entities": [], "result": var == 2}} + ] if var == 1: expected_trace["0/choose/0/sequence/0"] = [ {"result": {"event": "test_event", "event_data": {"choice": "first"}}}