From 81a659be0dd4c29f2c30c49307dad87dd542267e Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 3 Apr 2019 04:23:33 +0200 Subject: [PATCH] Hass.io discovery flow deconz (#22623) * Add Hass.io deCONZ discovery flow * add bridge ID * fix attribute * fix strings * Address comments * Add test * Add only instance / changed maybe later --- .../components/deconz/config_flow.py | 59 +++++++++++++++++-- homeassistant/components/deconz/const.py | 3 + homeassistant/components/deconz/strings.json | 10 +++- homeassistant/components/hassio/discovery.py | 4 +- tests/components/deconz/test_config_flow.py | 49 +++++++++++++++ 5 files changed, 116 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index cabb5b46ece..38849fb37b3 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -1,17 +1,18 @@ """Config flow to configure deCONZ component.""" import asyncio + import async_timeout import voluptuous as vol from homeassistant import config_entries -from homeassistant.core import callback from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT +from homeassistant.core import callback from homeassistant.helpers import aiohttp_client from .const import ( - CONF_ALLOW_DECONZ_GROUPS, CONF_ALLOW_CLIP_SENSOR, DEFAULT_PORT, DOMAIN) - -CONF_BRIDGEID = 'bridgeid' + CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID, + DEFAULT_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_DECONZ_GROUPS, DEFAULT_PORT, + DOMAIN) @callback @@ -28,6 +29,8 @@ class DeconzFlowHandler(config_entries.ConfigFlow): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + _hassio_discovery = None + def __init__(self): """Initialize the deCONZ config flow.""" self.bridges = [] @@ -151,8 +154,10 @@ class DeconzFlowHandler(config_entries.ConfigFlow): return self.async_show_form( step_id='options', data_schema=vol.Schema({ - vol.Optional(CONF_ALLOW_CLIP_SENSOR): bool, - vol.Optional(CONF_ALLOW_DECONZ_GROUPS): bool, + vol.Optional(CONF_ALLOW_CLIP_SENSOR, + default=DEFAULT_ALLOW_CLIP_SENSOR): bool, + vol.Optional(CONF_ALLOW_DECONZ_GROUPS, + default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, }), ) @@ -191,3 +196,45 @@ class DeconzFlowHandler(config_entries.ConfigFlow): user_input = {CONF_ALLOW_CLIP_SENSOR: True, CONF_ALLOW_DECONZ_GROUPS: True} return await self.async_step_options(user_input=user_input) + + async def async_step_hassio(self, user_input=None): + """Prepare configuration for a Hass.io deCONZ bridge. + + This flow is triggered by the discovery component. + """ + if configured_hosts(self.hass): + return self.async_abort(reason='one_instance_only') + + self._hassio_discovery = user_input + + return await self.async_step_hassio_confirm() + + async def async_step_hassio_confirm(self, user_input=None): + """Confirm a Hass.io discovery.""" + if user_input is not None: + data = self._hassio_discovery + + return self.async_create_entry( + title=data['addon'], data={ + CONF_HOST: data[CONF_HOST], + CONF_PORT: data[CONF_PORT], + CONF_BRIDGEID: data['serial'], + CONF_API_KEY: data[CONF_API_KEY], + CONF_ALLOW_CLIP_SENSOR: + user_input[CONF_ALLOW_CLIP_SENSOR], + CONF_ALLOW_DECONZ_GROUPS: + user_input[CONF_ALLOW_DECONZ_GROUPS], + }) + + return self.async_show_form( + step_id='hassio_confirm', + description_placeholders={ + 'addon': self._hassio_discovery['addon'] + }, + data_schema=vol.Schema({ + vol.Optional(CONF_ALLOW_CLIP_SENSOR, + default=DEFAULT_ALLOW_CLIP_SENSOR): bool, + vol.Optional(CONF_ALLOW_DECONZ_GROUPS, + default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, + }) + ) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index e728430f202..b26fddd9147 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -6,9 +6,12 @@ _LOGGER = logging.getLogger('.') DOMAIN = 'deconz' DEFAULT_PORT = 80 +DEFAULT_ALLOW_CLIP_SENSOR = False +DEFAULT_ALLOW_DECONZ_GROUPS = False CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor' CONF_ALLOW_DECONZ_GROUPS = 'allow_deconz_groups' +CONF_BRIDGEID = 'bridgeid' SUPPORTED_PLATFORMS = ['binary_sensor', 'climate', 'cover', 'light', 'scene', 'sensor', 'switch'] diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index 1bf7235713a..d0ae34e7c7a 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -19,6 +19,14 @@ "allow_clip_sensor": "Allow importing virtual sensors", "allow_deconz_groups": "Allow importing deCONZ groups" } + }, + "hassio_confirm": { + "title": "deCONZ Zigbee gateway via Hass.io add-on", + "description": "Do you want to configure Home Assistant to connect to the deCONZ gateway provided by the hass.io add-on {addon}?", + "data": { + "allow_clip_sensor": "Allow importing virtual sensors", + "allow_deconz_groups": "Allow importing deCONZ groups" + } } }, "error": { @@ -30,4 +38,4 @@ "one_instance_only": "Component only supports one deCONZ instance" } } -} \ No newline at end of file +} diff --git a/homeassistant/components/hassio/discovery.py b/homeassistant/components/hassio/discovery.py index 804247d2407..09a98edc148 100644 --- a/homeassistant/components/hassio/discovery.py +++ b/homeassistant/components/hassio/discovery.py @@ -81,7 +81,7 @@ class HassIODiscovery(HomeAssistantView): service = data[ATTR_SERVICE] config_data = data[ATTR_CONFIG] - # Read addinional Add-on info + # Read additional Add-on info try: addon_info = await self.hassio.get_addon_info(data[ATTR_ADDON]) except HassioAPIError as err: @@ -98,7 +98,7 @@ class HassIODiscovery(HomeAssistantView): service = data[ATTR_SERVICE] uuid = data[ATTR_UUID] - # Check if realy deletet / prevent injections + # Check if really deletet / prevent injections try: data = await self.hassio.get_discovery_message(uuid) except HassioAPIError: diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 20c74a82883..863e4e93fc5 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -265,3 +265,52 @@ async def test_options(hass, aioclient_mock): 'allow_clip_sensor': False, 'allow_deconz_groups': False } + + +async def test_hassio_single_instance(hass): + """Test we only allow a single config flow.""" + MockConfigEntry(domain='deconz', data={ + 'host': '1.2.3.4' + }).add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + 'deconz', context={'source': 'hassio'}) + assert result['type'] == 'abort' + assert result['reason'] == 'one_instance_only' + + +async def test_hassio_confirm(hass): + """Test we can finish a config flow.""" + result = await hass.config_entries.flow.async_init( + 'deconz', + data={ + 'addon': 'Mock Addon', + 'host': 'mock-deconz', + 'port': 8080, + 'serial': 'aa:bb', + 'api_key': '1234567890ABCDEF', + }, + context={'source': 'hassio'} + ) + assert result['type'] == 'form' + assert result['step_id'] == 'hassio_confirm' + assert result['description_placeholders'] == { + 'addon': 'Mock Addon', + } + + result = await hass.config_entries.flow.async_configure( + result['flow_id'], { + 'allow_clip_sensor': True, + 'allow_deconz_groups': True, + } + ) + + assert result['type'] == 'create_entry' + assert result['result'].data == { + 'host': 'mock-deconz', + 'port': 8080, + 'bridgeid': 'aa:bb', + 'api_key': '1234567890ABCDEF', + 'allow_clip_sensor': True, + 'allow_deconz_groups': True, + }