From 4efee9e9f055fda7e872eee1488ebd237c3e71ad Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Wed, 30 Sep 2026 05:14:05 -0500 Subject: [PATCH] Report unexposed entities as an error in HassGetState (#183627) Co-authored-by: Claude Opus 5 Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com> --- homeassistant/components/intent/__init__.py | 14 +++++--- .../conversation/test_default_agent.py | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/intent/__init__.py b/homeassistant/components/intent/__init__.py index 5ed14070dde3..446bb2f26bfa 100644 --- a/homeassistant/components/intent/__init__.py +++ b/homeassistant/components/intent/__init__.py @@ -339,13 +339,19 @@ class GetStateIntentHandler(intent.IntentHandler): match_result = intent.async_match_targets( hass, match_constraints, match_preferences ) + no_match_reason = match_result.no_match_reason if ( (not match_result.is_match) - and (match_result.no_match_reason is not None) - and (not match_result.no_match_reason.is_no_entities_reason()) + and (no_match_reason is not None) + and ( + (not no_match_reason.is_no_entities_reason()) + or ( + entity_name + and (no_match_reason is intent.MatchFailedReason.ASSISTANT) + ) + ) ): - # Don't try to answer questions for certain errors. - # Other match failure reasons are OK. + # Don't try to answer questions for certain errors or for named entities that are not exposed. raise intent.MatchFailedError( result=match_result, constraints=match_constraints ) diff --git a/tests/components/conversation/test_default_agent.py b/tests/components/conversation/test_default_agent.py index c3e730578c76..31ab6a0af06b 100644 --- a/tests/components/conversation/test_default_agent.py +++ b/tests/components/conversation/test_default_agent.py @@ -874,6 +874,41 @@ async def test_error_no_device_exposed(hass: HomeAssistant) -> None: ) +@pytest.mark.usefixtures("init_components") +async def test_error_no_device_exposed_query(hass: HomeAssistant) -> None: + """Test error message when querying an entity that exists but is not exposed.""" + hass.states.async_set("light.kitchen_light", "off") + expose_entity(hass, "light.kitchen_light", False) + + result = await conversation.async_converse( + hass, "is the kitchen light on?", None, Context(), None + ) + + assert result.response.response_type is intent.IntentResponseType.ERROR + assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS + assert ( + result.response.speech["plain"]["speech"] + == "Sorry, kitchen light is not exposed" + ) + + +@pytest.mark.usefixtures("init_components") +async def test_query_state_no_device_exposed(hass: HomeAssistant) -> None: + """Test that a query without a name is answered when no entity is exposed.""" + hass.states.async_set("light.kitchen_light", "off") + expose_entity(hass, "light.kitchen_light", False) + + hass.states.async_set("light.bedroom_light", "on") + expose_entity(hass, "light.bedroom_light", False) + + result = await conversation.async_converse( + hass, "how many lights are on?", None, Context(), None + ) + + assert result.response.response_type is intent.IntentResponseType.QUERY_ANSWER + assert not result.response.matched_states + + @pytest.mark.usefixtures("init_components") async def test_error_no_area(hass: HomeAssistant) -> None: """Test error message when area doesn't exist."""