Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 11 additions & 9 deletions homeassistant/components/deconz/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"""Config flow to configure deCONZ component."""
import asyncio
from urllib.parse import urlparse

import async_timeout
from pydeconz.errors import RequestError, ResponseError
from pydeconz.utils import async_discovery, async_get_api_key, async_get_gateway_config
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_SERIAL
from homeassistant.components import ssdp
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client
Expand All @@ -26,7 +27,6 @@

DECONZ_MANUFACTURERURL = "http://www.dresden-elektronik.de"
CONF_SERIAL = "serial"
ATTR_UUID = "udn"


@callback
Expand Down Expand Up @@ -170,18 +170,20 @@ async def _update_entry(self, entry, host):

async def async_step_ssdp(self, discovery_info):
"""Handle a discovered deCONZ bridge."""
if discovery_info[ATTR_MANUFACTURERURL] != DECONZ_MANUFACTURERURL:
if discovery_info[ssdp.ATTR_UPNP_MANUFACTURER_URL] != DECONZ_MANUFACTURERURL:
return self.async_abort(reason="not_deconz_bridge")

uuid = discovery_info[ATTR_UUID].replace("uuid:", "")
uuid = discovery_info[ssdp.ATTR_UPNP_UDN].replace("uuid:", "")

_LOGGER.debug("deCONZ gateway discovered (%s)", uuid)

parsed_url = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION])

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])
return await self._update_entry(entry, parsed_url.hostname)

bridgeid = discovery_info[ATTR_SERIAL]
bridgeid = discovery_info[ssdp.ATTR_UPNP_SERIAL]
if any(
bridgeid == flow["context"][CONF_BRIDGEID]
for flow in self._async_in_progress()
Expand All @@ -190,11 +192,11 @@ async def async_step_ssdp(self, discovery_info):

# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context[CONF_BRIDGEID] = bridgeid
self.context["title_placeholders"] = {"host": discovery_info[CONF_HOST]}
self.context["title_placeholders"] = {"host": parsed_url.hostname}

self.deconz_config = {
CONF_HOST: discovery_info[CONF_HOST],
CONF_PORT: discovery_info[CONF_PORT],
CONF_HOST: parsed_url.hostname,
CONF_PORT: parsed_url.port,
}

return await self.async_step_link()
Expand Down
10 changes: 7 additions & 3 deletions homeassistant/components/heos/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Config flow to configure Heos."""
from urllib.parse import urlparse

from pyheos import Heos, HeosError
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.components import ssdp
from homeassistant.const import CONF_HOST

from .const import DATA_DISCOVERED_HOSTS, DOMAIN

Expand All @@ -23,11 +26,12 @@ class HeosFlowHandler(config_entries.ConfigFlow):
async def async_step_ssdp(self, discovery_info):
"""Handle a discovered Heos device."""
# Store discovered host
hostname = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION]).hostname
friendly_name = "{} ({})".format(
discovery_info[CONF_NAME], discovery_info[CONF_HOST]
discovery_info[ssdp.ATTR_UPNP_FRIENDLY_NAME], hostname
)
self.hass.data.setdefault(DATA_DISCOVERED_HOSTS, {})
self.hass.data[DATA_DISCOVERED_HOSTS][friendly_name] = discovery_info[CONF_HOST]
self.hass.data[DATA_DISCOVERED_HOSTS][friendly_name] = hostname
# Abort if other flows in progress or an entry already exists
if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="already_setup")
Expand Down
8 changes: 5 additions & 3 deletions homeassistant/components/huawei_lte/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from collections import OrderedDict
import logging
from typing import Optional
from urllib.parse import urlparse

from huawei_lte_api.AuthorizedConnection import AuthorizedConnection
from huawei_lte_api.Client import Client
Expand All @@ -19,7 +20,7 @@
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.components.ssdp import ATTR_HOST, ATTR_NAME, ATTR_PRESENTATIONURL
from homeassistant.components import ssdp
from homeassistant.const import CONF_PASSWORD, CONF_RECIPIENT, CONF_URL, CONF_USERNAME
from homeassistant.core import callback

Expand Down Expand Up @@ -208,13 +209,14 @@ async def async_step_ssdp(self, discovery_info):
"""Handle SSDP initiated config flow."""
# Attempt to distinguish from other non-LTE Huawei router devices, at least
# some ones we are interested in have "Mobile Wi-Fi" friendlyName.
if "mobile" not in discovery_info.get(ATTR_NAME, "").lower():
if "mobile" not in discovery_info.get(ssdp.ATTR_UPNP_FRIENDLY_NAME, "").lower():
return self.async_abort(reason="not_huawei_lte")

# https://github.com/PyCQA/pylint/issues/3167
url = self.context[CONF_URL] = url_normalize( # pylint: disable=no-member
discovery_info.get(
ATTR_PRESENTATIONURL, f"http://{discovery_info[ATTR_HOST]}/"
ssdp.ATTR_UPNP_PRESENTATION_URL,
f"http://{urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION]).hostname}/",
)
)

Expand Down
18 changes: 11 additions & 7 deletions homeassistant/components/hue/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"""Config flow to configure Philips Hue."""
import asyncio
from typing import Dict, Optional
from urllib.parse import urlparse

import aiohue
from aiohue.discovery import discover_nupnp, normalize_bridge_id
import async_timeout
import voluptuous as vol

from homeassistant import config_entries, core
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_NAME
from homeassistant.components import ssdp
from homeassistant.helpers import aiohttp_client

from .bridge import authenticate_bridge
Expand Down Expand Up @@ -147,22 +148,25 @@ async def async_step_ssdp(self, discovery_info):
host is already configured and delegate to the import step if not.
"""
# Filter out non-Hue bridges #1
if discovery_info[ATTR_MANUFACTURERURL] != HUE_MANUFACTURERURL:
if discovery_info[ssdp.ATTR_UPNP_MANUFACTURER_URL] != HUE_MANUFACTURERURL:
return self.async_abort(reason="not_hue_bridge")

# Filter out non-Hue bridges #2
if any(
name in discovery_info.get(ATTR_NAME, "")
name in discovery_info.get(ssdp.ATTR_UPNP_FRIENDLY_NAME, "")
for name in HUE_IGNORED_BRIDGE_NAMES
):
return self.async_abort(reason="not_hue_bridge")

if "host" not in discovery_info or "serial" not in discovery_info:
if (
ssdp.ATTR_SSDP_LOCATION not in discovery_info
or ssdp.ATTR_UPNP_SERIAL not in discovery_info
):
return self.async_abort(reason="not_hue_bridge")

bridge = self._async_get_bridge(
discovery_info["host"], discovery_info["serial"]
)
host = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION]).hostname

bridge = self._async_get_bridge(host, discovery_info[ssdp.ATTR_UPNP_SERIAL])

await self.async_set_unique_id(bridge.id)
self._abort_if_unique_id_configured()
Expand Down
47 changes: 17 additions & 30 deletions homeassistant/components/ssdp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import asyncio
from datetime import timedelta
import logging
from urllib.parse import urlparse

import aiohttp
from defusedxml import ElementTree
Expand All @@ -14,19 +13,19 @@
DOMAIN = "ssdp"
SCAN_INTERVAL = timedelta(seconds=60)

ATTR_HOST = "host"
ATTR_PORT = "port"
ATTR_SSDP_DESCRIPTION = "ssdp_description"
ATTR_ST = "ssdp_st"
ATTR_NAME = "name"
ATTR_MODEL_NAME = "model_name"
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"
ATTR_PRESENTATIONURL = "presentation_url"
# Attributes for accessing info from SSDP response
ATTR_SSDP_LOCATION = "ssdp_location"
ATTR_SSDP_ST = "ssdp_st"
# Attributes for accessing info from retrieved UPnP device description
ATTR_UPNP_DEVICE_TYPE = "deviceType"
ATTR_UPNP_FRIENDLY_NAME = "friendlyName"
ATTR_UPNP_MANUFACTURER = "manufacturer"
ATTR_UPNP_MANUFACTURER_URL = "manufacturerURL"
ATTR_UPNP_MODEL_NAME = "modelName"
ATTR_UPNP_MODEL_NUMBER = "modelNumber"
ATTR_UPNP_PRESENTATION_URL = "presentationURL"
ATTR_UPNP_SERIAL = "serialNumber"
ATTR_UPNP_UDN = "UDN"

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -157,24 +156,12 @@ async def _fetch_description(self, xml_location):


def info_from_entry(entry, device_info):
"""Get most important info from an entry."""
url = urlparse(entry.location)
"""Get info from an entry."""
info = {
ATTR_HOST: url.hostname,
ATTR_PORT: url.port,
ATTR_SSDP_DESCRIPTION: entry.location,
ATTR_ST: entry.st,
ATTR_SSDP_LOCATION: entry.location,
ATTR_SSDP_ST: entry.st,
}

if device_info:
info[ATTR_NAME] = device_info.get("friendlyName")
info[ATTR_MODEL_NAME] = device_info.get("modelName")
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")
info[ATTR_PRESENTATIONURL] = device_info.get("presentationURL")
info.update(device_info)

return info
21 changes: 10 additions & 11 deletions tests/components/deconz/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import pydeconz

from homeassistant.components import ssdp
from homeassistant.components.deconz import config_flow
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_SERIAL

from tests.common import MockConfigEntry

Expand Down Expand Up @@ -213,11 +213,10 @@ async def test_bridge_ssdp_discovery(hass):
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,
ATTR_SERIAL: "id",
ATTR_MANUFACTURERURL: config_flow.DECONZ_MANUFACTURERURL,
config_flow.ATTR_UUID: "uuid:1234",
ssdp.ATTR_SSDP_LOCATION: "http://1.2.3.4:80/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.DECONZ_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: "id",
ssdp.ATTR_UPNP_UDN: "uuid:1234",
},
context={"source": "ssdp"},
)
Expand All @@ -230,7 +229,7 @@ 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={ATTR_MANUFACTURERURL: "not deconz bridge"},
data={ssdp.ATTR_UPNP_MANUFACTURER_URL: "not deconz bridge"},
context={"source": "ssdp"},
)

Expand All @@ -257,10 +256,10 @@ async def test_bridge_discovery_update_existing_entry(hass):
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
data={
config_flow.CONF_HOST: "mock-deconz",
ATTR_SERIAL: "123ABC",
ATTR_MANUFACTURERURL: config_flow.DECONZ_MANUFACTURERURL,
config_flow.ATTR_UUID: "uuid:456DEF",
ssdp.ATTR_SSDP_LOCATION: "http://mock-deconz/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.DECONZ_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: "123ABC",
ssdp.ATTR_UPNP_UDN: "uuid:456DEF",
},
context={"source": "ssdp"},
)
Expand Down
9 changes: 4 additions & 5 deletions tests/components/deconz/test_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,10 @@ async def test_update_address(hass):
await hass.config_entries.flow.async_init(
deconz.config_flow.DOMAIN,
data={
deconz.config_flow.CONF_HOST: "2.3.4.5",
deconz.config_flow.CONF_PORT: 80,
ssdp.ATTR_SERIAL: BRIDGEID,
ssdp.ATTR_MANUFACTURERURL: deconz.config_flow.DECONZ_MANUFACTURERURL,
deconz.config_flow.ATTR_UUID: "uuid:456DEF",
ssdp.ATTR_SSDP_LOCATION: "http://2.3.4.5:80/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: deconz.config_flow.DECONZ_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: BRIDGEID,
ssdp.ATTR_UPNP_UDN: "uuid:456DEF",
},
context={"source": "ssdp"},
)
Expand Down
19 changes: 9 additions & 10 deletions tests/components/heos/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pyheos import Dispatcher, Heos, HeosPlayer, HeosSource, InputSource, const
import pytest

from homeassistant.components import ssdp
from homeassistant.components.heos import DOMAIN
from homeassistant.const import CONF_HOST

Expand Down Expand Up @@ -118,16 +119,14 @@ def dispatcher_fixture() -> Dispatcher:
def discovery_data_fixture() -> dict:
"""Return mock discovery data for testing."""
return {
"host": "127.0.0.1",
"manufacturer": "Denon",
"model_name": "HEOS Drive",
"model_number": "DWSA-10 4.0",
"name": "Office",
"port": 60006,
"serial": None,
"ssdp_description": "http://127.0.0.1:60006/upnp/desc/aios_device/aios_device.xml",
"udn": "uuid:e61de70c-2250-1c22-0080-0005cdf512be",
"upnp_device_type": "urn:schemas-denon-com:device:AiosDevice:1",
ssdp.ATTR_SSDP_LOCATION: "http://127.0.0.1:60006/upnp/desc/aios_device/aios_device.xml",
ssdp.ATTR_UPNP_DEVICE_TYPE: "urn:schemas-denon-com:device:AiosDevice:1",
ssdp.ATTR_UPNP_FRIENDLY_NAME: "Office",
ssdp.ATTR_UPNP_MANUFACTURER: "Denon",
ssdp.ATTR_UPNP_MODEL_NAME: "HEOS Drive",
ssdp.ATTR_UPNP_MODEL_NUMBER: "DWSA-10 4.0",
ssdp.ATTR_UPNP_SERIAL: None,
ssdp.ATTR_UPNP_UDN: "uuid:e61de70c-2250-1c22-0080-0005cdf512be",
}


Expand Down
10 changes: 7 additions & 3 deletions tests/components/heos/test_config_flow.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"""Tests for the Heos config flow module."""
from urllib.parse import urlparse

from pyheos import HeosError

from homeassistant import data_entry_flow
from homeassistant.components import ssdp
from homeassistant.components.heos.config_flow import HeosFlowHandler
from homeassistant.components.heos.const import DATA_DISCOVERED_HOSTS, DOMAIN
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.const import CONF_HOST


async def test_flow_aborts_already_setup(hass, config_entry):
Expand Down Expand Up @@ -79,8 +82,9 @@ async def test_discovery_shows_create_form(hass, controller, discovery_data):
assert len(hass.config_entries.flow.async_progress()) == 1
assert hass.data[DATA_DISCOVERED_HOSTS] == {"Office (127.0.0.1)": "127.0.0.1"}

discovery_data[CONF_HOST] = "127.0.0.2"
discovery_data[CONF_NAME] = "Bedroom"
port = urlparse(discovery_data[ssdp.ATTR_SSDP_LOCATION]).port
discovery_data[ssdp.ATTR_SSDP_LOCATION] = f"http://127.0.0.2:{port}/"
discovery_data[ssdp.ATTR_UPNP_FRIENDLY_NAME] = "Bedroom"
await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "ssdp"}, data=discovery_data
)
Expand Down
Loading