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: 18 additions & 2 deletions homeassistant/components/ssdp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import annotations

import asyncio
from collections.abc import Awaitable
from collections.abc import Awaitable, Iterator
from dataclasses import dataclass, field
from datetime import timedelta
from enum import Enum
Expand Down Expand Up @@ -144,7 +144,7 @@ def __getitem__(self, name: str) -> Any:
# Use a property if it is available, fallback to upnp data
if hasattr(self, name):
return getattr(self, name)
return self.upnp.get(name)
return self.upnp[name]

def get(self, name: str, default: Any = None) -> Any:
"""
Expand All @@ -164,6 +164,22 @@ def get(self, name: str, default: Any = None) -> Any:
return getattr(self, name)
return self.upnp.get(name, default)

def __iter__(self) -> Iterator[str]:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required for in calls like in konnected:

if not any(
name in discovery_info[ATTR_UPNP_MODEL_NAME]
for name in KONN_PANEL_MODEL_NAMES
):

As in https://github.com/home-assistant/core/pull/60334/files#r756924593, this might need to be added to zeroconf/dhcp/usb/mqtt

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should since its a temp measure and the goal is to avoid a breaking change

"""
Implement iter(self) on upnp data.

Deprecated, and will be removed in version 2022.6.
"""
if not self._warning_logged:
report(
"accessed discovery_info.__iter__() instead of discovery_info.upnp.__iter__(); this will fail in version 2022.6",
exclude_integrations={"ssdp"},
error_if_core=False,
level=logging.DEBUG,
)
self._warning_logged = True
return self.upnp.__iter__()


@bind_hass
async def async_register_callback(
Expand Down
60 changes: 38 additions & 22 deletions tests/components/denonavr/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,16 @@ async def test_config_flow_ssdp(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_UPNP_MANUFACTURER: TEST_MANUFACTURER,
ssdp.ATTR_UPNP_MODEL_NAME: TEST_MODEL,
ssdp.ATTR_UPNP_SERIAL: TEST_SERIALNUMBER,
ssdp.ATTR_SSDP_LOCATION: TEST_SSDP_LOCATION,
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location=TEST_SSDP_LOCATION,
upnp={
ssdp.ATTR_UPNP_MANUFACTURER: TEST_MANUFACTURER,
ssdp.ATTR_UPNP_MODEL_NAME: TEST_MODEL,
ssdp.ATTR_UPNP_SERIAL: TEST_SERIALNUMBER,
},
),
)

assert result["type"] == "form"
Expand Down Expand Up @@ -336,12 +340,16 @@ async def test_config_flow_ssdp_not_denon(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_UPNP_MANUFACTURER: "NotSupported",
ssdp.ATTR_UPNP_MODEL_NAME: TEST_MODEL,
ssdp.ATTR_UPNP_SERIAL: TEST_SERIALNUMBER,
ssdp.ATTR_SSDP_LOCATION: TEST_SSDP_LOCATION,
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location=TEST_SSDP_LOCATION,
upnp={
ssdp.ATTR_UPNP_MANUFACTURER: "NotSupported",
ssdp.ATTR_UPNP_MODEL_NAME: TEST_MODEL,
ssdp.ATTR_UPNP_SERIAL: TEST_SERIALNUMBER,
},
),
)

assert result["type"] == "abort"
Expand All @@ -357,10 +365,14 @@ async def test_config_flow_ssdp_missing_info(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_UPNP_MANUFACTURER: TEST_MANUFACTURER,
ssdp.ATTR_SSDP_LOCATION: TEST_SSDP_LOCATION,
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location=TEST_SSDP_LOCATION,
upnp={
ssdp.ATTR_UPNP_MANUFACTURER: TEST_MANUFACTURER,
},
),
)

assert result["type"] == "abort"
Expand All @@ -376,12 +388,16 @@ async def test_config_flow_ssdp_ignored_model(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_UPNP_MANUFACTURER: TEST_MANUFACTURER,
ssdp.ATTR_UPNP_MODEL_NAME: TEST_IGNORED_MODEL,
ssdp.ATTR_UPNP_SERIAL: TEST_SERIALNUMBER,
ssdp.ATTR_SSDP_LOCATION: TEST_SSDP_LOCATION,
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location=TEST_SSDP_LOCATION,
upnp={
ssdp.ATTR_UPNP_MANUFACTURER: TEST_MANUFACTURER,
ssdp.ATTR_UPNP_MODEL_NAME: TEST_IGNORED_MODEL,
ssdp.ATTR_UPNP_SERIAL: TEST_SERIALNUMBER,
},
),
)

assert result["type"] == "abort"
Expand Down
104 changes: 68 additions & 36 deletions tests/components/hue/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,15 @@ async def test_bridge_ssdp(hass, mf_url, aioclient_mock):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: mf_url,
ssdp.ATTR_UPNP_SERIAL: "1234",
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location="http://0.0.0.0/",
upnp={
ssdp.ATTR_UPNP_MANUFACTURER_URL: mf_url,
ssdp.ATTR_UPNP_SERIAL: "1234",
},
),
)

assert result["type"] == "form"
Expand All @@ -349,7 +353,11 @@ async def test_bridge_ssdp_discover_other_bridge(hass):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={ssdp.ATTR_UPNP_MANUFACTURER_URL: "http://www.notphilips.com"},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
upnp={ssdp.ATTR_UPNP_MANUFACTURER_URL: "http://www.notphilips.com"},
),
)

assert result["type"] == "abort"
Expand All @@ -361,12 +369,16 @@ async def test_bridge_ssdp_emulated_hue(hass):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_FRIENDLY_NAME: "Home Assistant Bridge",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location="http://0.0.0.0/",
upnp={
ssdp.ATTR_UPNP_FRIENDLY_NAME: "Home Assistant Bridge",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
),
)

assert result["type"] == "abort"
Expand All @@ -378,10 +390,14 @@ async def test_bridge_ssdp_missing_location(hass):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
upnp={
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
),
)

assert result["type"] == "abort"
Expand All @@ -393,10 +409,14 @@ async def test_bridge_ssdp_missing_serial(hass):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location="http://0.0.0.0/",
upnp={
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
},
),
)

assert result["type"] == "abort"
Expand All @@ -408,12 +428,16 @@ async def test_bridge_ssdp_espalexa(hass):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_FRIENDLY_NAME: "Espalexa (0.0.0.0)",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location="http://0.0.0.0/",
upnp={
ssdp.ATTR_UPNP_FRIENDLY_NAME: "Espalexa (0.0.0.0)",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
),
)

assert result["type"] == "abort"
Expand All @@ -430,11 +454,15 @@ async def test_bridge_ssdp_already_configured(hass, aioclient_mock):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location="http://0.0.0.0/",
upnp={
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "1234",
},
),
)

assert result["type"] == "abort"
Expand Down Expand Up @@ -587,11 +615,15 @@ async def test_ssdp_discovery_update_configuration(hass, aioclient_mock):
result = await hass.config_entries.flow.async_init(
const.DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
ssdp.ATTR_SSDP_LOCATION: "http://1.1.1.1/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "aabbccddeeff",
},
data=ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location="http://1.1.1.1/",
upnp={
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL[0],
ssdp.ATTR_UPNP_SERIAL: "aabbccddeeff",
},
),
)

assert result["type"] == "abort"
Expand Down
Loading