Send ssdp requests to ipv4 broadcast as well (#52760)

* Send ssdp requests to 255.255.255.255 as well

- This matches pysonos behavior and may fix reports of
  inability to discover some sonos devices
  https://github.com/amelchio/pysonos/blob/master/pysonos/discovery.py#L120

* Update homeassistant/components/ssdp/__init__.py
pull/52895/head
J. Nick Koston 2021-07-11 11:03:48 -10:00 committed by GitHub
parent 0f076610fd
commit 574cb03acc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 14 deletions

View File

@ -29,6 +29,8 @@ from .flow import FlowDispatcher, SSDPFlow
DOMAIN = "ssdp"
SCAN_INTERVAL = timedelta(seconds=60)
IPV4_BROADCAST = IPv4Address("255.255.255.255")
# Attributes for accessing info from SSDP response
ATTR_SSDP_LOCATION = "ssdp_location"
ATTR_SSDP_ST = "ssdp_st"
@ -236,7 +238,20 @@ class Scanner:
async_callback=self._async_process_entry, source_ip=source_ip
)
)
try:
IPv4Address(source_ip)
except ValueError:
continue
# Some sonos devices only seem to respond if we send to the broadcast
# address. This matches pysonos' behavior
# https://github.com/amelchio/pysonos/blob/d4329b4abb657d106394ae69357805269708c996/pysonos/discovery.py#L120
self._ssdp_listeners.append(
SSDPListener(
async_callback=self._async_process_entry,
source_ip=source_ip,
target_ip=IPV4_BROADCAST,
)
)
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.async_stop)
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STARTED, self.flow_dispatcher.async_start

View File

@ -295,15 +295,15 @@ async def test_start_stop_scanner(async_start_mock, async_search_mock, hass):
await hass.async_block_till_done()
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
await hass.async_block_till_done()
assert async_start_mock.call_count == 1
assert async_search_mock.call_count == 1
assert async_start_mock.call_count == 2
assert async_search_mock.call_count == 2
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
await hass.async_block_till_done()
assert async_start_mock.call_count == 1
assert async_search_mock.call_count == 1
assert async_start_mock.call_count == 2
assert async_search_mock.call_count == 2
async def test_unexpected_exception_while_fetching(hass, aioclient_mock, caplog):
@ -459,11 +459,11 @@ async def test_scan_with_registered_callback(hass, aioclient_mock, caplog):
await hass.async_block_till_done()
assert hass.state == CoreState.running
assert len(integration_callbacks) == 3
assert len(integration_callbacks_from_cache) == 3
assert len(integration_match_all_callbacks) == 3
assert len(integration_callbacks) == 5
assert len(integration_callbacks_from_cache) == 5
assert len(integration_match_all_callbacks) == 5
assert len(integration_match_all_not_present_callbacks) == 0
assert len(match_any_callbacks) == 3
assert len(match_any_callbacks) == 5
assert len(not_matching_integration_callbacks) == 0
assert integration_callbacks[0] == {
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
@ -546,7 +546,7 @@ async def test_unsolicited_ssdp_registered_callback(hass, aioclient_mock, caplog
assert hass.state == CoreState.running
assert (
len(integration_callbacks) == 2
len(integration_callbacks) == 4
) # unsolicited callbacks without st are not cached
assert integration_callbacks[0] == {
"UDN": "uuid:RINCON_1111BB963FD801400",
@ -635,7 +635,7 @@ async def test_scan_second_hit(hass, aioclient_mock, caplog):
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
await hass.async_block_till_done()
assert len(integration_callbacks) == 2
assert len(integration_callbacks) == 4
assert integration_callbacks[0] == {
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
ssdp.ATTR_SSDP_EXT: "",
@ -781,7 +781,12 @@ async def test_async_detect_interfaces_setting_empty_route(hass):
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert {create_args[0][1]["source_ip"], create_args[1][1]["source_ip"]} == {
IPv4Address("192.168.1.5"),
IPv6Address("2001:db8::"),
argset = set()
for argmap in create_args:
argset.add((argmap[1].get("source_ip"), argmap[1].get("target_ip")))
assert argset == {
(IPv6Address("2001:db8::"), None),
(IPv4Address("192.168.1.5"), IPv4Address("255.255.255.255")),
(IPv4Address("192.168.1.5"), None),
}