Support multiple deCONZ gateways (#22449)

* Store gateways inside a dict in deconz domain

* Make reachable events gateway specific

* Gateway shall always exist

* Adapt new device signalling to support multiple gateways

* Services follow gateway master

* Working on unload entry

* Make unload and master handover work
Improve tests for init

* Fix config flow

* Fix linting

* Clean up init tests

* Clean up hassio discovery to fit with the rest

* Store gateways inside a dict in deconz domain

* Make reachable events gateway specific

* Gateway shall always exist

* Adapt new device signalling to support multiple gateways

* Services follow gateway master

* Working on unload entry

* Make unload and master handover work
Improve tests for init

* Fix config flow

* Fix linting

* Clean up init tests

* Clean up hassio discovery to fit with the rest

* Add support for services to specify bridgeid
This commit is contained in:
Robert Svensson 2019-04-05 02:48:24 +02:00 committed by Jason Hu
parent b9ec623ad9
commit b50afec5f1
22 changed files with 535 additions and 426 deletions

View file

@ -9,10 +9,7 @@ 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_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID,
DEFAULT_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_DECONZ_GROUPS, DEFAULT_PORT,
DOMAIN)
from .const import CONF_BRIDGEID, DEFAULT_PORT, DOMAIN
@callback
@ -22,6 +19,14 @@ def configured_hosts(hass):
in hass.config_entries.async_entries(DOMAIN))
@callback
def get_master_gateway(hass):
"""Return a bool telling if this is the master gateway."""
for gateway in hass.data[DOMAIN].values():
if gateway.master:
return gateway
@config_entries.HANDLERS.register(DOMAIN)
class DeconzFlowHandler(config_entries.ConfigFlow):
"""Handle a deCONZ config flow."""
@ -39,16 +44,12 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
async def async_step_user(self, user_input=None):
"""Handle a deCONZ config flow start.
Only allows one instance to be set up.
If only one bridge is found go to link step.
If more than one bridge is found let user choose bridge to link.
If no bridge is found allow user to manually input configuration.
"""
from pydeconz.utils import async_discovery
if configured_hosts(self.hass):
return self.async_abort(reason='one_instance_only')
if user_input is not None:
for bridge in self.bridges:
if bridge[CONF_HOST] == user_input[CONF_HOST]:
@ -99,9 +100,6 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
errors = {}
if user_input is not None:
if configured_hosts(self.hass):
return self.async_abort(reason='one_instance_only')
session = aiohttp_client.async_get_clientsession(self.hass)
try:
@ -114,51 +112,32 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
else:
self.deconz_config[CONF_API_KEY] = api_key
return await self.async_step_options()
return await self._create_entry()
return self.async_show_form(
step_id='link',
errors=errors,
)
async def async_step_options(self, user_input=None):
"""Extra options for deCONZ.
CONF_CLIP_SENSOR -- Allow user to choose if they want clip sensors.
CONF_DECONZ_GROUPS -- Allow user to choose if they want deCONZ groups.
"""
async def _create_entry(self):
"""Create entry for gateway."""
from pydeconz.utils import async_get_bridgeid
if user_input is not None:
self.deconz_config[CONF_ALLOW_CLIP_SENSOR] = \
user_input[CONF_ALLOW_CLIP_SENSOR]
self.deconz_config[CONF_ALLOW_DECONZ_GROUPS] = \
user_input[CONF_ALLOW_DECONZ_GROUPS]
if CONF_BRIDGEID not in self.deconz_config:
session = aiohttp_client.async_get_clientsession(self.hass)
if CONF_BRIDGEID not in self.deconz_config:
session = aiohttp_client.async_get_clientsession(self.hass)
try:
with async_timeout.timeout(10):
self.deconz_config[CONF_BRIDGEID] = \
await async_get_bridgeid(
session, **self.deconz_config)
try:
with async_timeout.timeout(10):
self.deconz_config[CONF_BRIDGEID] = \
await async_get_bridgeid(
session, **self.deconz_config)
except asyncio.TimeoutError:
return self.async_abort(reason='no_bridges')
except asyncio.TimeoutError:
return self.async_abort(reason='no_bridges')
return self.async_create_entry(
title='deCONZ-' + self.deconz_config[CONF_BRIDGEID],
data=self.deconz_config
)
return self.async_show_form(
step_id='options',
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,
}),
return self.async_create_entry(
title='deCONZ-' + self.deconz_config[CONF_BRIDGEID],
data=self.deconz_config
)
async def async_step_discovery(self, discovery_info):
@ -166,10 +145,14 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
This flow is triggered by the discovery component.
"""
deconz_config = {}
deconz_config[CONF_HOST] = discovery_info.get(CONF_HOST)
deconz_config[CONF_PORT] = discovery_info.get(CONF_PORT)
deconz_config[CONF_BRIDGEID] = discovery_info.get('serial')
deconz_config = {
CONF_HOST: discovery_info[CONF_HOST],
CONF_PORT: discovery_info[CONF_PORT],
CONF_BRIDGEID: discovery_info['serial']
}
if deconz_config[CONF_HOST] in configured_hosts(self.hass):
return self.async_abort(reason='one_instance_only')
return await self.async_step_import(deconz_config)
@ -186,16 +169,11 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
Otherwise we will delegate to `link` step which
will ask user to link the bridge.
"""
if configured_hosts(self.hass):
return self.async_abort(reason='one_instance_only')
self.deconz_config = import_config
if CONF_API_KEY not in import_config:
return await self.async_step_link()
user_input = {CONF_ALLOW_CLIP_SENSOR: True,
CONF_ALLOW_DECONZ_GROUPS: True}
return await self.async_step_options(user_input=user_input)
return await self._create_entry()
async def async_step_hassio(self, user_input=None):
"""Prepare configuration for a Hass.io deCONZ bridge.
@ -212,29 +190,18 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
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
self.deconz_config = {
CONF_HOST: self._hassio_discovery[CONF_HOST],
CONF_PORT: self._hassio_discovery[CONF_PORT],
CONF_BRIDGEID: self._hassio_discovery['serial'],
CONF_API_KEY: self._hassio_discovery[CONF_API_KEY]
}
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 await self._create_entry()
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,
})
}
)