diff --git a/homeassistant/components/conversation/default_agent.py b/homeassistant/components/conversation/default_agent.py index 3e7a4cc8e90a..a7fa22670d65 100644 --- a/homeassistant/components/conversation/default_agent.py +++ b/homeassistant/components/conversation/default_agent.py @@ -83,6 +83,7 @@ _LOGGER = logging.getLogger(__name__) _DEFAULT_ERROR_TEXT = "Sorry, I couldn't understand that" _ENTITY_REGISTRY_UPDATE_FIELDS = ["aliases", "name", "original_name"] +_DEVICE_REGISTRY_UPDATE_FIELDS = ["name", "name_by_user"] _DEFAULT_EXPOSED_ATTRIBUTES = {"device_class"} @@ -288,6 +289,15 @@ class DefaultAgent(ConversationEntity): field in event_data["changes"] for field in _ENTITY_REGISTRY_UPDATE_FIELDS ) + @callback + def _filter_device_registry_changes( + self, event_data: dr.EventDeviceRegistryUpdatedData + ) -> bool: + """Filter device registry changed events.""" + return event_data["action"] == "update" and any( + field in event_data["changes"] for field in _DEVICE_REGISTRY_UPDATE_FIELDS + ) + @callback def _filter_state_changes(self, event_data: EventStateChangedData) -> bool: """Filter state changed events.""" @@ -312,6 +322,11 @@ class DefaultAgent(ConversationEntity): self._async_clear_slot_list, event_filter=self._filter_entity_registry_changes, ), + self.hass.bus.async_listen( + dr.EVENT_DEVICE_REGISTRY_UPDATED, + self._async_clear_slot_list, + event_filter=self._filter_device_registry_changes, + ), self.hass.bus.async_listen( EVENT_STATE_CHANGED, self._async_clear_slot_list, diff --git a/tests/components/conversation/test_default_agent.py b/tests/components/conversation/test_default_agent.py index 3b852d7883fc..fcf3212f0608 100644 --- a/tests/components/conversation/test_default_agent.py +++ b/tests/components/conversation/test_default_agent.py @@ -492,6 +492,54 @@ async def test_duplicated_names_resolved_with_device_area( assert result.response.intent.slots.get("name", {}).get("text") == name +@pytest.mark.usefixtures("init_components") +async def test_device_rename_refreshes_slot_list( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, +) -> None: + """Test renaming a device makes the entity matchable by its new computed name.""" + config_entry = MockConfigEntry() + config_entry.add_to_hass(hass) + device = device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections=set(), + identifiers={("demo", "device-1")}, + name="Kitchen", + ) + + light = entity_registry.async_get_or_create( + "light", + "demo", + "1234", + device_id=device.id, + has_entity_name=True, + original_name="Light", + ) + hass.states.async_set(light.entity_id, "off") + expose_entity(hass, light.entity_id, True) + + # Populate the slot list cache: the current computed name matches. + calls = async_mock_service(hass, "light", "turn_on") + result = await conversation.async_converse( + hass, "turn on Kitchen Light", None, Context(), None + ) + assert result.response.response_type is intent.IntentResponseType.ACTION_DONE + assert len(calls) == 1 + + # Renaming the device changes the light's computed name to "Bedroom Light". + device_registry.async_update_device(device.id, name_by_user="Bedroom") + await hass.async_block_till_done() + + # The new name is now matchable. + calls = async_mock_service(hass, "light", "turn_on") + result = await conversation.async_converse( + hass, "turn on Bedroom Light", None, Context(), None + ) + assert result.response.response_type is intent.IntentResponseType.ACTION_DONE + assert len(calls) == 1 + + @pytest.mark.usefixtures("init_components") async def test_trigger_sentences(hass: HomeAssistant) -> None: """Test registering/unregistering/matching a few trigger sentences."""