From 7571fdc95700defb1477fdfbcd69cfd3f4f079fa Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 27 May 2020 16:45:28 -0600 Subject: [PATCH] Allow Guardian config flow to be ignored (#36207) * Allow Guardian config flow to be ignored * Tests --- .../components/guardian/config_flow.py | 38 ++++++++++++++----- tests/components/guardian/test_config_flow.py | 24 ++++++++++-- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/guardian/config_flow.py b/homeassistant/components/guardian/config_flow.py index 3a7558bb222..dae8fafb1e0 100644 --- a/homeassistant/components/guardian/config_flow.py +++ b/homeassistant/components/guardian/config_flow.py @@ -5,6 +5,7 @@ import voluptuous as vol from homeassistant import config_entries, core from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT +from homeassistant.core import callback from .const import CONF_UID, DOMAIN, LOGGER # pylint:disable=unused-import @@ -12,6 +13,20 @@ DATA_SCHEMA = vol.Schema( {vol.Required(CONF_IP_ADDRESS): str, vol.Required(CONF_PORT, default=7777): int} ) +UNIQUE_ID = "guardian_{0}" + + +@callback +def async_get_pin_from_discovery_hostname(hostname): + """Get the device's 4-digit PIN from its zeroconf-discovered hostname.""" + return hostname.split(".")[0].split("-")[1] + + +@callback +def async_get_pin_from_uid(uid): + """Get the device's 4-digit PIN from its UID.""" + return uid[-4:] + async def validate_input(hass: core.HomeAssistant, data): """Validate the user input allows us to connect. @@ -22,7 +37,6 @@ async def validate_input(hass: core.HomeAssistant, data): ping_data = await client.device.ping() return { - "title": f"Elexa Guardian ({data[CONF_IP_ADDRESS]})", CONF_UID: ping_data["data"]["uid"], } @@ -37,6 +51,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Initialize.""" self.discovery_info = {} + async def _async_set_unique_id(self, pin): + """Set the config entry's unique ID (based on the device's 4-digit PIN).""" + await self.async_set_unique_id(UNIQUE_ID.format(pin)) + self._abort_if_unique_id_configured() + async def async_step_user(self, user_input=None): """Handle configuration via the UI.""" if user_input is None: @@ -44,9 +63,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): step_id="user", data_schema=DATA_SCHEMA, errors={} ) - await self.async_set_unique_id(user_input[CONF_IP_ADDRESS]) - self._abort_if_unique_id_configured() - try: info = await validate_input(self.hass, user_input) except GuardianError as err: @@ -57,8 +73,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors={CONF_IP_ADDRESS: "cannot_connect"}, ) + pin = async_get_pin_from_uid(info[CONF_UID]) + await self._async_set_unique_id(pin) + return self.async_create_entry( - title=info["title"], data={CONF_UID: info["uid"], **user_input} + title=info[CONF_UID], data={CONF_UID: info["uid"], **user_input} ) async def async_step_zeroconf(self, discovery_info=None): @@ -66,19 +85,20 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if discovery_info is None: return self.async_abort(reason="connection_error") - ip_address = discovery_info["host"] + pin = async_get_pin_from_discovery_hostname(discovery_info["hostname"]) + await self._async_set_unique_id(pin) # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 - self.context[CONF_IP_ADDRESS] = ip_address + self.context[CONF_IP_ADDRESS] = discovery_info["host"] if any( - ip_address == flow["context"][CONF_IP_ADDRESS] + discovery_info["host"] == flow["context"][CONF_IP_ADDRESS] for flow in self._async_in_progress() ): return self.async_abort(reason="already_in_progress") self.discovery_info = { - CONF_IP_ADDRESS: ip_address, + CONF_IP_ADDRESS: discovery_info["host"], CONF_PORT: discovery_info["port"], } diff --git a/tests/components/guardian/test_config_flow.py b/tests/components/guardian/test_config_flow.py index 1625e48f1ba..8e44b9f1417 100644 --- a/tests/components/guardian/test_config_flow.py +++ b/tests/components/guardian/test_config_flow.py @@ -4,17 +4,21 @@ from asynctest import patch from homeassistant import data_entry_flow from homeassistant.components.guardian import CONF_UID, DOMAIN +from homeassistant.components.guardian.config_flow import ( + async_get_pin_from_discovery_hostname, + async_get_pin_from_uid, +) from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT from tests.common import MockConfigEntry -async def test_duplicate_error(hass): +async def test_duplicate_error(hass, ping_client): """Test that errors are shown when duplicate entries are added.""" conf = {CONF_IP_ADDRESS: "192.168.1.100", CONF_PORT: 7777} - MockConfigEntry(domain=DOMAIN, unique_id="192.168.1.100", data=conf).add_to_hass( + MockConfigEntry(domain=DOMAIN, unique_id="guardian_3456", data=conf).add_to_hass( hass ) @@ -40,6 +44,18 @@ async def test_connect_error(hass): assert result["errors"] == {CONF_IP_ADDRESS: "cannot_connect"} +async def test_get_pin_from_discovery_hostname(): + """Test getting a device PIN from the zeroconf-discovered hostname.""" + pin = async_get_pin_from_discovery_hostname("GVC1-3456.local.") + assert pin == "3456" + + +async def test_get_pin_from_uid(): + """Test getting a device PIN from its UID.""" + pin = async_get_pin_from_uid("ABCDEF123456") + assert pin == "3456" + + async def test_step_user(hass, ping_client): """Test the user step.""" conf = {CONF_IP_ADDRESS: "192.168.1.100", CONF_PORT: 7777} @@ -54,7 +70,7 @@ async def test_step_user(hass, ping_client): DOMAIN, context={"source": SOURCE_USER}, data=conf ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == "Elexa Guardian (192.168.1.100)" + assert result["title"] == "ABCDEF123456" assert result["data"] == { CONF_IP_ADDRESS: "192.168.1.100", CONF_PORT: 7777, @@ -83,7 +99,7 @@ async def test_step_zeroconf(hass, ping_client): result["flow_id"], user_input={} ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == "Elexa Guardian (192.168.1.100)" + assert result["title"] == "ABCDEF123456" assert result["data"] == { CONF_IP_ADDRESS: "192.168.1.100", CONF_PORT: 7777,