diff --git a/homeassistant/components/deconz/.translations/en.json b/homeassistant/components/deconz/.translations/en.json index 1a95816dab7..57c77e18bb0 100644 --- a/homeassistant/components/deconz/.translations/en.json +++ b/homeassistant/components/deconz/.translations/en.json @@ -17,16 +17,22 @@ "description": "Do you want to configure Home Assistant to connect to the deCONZ gateway provided by the Hass.io add-on {addon}?", "title": "deCONZ Zigbee gateway via Hass.io add-on" }, - "init": { + "user": { "data": { - "host": "Host", - "port": "Port" + "host": "Select discovered deCONZ gateway" }, - "title": "Define deCONZ gateway" + "title": "Select deCONZ gateway" }, "link": { "description": "Unlock your deCONZ gateway to register with Home Assistant.\n\n1. Go to deCONZ Settings -> Gateway -> Advanced\n2. Press \"Authenticate app\" button", "title": "Link with deCONZ" + }, + "manual_input": { + "data": { + "host": "Host", + "port": "Port" + }, + "title": "Configure deCONZ gateway" } } }, diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index 3a38a67f0c6..f52a18bbd07 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -32,6 +32,7 @@ from .const import ( DECONZ_MANUFACTURERURL = "http://www.dresden-elektronik.de" CONF_SERIAL = "serial" +CONF_MANUAL_INPUT = "Manually define gateway" @callback @@ -62,18 +63,17 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self.bridges = [] self.deconz_config = {} - async def async_step_init(self, user_input=None): - """Needed in order to not require re-translation of strings.""" - return await self.async_step_user(user_input) - async def async_step_user(self, user_input=None): """Handle a deCONZ config flow start. - If only one bridge is found go to link step. - If more than one bridge is found let user choose bridge to link. + Let user choose between discovered bridges and manual configuration. If no bridge is found allow user to manually input configuration. """ if user_input is not None: + + if CONF_MANUAL_INPUT == user_input[CONF_HOST]: + return await self.async_step_manual_input() + for bridge in self.bridges: if bridge[CONF_HOST] == user_input[CONF_HOST]: self.bridge_id = bridge[CONF_BRIDGE_ID] @@ -83,9 +83,6 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): } return await self.async_step_link() - self.deconz_config = user_input - return await self.async_step_link() - session = aiohttp_client.async_get_clientsession(self.hass) try: @@ -97,22 +94,29 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): LOGGER.debug("Discovered deCONZ gateways %s", pformat(self.bridges)) - if len(self.bridges) == 1: - return await self.async_step_user(self.bridges[0]) - - if len(self.bridges) > 1: + if self.bridges: hosts = [] for bridge in self.bridges: hosts.append(bridge[CONF_HOST]) + hosts.append(CONF_MANUAL_INPUT) + return self.async_show_form( - step_id="init", - data_schema=vol.Schema({vol.Required(CONF_HOST): vol.In(hosts)}), + step_id="user", + data_schema=vol.Schema({vol.Optional(CONF_HOST): vol.In(hosts)}), ) + return await self.async_step_manual_input() + + async def async_step_manual_input(self, user_input=None): + """Manual configuration.""" + if user_input: + self.deconz_config = user_input + return await self.async_step_link() + return self.async_show_form( - step_id="init", + step_id="manual_input", data_schema=vol.Schema( { vol.Required(CONF_HOST): str, diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index baa749166de..7b8da3ee81a 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -2,9 +2,18 @@ "config": { "flow_title": "deCONZ Zigbee gateway ({host})", "step": { - "init": { - "title": "Define deCONZ gateway", - "data": { "host": "Host", "port": "Port" } + "user": { + "title": "Select deCONZ gateway", + "data": { + "host": "Select discovered deCONZ gateway" + } + }, + "manual_input": { + "title": "Configure deCONZ gateway", + "data": { + "host": "Host", + "port": "Port" + } }, "link": { "title": "Link with deCONZ", diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 4873528d982..b29e4303f7b 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -7,6 +7,7 @@ from homeassistant import data_entry_flow from homeassistant.components import ssdp from homeassistant.components.deconz import config_flow from homeassistant.components.deconz.config_flow import ( + CONF_MANUAL_INPUT, CONF_SERIAL, DECONZ_MANUFACTURERURL, ) @@ -21,11 +22,14 @@ from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT from .test_gateway import API_KEY, BRIDGEID, setup_deconz_integration -async def test_flow_1_discovered_bridge(hass, aioclient_mock): - """Test that config flow for one discovered bridge works.""" +async def test_flow_discovered_bridges(hass, aioclient_mock): + """Test that config flow works for discovered bridges.""" aioclient_mock.get( pydeconz.utils.URL_DISCOVER, - json=[{"id": BRIDGEID, "internalipaddress": "1.2.3.4", "internalport": 80}], + json=[ + {"id": BRIDGEID, "internalipaddress": "1.2.3.4", "internalport": 80}, + {"id": "1234E567890A", "internalipaddress": "5.6.7.8", "internalport": 80}, + ], headers={"content-type": "application/json"}, ) @@ -33,6 +37,13 @@ async def test_flow_1_discovered_bridge(hass, aioclient_mock): DOMAIN, context={"source": "user"} ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_HOST: "1.2.3.4"} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "link" @@ -55,14 +66,11 @@ async def test_flow_1_discovered_bridge(hass, aioclient_mock): } -async def test_flow_2_discovered_bridges(hass, aioclient_mock): - """Test that config flow works for multiple discovered bridges.""" +async def test_flow_manual_configuration_decision(hass, aioclient_mock): + """Test that config flow for one discovered bridge works.""" aioclient_mock.get( pydeconz.utils.URL_DISCOVER, - json=[ - {"id": BRIDGEID, "internalipaddress": "1.2.3.4", "internalport": 80}, - {"id": "1234E567890A", "internalipaddress": "5.6.7.8", "internalport": 80}, - ], + json=[{"id": BRIDGEID, "internalipaddress": "1.2.3.4", "internalport": 80}], headers={"content-type": "application/json"}, ) @@ -70,11 +78,15 @@ async def test_flow_2_discovered_bridges(hass, aioclient_mock): DOMAIN, context={"source": "user"} ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_HOST: CONF_MANUAL_INPUT} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" + assert result["step_id"] == "manual_input" result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_HOST: "1.2.3.4"} + result["flow_id"], user_input={CONF_HOST: "1.2.3.4", CONF_PORT: 80}, ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM @@ -86,6 +98,12 @@ async def test_flow_2_discovered_bridges(hass, aioclient_mock): headers={"content-type": "application/json"}, ) + aioclient_mock.get( + f"http://1.2.3.4:80/api/{API_KEY}/config", + json={"bridgeid": BRIDGEID}, + headers={"content-type": "application/json"}, + ) + result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) @@ -112,7 +130,7 @@ async def test_flow_manual_configuration(hass, aioclient_mock): ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" + assert result["step_id"] == "manual_input" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_HOST: "1.2.3.4", CONF_PORT: 80}, @@ -155,7 +173,7 @@ async def test_manual_configuration_after_discovery_timeout(hass, aioclient_mock ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" + assert result["step_id"] == "manual_input" assert not hass.config_entries.flow._progress[result["flow_id"]].bridges @@ -168,7 +186,7 @@ async def test_manual_configuration_after_discovery_ResponseError(hass, aioclien ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" + assert result["step_id"] == "manual_input" assert not hass.config_entries.flow._progress[result["flow_id"]].bridges @@ -187,7 +205,7 @@ async def test_manual_configuration_update_configuration(hass, aioclient_mock): ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" + assert result["step_id"] == "manual_input" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_HOST: "2.3.4.5", CONF_PORT: 80}, @@ -232,7 +250,7 @@ async def test_manual_configuration_dont_update_configuration(hass, aioclient_mo ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" + assert result["step_id"] == "manual_input" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_HOST: "1.2.3.4", CONF_PORT: 80}, @@ -274,7 +292,7 @@ async def test_manual_configuration_timeout_get_bridge(hass, aioclient_mock): ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" + assert result["step_id"] == "manual_input" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_HOST: "1.2.3.4", CONF_PORT: 80}, @@ -313,6 +331,10 @@ async def test_link_get_api_key_ResponseError(hass, aioclient_mock): DOMAIN, context={"source": "user"} ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_HOST: "1.2.3.4"} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "link"