deCONZ - Improve ssdp discovery by storing uuid in config entry (#26882)

* Improve ssdp discovery by storing uuid in config entry so discovery can update any deconz entry, loaded or not
This commit is contained in:
Robert Svensson 2019-09-25 18:56:31 +02:00 committed by GitHub
parent cc611615aa
commit 4582b6e668
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 59 additions and 44 deletions

View File

@ -4,8 +4,8 @@ import voluptuous as vol
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from .config_flow import get_master_gateway
from .const import CONF_BRIDGEID, CONF_MASTER_GATEWAY, DOMAIN
from .gateway import DeconzGateway
from .const import CONF_BRIDGEID, CONF_MASTER_GATEWAY, CONF_UUID, DOMAIN
from .gateway import DeconzGateway, get_gateway_from_config_entry
from .services import async_setup_services, async_unload_services
CONFIG_SCHEMA = vol.Schema(
@ -39,6 +39,9 @@ async def async_setup_entry(hass, config_entry):
await gateway.async_update_device_registry()
if CONF_UUID not in config_entry.data:
await async_add_uuid_to_config_entry(hass, config_entry)
await async_setup_services(hass)
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gateway.shutdown)
@ -68,11 +71,14 @@ async def async_update_master_gateway(hass, config_entry):
Makes sure there is always one master available.
"""
master = not get_master_gateway(hass)
old_options = dict(config_entry.options)
new_options = {CONF_MASTER_GATEWAY: master}
options = {**old_options, **new_options}
options = {**config_entry.options, CONF_MASTER_GATEWAY: master}
hass.config_entries.async_update_entry(config_entry, options=options)
async def async_add_uuid_to_config_entry(hass, config_entry):
"""Add UUID to config entry to help discovery identify entries."""
gateway = get_gateway_from_config_entry(hass, config_entry)
config = {**config_entry.data, CONF_UUID: gateway.api.config.uuid}
hass.config_entries.async_update_entry(config_entry, data=config)

View File

@ -5,7 +5,7 @@ import async_timeout
import voluptuous as vol
from pydeconz.errors import ResponseError, RequestError
from pydeconz.utils import async_discovery, async_get_api_key, async_get_bridgeid
from pydeconz.utils import async_discovery, async_get_api_key, async_get_gateway_config
from homeassistant import config_entries
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
@ -16,6 +16,7 @@ from .const import (
CONF_ALLOW_CLIP_SENSOR,
CONF_ALLOW_DECONZ_GROUPS,
CONF_BRIDGEID,
CONF_UUID,
DEFAULT_ALLOW_CLIP_SENSOR,
DEFAULT_ALLOW_DECONZ_GROUPS,
DEFAULT_PORT,
@ -144,9 +145,11 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
try:
with async_timeout.timeout(10):
self.deconz_config[CONF_BRIDGEID] = await async_get_bridgeid(
gateway_config = await async_get_gateway_config(
session, **self.deconz_config
)
self.deconz_config[CONF_BRIDGEID] = gateway_config.bridgeid
self.deconz_config[CONF_UUID] = gateway_config.uuid
except asyncio.TimeoutError:
return self.async_abort(reason="no_bridges")
@ -172,14 +175,10 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="not_deconz_bridge")
uuid = discovery_info[ATTR_UUID].replace("uuid:", "")
gateways = {
gateway.api.config.uuid: gateway
for gateway in self.hass.data.get(DOMAIN, {}).values()
}
if uuid in gateways:
entry = gateways[uuid].config_entry
return await self._update_entry(entry, discovery_info[CONF_HOST])
for entry in self.hass.config_entries.async_entries(DOMAIN):
if uuid == entry.data.get(CONF_UUID):
return await self._update_entry(entry, discovery_info[CONF_HOST])
bridgeid = discovery_info[ATTR_SERIAL]
if any(

View File

@ -5,13 +5,15 @@ _LOGGER = logging.getLogger(__package__)
DOMAIN = "deconz"
CONF_BRIDGEID = "bridgeid"
CONF_UUID = "uuid"
DEFAULT_PORT = 80
DEFAULT_ALLOW_CLIP_SENSOR = False
DEFAULT_ALLOW_DECONZ_GROUPS = True
CONF_ALLOW_CLIP_SENSOR = "allow_clip_sensor"
CONF_ALLOW_DECONZ_GROUPS = "allow_deconz_groups"
CONF_BRIDGEID = "bridgeid"
CONF_MASTER_GATEWAY = "master"
SUPPORTED_PLATFORMS = [

View File

@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/components/deconz",
"requirements": [
"pydeconz==62"
"pydeconz==63"
],
"ssdp": {
"manufacturer": [

View File

@ -1129,7 +1129,7 @@ pydaikin==1.6.1
pydanfossair==0.1.0
# homeassistant.components.deconz
pydeconz==62
pydeconz==63
# homeassistant.components.delijn
pydelijn==0.5.1

View File

@ -294,7 +294,7 @@ pyblackbird==0.5
pychromecast==4.0.1
# homeassistant.components.deconz
pydeconz==62
pydeconz==63
# homeassistant.components.zwave
pydispatcher==2.0.5

View File

@ -209,22 +209,25 @@ async def test_bridge_discovery_update_existing_entry(hass):
"""Test if a discovered bridge has already been configured."""
entry = MockConfigEntry(
domain=config_flow.DOMAIN,
data={config_flow.CONF_HOST: "1.2.3.4", config_flow.CONF_BRIDGEID: "id"},
data={
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_BRIDGEID: "123ABC",
config_flow.CONF_UUID: "456DEF",
},
)
entry.add_to_hass(hass)
gateway = Mock()
gateway.config_entry = entry
gateway.api.config.uuid = "1234"
hass.data[config_flow.DOMAIN] = {"id": gateway}
hass.data[config_flow.DOMAIN] = {"123ABC": gateway}
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
data={
config_flow.CONF_HOST: "mock-deconz",
ATTR_SERIAL: "id",
ATTR_SERIAL: "123ABC",
ATTR_MANUFACTURERURL: config_flow.DECONZ_MANUFACTURERURL,
config_flow.ATTR_UUID: "uuid:1234",
config_flow.ATTR_UUID: "uuid:456DEF",
},
context={"source": "ssdp"},
)
@ -238,7 +241,7 @@ async def test_create_entry(hass, aioclient_mock):
"""Test that _create_entry work and that bridgeid can be requested."""
aioclient_mock.get(
"http://1.2.3.4:80/api/1234567890ABCDEF/config",
json={"bridgeid": "id"},
json={"bridgeid": "123ABC", "uuid": "456DEF"},
headers={"content-type": "application/json"},
)
@ -253,12 +256,13 @@ async def test_create_entry(hass, aioclient_mock):
result = await flow._create_entry()
assert result["type"] == "create_entry"
assert result["title"] == "deCONZ-id"
assert result["title"] == "deCONZ-123ABC"
assert result["data"] == {
config_flow.CONF_BRIDGEID: "id",
config_flow.CONF_BRIDGEID: "123ABC",
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_PORT: 80,
config_flow.CONF_API_KEY: "1234567890ABCDEF",
config_flow.CONF_UUID: "456DEF",
}
@ -273,7 +277,7 @@ async def test_create_entry_timeout(hass, aioclient_mock):
}
with patch(
"homeassistant.components.deconz.config_flow.async_get_bridgeid",
"homeassistant.components.deconz.config_flow.async_get_gateway_config",
side_effect=asyncio.TimeoutError,
):
result = await flow._create_entry()

View File

@ -20,6 +20,7 @@ ENTRY_CONFIG = {
deconz.config_flow.CONF_BRIDGEID: BRIDGEID,
deconz.config_flow.CONF_HOST: "1.2.3.4",
deconz.config_flow.CONF_PORT: 80,
deconz.config_flow.CONF_UUID: "456DEF",
}
DECONZ_CONFIG = {
@ -147,7 +148,7 @@ async def test_update_address(hass):
deconz.config_flow.CONF_PORT: 80,
ssdp.ATTR_SERIAL: BRIDGEID,
ssdp.ATTR_MANUFACTURERURL: deconz.config_flow.DECONZ_MANUFACTURERURL,
deconz.config_flow.ATTR_UUID: "uuid:1234",
deconz.config_flow.ATTR_UUID: "uuid:456DEF",
},
context={"source": "ssdp"},
)

View File

@ -1,33 +1,34 @@
"""Test deCONZ component setup process."""
from unittest.mock import Mock, patch
import asyncio
from asynctest import Mock, patch
import pytest
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.components import deconz
from tests.common import mock_coro, MockConfigEntry
from tests.common import MockConfigEntry
ENTRY1_HOST = "1.2.3.4"
ENTRY1_PORT = 80
ENTRY1_API_KEY = "1234567890ABCDEF"
ENTRY1_BRIDGEID = "12345ABC"
ENTRY1_UUID = "456DEF"
ENTRY2_HOST = "2.3.4.5"
ENTRY2_PORT = 80
ENTRY2_API_KEY = "1234567890ABCDEF"
ENTRY2_BRIDGEID = "23456DEF"
ENTRY2_UUID = "789ACE"
async def setup_entry(hass, entry):
"""Test that setup entry works."""
with patch.object(
deconz.DeconzGateway, "async_setup", return_value=mock_coro(True)
deconz.DeconzGateway, "async_setup", return_value=True
), patch.object(
deconz.DeconzGateway,
"async_update_device_registry",
return_value=mock_coro(True),
deconz.DeconzGateway, "async_update_device_registry", return_value=True
):
assert await deconz.async_setup_entry(hass, entry) is True
@ -67,6 +68,7 @@ async def test_setup_entry_successful(hass):
deconz.config_flow.CONF_PORT: ENTRY1_PORT,
deconz.config_flow.CONF_API_KEY: ENTRY1_API_KEY,
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID,
deconz.CONF_UUID: ENTRY1_UUID,
},
)
entry.add_to_hass(hass)
@ -86,6 +88,7 @@ async def test_setup_entry_multiple_gateways(hass):
deconz.config_flow.CONF_PORT: ENTRY1_PORT,
deconz.config_flow.CONF_API_KEY: ENTRY1_API_KEY,
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID,
deconz.CONF_UUID: ENTRY1_UUID,
},
)
entry.add_to_hass(hass)
@ -97,6 +100,7 @@ async def test_setup_entry_multiple_gateways(hass):
deconz.config_flow.CONF_PORT: ENTRY2_PORT,
deconz.config_flow.CONF_API_KEY: ENTRY2_API_KEY,
deconz.CONF_BRIDGEID: ENTRY2_BRIDGEID,
deconz.CONF_UUID: ENTRY2_UUID,
},
)
entry2.add_to_hass(hass)
@ -119,15 +123,14 @@ async def test_unload_entry(hass):
deconz.config_flow.CONF_PORT: ENTRY1_PORT,
deconz.config_flow.CONF_API_KEY: ENTRY1_API_KEY,
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID,
deconz.CONF_UUID: ENTRY1_UUID,
},
)
entry.add_to_hass(hass)
await setup_entry(hass, entry)
with patch.object(
deconz.DeconzGateway, "async_reset", return_value=mock_coro(True)
):
with patch.object(deconz.DeconzGateway, "async_reset", return_value=True):
assert await deconz.async_unload_entry(hass, entry)
assert not hass.data[deconz.DOMAIN]
@ -142,6 +145,7 @@ async def test_unload_entry_multiple_gateways(hass):
deconz.config_flow.CONF_PORT: ENTRY1_PORT,
deconz.config_flow.CONF_API_KEY: ENTRY1_API_KEY,
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID,
deconz.CONF_UUID: ENTRY1_UUID,
},
)
entry.add_to_hass(hass)
@ -153,6 +157,7 @@ async def test_unload_entry_multiple_gateways(hass):
deconz.config_flow.CONF_PORT: ENTRY2_PORT,
deconz.config_flow.CONF_API_KEY: ENTRY2_API_KEY,
deconz.CONF_BRIDGEID: ENTRY2_BRIDGEID,
deconz.CONF_UUID: ENTRY2_UUID,
},
)
entry2.add_to_hass(hass)
@ -160,9 +165,7 @@ async def test_unload_entry_multiple_gateways(hass):
await setup_entry(hass, entry)
await setup_entry(hass, entry2)
with patch.object(
deconz.DeconzGateway, "async_reset", return_value=mock_coro(True)
):
with patch.object(deconz.DeconzGateway, "async_reset", return_value=True):
assert await deconz.async_unload_entry(hass, entry)
assert ENTRY2_BRIDGEID in hass.data[deconz.DOMAIN]