diff --git a/homeassistant/components/technove/config_flow.py b/homeassistant/components/technove/config_flow.py index 7ad9829b631..0949b859884 100644 --- a/homeassistant/components/technove/config_flow.py +++ b/homeassistant/components/technove/config_flow.py @@ -6,7 +6,11 @@ from technove import Station as TechnoVEStation, TechnoVE, TechnoVEConnectionErr import voluptuous as vol from homeassistant.components import onboarding -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.config_entries import ( + SOURCE_RECONFIGURE, + ConfigFlow, + ConfigFlowResult, +) from homeassistant.const import CONF_HOST, CONF_MAC from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo @@ -32,7 +36,23 @@ class TechnoVEConfigFlow(ConfigFlow, domain=DOMAIN): except TechnoVEConnectionError: errors["base"] = "cannot_connect" else: - await self.async_set_unique_id(station.info.mac_address) + await self.async_set_unique_id( + station.info.mac_address, raise_on_progress=False + ) + if self.source == SOURCE_RECONFIGURE: + entry = self._get_reconfigure_entry() + assert entry.unique_id is not None + self._abort_if_unique_id_mismatch( + reason="unique_id_mismatch", + description_placeholders={ + "expected_mac": entry.unique_id.upper(), + "actual_mac": station.info.mac_address.upper(), + }, + ) + return self.async_update_reload_and_abort( + entry, + data_updates={CONF_HOST: user_input[CONF_HOST]}, + ) self._abort_if_unique_id_configured( updates={CONF_HOST: user_input[CONF_HOST]} ) @@ -43,12 +63,25 @@ class TechnoVEConfigFlow(ConfigFlow, domain=DOMAIN): }, ) + data_schema = vol.Schema({vol.Required(CONF_HOST): str}) + if self.source == SOURCE_RECONFIGURE: + data_schema = self.add_suggested_values_to_schema( + data_schema, + self._get_reconfigure_entry().data, + ) + return self.async_show_form( step_id="user", - data_schema=vol.Schema({vol.Required(CONF_HOST): str}), + data_schema=data_schema, errors=errors, ) + async def async_step_reconfigure( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle reconfiguration of the TechnoVE station.""" + return await self.async_step_user(user_input) + async def async_step_zeroconf( self, discovery_info: ZeroconfServiceInfo ) -> ConfigFlowResult: diff --git a/homeassistant/components/technove/strings.json b/homeassistant/components/technove/strings.json index 351ee78b966..c2e27854ccc 100644 --- a/homeassistant/components/technove/strings.json +++ b/homeassistant/components/technove/strings.json @@ -2,7 +2,9 @@ "config": { "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]", + "unique_id_mismatch": "MAC address does not match the configured device. Expected to connect to device with MAC: `{expected_mac}`, but connected to device with MAC: `{actual_mac}`. \n\nPlease ensure you reconfigure against the same device." }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" diff --git a/tests/components/technove/test_config_flow.py b/tests/components/technove/test_config_flow.py index 99a8e231f73..ba4ef452163 100644 --- a/tests/components/technove/test_config_flow.py +++ b/tests/components/technove/test_config_flow.py @@ -240,7 +240,6 @@ async def test_zeroconf_without_mac_station_exists_abort( assert result.get("reason") == "already_configured" -@pytest.mark.usefixtures("mock_technove") async def test_zeroconf_with_mac_station_exists_abort( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_technove: MagicMock ) -> None: @@ -264,3 +263,89 @@ async def test_zeroconf_with_mac_station_exists_abort( mock_technove.update.assert_not_called() assert result.get("type") is FlowResultType.ABORT assert result.get("reason") == "already_configured" + + +@pytest.mark.usefixtures("mock_setup_entry") +async def test_full_reconfigure_flow_success( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_technove: MagicMock, +) -> None: + """Test the full reconfigure flow from start to finish.""" + mock_config_entry.add_to_hass(hass) + + result = await mock_config_entry.start_reconfigure_flow(hass) + + assert result.get("step_id") == "user" + assert result.get("type") is FlowResultType.FORM + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_HOST: "192.168.1.200"} + ) + + assert result.get("type") is FlowResultType.ABORT + assert result.get("reason") == "reconfigure_successful" + + assert mock_config_entry.data[CONF_HOST] == "192.168.1.200" + + +@pytest.mark.usefixtures("mock_setup_entry") +async def test_full_reconfigure_flow_unique_id_mismatch( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_technove: MagicMock, +) -> None: + """Test reconfiguration failure when the unique ID changes.""" + mock_config_entry.add_to_hass(hass) + + # Change mac address to simulate a different device + device = mock_technove.update.return_value + device.info.mac_address = "AA:AA:AA:AA:AA:CC" + + result = await mock_config_entry.start_reconfigure_flow(hass) + + assert result.get("step_id") == "user" + assert result.get("type") is FlowResultType.FORM + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_HOST: "192.168.1.200"} + ) + + assert result.get("type") is FlowResultType.ABORT + assert result.get("reason") == "unique_id_mismatch" + + +@pytest.mark.usefixtures("mock_setup_entry") +async def test_full_reconfigure_flow_connection_error_and_success( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_technove: MagicMock, +) -> None: + """Test reconfigure flow with connection error, then successful recovery.""" + mock_config_entry.add_to_hass(hass) + + mock_technove.update.side_effect = TechnoVEConnectionError + + result = await mock_config_entry.start_reconfigure_flow(hass) + + assert result.get("step_id") == "user" + assert result.get("type") is FlowResultType.FORM + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_HOST: "192.168.1.200"} + ) + + assert result.get("type") is FlowResultType.FORM + assert result.get("step_id") == "user" + assert result.get("errors") == {"base": "cannot_connect"} + + mock_technove.update.side_effect = None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_HOST: "192.168.1.200"} + ) + + assert result.get("type") is FlowResultType.ABORT + assert result.get("reason") == "reconfigure_successful" + + assert mock_config_entry.data[CONF_HOST] == "192.168.1.200"