From 97fd05eb9f71ad16707dd5358501b69e4d296aa0 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 29 Jan 2021 18:14:39 +0100 Subject: [PATCH] Allow new UniFi flows to update existing entries if host and site match (#45668) --- homeassistant/components/unifi/config_flow.py | 58 +++++++++-------- homeassistant/components/unifi/strings.json | 1 + tests/components/unifi/test_config_flow.py | 63 ++++++++++++++++--- 3 files changed, 90 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/unifi/config_flow.py b/homeassistant/components/unifi/config_flow.py index 85203204b2f..f5e947c5e6f 100644 --- a/homeassistant/components/unifi/config_flow.py +++ b/homeassistant/components/unifi/config_flow.py @@ -38,9 +38,9 @@ from .const import ( LOGGER, ) from .controller import get_controller -from .errors import AlreadyConfigured, AuthenticationRequired, CannotConnect +from .errors import AuthenticationRequired, CannotConnect -DEFAULT_PORT = 8443 +DEFAULT_PORT = 443 DEFAULT_SITE_ID = "default" DEFAULT_VERIFY_SSL = False @@ -76,7 +76,7 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): """Initialize the UniFi flow.""" self.config = {} self.sites = None - self.reauth_config_entry = {} + self.reauth_config_entry = None self.reauth_config = {} self.reauth_schema = {} @@ -146,32 +146,40 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): errors = {} if user_input is not None: - try: - self.config[CONF_SITE_ID] = user_input[CONF_SITE_ID] - data = {CONF_CONTROLLER: self.config} - if self.reauth_config_entry: - self.hass.config_entries.async_update_entry( - self.reauth_config_entry, data=data - ) - await self.hass.config_entries.async_reload( - self.reauth_config_entry.entry_id - ) - return self.async_abort(reason="reauth_successful") + self.config[CONF_SITE_ID] = user_input[CONF_SITE_ID] + data = {CONF_CONTROLLER: self.config} - for entry in self._async_current_entries(): - controller = entry.data[CONF_CONTROLLER] - if ( - controller[CONF_HOST] == self.config[CONF_HOST] - and controller[CONF_SITE_ID] == self.config[CONF_SITE_ID] - ): - raise AlreadyConfigured + if self.reauth_config_entry: + self.hass.config_entries.async_update_entry( + self.reauth_config_entry, data=data + ) + await self.hass.config_entries.async_reload( + self.reauth_config_entry.entry_id + ) + return self.async_abort(reason="reauth_successful") - site_nice_name = self.sites[self.config[CONF_SITE_ID]] - return self.async_create_entry(title=site_nice_name, data=data) + for config_entry in self._async_current_entries(): + controller_data = config_entry.data[CONF_CONTROLLER] + if ( + controller_data[CONF_HOST] != self.config[CONF_HOST] + or controller_data[CONF_SITE_ID] != self.config[CONF_SITE_ID] + ): + continue - except AlreadyConfigured: - return self.async_abort(reason="already_configured") + controller = self.hass.data.get(UNIFI_DOMAIN, {}).get( + config_entry.entry_id + ) + + if controller and controller.available: + return self.async_abort(reason="already_configured") + + self.hass.config_entries.async_update_entry(config_entry, data=data) + await self.hass.config_entries.async_reload(config_entry.entry_id) + return self.async_abort(reason="configuration_updated") + + site_nice_name = self.sites[self.config[CONF_SITE_ID]] + return self.async_create_entry(title=site_nice_name, data=data) if len(self.sites) == 1: return await self.async_step_site({CONF_SITE_ID: next(iter(self.sites))}) diff --git a/homeassistant/components/unifi/strings.json b/homeassistant/components/unifi/strings.json index 15cc2fb45e7..be0bda37971 100644 --- a/homeassistant/components/unifi/strings.json +++ b/homeassistant/components/unifi/strings.json @@ -21,6 +21,7 @@ }, "abort": { "already_configured": "Controller site is already configured", + "configuration_updated": "Configuration updated.", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } }, diff --git a/tests/components/unifi/test_config_flow.py b/tests/components/unifi/test_config_flow.py index 6eb049b573a..54c5fe291ea 100644 --- a/tests/components/unifi/test_config_flow.py +++ b/tests/components/unifi/test_config_flow.py @@ -97,7 +97,7 @@ async def test_flow_works(hass, aioclient_mock, mock_discovery): CONF_HOST: "unifi", CONF_USERNAME: "", CONF_PASSWORD: "", - CONF_PORT: 8443, + CONF_PORT: 443, CONF_VERIFY_SSL: False, } @@ -189,12 +189,9 @@ async def test_flow_works_multiple_sites(hass, aioclient_mock): assert result["data_schema"]({"site": "site2"}) -async def test_flow_fails_site_already_configured(hass, aioclient_mock): - """Test config flow.""" - entry = MockConfigEntry( - domain=UNIFI_DOMAIN, data={"controller": {"host": "1.2.3.4", "site": "site_id"}} - ) - entry.add_to_hass(hass) +async def test_flow_raise_already_configured(hass, aioclient_mock): + """Test config flow aborts since a connected config entry already exists.""" + await setup_unifi_integration(hass) result = await hass.config_entries.flow.async_init( UNIFI_DOMAIN, context={"source": "user"} @@ -235,6 +232,58 @@ async def test_flow_fails_site_already_configured(hass, aioclient_mock): assert result["reason"] == "already_configured" +async def test_flow_aborts_configuration_updated(hass, aioclient_mock): + """Test config flow aborts since a connected config entry already exists.""" + entry = MockConfigEntry( + domain=UNIFI_DOMAIN, data={"controller": {"host": "1.2.3.4", "site": "office"}} + ) + entry.add_to_hass(hass) + + entry = MockConfigEntry( + domain=UNIFI_DOMAIN, data={"controller": {"host": "1.2.3.4", "site": "site_id"}} + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + UNIFI_DOMAIN, context={"source": "user"} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + aioclient_mock.get("https://1.2.3.4:1234", status=302) + + aioclient_mock.post( + "https://1.2.3.4:1234/api/login", + json={"data": "login successful", "meta": {"rc": "ok"}}, + headers={"content-type": CONTENT_TYPE_JSON}, + ) + + aioclient_mock.get( + "https://1.2.3.4:1234/api/self/sites", + json={ + "data": [{"desc": "Site name", "name": "site_id", "role": "admin"}], + "meta": {"rc": "ok"}, + }, + headers={"content-type": CONTENT_TYPE_JSON}, + ) + + with patch("homeassistant.components.unifi.async_setup_entry"): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "1.2.3.4", + CONF_USERNAME: "username", + CONF_PASSWORD: "password", + CONF_PORT: 1234, + CONF_VERIFY_SSL: True, + }, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "configuration_updated" + + async def test_flow_fails_user_credentials_faulty(hass, aioclient_mock): """Test config flow.""" result = await hass.config_entries.flow.async_init(