From ebe6c35b4c8f3cef067e20773003cc13a940a8aa Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 23 Mar 2024 11:01:59 +0100 Subject: [PATCH] Smhi add reconfigure step to config flow (#114044) * Add reconfigure step to SMHI * Check location * Add test * Add test entity and device --- homeassistant/components/smhi/config_flow.py | 69 +++++++++++++- homeassistant/components/smhi/strings.json | 10 ++- homeassistant/strings.json | 1 + tests/components/smhi/test_config_flow.py | 94 ++++++++++++++++++++ 4 files changed, 171 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/smhi/config_flow.py b/homeassistant/components/smhi/config_flow.py index 4a28f475ad6..b3350f6bb18 100644 --- a/homeassistant/components/smhi/config_flow.py +++ b/homeassistant/components/smhi/config_flow.py @@ -7,10 +7,15 @@ from typing import Any from smhi.smhi_lib import Smhi, SmhiForecastException import voluptuous as vol -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.components.weather import DOMAIN as WEATHER_DOMAIN +from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE, CONF_NAME from homeassistant.core import HomeAssistant -from homeassistant.helpers import aiohttp_client +from homeassistant.helpers import ( + aiohttp_client, + device_registry as dr, + entity_registry as er, +) from homeassistant.helpers.selector import LocationSelector from .const import DEFAULT_NAME, DOMAIN, HOME_LOCATION_NAME @@ -34,6 +39,7 @@ class SmhiFlowHandler(ConfigFlow, domain=DOMAIN): """Config flow for SMHI component.""" VERSION = 2 + config_entry: ConfigEntry | None async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -74,3 +80,62 @@ class SmhiFlowHandler(ConfigFlow, domain=DOMAIN): ), errors=errors, ) + + async def async_step_reconfigure( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle a reconfiguration flow initialized by the user.""" + self.config_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + return await self.async_step_reconfigure_confirm() + + async def async_step_reconfigure_confirm( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle a reconfiguration flow initialized by the user.""" + errors: dict[str, str] = {} + assert self.config_entry + + if user_input is not None: + lat: float = user_input[CONF_LOCATION][CONF_LATITUDE] + lon: float = user_input[CONF_LOCATION][CONF_LONGITUDE] + if await async_check_location(self.hass, lon, lat): + unique_id = f"{lat}-{lon}" + await self.async_set_unique_id(unique_id) + self._abort_if_unique_id_configured() + + old_lat = self.config_entry.data[CONF_LOCATION][CONF_LATITUDE] + old_lon = self.config_entry.data[CONF_LOCATION][CONF_LONGITUDE] + + entity_reg = er.async_get(self.hass) + if entity := entity_reg.async_get_entity_id( + WEATHER_DOMAIN, DOMAIN, f"{old_lat}, {old_lon}" + ): + entity_reg.async_update_entity( + entity, new_unique_id=f"{lat}, {lon}" + ) + + device_reg = dr.async_get(self.hass) + if device := device_reg.async_get_device( + identifiers={(DOMAIN, f"{old_lat}, {old_lon}")} + ): + device_reg.async_update_device( + device.id, new_identifiers={(DOMAIN, f"{lat}, {lon}")} + ) + + return self.async_update_reload_and_abort( + self.config_entry, + unique_id=unique_id, + data={**self.config_entry.data, **user_input}, + reason="reconfigure_successful", + ) + errors["base"] = "wrong_location" + + schema = self.add_suggested_values_to_schema( + vol.Schema({vol.Required(CONF_LOCATION): LocationSelector()}), + self.config_entry.data, + ) + return self.async_show_form( + step_id="reconfigure_confirm", data_schema=schema, errors=errors + ) diff --git a/homeassistant/components/smhi/strings.json b/homeassistant/components/smhi/strings.json index 2b155f58f96..e78fee64a2b 100644 --- a/homeassistant/components/smhi/strings.json +++ b/homeassistant/components/smhi/strings.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_account%]" + "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", + "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]" }, "step": { "user": { @@ -10,6 +11,13 @@ "latitude": "[%key:common::config_flow::data::latitude%]", "longitude": "[%key:common::config_flow::data::longitude%]" } + }, + "reconfigure_confirm": { + "title": "Reconfigure your location in Sweden", + "data": { + "latitude": "[%key:common::config_flow::data::latitude%]", + "longitude": "[%key:common::config_flow::data::longitude%]" + } } }, "error": { diff --git a/homeassistant/strings.json b/homeassistant/strings.json index 2f112d45dc0..97bba2fb3b7 100644 --- a/homeassistant/strings.json +++ b/homeassistant/strings.json @@ -130,6 +130,7 @@ "oauth2_unauthorized": "OAuth authorization error while obtaining access token.", "oauth2_failed": "Error while obtaining access token.", "reauth_successful": "Re-authentication was successful", + "reconfigure_successful": "Re-configuration was successful", "unknown_authorize_url_generation": "Unknown error generating an authorize URL.", "cloud_not_connected": "Not connected to Home Assistant Cloud." } diff --git a/tests/components/smhi/test_config_flow.py b/tests/components/smhi/test_config_flow.py index b8742d0cbf6..783e2d7eec1 100644 --- a/tests/components/smhi/test_config_flow.py +++ b/tests/components/smhi/test_config_flow.py @@ -8,9 +8,11 @@ from smhi.smhi_lib import SmhiForecastException from homeassistant import config_entries from homeassistant.components.smhi.const import DOMAIN +from homeassistant.components.weather import DOMAIN as WEATHER_DOMAIN from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers import device_registry as dr, entity_registry as er from tests.common import MockConfigEntry @@ -178,3 +180,95 @@ async def test_form_unique_id_exist(hass: HomeAssistant) -> None: assert result2["type"] == FlowResultType.ABORT assert result2["reason"] == "already_configured" + + +async def test_reconfigure_flow( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + device_registry: dr.DeviceRegistry, +) -> None: + """Test re-configuration flow.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="Home", + unique_id="57.2898-13.6304", + data={"location": {"latitude": 57.2898, "longitude": 13.6304}, "name": "Home"}, + version=2, + ) + entry.add_to_hass(hass) + + entity = entity_registry.async_get_or_create( + WEATHER_DOMAIN, DOMAIN, "57.2898, 13.6304" + ) + device = device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, "57.2898, 13.6304")}, + manufacturer="SMHI", + model="v2", + name=entry.title, + ) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_RECONFIGURE, + "entry_id": entry.entry_id, + }, + ) + assert result["type"] == FlowResultType.FORM + + with patch( + "homeassistant.components.smhi.config_flow.Smhi.async_get_forecast", + side_effect=SmhiForecastException, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_LOCATION: { + CONF_LATITUDE: 0.0, + CONF_LONGITUDE: 0.0, + } + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": "wrong_location"} + + with patch( + "homeassistant.components.smhi.config_flow.Smhi.async_get_forecast", + return_value={"test": "something", "test2": "something else"}, + ), patch( + "homeassistant.components.smhi.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_LOCATION: { + CONF_LATITUDE: 58.2898, + CONF_LONGITUDE: 14.6304, + } + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + entry = hass.config_entries.async_get_entry(entry.entry_id) + assert entry.title == "Home" + assert entry.unique_id == "58.2898-14.6304" + assert entry.data == { + "location": { + "latitude": 58.2898, + "longitude": 14.6304, + }, + "name": "Home", + } + entity = entity_registry.async_get(entity.entity_id) + assert entity + assert entity.unique_id == "58.2898, 14.6304" + device = device_registry.async_get(device.id) + assert device + assert device.identifiers == {(DOMAIN, "58.2898, 14.6304")} + assert len(mock_setup_entry.mock_calls) == 1