Migrate the Lyngdorf select platform to the lyngdorf 2.0 API (#180703)

This commit is contained in:
Alex Fishlock
2026-08-30 16:43:32 +02:00
committed by GitHub
parent 235a9b3e00
commit 13b0bf04bb
3 changed files with 42 additions and 12 deletions
+12 -9
View File
@@ -1,10 +1,10 @@
"""Select platform for Lyngdorf integration."""
from collections.abc import Callable
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING, override
from lyngdorf.device import Receiver
from lyngdorf import LyngdorfReceiver
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.core import HomeAssistant
@@ -21,9 +21,10 @@ PARALLEL_UPDATES = 1
class LyngdorfSelectEntityDescription(SelectEntityDescription):
"""Describe a Lyngdorf select entity."""
current_option_fn: Callable[[Receiver], str | None]
options_fn: Callable[[Receiver], list[str]]
select_option_fn: Callable[[Receiver, str], None]
current_option_fn: Callable[[LyngdorfReceiver], str | None]
options_fn: Callable[[LyngdorfReceiver], list[str]]
# None on the pinned library, a coroutine on 2.x: await whichever it is.
select_option_fn: Callable[[LyngdorfReceiver, str], Awaitable[None] | None]
SELECT_ENTITIES: tuple[LyngdorfSelectEntityDescription, ...] = (
@@ -31,14 +32,14 @@ SELECT_ENTITIES: tuple[LyngdorfSelectEntityDescription, ...] = (
key="room_perfect_position",
translation_key="room_perfect_position",
current_option_fn=lambda r: r.room_perfect_position,
options_fn=lambda r: r.available_room_perfect_positions,
options_fn=lambda r: r.room_perfect_positions,
select_option_fn=lambda r, o: r.set_room_perfect_position(o),
),
LyngdorfSelectEntityDescription(
key="voicing",
translation_key="voicing",
current_option_fn=lambda r: r.voicing,
options_fn=lambda r: r.available_voicings,
options_fn=lambda r: r.voicings,
select_option_fn=lambda r, o: r.set_voicing(o),
),
)
@@ -67,7 +68,7 @@ class LyngdorfSelect(LyngdorfEntity, SelectEntity):
def __init__(
self,
receiver: Receiver,
receiver: LyngdorfReceiver,
config_entry: LyngdorfConfigEntry,
device_info: DeviceInfo,
description: LyngdorfSelectEntityDescription,
@@ -94,4 +95,6 @@ class LyngdorfSelect(LyngdorfEntity, SelectEntity):
@override
async def async_select_option(self, option: str) -> None:
"""Set the selected option."""
self.entity_description.select_option_fn(self._receiver, option)
result = self.entity_description.select_option_fn(self._receiver, option)
if result is not None:
await result
+5
View File
@@ -115,8 +115,13 @@ def mock_receiver(mock_create_receiver: MagicMock) -> MagicMock:
receiver.max_volume = 0.0
receiver.room_perfect_position = "Focus 1"
receiver.available_room_perfect_positions = ["Global", "Focus 1"]
receiver.room_perfect_positions = ["Global", "Focus 1"]
receiver.voicing = "Neutral"
receiver.available_voicings = ["Neutral", "Music", "Movie"]
receiver.voicings = ["Neutral", "Music", "Movie"]
# Sync on the pinned library: they return None rather than a coroutine.
receiver.set_voicing.return_value = None
receiver.set_room_perfect_position.return_value = None
receiver.lipsync = None
receiver.lipsync_range = NumericRange(0, 500, 1)
for _t in ("bass", "treble"):
+25 -3
View File
@@ -1,6 +1,6 @@
"""Tests for the Lyngdorf select platform."""
from unittest.mock import MagicMock
from unittest.mock import AsyncMock, MagicMock
import pytest
from syrupy.assertion import SnapshotAssertion
@@ -46,7 +46,7 @@ async def test_room_perfect_select_option(
) -> None:
"""Test selecting a RoomPerfect position."""
mock_receiver.room_perfect_position = "focus"
mock_receiver.available_room_perfect_positions = ["focus", "global"]
mock_receiver.room_perfect_positions = ["focus", "global"]
notify_receiver_update(mock_receiver)
await hass.async_block_till_done()
@@ -64,6 +64,28 @@ async def test_room_perfect_select_option(
mock_receiver.set_room_perfect_position.assert_called_once_with("global")
async def test_select_option_awaits_an_awaitable_setter(
hass: HomeAssistant,
init_integration: MockConfigEntry,
mock_receiver: MagicMock,
) -> None:
"""Test a setter that returns a coroutine is awaited rather than dropped."""
mock_receiver.set_voicing = AsyncMock()
mock_receiver.voicing = "Neutral"
mock_receiver.voicings = ["Neutral", "Music", "Movie"]
notify_receiver_update(mock_receiver)
await hass.async_block_till_done()
await hass.services.async_call(
SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
{ATTR_ENTITY_ID: VOICING_ENTITY_ID, ATTR_OPTION: "Music"},
blocking=True,
)
mock_receiver.set_voicing.assert_awaited_once_with("Music")
async def test_voicing_select_option(
hass: HomeAssistant,
init_integration: MockConfigEntry,
@@ -71,7 +93,7 @@ async def test_voicing_select_option(
) -> None:
"""Test selecting a voicing."""
mock_receiver.voicing = "Neutral"
mock_receiver.available_voicings = ["Neutral", "Music", "Movie"]
mock_receiver.voicings = ["Neutral", "Music", "Movie"]
notify_receiver_update(mock_receiver)
await hass.async_block_till_done()