Add entity deduplication by assist device ID in conversation agent (#123957)

* Add entities deduplication by assist device ID in default conversation agent

* Updated test.
pull/124254/head
Andrii Mitnovych 2024-08-19 13:00:23 -07:00 committed by GitHub
parent b4afca3e7e
commit 254aa8c9ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 84 additions and 7 deletions

View File

@ -349,6 +349,9 @@ class DefaultAgent(ConversationEntity):
}
for entity in result.entities_list
}
device_area = self._get_device_area(user_input.device_id)
if device_area:
slots["preferred_area_id"] = {"value": device_area.id}
async_conversation_trace_append(
ConversationTraceEventType.TOOL_CALL,
{
@ -917,18 +920,26 @@ class DefaultAgent(ConversationEntity):
if not user_input.device_id:
return None
devices = dr.async_get(self.hass)
device = devices.async_get(user_input.device_id)
if (device is None) or (device.area_id is None):
return None
areas = ar.async_get(self.hass)
device_area = areas.async_get_area(device.area_id)
device_area = self._get_device_area(user_input.device_id)
if device_area is None:
return None
return {"area": {"value": device_area.name, "text": device_area.name}}
def _get_device_area(self, device_id: str | None) -> ar.AreaEntry | None:
"""Return area object for given device identifier."""
if device_id is None:
return None
devices = dr.async_get(self.hass)
device = devices.async_get(device_id)
if (device is None) or (device.area_id is None):
return None
areas = ar.async_get(self.hass)
return areas.async_get_area(device.area_id)
def _get_error_text(
self,
error_key: ErrorKey | str,

View File

@ -308,6 +308,72 @@ async def test_unexposed_entities_skipped(
assert result.response.matched_states[0].entity_id == exposed_light.entity_id
@pytest.mark.usefixtures("init_components")
async def test_duplicated_names_resolved_with_device_area(
hass: HomeAssistant,
area_registry: ar.AreaRegistry,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test entities deduplication with device ID context."""
area_kitchen = area_registry.async_get_or_create("kitchen_id")
area_bedroom = area_registry.async_get_or_create("bedroom_id")
kitchen_light = entity_registry.async_get_or_create("light", "demo", "1234")
bedroom_light = entity_registry.async_get_or_create("light", "demo", "5678")
# Same name and alias
for light in (kitchen_light, bedroom_light):
light = entity_registry.async_update_entity(
light.entity_id,
name="top light",
aliases={"overhead light"},
)
hass.states.async_set(
light.entity_id,
"off",
attributes={ATTR_FRIENDLY_NAME: light.name},
)
# Different areas
kitchen_light = entity_registry.async_update_entity(
kitchen_light.entity_id,
area_id=area_kitchen.id,
)
bedroom_light = entity_registry.async_update_entity(
bedroom_light.entity_id,
area_id=area_bedroom.id,
)
# Pipeline device in bedroom area
entry = MockConfigEntry()
entry.add_to_hass(hass)
assist_device = device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections=set(),
identifiers={("demo", "id-1234")},
)
assist_device = device_registry.async_update_device(
assist_device.id,
area_id=area_bedroom.id,
)
# Check name and alias
for name in ("top light", "overhead light"):
# Only one light should be turned on
calls = async_mock_service(hass, "light", "turn_on")
result = await conversation.async_converse(
hass, f"turn on {name}", None, Context(), device_id=assist_device.id
)
assert len(calls) == 1
assert calls[0].data["entity_id"][0] == bedroom_light.entity_id
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
assert result.response.intent is not None
assert result.response.intent.slots.get("name", {}).get("value") == name
assert result.response.intent.slots.get("name", {}).get("text") == name
@pytest.mark.usefixtures("init_components")
async def test_trigger_sentences(hass: HomeAssistant) -> None:
"""Test registering/unregistering/matching a few trigger sentences."""