Ensure ssdp can callback messages that do not have an ST (#51436)

* Ensure ssdp can callback messages that do not have an ST

Sonos sends unsolicited messages when the device reboots. We want
to capture these to ensure we can recover the subscriptions as soon
as the device reboots

* Update homeassistant/components/ssdp/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
J. Nick Koston 2021-06-04 21:23:51 -10:00 committed by GitHub
parent 909140a7c6
commit 12ac4109f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 107 additions and 10 deletions

View file

@ -478,6 +478,94 @@ async def test_scan_with_registered_callback(hass, aioclient_mock, caplog):
assert "Failed to callback info" in caplog.text
async def test_unsolicited_ssdp_registered_callback(hass, aioclient_mock, caplog):
"""Test matching based on callback can handle unsolicited ssdp traffic without st."""
aioclient_mock.get(
"http://10.6.9.12:1400/xml/device_description.xml",
text="""
<root>
<device>
<deviceType>Paulus</deviceType>
</device>
</root>
""",
)
mock_ssdp_response = {
"location": "http://10.6.9.12:1400/xml/device_description.xml",
"nt": "uuid:RINCON_1111BB963FD801400",
"nts": "ssdp:alive",
"server": "Linux UPnP/1.0 Sonos/63.2-88230 (ZPS12)",
"usn": "uuid:RINCON_1111BB963FD801400",
"x-rincon-household": "Sonos_dfjfkdghjhkjfhkdjfhkd",
"x-rincon-bootseq": "250",
"bootid.upnp.org": "250",
"x-rincon-wifimode": "0",
"x-rincon-variant": "1",
"household.smartspeaker.audio": "Sonos_v3294823948542543534",
}
intergration_callbacks = []
@callback
def _async_intergration_callbacks(info):
intergration_callbacks.append(info)
def _generate_fake_ssdp_listener(*args, **kwargs):
listener = SSDPListener(*args, **kwargs)
async def _async_callback(*_):
await listener.async_callback(mock_ssdp_response)
@callback
def _callback(*_):
hass.async_create_task(listener.async_callback(mock_ssdp_response))
listener.async_start = _async_callback
listener.async_search = _callback
return listener
with patch(
"homeassistant.components.ssdp.SSDPListener",
new=_generate_fake_ssdp_listener,
):
hass.state = CoreState.stopped
assert await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}})
await hass.async_block_till_done()
ssdp.async_register_callback(
hass,
_async_intergration_callbacks,
{"nts": "ssdp:alive", "x-rincon-bootseq": MATCH_ALL},
)
await hass.async_block_till_done()
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
hass.state = CoreState.running
await hass.async_block_till_done()
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
await hass.async_block_till_done()
assert hass.state == CoreState.running
assert (
len(intergration_callbacks) == 2
) # unsolicited callbacks without st are not cached
assert intergration_callbacks[0] == {
"UDN": "uuid:RINCON_1111BB963FD801400",
"bootid.upnp.org": "250",
"deviceType": "Paulus",
"household.smartspeaker.audio": "Sonos_v3294823948542543534",
"nt": "uuid:RINCON_1111BB963FD801400",
"nts": "ssdp:alive",
"ssdp_location": "http://10.6.9.12:1400/xml/device_description.xml",
"ssdp_server": "Linux UPnP/1.0 Sonos/63.2-88230 (ZPS12)",
"ssdp_usn": "uuid:RINCON_1111BB963FD801400",
"x-rincon-bootseq": "250",
"x-rincon-household": "Sonos_dfjfkdghjhkjfhkdjfhkd",
"x-rincon-variant": "1",
"x-rincon-wifimode": "0",
}
assert "Failed to callback info" not in caplog.text
async def test_scan_second_hit(hass, aioclient_mock, caplog):
"""Test matching on second scan."""
aioclient_mock.get(