Flush conversation name cache when an entity is renamed (#91214)
This commit is contained in:
parent
0d7711f787
commit
3c8397a7b9
2 changed files with 289 additions and 4 deletions
|
@ -32,6 +32,7 @@ from .const import DEFAULT_EXPOSED_ATTRIBUTES, DEFAULT_EXPOSED_DOMAINS, DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_DEFAULT_ERROR_TEXT = "Sorry, I couldn't understand that"
|
_DEFAULT_ERROR_TEXT = "Sorry, I couldn't understand that"
|
||||||
|
_ENTITY_REGISTRY_UPDATE_FIELDS = ["aliases", "name", "original_name"]
|
||||||
|
|
||||||
REGEX_TYPE = type(re.compile(""))
|
REGEX_TYPE = type(re.compile(""))
|
||||||
|
|
||||||
|
@ -450,8 +451,10 @@ class DefaultAgent(AbstractConversationAgent):
|
||||||
|
|
||||||
@core.callback
|
@core.callback
|
||||||
def _async_handle_entity_registry_changed(self, event: core.Event) -> None:
|
def _async_handle_entity_registry_changed(self, event: core.Event) -> None:
|
||||||
"""Clear names list cache when an entity changes aliases."""
|
"""Clear names list cache when an entity registry entry has changed."""
|
||||||
if event.data["action"] == "update" and "aliases" not in event.data["changes"]:
|
if event.data["action"] == "update" and not any(
|
||||||
|
field in event.data["changes"] for field in _ENTITY_REGISTRY_UPDATE_FIELDS
|
||||||
|
):
|
||||||
return
|
return
|
||||||
self._slot_lists = None
|
self._slot_lists = None
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ async def test_http_processing_intent_target_ha_agent(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_http_processing_intent_entity_added(
|
async def test_http_processing_intent_entity_added_removed(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
init_components,
|
init_components,
|
||||||
hass_client: ClientSessionGenerator,
|
hass_client: ClientSessionGenerator,
|
||||||
|
@ -198,7 +198,7 @@ async def test_http_processing_intent_entity_added(
|
||||||
"conversation_id": None,
|
"conversation_id": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add an alias
|
# Add an entity
|
||||||
entity_registry.async_get_or_create(
|
entity_registry.async_get_or_create(
|
||||||
"light", "demo", "5678", suggested_object_id="late"
|
"light", "demo", "5678", suggested_object_id="late"
|
||||||
)
|
)
|
||||||
|
@ -294,6 +294,288 @@ async def test_http_processing_intent_entity_added(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_http_processing_intent_alias_added_removed(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
init_components,
|
||||||
|
hass_client: ClientSessionGenerator,
|
||||||
|
hass_admin_user: MockUser,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test processing intent via HTTP API with aliases added later.
|
||||||
|
|
||||||
|
We want to ensure that adding an alias later busts the cache
|
||||||
|
so that the new alias is available.
|
||||||
|
"""
|
||||||
|
entity_registry.async_get_or_create(
|
||||||
|
"light", "demo", "1234", suggested_object_id="kitchen"
|
||||||
|
)
|
||||||
|
hass.states.async_set("light.kitchen", "off", {"friendly_name": "kitchen light"})
|
||||||
|
|
||||||
|
calls = async_mock_service(hass, LIGHT_DOMAIN, "turn_on")
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on kitchen light"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
assert len(calls) == 1
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
assert data == {
|
||||||
|
"response": {
|
||||||
|
"response_type": "action_done",
|
||||||
|
"card": {},
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Turned on light",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"data": {
|
||||||
|
"targets": [],
|
||||||
|
"success": [
|
||||||
|
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||||
|
],
|
||||||
|
"failed": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"conversation_id": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add an alias
|
||||||
|
entity_registry.async_update_entity("light.kitchen", aliases={"late added alias"})
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on late added alias"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
assert data == {
|
||||||
|
"response": {
|
||||||
|
"response_type": "action_done",
|
||||||
|
"card": {},
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Turned on light",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"data": {
|
||||||
|
"targets": [],
|
||||||
|
"success": [
|
||||||
|
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||||
|
],
|
||||||
|
"failed": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"conversation_id": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Now remove the alieas
|
||||||
|
entity_registry.async_update_entity("light.kitchen", aliases={})
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on late added alias"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
assert data == {
|
||||||
|
"conversation_id": None,
|
||||||
|
"response": {
|
||||||
|
"card": {},
|
||||||
|
"data": {"code": "no_intent_match"},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"response_type": "error",
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Sorry, I couldn't understand that",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_http_processing_intent_entity_renamed(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
init_components,
|
||||||
|
hass_client: ClientSessionGenerator,
|
||||||
|
hass_admin_user: MockUser,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
) -> None:
|
||||||
|
"""Test processing intent via HTTP API with entities renamed later.
|
||||||
|
|
||||||
|
We want to ensure that renaming an entity later busts the cache
|
||||||
|
so that the new name is used.
|
||||||
|
"""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
platform.init(empty=True)
|
||||||
|
|
||||||
|
entity = platform.MockLight("kitchen light", "on")
|
||||||
|
entity._attr_unique_id = "1234"
|
||||||
|
entity.entity_id = "light.kitchen"
|
||||||
|
platform.ENTITIES.append(entity)
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
{LIGHT_DOMAIN: [{"platform": "test"}]},
|
||||||
|
)
|
||||||
|
|
||||||
|
calls = async_mock_service(hass, LIGHT_DOMAIN, "turn_on")
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on kitchen light"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
assert len(calls) == 1
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
assert data == {
|
||||||
|
"response": {
|
||||||
|
"response_type": "action_done",
|
||||||
|
"card": {},
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Turned on light",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"data": {
|
||||||
|
"targets": [],
|
||||||
|
"success": [
|
||||||
|
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||||
|
],
|
||||||
|
"failed": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"conversation_id": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rename the entity
|
||||||
|
entity_registry.async_update_entity("light.kitchen", name="renamed light")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on renamed light"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
assert data == {
|
||||||
|
"response": {
|
||||||
|
"response_type": "action_done",
|
||||||
|
"card": {},
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Turned on light",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"data": {
|
||||||
|
"targets": [],
|
||||||
|
"success": [
|
||||||
|
{"id": "light.kitchen", "name": "renamed light", "type": "entity"}
|
||||||
|
],
|
||||||
|
"failed": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"conversation_id": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on kitchen light"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
assert data == {
|
||||||
|
"conversation_id": None,
|
||||||
|
"response": {
|
||||||
|
"card": {},
|
||||||
|
"data": {"code": "no_intent_match"},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"response_type": "error",
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Sorry, I couldn't understand that",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Now clear the custom name
|
||||||
|
entity_registry.async_update_entity("light.kitchen", name=None)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on kitchen light"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
assert data == {
|
||||||
|
"response": {
|
||||||
|
"response_type": "action_done",
|
||||||
|
"card": {},
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Turned on light",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"data": {
|
||||||
|
"targets": [],
|
||||||
|
"success": [
|
||||||
|
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||||
|
],
|
||||||
|
"failed": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"conversation_id": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
resp = await client.post(
|
||||||
|
"/api/conversation/process", json={"text": "turn on renamed light"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
assert data == {
|
||||||
|
"conversation_id": None,
|
||||||
|
"response": {
|
||||||
|
"card": {},
|
||||||
|
"data": {"code": "no_intent_match"},
|
||||||
|
"language": hass.config.language,
|
||||||
|
"response_type": "error",
|
||||||
|
"speech": {
|
||||||
|
"plain": {
|
||||||
|
"extra_data": None,
|
||||||
|
"speech": "Sorry, I couldn't understand that",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("agent_id", AGENT_ID_OPTIONS)
|
@pytest.mark.parametrize("agent_id", AGENT_ID_OPTIONS)
|
||||||
@pytest.mark.parametrize("sentence", ("turn on kitchen", "turn kitchen on"))
|
@pytest.mark.parametrize("sentence", ("turn on kitchen", "turn kitchen on"))
|
||||||
async def test_turn_on_intent(
|
async def test_turn_on_intent(
|
||||||
|
|
Loading…
Add table
Reference in a new issue