From 2ea537e1a61afba929a9756a7072b6d6bf677f27 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Wed, 27 Oct 2021 05:19:18 +1100 Subject: [PATCH] dlna_dmr will gracefully handle device's rejection of subscription attempt (#58451) --- .../components/dlna_dmr/media_player.py | 7 +++- .../components/dlna_dmr/test_media_player.py | 33 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index e91ea1e830e..2835117e57c 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -7,8 +7,9 @@ from datetime import datetime, timedelta import functools from typing import Any, Callable, TypeVar, cast -from async_upnp_client import UpnpError, UpnpService, UpnpStateVariable +from async_upnp_client import UpnpService, UpnpStateVariable from async_upnp_client.const import NotificationSubType +from async_upnp_client.exceptions import UpnpError, UpnpResponseError from async_upnp_client.profiles.dlna import DmrDevice, TransportState from async_upnp_client.utils import async_get_local_ip import voluptuous as vol @@ -347,6 +348,10 @@ class DlnaDmrEntity(MediaPlayerEntity): try: self._device.on_event = self._on_event await self._device.async_subscribe_services(auto_resubscribe=True) + except UpnpResponseError as err: + # Device rejected subscription request. This is OK, variables + # will be polled instead. + _LOGGER.debug("Device rejected subscription: %r", err) except UpnpError as err: # Don't leave the device half-constructed self._device.on_event = None diff --git a/tests/components/dlna_dmr/test_media_player.py b/tests/components/dlna_dmr/test_media_player.py index 2d02c8f1a8f..e12c23535fa 100644 --- a/tests/components/dlna_dmr/test_media_player.py +++ b/tests/components/dlna_dmr/test_media_player.py @@ -8,7 +8,11 @@ from types import MappingProxyType from typing import Any from unittest.mock import ANY, DEFAULT, Mock, patch -from async_upnp_client.exceptions import UpnpConnectionError, UpnpError +from async_upnp_client.exceptions import ( + UpnpConnectionError, + UpnpError, + UpnpResponseError, +) from async_upnp_client.profiles.dlna import TransportState import pytest @@ -357,6 +361,33 @@ async def test_event_subscribe_failure( } +async def test_event_subscribe_rejected( + hass: HomeAssistant, + config_entry_mock: MockConfigEntry, + dmr_device_mock: Mock, +) -> None: + """Test _device_connect continues when the device rejects a subscription. + + Device state will instead be obtained via polling in async_update. + """ + dmr_device_mock.async_subscribe_services.side_effect = UpnpResponseError(501) + + mock_entity_id = await setup_mock_component(hass, config_entry_mock) + mock_state = hass.states.get(mock_entity_id) + assert mock_state is not None + + # Device should be connected + assert mock_state.state == ha_const.STATE_IDLE + + # Device should not be unsubscribed + dmr_device_mock.async_unsubscribe_services.assert_not_awaited() + + # Unload config entry to clean up + assert await hass.config_entries.async_remove(config_entry_mock.entry_id) == { + "require_restart": False + } + + async def test_available_device( hass: HomeAssistant, dmr_device_mock: Mock,