mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Skip unexposed entities in intent handlers (#92415)
* Filter intent handler entities by exposure * Add test for skipping unexposed entities
This commit is contained in:
parent
2ae3e90238
commit
74560ab139
@ -201,6 +201,7 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
user_input.text,
|
||||
user_input.context,
|
||||
language,
|
||||
assistant=DOMAIN,
|
||||
)
|
||||
except intent.IntentHandleError:
|
||||
_LOGGER.exception("Intent handling error")
|
||||
|
@ -140,16 +140,18 @@ class GetStateIntentHandler(intent.IntentHandler):
|
||||
area=area,
|
||||
domains=domains,
|
||||
device_classes=device_classes,
|
||||
assistant=intent_obj.assistant,
|
||||
)
|
||||
)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Found %s state(s) that matched: name=%s, area=%s, domains=%s, device_classes=%s",
|
||||
"Found %s state(s) that matched: name=%s, area=%s, domains=%s, device_classes=%s, assistant=%s",
|
||||
len(states),
|
||||
name,
|
||||
area,
|
||||
domains,
|
||||
device_classes,
|
||||
intent_obj.assistant,
|
||||
)
|
||||
|
||||
# Create response
|
||||
|
@ -11,6 +11,7 @@ from typing import Any, TypeVar
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.homeassistant.exposed_entities import async_should_expose
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_ENTITY_ID,
|
||||
@ -65,6 +66,7 @@ async def async_handle(
|
||||
text_input: str | None = None,
|
||||
context: Context | None = None,
|
||||
language: str | None = None,
|
||||
assistant: str | None = None,
|
||||
) -> IntentResponse:
|
||||
"""Handle an intent."""
|
||||
handler: IntentHandler = hass.data.get(DATA_KEY, {}).get(intent_type)
|
||||
@ -79,7 +81,14 @@ async def async_handle(
|
||||
language = hass.config.language
|
||||
|
||||
intent = Intent(
|
||||
hass, platform, intent_type, slots or {}, text_input, context, language
|
||||
hass,
|
||||
platform=platform,
|
||||
intent_type=intent_type,
|
||||
slots=slots or {},
|
||||
text_input=text_input,
|
||||
context=context,
|
||||
language=language,
|
||||
assistant=assistant,
|
||||
)
|
||||
|
||||
try:
|
||||
@ -208,6 +217,7 @@ def async_match_states(
|
||||
entities: entity_registry.EntityRegistry | None = None,
|
||||
areas: area_registry.AreaRegistry | None = None,
|
||||
devices: device_registry.DeviceRegistry | None = None,
|
||||
assistant: str | None = None,
|
||||
) -> Iterable[State]:
|
||||
"""Find states that match the constraints."""
|
||||
if states is None:
|
||||
@ -258,6 +268,14 @@ def async_match_states(
|
||||
|
||||
states_and_entities = list(_filter_by_area(states_and_entities, area, devices))
|
||||
|
||||
if assistant is not None:
|
||||
# Filter by exposure
|
||||
states_and_entities = [
|
||||
(state, entity)
|
||||
for state, entity in states_and_entities
|
||||
if async_should_expose(hass, assistant, state.entity_id)
|
||||
]
|
||||
|
||||
if name is not None:
|
||||
if devices is None:
|
||||
devices = device_registry.async_get(hass)
|
||||
@ -387,6 +405,7 @@ class ServiceIntentHandler(IntentHandler):
|
||||
area=area,
|
||||
domains=domains,
|
||||
device_classes=device_classes,
|
||||
assistant=intent_obj.assistant,
|
||||
)
|
||||
)
|
||||
|
||||
@ -496,6 +515,7 @@ class Intent:
|
||||
"context",
|
||||
"language",
|
||||
"category",
|
||||
"assistant",
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@ -508,6 +528,7 @@ class Intent:
|
||||
context: Context,
|
||||
language: str,
|
||||
category: IntentCategory | None = None,
|
||||
assistant: str | None = None,
|
||||
) -> None:
|
||||
"""Initialize an intent."""
|
||||
self.hass = hass
|
||||
@ -518,6 +539,7 @@ class Intent:
|
||||
self.context = context
|
||||
self.language = language
|
||||
self.category = category
|
||||
self.assistant = assistant
|
||||
|
||||
@callback
|
||||
def create_response(self) -> IntentResponse:
|
||||
|
@ -174,3 +174,52 @@ async def test_expose_flag_automatically_set(
|
||||
new_light: {"should_expose": True},
|
||||
test.entity_id: {"should_expose": False},
|
||||
}
|
||||
|
||||
|
||||
async def test_unexposed_entities_skipped(
|
||||
hass: HomeAssistant,
|
||||
init_components,
|
||||
area_registry: ar.AreaRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test that unexposed entities are skipped in exposed areas."""
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen")
|
||||
|
||||
# Both lights are in the kitchen
|
||||
exposed_light = entity_registry.async_get_or_create("light", "demo", "1234")
|
||||
entity_registry.async_update_entity(
|
||||
exposed_light.entity_id,
|
||||
area_id=area_kitchen.id,
|
||||
)
|
||||
hass.states.async_set(exposed_light.entity_id, "off")
|
||||
|
||||
unexposed_light = entity_registry.async_get_or_create("light", "demo", "5678")
|
||||
entity_registry.async_update_entity(
|
||||
unexposed_light.entity_id,
|
||||
area_id=area_kitchen.id,
|
||||
)
|
||||
hass.states.async_set(unexposed_light.entity_id, "off")
|
||||
|
||||
# On light is exposed, the other is not
|
||||
expose_entity(hass, exposed_light.entity_id, True)
|
||||
expose_entity(hass, unexposed_light.entity_id, False)
|
||||
|
||||
# Only one light should be turned on
|
||||
calls = async_mock_service(hass, "light", "turn_on")
|
||||
result = await conversation.async_converse(
|
||||
hass, "turn on kitchen lights", None, Context(), None
|
||||
)
|
||||
|
||||
assert len(calls) == 1
|
||||
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
|
||||
# Only one light should be returned
|
||||
hass.states.async_set(exposed_light.entity_id, "on")
|
||||
hass.states.async_set(unexposed_light.entity_id, "on")
|
||||
result = await conversation.async_converse(
|
||||
hass, "how many lights are on in the kitchen", None, Context(), None
|
||||
)
|
||||
|
||||
assert result.response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||
assert len(result.response.matched_states) == 1
|
||||
assert result.response.matched_states[0].entity_id == exposed_light.entity_id
|
||||
|
Loading…
x
Reference in New Issue
Block a user