Config-flow for DLNA-DMR integration (#55267)
* Modernize dlna_dmr component: configflow, test, types
* Support config-flow with ssdp discovery
* Add unit tests
* Enforce strict typing
* Gracefully handle network devices (dis)appearing
* Fix Aiohttp mock response headers type to match actual response class
* Fixes from code review
* Fixes from code review
* Import device config in flow if unavailable at hass start
* Support SSDP advertisements
* Ignore bad BOOTID, fix ssdp:byebye handling
* Only listen for events on interface connected to device
* Release all listeners when entities are removed
* Warn about deprecated dlna_dmr configuration
* Use sublogger for dlna_dmr.config_flow for easier filtering
* Tests for dlna_dmr.data module
* Rewrite DMR tests for HA style
* Fix DMR strings: "Digital Media *Renderer*"
* Update DMR entity state and device info when changed
* Replace deprecated async_upnp_client State with TransportState
* supported_features are dynamic, based on current device state
* Cleanup fully when subscription fails
* Log warnings when device connection fails unexpectedly
* Set PARALLEL_UPDATES to unlimited
* Fix spelling
* Fixes from code review
* Simplify has & can checks to just can, which includes has
* Treat transitioning state as playing (not idle) to reduce UI jerking
* Test if device is usable
* Handle ssdp:update messages properly
* Fix _remove_ssdp_callbacks being shared by all DlnaDmrEntity instances
* Fix tests for transitioning state
* Mock DmrDevice.is_profile_device (added to support embedded devices)
* Use ST & NT SSDP headers to find DMR devices, not deviceType
The deviceType is extracted from the device's description XML, and will not
be what we want when dealing with embedded devices.
* Use UDN from SSDP headers, not device description, as unique_id
The SSDP headers have the UDN of the embedded device that we're interested
in, whereas the device description (`ATTR_UPNP_UDN`) field will always be
for the root device.
* Fix DMR string English localization
* Test config flow with UDN from SSDP headers
* Bump async-upnp-client==0.22.1, fix flake8 error
* fix test for remapping
* DMR HA Device connections based on root and embedded UDN
* DmrDevice's UpnpDevice is now named profile_device
* Use device type from SSDP headers, not device description
* Mark dlna_dmr constants as Final
* Use embedded device UDN and type for unique ID when connected via URL
* More informative connection error messages
* Also match SSDP messages on NT headers
The NT header is to ssdp:alive messages what ST is to M-SEARCH responses.
* Bump async-upnp-client==0.22.2
* fix merge
* Bump async-upnp-client==0.22.3
Co-authored-by: Steven Looman <steven.looman@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2021-09-27 20:47:01 +00:00
|
|
|
"""Fixtures for DLNA tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from collections.abc import Iterable
|
|
|
|
from socket import AddressFamily # pylint: disable=no-name-in-module
|
|
|
|
from unittest.mock import Mock, create_autospec, patch, seal
|
|
|
|
|
|
|
|
from async_upnp_client import UpnpDevice, UpnpFactory
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from homeassistant.components.dlna_dmr.const import DOMAIN as DLNA_DOMAIN
|
|
|
|
from homeassistant.components.dlna_dmr.data import DlnaDmrData
|
|
|
|
from homeassistant.const import CONF_DEVICE_ID, CONF_TYPE, CONF_URL
|
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
|
|
|
|
MOCK_DEVICE_BASE_URL = "http://192.88.99.4"
|
|
|
|
MOCK_DEVICE_LOCATION = MOCK_DEVICE_BASE_URL + "/dmr_description.xml"
|
|
|
|
MOCK_DEVICE_NAME = "Test Renderer Device"
|
|
|
|
MOCK_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaRenderer:1"
|
|
|
|
MOCK_DEVICE_UDN = "uuid:7cc6da13-7f5d-4ace-9729-58b275c52f1e"
|
|
|
|
MOCK_DEVICE_USN = f"{MOCK_DEVICE_UDN}::{MOCK_DEVICE_TYPE}"
|
|
|
|
|
|
|
|
LOCAL_IP = "192.88.99.1"
|
|
|
|
EVENT_CALLBACK_URL = "http://192.88.99.1/notify"
|
|
|
|
|
|
|
|
NEW_DEVICE_LOCATION = "http://192.88.99.7" + "/dmr_description.xml"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def domain_data_mock(hass: HomeAssistant) -> Iterable[Mock]:
|
|
|
|
"""Mock the global data used by this component.
|
|
|
|
|
|
|
|
This includes network clients and library object factories. Mocking it
|
|
|
|
prevents network use.
|
|
|
|
"""
|
|
|
|
domain_data = create_autospec(DlnaDmrData, instance=True)
|
|
|
|
domain_data.upnp_factory = create_autospec(
|
|
|
|
UpnpFactory, spec_set=True, instance=True
|
|
|
|
)
|
|
|
|
|
|
|
|
upnp_device = create_autospec(UpnpDevice, instance=True)
|
|
|
|
upnp_device.name = MOCK_DEVICE_NAME
|
|
|
|
upnp_device.udn = MOCK_DEVICE_UDN
|
|
|
|
upnp_device.device_url = MOCK_DEVICE_LOCATION
|
|
|
|
upnp_device.device_type = "urn:schemas-upnp-org:device:MediaRenderer:1"
|
|
|
|
upnp_device.available = True
|
|
|
|
upnp_device.parent_device = None
|
|
|
|
upnp_device.root_device = upnp_device
|
|
|
|
upnp_device.all_devices = [upnp_device]
|
|
|
|
seal(upnp_device)
|
|
|
|
domain_data.upnp_factory.async_create_device.return_value = upnp_device
|
|
|
|
|
|
|
|
with patch.dict(hass.data, {DLNA_DOMAIN: domain_data}):
|
|
|
|
yield domain_data
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2021-10-07 20:14:00 +00:00
|
|
|
def config_entry_mock() -> MockConfigEntry:
|
Config-flow for DLNA-DMR integration (#55267)
* Modernize dlna_dmr component: configflow, test, types
* Support config-flow with ssdp discovery
* Add unit tests
* Enforce strict typing
* Gracefully handle network devices (dis)appearing
* Fix Aiohttp mock response headers type to match actual response class
* Fixes from code review
* Fixes from code review
* Import device config in flow if unavailable at hass start
* Support SSDP advertisements
* Ignore bad BOOTID, fix ssdp:byebye handling
* Only listen for events on interface connected to device
* Release all listeners when entities are removed
* Warn about deprecated dlna_dmr configuration
* Use sublogger for dlna_dmr.config_flow for easier filtering
* Tests for dlna_dmr.data module
* Rewrite DMR tests for HA style
* Fix DMR strings: "Digital Media *Renderer*"
* Update DMR entity state and device info when changed
* Replace deprecated async_upnp_client State with TransportState
* supported_features are dynamic, based on current device state
* Cleanup fully when subscription fails
* Log warnings when device connection fails unexpectedly
* Set PARALLEL_UPDATES to unlimited
* Fix spelling
* Fixes from code review
* Simplify has & can checks to just can, which includes has
* Treat transitioning state as playing (not idle) to reduce UI jerking
* Test if device is usable
* Handle ssdp:update messages properly
* Fix _remove_ssdp_callbacks being shared by all DlnaDmrEntity instances
* Fix tests for transitioning state
* Mock DmrDevice.is_profile_device (added to support embedded devices)
* Use ST & NT SSDP headers to find DMR devices, not deviceType
The deviceType is extracted from the device's description XML, and will not
be what we want when dealing with embedded devices.
* Use UDN from SSDP headers, not device description, as unique_id
The SSDP headers have the UDN of the embedded device that we're interested
in, whereas the device description (`ATTR_UPNP_UDN`) field will always be
for the root device.
* Fix DMR string English localization
* Test config flow with UDN from SSDP headers
* Bump async-upnp-client==0.22.1, fix flake8 error
* fix test for remapping
* DMR HA Device connections based on root and embedded UDN
* DmrDevice's UpnpDevice is now named profile_device
* Use device type from SSDP headers, not device description
* Mark dlna_dmr constants as Final
* Use embedded device UDN and type for unique ID when connected via URL
* More informative connection error messages
* Also match SSDP messages on NT headers
The NT header is to ssdp:alive messages what ST is to M-SEARCH responses.
* Bump async-upnp-client==0.22.2
* fix merge
* Bump async-upnp-client==0.22.3
Co-authored-by: Steven Looman <steven.looman@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2021-09-27 20:47:01 +00:00
|
|
|
"""Mock a config entry for this platform."""
|
|
|
|
mock_entry = MockConfigEntry(
|
|
|
|
unique_id=MOCK_DEVICE_UDN,
|
|
|
|
domain=DLNA_DOMAIN,
|
|
|
|
data={
|
|
|
|
CONF_URL: MOCK_DEVICE_LOCATION,
|
|
|
|
CONF_DEVICE_ID: MOCK_DEVICE_UDN,
|
|
|
|
CONF_TYPE: MOCK_DEVICE_TYPE,
|
|
|
|
},
|
|
|
|
title=MOCK_DEVICE_NAME,
|
|
|
|
options={},
|
|
|
|
)
|
2021-10-07 20:14:00 +00:00
|
|
|
return mock_entry
|
Config-flow for DLNA-DMR integration (#55267)
* Modernize dlna_dmr component: configflow, test, types
* Support config-flow with ssdp discovery
* Add unit tests
* Enforce strict typing
* Gracefully handle network devices (dis)appearing
* Fix Aiohttp mock response headers type to match actual response class
* Fixes from code review
* Fixes from code review
* Import device config in flow if unavailable at hass start
* Support SSDP advertisements
* Ignore bad BOOTID, fix ssdp:byebye handling
* Only listen for events on interface connected to device
* Release all listeners when entities are removed
* Warn about deprecated dlna_dmr configuration
* Use sublogger for dlna_dmr.config_flow for easier filtering
* Tests for dlna_dmr.data module
* Rewrite DMR tests for HA style
* Fix DMR strings: "Digital Media *Renderer*"
* Update DMR entity state and device info when changed
* Replace deprecated async_upnp_client State with TransportState
* supported_features are dynamic, based on current device state
* Cleanup fully when subscription fails
* Log warnings when device connection fails unexpectedly
* Set PARALLEL_UPDATES to unlimited
* Fix spelling
* Fixes from code review
* Simplify has & can checks to just can, which includes has
* Treat transitioning state as playing (not idle) to reduce UI jerking
* Test if device is usable
* Handle ssdp:update messages properly
* Fix _remove_ssdp_callbacks being shared by all DlnaDmrEntity instances
* Fix tests for transitioning state
* Mock DmrDevice.is_profile_device (added to support embedded devices)
* Use ST & NT SSDP headers to find DMR devices, not deviceType
The deviceType is extracted from the device's description XML, and will not
be what we want when dealing with embedded devices.
* Use UDN from SSDP headers, not device description, as unique_id
The SSDP headers have the UDN of the embedded device that we're interested
in, whereas the device description (`ATTR_UPNP_UDN`) field will always be
for the root device.
* Fix DMR string English localization
* Test config flow with UDN from SSDP headers
* Bump async-upnp-client==0.22.1, fix flake8 error
* fix test for remapping
* DMR HA Device connections based on root and embedded UDN
* DmrDevice's UpnpDevice is now named profile_device
* Use device type from SSDP headers, not device description
* Mark dlna_dmr constants as Final
* Use embedded device UDN and type for unique ID when connected via URL
* More informative connection error messages
* Also match SSDP messages on NT headers
The NT header is to ssdp:alive messages what ST is to M-SEARCH responses.
* Bump async-upnp-client==0.22.2
* fix merge
* Bump async-upnp-client==0.22.3
Co-authored-by: Steven Looman <steven.looman@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2021-09-27 20:47:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def dmr_device_mock(domain_data_mock: Mock) -> Iterable[Mock]:
|
|
|
|
"""Mock the async_upnp_client DMR device, initially connected."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.dlna_dmr.media_player.DmrDevice", autospec=True
|
|
|
|
) as constructor:
|
|
|
|
device = constructor.return_value
|
|
|
|
device.on_event = None
|
|
|
|
device.profile_device = (
|
|
|
|
domain_data_mock.upnp_factory.async_create_device.return_value
|
|
|
|
)
|
|
|
|
device.media_image_url = "http://192.88.99.20:8200/AlbumArt/2624-17620.jpg"
|
|
|
|
device.udn = "device_udn"
|
|
|
|
device.manufacturer = "device_manufacturer"
|
|
|
|
device.model_name = "device_model_name"
|
|
|
|
device.name = "device_name"
|
|
|
|
|
|
|
|
yield device
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def ssdp_scanner_mock() -> Iterable[Mock]:
|
|
|
|
"""Mock the SSDP module."""
|
|
|
|
with patch("homeassistant.components.ssdp.Scanner", autospec=True) as mock_scanner:
|
|
|
|
reg_callback = mock_scanner.return_value.async_register_callback
|
|
|
|
reg_callback.return_value = Mock(return_value=None)
|
|
|
|
yield mock_scanner.return_value
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def async_get_local_ip_mock() -> Iterable[Mock]:
|
|
|
|
"""Mock the async_get_local_ip utility function to prevent network access."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.dlna_dmr.media_player.async_get_local_ip",
|
|
|
|
autospec=True,
|
|
|
|
) as func:
|
|
|
|
func.return_value = AddressFamily.AF_INET, LOCAL_IP
|
|
|
|
yield func
|
2021-10-04 18:25:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def dlna_dmr_mock_get_source_ip(mock_get_source_ip):
|
|
|
|
"""Mock network util's async_get_source_ip."""
|