mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 07:51:46 -05:00
Migrate Lyngdorf core to the lyngdorf 2.0 API (#180529)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from lyngdorf.device import async_create_receiver, lookup_receiver_model
|
||||
from lyngdorf import LyngdorfReceiver, create_receiver, lookup_model
|
||||
|
||||
from homeassistant.const import CONF_HOST, CONF_MODEL, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
@@ -37,14 +37,14 @@ async def async_setup_entry(
|
||||
hass: HomeAssistant, config_entry: LyngdorfConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Lyngdorf from a config entry."""
|
||||
lyngdorf_model = lookup_receiver_model(config_entry.data[CONF_MODEL])
|
||||
lyngdorf_model = lookup_model(config_entry.data[CONF_MODEL])
|
||||
assert lyngdorf_model is not None
|
||||
|
||||
try:
|
||||
receiver = await async_create_receiver(
|
||||
receiver: LyngdorfReceiver = await create_receiver(
|
||||
config_entry.data[CONF_HOST], lyngdorf_model
|
||||
)
|
||||
await receiver.async_connect()
|
||||
await receiver.connect()
|
||||
except TimeoutError as err:
|
||||
raise ConfigEntryNotReady(
|
||||
translation_domain=DOMAIN,
|
||||
@@ -72,7 +72,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
zone_b_device_info: DeviceInfo | None = None
|
||||
if lyngdorf_model.has_zone_b_feature():
|
||||
if receiver.zone_b is not None:
|
||||
# Register the main device up front so Zone B can resolve its via_device_id.
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
@@ -113,16 +113,13 @@ async def async_setup_entry(
|
||||
else:
|
||||
_LOGGER.info("Lyngdorf %s is unavailable", host)
|
||||
|
||||
receiver.register_notification_callback(_log_availability_change)
|
||||
config_entry.async_on_unload(
|
||||
lambda: receiver.un_register_notification_callback(_log_availability_change)
|
||||
)
|
||||
config_entry.async_on_unload(receiver.on_change(_log_availability_change))
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
async def _async_disconnect(event: Event) -> None:
|
||||
"""Disconnect from receiver."""
|
||||
await receiver.async_disconnect()
|
||||
await receiver.disconnect()
|
||||
|
||||
config_entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_disconnect)
|
||||
@@ -139,5 +136,5 @@ async def async_unload_entry(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
if unload_ok:
|
||||
await config_entry.runtime_data.receiver.async_disconnect()
|
||||
await config_entry.runtime_data.receiver.disconnect()
|
||||
return unload_ok
|
||||
|
||||
@@ -4,11 +4,12 @@ import logging
|
||||
from typing import Any, override
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from lyngdorf.const import LyngdorfModel
|
||||
from lyngdorf.device import (
|
||||
async_find_receiver_model,
|
||||
async_get_device_serial,
|
||||
lookup_receiver_model,
|
||||
from lyngdorf import (
|
||||
LyngdorfModel,
|
||||
discover_model,
|
||||
discover_ssdp_location,
|
||||
fetch_device_serial,
|
||||
lookup_model,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -89,7 +90,7 @@ class LyngdorfFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
async def _async_probe(self, host: str) -> tuple[LyngdorfModel, str]:
|
||||
"""Return the model and serial of the device at a host."""
|
||||
try:
|
||||
model = await async_find_receiver_model(host)
|
||||
model = await discover_model(host)
|
||||
except TimeoutError as err:
|
||||
raise TimeoutConnect from err
|
||||
except OSError as err:
|
||||
@@ -98,7 +99,8 @@ class LyngdorfFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
raise UnsupportedModel
|
||||
|
||||
try:
|
||||
serial = await async_get_device_serial(host)
|
||||
location = await discover_ssdp_location(host)
|
||||
serial = await fetch_device_serial(location) if location else None
|
||||
except TimeoutError as err:
|
||||
raise TimeoutConnect from err
|
||||
except OSError as err:
|
||||
@@ -165,7 +167,7 @@ class LyngdorfFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
assert self._host
|
||||
try:
|
||||
model = await async_find_receiver_model(self._host)
|
||||
model = await discover_model(self._host)
|
||||
except TimeoutError, OSError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
if not model:
|
||||
@@ -226,7 +228,7 @@ class LyngdorfFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
raise AbortFlow("cannot_connect")
|
||||
|
||||
device_model_name = discovery_info.upnp.get(ATTR_UPNP_MODEL_NAME) or ""
|
||||
if not (model := lookup_receiver_model(device_model_name)):
|
||||
if not (model := lookup_model(device_model_name)):
|
||||
_LOGGER.warning(
|
||||
"SSDP discovered device with unrecognized model name %r at %s",
|
||||
device_model_name,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from typing import override
|
||||
|
||||
from lyngdorf.device import Receiver
|
||||
from lyngdorf import LyngdorfReceiver
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -16,7 +16,7 @@ class LyngdorfEntity(Entity):
|
||||
_attr_available = True
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, receiver: Receiver, device_info: DeviceInfo) -> None:
|
||||
def __init__(self, receiver: LyngdorfReceiver, device_info: DeviceInfo) -> None:
|
||||
"""Initialize the entity."""
|
||||
self._receiver = receiver
|
||||
self._attr_device_info = device_info
|
||||
@@ -25,15 +25,9 @@ class LyngdorfEntity(Entity):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register notification callback when added to hass."""
|
||||
await super().async_added_to_hass()
|
||||
self._receiver.register_notification_callback(self._handle_receiver_update)
|
||||
self.async_on_remove(self._receiver.on_change(self._handle_receiver_update))
|
||||
self._update_availability()
|
||||
|
||||
@override
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Unregister notification callback when removed from hass."""
|
||||
await super().async_will_remove_from_hass()
|
||||
self._receiver.un_register_notification_callback(self._handle_receiver_update)
|
||||
|
||||
@callback
|
||||
def _handle_receiver_update(self) -> None:
|
||||
"""Handle receiver updates."""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from lyngdorf.device import Receiver
|
||||
from lyngdorf import LyngdorfReceiver
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -12,7 +12,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
||||
class LyngdorfRuntimeData:
|
||||
"""Runtime data for Lyngdorf integration."""
|
||||
|
||||
receiver: Receiver
|
||||
receiver: LyngdorfReceiver
|
||||
device_info: DeviceInfo
|
||||
zone_b_device_info: DeviceInfo | None
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ from __future__ import annotations
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||
|
||||
from lyngdorf.const import LyngdorfModel
|
||||
from lyngdorf.device import Receiver
|
||||
from lyngdorf import LyngdorfModel, LyngdorfReceiver
|
||||
from lyngdorf.models.base import NumericRange
|
||||
from lyngdorf.remote import RemoteKey
|
||||
import pytest
|
||||
@@ -58,10 +57,8 @@ def mock_setup_entry() -> Generator[None]:
|
||||
@pytest.fixture
|
||||
def mock_receiver() -> Generator[MagicMock]:
|
||||
"""Return a mocked Lyngdorf receiver."""
|
||||
with patch(
|
||||
"homeassistant.components.lyngdorf.async_create_receiver"
|
||||
) as create_mock:
|
||||
receiver = MagicMock(spec=Receiver)
|
||||
with patch("homeassistant.components.lyngdorf.create_receiver") as create_mock:
|
||||
receiver = MagicMock(spec=LyngdorfReceiver)
|
||||
receiver.name = "Mock Lyngdorf"
|
||||
receiver.connected = True
|
||||
receiver.has_remote_keys = True
|
||||
@@ -148,19 +145,25 @@ def mock_receiver() -> Generator[MagicMock]:
|
||||
|
||||
@pytest.fixture
|
||||
def mock_get_device_serial() -> Generator[AsyncMock]:
|
||||
"""Return a mocked async_get_device_serial function."""
|
||||
with patch(
|
||||
"homeassistant.components.lyngdorf.config_flow.async_get_device_serial",
|
||||
new=AsyncMock(return_value="0050c27c76b2"),
|
||||
) as serial_mock:
|
||||
"""Return a mocked fetch_device_serial function."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.lyngdorf.config_flow.discover_ssdp_location",
|
||||
new=AsyncMock(return_value="http://127.0.0.1:8080/desc.xml"),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.lyngdorf.config_flow.fetch_device_serial",
|
||||
new=AsyncMock(return_value="0050c27c76b2"),
|
||||
) as serial_mock,
|
||||
):
|
||||
yield serial_mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_find_receiver_model() -> Generator[AsyncMock]:
|
||||
"""Return a mocked async_find_receiver_model function."""
|
||||
"""Return a mocked discover_model function."""
|
||||
with patch(
|
||||
"homeassistant.components.lyngdorf.config_flow.async_find_receiver_model",
|
||||
"homeassistant.components.lyngdorf.config_flow.discover_model",
|
||||
new=AsyncMock(return_value=LyngdorfModel.MP_60),
|
||||
) as find_mock:
|
||||
yield find_mock
|
||||
@@ -168,7 +171,7 @@ def mock_find_receiver_model() -> Generator[AsyncMock]:
|
||||
|
||||
def notify_receiver_update(receiver: MagicMock) -> None:
|
||||
"""Fire every notification callback the entities registered."""
|
||||
for call in receiver.register_notification_callback.call_args_list:
|
||||
for call in receiver.on_change.call_args_list:
|
||||
call.args[0]()
|
||||
|
||||
|
||||
@@ -195,7 +198,7 @@ async def init_integration(
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
with (
|
||||
patch("homeassistant.components.lyngdorf.lookup_receiver_model") as lookup,
|
||||
patch("homeassistant.components.lyngdorf.lookup_model") as lookup,
|
||||
patch("homeassistant.components.lyngdorf.PLATFORMS", platforms),
|
||||
):
|
||||
lookup.return_value = LyngdorfModel.MP_60
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from lyngdorf.const import LyngdorfModel
|
||||
from lyngdorf import LyngdorfModel
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.lyngdorf.const import CONF_SERIAL_NUMBER, DOMAIN
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from lyngdorf.const import LyngdorfModel
|
||||
from lyngdorf import LyngdorfModel
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.lyngdorf.const import CONF_SERIAL_NUMBER, DOMAIN
|
||||
@@ -31,10 +31,10 @@ async def test_setup_entry_connection_failures(
|
||||
) -> None:
|
||||
"""Test setup retries when connecting to the receiver fails."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_receiver.async_connect.side_effect = exc
|
||||
mock_receiver.connect.side_effect = exc
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lyngdorf.lookup_receiver_model",
|
||||
"homeassistant.components.lyngdorf.lookup_model",
|
||||
return_value=LyngdorfModel.MP_60,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
@@ -54,7 +54,7 @@ async def test_receiver_disconnects_on_hass_stop(
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_receiver.async_disconnect.assert_awaited_once()
|
||||
mock_receiver.disconnect.assert_awaited_once()
|
||||
|
||||
|
||||
async def test_unload_entry(
|
||||
@@ -69,6 +69,23 @@ async def test_unload_entry(
|
||||
assert init_integration.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_unload_releases_receiver_subscriptions(
|
||||
hass: HomeAssistant,
|
||||
init_integration: MockConfigEntry,
|
||||
mock_receiver: MagicMock,
|
||||
) -> None:
|
||||
"""Test every receiver subscription is released when the entry unloads."""
|
||||
unsubscribe = mock_receiver.on_change.return_value
|
||||
registered = mock_receiver.on_change.call_count
|
||||
assert registered > 0
|
||||
assert unsubscribe.call_count == 0
|
||||
|
||||
assert await hass.config_entries.async_unload(init_integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert unsubscribe.call_count == registered
|
||||
|
||||
|
||||
async def test_zone_b_via_device_id(
|
||||
init_integration: MockConfigEntry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
@@ -115,7 +132,7 @@ async def test_mac_connection_registered_when_serial_is_mac(
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lyngdorf.lookup_receiver_model",
|
||||
"homeassistant.components.lyngdorf.lookup_model",
|
||||
return_value=LyngdorfModel.MP_60,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
@@ -131,17 +148,18 @@ async def test_mac_connection_registered_when_serial_is_mac(
|
||||
assert mac_connections == expected_mac_connections
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_receiver")
|
||||
async def test_no_zone_b_device_for_model_without_zone_b(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_receiver: MagicMock,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test no Zone B device is created for a model without Zone B."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_receiver.zone_b = None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lyngdorf.lookup_receiver_model",
|
||||
"homeassistant.components.lyngdorf.lookup_model",
|
||||
return_value=LyngdorfModel.TDAI_3400,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
|
||||
@@ -117,17 +117,18 @@ async def test_entities(
|
||||
await snapshot_platform(hass, entity_registry, snapshot, init_integration.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_receiver")
|
||||
async def test_no_zone_b_entity_for_model_without_zone_b(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_receiver: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test no Zone B media player entity is created for a model without Zone B."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_receiver.zone_b = None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lyngdorf.lookup_receiver_model",
|
||||
"homeassistant.components.lyngdorf.lookup_model",
|
||||
return_value=LyngdorfModel.TDAI_3400,
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
|
||||
@@ -144,7 +144,7 @@ async def test_entities_absent_for_controls_the_model_lacks(
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.lyngdorf.lookup_receiver_model",
|
||||
"homeassistant.components.lyngdorf.lookup_model",
|
||||
return_value=LyngdorfModel.MP_60,
|
||||
),
|
||||
patch("homeassistant.components.lyngdorf.PLATFORMS", [Platform.NUMBER]),
|
||||
|
||||
@@ -151,7 +151,7 @@ async def test_no_entity_for_model_without_remote_keys(
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.lyngdorf.lookup_receiver_model",
|
||||
"homeassistant.components.lyngdorf.lookup_model",
|
||||
return_value=LyngdorfModel.TDAI_3400,
|
||||
),
|
||||
patch("homeassistant.components.lyngdorf.PLATFORMS", [Platform.REMOTE]),
|
||||
|
||||
Reference in New Issue
Block a user