From 8237ccfa03805db7129d06468cb6fc99667216e7 Mon Sep 17 00:00:00 2001 From: escoand Date: Mon, 27 Apr 2020 20:18:32 +0200 Subject: [PATCH] Add unique_id to fritzbox (#34716) --- .../components/fritzbox/config_flow.py | 20 +++++++++--- tests/components/fritzbox/test_config_flow.py | 32 +++++++++++++++++-- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/fritzbox/config_flow.py b/homeassistant/components/fritzbox/config_flow.py index 816855b46a8..b4265aa01fe 100644 --- a/homeassistant/components/fritzbox/config_flow.py +++ b/homeassistant/components/fritzbox/config_flow.py @@ -5,7 +5,11 @@ from pyfritzhome import Fritzhome, LoginError import voluptuous as vol from homeassistant import config_entries -from homeassistant.components.ssdp import ATTR_SSDP_LOCATION, ATTR_UPNP_FRIENDLY_NAME +from homeassistant.components.ssdp import ( + ATTR_SSDP_LOCATION, + ATTR_UPNP_FRIENDLY_NAME, + ATTR_UPNP_UDN, +) from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME # pylint:disable=unused-import @@ -82,10 +86,6 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): for entry in self.hass.config_entries.async_entries(DOMAIN): if entry.data[CONF_HOST] == user_input[CONF_HOST]: - if entry.data != user_input: - self.hass.config_entries.async_update_entry( - entry, data=user_input - ) return self.async_abort(reason="already_configured") self._host = user_input[CONF_HOST] @@ -110,12 +110,22 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): host = urlparse(user_input[ATTR_SSDP_LOCATION]).hostname self.context[CONF_HOST] = host + uuid = user_input.get(ATTR_UPNP_UDN) + if uuid: + if uuid.startswith("uuid:"): + uuid = uuid[5:] + await self.async_set_unique_id(uuid) + self._abort_if_unique_id_configured({CONF_HOST: host}) + for progress in self._async_in_progress(): if progress.get("context", {}).get(CONF_HOST) == host: return self.async_abort(reason="already_in_progress") + # update old and user-configured config entries for entry in self.hass.config_entries.async_entries(DOMAIN): if entry.data[CONF_HOST] == host: + if uuid and not entry.unique_id: + self.hass.config_entries.async_update_entry(entry, unique_id=uuid) return self.async_abort(reason="already_configured") self._host = host diff --git a/tests/components/fritzbox/test_config_flow.py b/tests/components/fritzbox/test_config_flow.py index d6b43dc4b71..c73578de646 100644 --- a/tests/components/fritzbox/test_config_flow.py +++ b/tests/components/fritzbox/test_config_flow.py @@ -6,7 +6,11 @@ from pyfritzhome import LoginError import pytest from homeassistant.components.fritzbox.const import DOMAIN -from homeassistant.components.ssdp import ATTR_SSDP_LOCATION, ATTR_UPNP_FRIENDLY_NAME +from homeassistant.components.ssdp import ( + ATTR_SSDP_LOCATION, + ATTR_UPNP_FRIENDLY_NAME, + ATTR_UPNP_UDN, +) from homeassistant.const import CONF_DEVICES, CONF_HOST, CONF_PASSWORD, CONF_USERNAME from homeassistant.helpers.typing import HomeAssistantType @@ -16,6 +20,7 @@ MOCK_USER_DATA = MOCK_CONFIG[DOMAIN][CONF_DEVICES][0] MOCK_SSDP_DATA = { ATTR_SSDP_LOCATION: "https://fake_host:12345/test", ATTR_UPNP_FRIENDLY_NAME: "fake_name", + ATTR_UPNP_UDN: "uuid:only-a-test", } @@ -42,6 +47,7 @@ async def test_user(hass: HomeAssistantType, fritz: Mock): assert result["data"][CONF_HOST] == "fake_host" assert result["data"][CONF_PASSWORD] == "fake_pass" assert result["data"][CONF_USERNAME] == "fake_user" + assert not result["result"].unique_id async def test_user_auth_failed(hass: HomeAssistantType, fritz: Mock): @@ -73,6 +79,7 @@ async def test_user_already_configured(hass: HomeAssistantType, fritz: Mock): DOMAIN, context={"source": "user"}, data=MOCK_USER_DATA ) assert result["type"] == "create_entry" + assert not result["result"].unique_id result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "user"}, data=MOCK_USER_DATA @@ -91,6 +98,7 @@ async def test_import(hass: HomeAssistantType, fritz: Mock): assert result["data"][CONF_HOST] == "fake_host" assert result["data"][CONF_PASSWORD] == "fake_pass" assert result["data"][CONF_USERNAME] == "fake_user" + assert not result["result"].unique_id async def test_ssdp(hass: HomeAssistantType, fritz: Mock): @@ -110,6 +118,7 @@ async def test_ssdp(hass: HomeAssistantType, fritz: Mock): assert result["data"][CONF_HOST] == "fake_host" assert result["data"][CONF_PASSWORD] == "fake_pass" assert result["data"][CONF_USERNAME] == "fake_user" + assert result["result"].unique_id == "only-a-test" async def test_ssdp_auth_failed(hass: HomeAssistantType, fritz: Mock): @@ -150,7 +159,7 @@ async def test_ssdp_not_successful(hass: HomeAssistantType, fritz: Mock): assert result["reason"] == "not_found" -async def test_ssdp_already_in_progress(hass: HomeAssistantType, fritz: Mock): +async def test_ssdp_already_in_progress_unique_id(hass: HomeAssistantType, fritz: Mock): """Test starting a flow from discovery twice.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "ssdp"}, data=MOCK_SSDP_DATA @@ -165,15 +174,34 @@ async def test_ssdp_already_in_progress(hass: HomeAssistantType, fritz: Mock): assert result["reason"] == "already_in_progress" +async def test_ssdp_already_in_progress_host(hass: HomeAssistantType, fritz: Mock): + """Test starting a flow from discovery twice.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": "ssdp"}, data=MOCK_SSDP_DATA + ) + assert result["type"] == "form" + assert result["step_id"] == "confirm" + + MOCK_NO_UNIQUE_ID = MOCK_SSDP_DATA.copy() + del MOCK_NO_UNIQUE_ID[ATTR_UPNP_UDN] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": "ssdp"}, data=MOCK_NO_UNIQUE_ID + ) + assert result["type"] == "abort" + assert result["reason"] == "already_in_progress" + + async def test_ssdp_already_configured(hass: HomeAssistantType, fritz: Mock): """Test starting a flow from discovery when already configured.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "user"}, data=MOCK_USER_DATA ) assert result["type"] == "create_entry" + assert not result["result"].unique_id result2 = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "ssdp"}, data=MOCK_SSDP_DATA ) assert result2["type"] == "abort" assert result2["reason"] == "already_configured" + assert result["result"].unique_id == "only-a-test"