Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deCONZ migrate to SSDP discovery #24252

Merged
merged 8 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions homeassistant/components/deconz/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
async_discovery, async_get_api_key, async_get_bridgeid)

from homeassistant import config_entries
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_SERIAL
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_BRIDGEID, DEFAULT_PORT, DOMAIN

DECONZ_MANUFACTURERURL = 'http://www.dresden-elektronik.de'
CONF_SERIAL = 'serial'


Expand Down Expand Up @@ -149,23 +151,30 @@ async def _update_entry(self, entry, host):
entry.data[CONF_HOST] = host
self.hass.config_entries.async_update_entry(entry)

async def async_step_discovery(self, discovery_info):
"""Prepare configuration for a discovered deCONZ bridge.
async def async_step_ssdp(self, discovery_info):
"""Handle a discovered deCONZ bridge."""
if discovery_info[ATTR_MANUFACTURERURL] != DECONZ_MANUFACTURERURL:
return self.async_abort(reason='not_deconz_bridge')

This flow is triggered by the discovery component.
"""
bridgeid = discovery_info[CONF_SERIAL]
bridgeid = discovery_info[ATTR_SERIAL]
gateway_entries = configured_gateways(self.hass)

if bridgeid in gateway_entries:
entry = gateway_entries[bridgeid]
await self._update_entry(entry, discovery_info[CONF_HOST])
return self.async_abort(reason='updated_instance')

# pylint: disable=unsupported-assignment-operation
self.context[ATTR_SERIAL] = bridgeid

if any(bridgeid == flow['context'][ATTR_SERIAL]
for flow in self._async_in_progress()):
return self.async_abort(reason='already_in_progress')

deconz_config = {
CONF_HOST: discovery_info[CONF_HOST],
CONF_PORT: discovery_info[CONF_PORT],
CONF_BRIDGEID: discovery_info[CONF_SERIAL]
CONF_BRIDGEID: bridgeid
}

return await self.async_step_import(deconz_config)
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/deconz/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"requirements": [
"pydeconz==59"
],
"ssdp": {
"manufacturer": [
"Royal Philips Electronics"
]
},
"dependencies": [],
"codeowners": [
"@kane610"
Expand Down
6 changes: 4 additions & 2 deletions homeassistant/components/deconz/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
},
"abort": {
"already_configured": "Bridge is already configured",
"already_in_progress": "Config flow for bridge is already in progress.",
"no_bridges": "No deCONZ bridges discovered",
"updated_instance": "Updated deCONZ instance with new host address",
"one_instance_only": "Component only supports one deCONZ instance"
"not_deconz_bridge": "Not a deCONZ bridge",
"one_instance_only": "Component only supports one deCONZ instance",
"updated_instance": "Updated deCONZ instance with new host address"
}
}
}
3 changes: 1 addition & 2 deletions homeassistant/components/discovery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
SCAN_INTERVAL = timedelta(seconds=300)
SERVICE_APPLE_TV = 'apple_tv'
SERVICE_DAIKIN = 'daikin'
SERVICE_DECONZ = 'deconz'
SERVICE_DLNA_DMR = 'dlna_dmr'
SERVICE_ENIGMA2 = 'enigma2'
SERVICE_FREEBOX = 'freebox'
Expand All @@ -48,7 +47,6 @@

CONFIG_ENTRY_HANDLERS = {
SERVICE_DAIKIN: 'daikin',
SERVICE_DECONZ: 'deconz',
'google_cast': 'cast',
SERVICE_HEOS: 'heos',
SERVICE_TELLDUSLIVE: 'tellduslive',
Expand Down Expand Up @@ -98,6 +96,7 @@

MIGRATED_SERVICE_HANDLERS = {
'axis': None,
'deconz': None,
'esphome': None,
'ikea_tradfri': None,
'homekit': None,
Expand Down
6 changes: 6 additions & 0 deletions homeassistant/components/hue/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.components.deconz.config_flow import (
DECONZ_MANUFACTURERURL)
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client

Expand Down Expand Up @@ -143,6 +146,9 @@ async def async_step_ssdp(self, discovery_info):
This flow is triggered by the SSDP component. It will check if the
host is already configured and delegate to the import step if not.
"""
if discovery_info[ATTR_MANUFACTURERURL] == DECONZ_MANUFACTURERURL:
Kane610 marked this conversation as resolved.
Show resolved Hide resolved
return self.async_abort(reason='not_hue_bridge')

# Filter out emulated Hue
if "HASS Bridge" in discovery_info.get('name', ''):
return self.async_abort(reason='already_configured')
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/hue/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"unknown": "Unknown error occurred",
"cannot_connect": "Unable to connect to the bridge",
"already_configured": "Bridge is already configured",
"already_in_progress": "Config flow for bridge is already in progress."
"already_in_progress": "Config flow for bridge is already in progress.",
"not_hue_bridge": "Not a Hue bridge"
}
}
}
2 changes: 2 additions & 0 deletions homeassistant/components/ssdp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
ATTR_MODEL_NUMBER = 'model_number'
ATTR_SERIAL = 'serial_number'
ATTR_MANUFACTURER = 'manufacturer'
ATTR_MANUFACTURERURL = 'manufacturerURL'
ATTR_UDN = 'udn'
ATTR_UPNP_DEVICE_TYPE = 'upnp_device_type'

Expand Down Expand Up @@ -164,6 +165,7 @@ def info_from_entry(entry, device_info):
info[ATTR_MODEL_NUMBER] = device_info.get('modelNumber')
info[ATTR_SERIAL] = device_info.get('serialNumber')
info[ATTR_MANUFACTURER] = device_info.get('manufacturer')
info[ATTR_MANUFACTURERURL] = device_info.get('manufacturerURL')
info[ATTR_UDN] = device_info.get('UDN')
info[ATTR_UPNP_DEVICE_TYPE] = device_info.get('deviceType')

Expand Down
1 change: 1 addition & 0 deletions homeassistant/generated/ssdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"device_type": {},
"manufacturer": {
"Royal Philips Electronics": [
"deconz",
"hue"
]
},
Expand Down
30 changes: 24 additions & 6 deletions tests/components/deconz/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,22 +168,38 @@ async def test_link_no_api_key(hass):
assert result['errors'] == {'base': 'no_key'}


async def test_bridge_discovery(hass):
"""Test a bridge being discovered."""
async def test_bridge_ssdp_discovery(hass):
"""Test a bridge being discovered over ssdp."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
data={
config_flow.CONF_HOST: '1.2.3.4',
config_flow.CONF_PORT: 80,
config_flow.CONF_SERIAL: 'id',
config_flow.ATTR_SERIAL: 'id',
config_flow.ATTR_MANUFACTURERURL:
config_flow.DECONZ_MANUFACTURERURL
},
context={'source': 'discovery'}
context={'source': 'ssdp'}
)

assert result['type'] == 'form'
assert result['step_id'] == 'link'


async def test_bridge_ssdp_discovery_not_deconz_bridge(hass):
"""Test a non deconz bridge being discovered over ssdp."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
data={
config_flow.ATTR_MANUFACTURERURL: 'not deconz bridge'
},
context={'source': 'ssdp'}
)

assert result['type'] == 'abort'
assert result['reason'] == 'not_deconz_bridge'


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={
Expand All @@ -195,9 +211,11 @@ async def test_bridge_discovery_update_existing_entry(hass):
config_flow.DOMAIN,
data={
config_flow.CONF_HOST: 'mock-deconz',
config_flow.CONF_SERIAL: 'id',
config_flow.ATTR_SERIAL: 'id',
config_flow.ATTR_MANUFACTURERURL:
config_flow.DECONZ_MANUFACTURERURL
},
context={'source': 'discovery'}
context={'source': 'ssdp'}
)

assert result['type'] == 'abort'
Expand Down
23 changes: 20 additions & 3 deletions tests/components/hue/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import pytest
import voluptuous as vol

from homeassistant.components.deconz.config_flow import (
DECONZ_MANUFACTURERURL)
from homeassistant.components.hue import config_flow, const, errors

from tests.common import MockConfigEntry, mock_coro
Expand Down Expand Up @@ -195,13 +197,26 @@ async def test_bridge_ssdp(hass):
side_effect=errors.AuthenticationRequired):
result = await flow.async_step_ssdp({
'host': '0.0.0.0',
'serial': '1234'
'serial': '1234',
'manufacturerURL': 'http://www.philips.com'
})

assert result['type'] == 'form'
assert result['step_id'] == 'link'


async def test_bridge_ssdp_discover_deconz(hass):
"""Test that discovery ignores deCONZ bridges."""
flow = config_flow.HueFlowHandler()
flow.hass = hass

result = await flow.async_step_ssdp({
'manufacturerURL': DECONZ_MANUFACTURERURL
})

assert result['type'] == 'abort'


async def test_bridge_ssdp_emulated_hue(hass):
"""Test if discovery info is from an emulated hue instance."""
flow = config_flow.HueFlowHandler()
Expand All @@ -211,7 +226,8 @@ async def test_bridge_ssdp_emulated_hue(hass):
result = await flow.async_step_ssdp({
'name': 'HASS Bridge',
'host': '0.0.0.0',
'serial': '1234'
'serial': '1234',
'manufacturerURL': 'http://www.philips.com'
})

assert result['type'] == 'abort'
Expand All @@ -229,7 +245,8 @@ async def test_bridge_ssdp_already_configured(hass):

result = await flow.async_step_ssdp({
'host': '0.0.0.0',
'serial': '1234'
'serial': '1234',
'manufacturerURL': 'http://www.philips.com'
})

assert result['type'] == 'abort'
Expand Down