diff --git a/homeassistant/components/zwave_js/config_flow.py b/homeassistant/components/zwave_js/config_flow.py index 5519942c4441..dd855cd4a6ca 100644 --- a/homeassistant/components/zwave_js/config_flow.py +++ b/homeassistant/components/zwave_js/config_flow.py @@ -28,6 +28,7 @@ from homeassistant.components.hassio import ( from homeassistant.config_entries import ( SOURCE_ESPHOME, SOURCE_USB, + ConfigEntry, ConfigEntryState, ConfigFlow, ConfigFlowResult, @@ -1516,6 +1517,7 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN): return self.async_abort(reason="not_hassio") if discovery_info.zwave_home_id: + existing_entry: ConfigEntry | None = None if ( ( current_config_entries := self._async_current_entries( @@ -1533,26 +1535,30 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN): None, ) ) - # Only update existing entries that are configured via sockets - and existing_entry.data.get(CONF_SOCKET_PATH) - # And use the add-on - and existing_entry.data.get(CONF_USE_ADDON) ): - manager = get_addon_manager(self.hass) - await self._async_set_addon_config( - {CONF_ADDON_SOCKET: discovery_info.socket_path} - ) - if self.restart_addon: - await manager.async_stop_addon() - self.hass.config_entries.async_update_entry( - existing_entry, - data={ - **existing_entry.data, - CONF_SOCKET_PATH: discovery_info.socket_path, - }, - ) - self.hass.config_entries.async_schedule_reload(existing_entry.entry_id) - return self.async_abort(reason="already_configured") + # We can't migrate entries that are not using the add-on + if not existing_entry.data.get(CONF_USE_ADDON): + return self.async_abort(reason="already_configured") + + # Only update config automatically if using socket + if existing_entry.data.get(CONF_SOCKET_PATH): + manager = get_addon_manager(self.hass) + await self._async_set_addon_config( + {CONF_ADDON_SOCKET: discovery_info.socket_path} + ) + if self.restart_addon: + await manager.async_stop_addon() + self.hass.config_entries.async_update_entry( + existing_entry, + data={ + **existing_entry.data, + CONF_SOCKET_PATH: discovery_info.socket_path, + }, + ) + self.hass.config_entries.async_schedule_reload( + existing_entry.entry_id + ) + return self.async_abort(reason="already_configured") # We are not aborting if home ID configured here, we just want to make sure that it's set # We will update a USB based config entry automatically in `async_step_finish_addon_setup_user` diff --git a/tests/components/zwave_js/test_config_flow.py b/tests/components/zwave_js/test_config_flow.py index 40b648a3033a..71e0c963f52b 100644 --- a/tests/components/zwave_js/test_config_flow.py +++ b/tests/components/zwave_js/test_config_flow.py @@ -1373,7 +1373,7 @@ async def test_esphome_discovery_already_configured( addon_options: dict[str, Any], stop_addon: AsyncMock, ) -> None: - """Test ESPHome discovery success path.""" + """Test ESPHome discovery when already configured.""" addon_options[CONF_ADDON_SOCKET] = "esphome://existing-device:6053" addon_options["another_key"] = "should_not_be_touched" @@ -1421,6 +1421,45 @@ async def test_esphome_discovery_already_configured( assert stop_addon.call_args == call("core_zwave_js") +@pytest.mark.usefixtures("supervisor", "addon_running", "addon_info") +async def test_esphome_discovery_already_configured_unmanaged_addon( + hass: HomeAssistant, + set_addon_options: AsyncMock, + addon_options: dict[str, Any], + stop_addon: AsyncMock, +) -> None: + """Test ESPHome discovery aborts when home ID already configured with unmanaged add-on.""" + addon_options[CONF_ADDON_SOCKET] = "esphome://existing-device:6053" + addon_options["another_key"] = "should_not_be_touched" + + entry = MockConfigEntry( + entry_id="mock-entry-id", + domain=DOMAIN, + data={ + "use_addon": False, + "integration_created_addon": False, + }, + title=TITLE, + unique_id="1234", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ESPHOME}, + data=ESPHOME_DISCOVERY_INFO, + ) + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "already_configured" + + # Data did not get updated. Since we don't use the addon, we don't update the data + assert entry.data == { + "use_addon": False, + "integration_created_addon": False, + } + + @pytest.mark.usefixtures("supervisor", "addon_not_installed", "addon_info") async def test_esphome_discovery_usb_same_home_id( hass: HomeAssistant,