diff --git a/homeassistant/components/esphome/config_flow.py b/homeassistant/components/esphome/config_flow.py index 2b1babfc0ba..72d670ee029 100644 --- a/homeassistant/components/esphome/config_flow.py +++ b/homeassistant/components/esphome/config_flow.py @@ -306,7 +306,32 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): updates[CONF_HOST] = host if port is not None: updates[CONF_PORT] = port - self._abort_if_unique_id_configured(updates=updates) + self._abort_unique_id_configured_with_details(updates=updates) + + @callback + def _abort_unique_id_configured_with_details(self, updates: dict[str, Any]) -> None: + """Abort if unique_id is already configured with details.""" + assert self.unique_id is not None + if not ( + conflict_entry := self.hass.config_entries.async_entry_for_domain_unique_id( + self.handler, self.unique_id + ) + ): + return + assert conflict_entry.unique_id is not None + if updates: + error = "already_configured_updates" + else: + error = "already_configured_detailed" + self._abort_if_unique_id_configured( + updates=updates, + error=error, + description_placeholders={ + "title": conflict_entry.title, + "name": conflict_entry.data.get(CONF_DEVICE_NAME, "unknown"), + "mac": format_mac(conflict_entry.unique_id), + }, + ) async def async_step_mqtt( self, discovery_info: MqttServiceInfo @@ -341,7 +366,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): # Check if already configured await self.async_set_unique_id(mac_address) - self._abort_if_unique_id_configured( + self._abort_unique_id_configured_with_details( updates={CONF_HOST: self._host, CONF_PORT: self._port} ) @@ -479,7 +504,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): data=self._reauth_entry.data | self._async_make_config_data(), ) assert self._host is not None - self._abort_if_unique_id_configured( + self._abort_unique_id_configured_with_details( updates={ CONF_HOST: self._host, CONF_PORT: self._port, @@ -510,7 +535,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): if not ( unique_id_matches := (self.unique_id == self._reconfig_entry.unique_id) ): - self._abort_if_unique_id_configured( + self._abort_unique_id_configured_with_details( updates={ CONF_HOST: self._host, CONF_PORT: self._port, @@ -640,7 +665,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): mac_address = format_mac(self._device_info.mac_address) await self.async_set_unique_id(mac_address, raise_on_progress=False) if self.source not in (SOURCE_REAUTH, SOURCE_RECONFIGURE): - self._abort_if_unique_id_configured( + self._abort_unique_id_configured_with_details( updates={ CONF_HOST: self._host, CONF_PORT: self._port, diff --git a/homeassistant/components/esphome/strings.json b/homeassistant/components/esphome/strings.json index 6c10a2e5fe8..b95537e448e 100644 --- a/homeassistant/components/esphome/strings.json +++ b/homeassistant/components/esphome/strings.json @@ -2,6 +2,8 @@ "config": { "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "already_configured_detailed": "A device `{name}`, with MAC address `{mac}` is already configured as `{title}`.", + "already_configured_updates": "A device `{name}`, with MAC address `{mac}` is already configured as `{title}`; the existing configuration will be updated with the validated data.", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "mdns_missing_mac": "Missing MAC address in mDNS properties.", diff --git a/tests/components/esphome/test_config_flow.py b/tests/components/esphome/test_config_flow.py index 9d400ba618b..19a42792ec2 100644 --- a/tests/components/esphome/test_config_flow.py +++ b/tests/components/esphome/test_config_flow.py @@ -119,7 +119,12 @@ async def test_user_connection_updates_host( data={CONF_HOST: "127.0.0.1", CONF_PORT: 80}, ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "unknown", + "mac": "11:22:33:44:55:aa", + } assert entry.data[CONF_HOST] == "127.0.0.1" @@ -173,7 +178,12 @@ async def test_user_sets_unique_id( {CONF_HOST: "127.0.0.1", CONF_PORT: 6053}, ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "test", + "name": "test", + "mac": "11:22:33:44:55:aa", + } @pytest.mark.usefixtures("mock_zeroconf") @@ -645,7 +655,12 @@ async def test_discovery_already_configured( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "unknown", + "mac": "11:22:33:44:55:aa", + } async def test_discovery_duplicate_data( @@ -701,7 +716,12 @@ async def test_discovery_updates_unique_id( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "unknown", + "mac": "11:22:33:44:55:aa", + } assert entry.unique_id == "11:22:33:44:55:aa" @@ -1159,7 +1179,12 @@ async def test_discovery_dhcp_updates_host( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "unknown", + "mac": "11:22:33:44:55:aa", + } assert entry.data[CONF_HOST] == "192.168.43.184" @@ -1188,7 +1213,12 @@ async def test_discovery_dhcp_does_not_update_host_wrong_mac( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_detailed" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "unknown", + "mac": "11:22:33:44:55:aa", + } # Mac was wrong, should not update assert entry.data[CONF_HOST] == "192.168.43.183" @@ -1217,7 +1247,12 @@ async def test_discovery_dhcp_does_not_update_host_wrong_mac_bad_key( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_detailed" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "unknown", + "mac": "11:22:33:44:55:aa", + } # Mac was wrong, should not update assert entry.data[CONF_HOST] == "192.168.43.183" @@ -1246,7 +1281,12 @@ async def test_discovery_dhcp_does_not_update_host_missing_mac_bad_key( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_detailed" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "unknown", + "mac": "11:22:33:44:55:aa", + } # Mac was missing, should not update assert entry.data[CONF_HOST] == "192.168.43.183" @@ -1999,7 +2039,12 @@ async def test_reconfig_mac_used_by_other_entry( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "test4", + "mac": "11:22:33:44:55:bb", + } @pytest.mark.usefixtures("mock_zeroconf", "mock_setup_entry") diff --git a/tests/components/esphome/test_manager.py b/tests/components/esphome/test_manager.py index 12ae58a8240..aa4ca665602 100644 --- a/tests/components/esphome/test_manager.py +++ b/tests/components/esphome/test_manager.py @@ -743,7 +743,12 @@ async def test_connection_aborted_wrong_device( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "test", + "mac": "11:22:33:44:55:aa", + } assert entry.data[CONF_HOST] == "192.168.43.184" await hass.async_block_till_done() assert len(new_info.mock_calls) == 2 @@ -812,7 +817,12 @@ async def test_connection_aborted_wrong_device_same_name( ) assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + assert result["reason"] == "already_configured_updates" + assert result["description_placeholders"] == { + "title": "Mock Title", + "name": "test", + "mac": "11:22:33:44:55:aa", + } assert entry.data[CONF_HOST] == "192.168.43.184" await hass.async_block_till_done() assert len(new_info.mock_calls) == 2