Migrate the Lyngdorf sensor platform to the lyngdorf 2.0 API (#180704)

This commit is contained in:
Alex Fishlock
2026-08-30 16:42:43 +02:00
committed by GitHub
parent d6cc4252fa
commit 235a9b3e00
3 changed files with 78 additions and 17 deletions
+18 -14
View File
@@ -4,7 +4,7 @@ from collections.abc import Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING, override
from lyngdorf.device import Receiver
from lyngdorf import LyngdorfReceiver
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -26,8 +26,8 @@ PARALLEL_UPDATES = 0
class LyngdorfSensorEntityDescription(SensorEntityDescription):
"""Describe a Lyngdorf sensor entity."""
value_fn: Callable[[Receiver], str | None]
options_fn: Callable[[Receiver], list[str]] | None = None
value_fn: Callable[[LyngdorfReceiver], str | None]
options_fn: Callable[[LyngdorfReceiver], list[str]] | None = None
def _known(value: str | None, options: list[str]) -> str | None:
@@ -52,24 +52,24 @@ MAIN_ZONE_SENSORS: tuple[LyngdorfSensorEntityDescription, ...] = (
key="audio_input",
translation_key="audio_input",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda r: _known(r.audio_input, r.available_audio_inputs),
options_fn=lambda r: r.available_audio_inputs,
value_fn=lambda r: _known(r.audio_input, r.audio_inputs),
options_fn=lambda r: r.audio_inputs,
entity_category=EntityCategory.DIAGNOSTIC,
),
LyngdorfSensorEntityDescription(
key="video_input",
translation_key="video_input",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda r: _known(r.video_input, r.available_video_inputs),
options_fn=lambda r: r.available_video_inputs,
value_fn=lambda r: _known(r.video_input, r.video_inputs),
options_fn=lambda r: r.video_inputs,
entity_category=EntityCategory.DIAGNOSTIC,
),
LyngdorfSensorEntityDescription(
key="streaming_source",
translation_key="streaming_source",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda r: _known(r.streaming_source, r.available_stream_types),
options_fn=lambda r: r.available_stream_types,
value_fn=lambda r: _known(r.streaming_source, r.stream_types),
options_fn=lambda r: r.stream_types,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
@@ -79,16 +79,20 @@ ZONE_B_SENSORS: tuple[LyngdorfSensorEntityDescription, ...] = (
key="zone_b_audio_input",
translation_key="zone_b_audio_input",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda r: _known(r.zone_b_audio_input, r.available_audio_inputs),
options_fn=lambda r: r.available_audio_inputs,
value_fn=lambda r: (
_known(zb.audio_input, r.audio_inputs) if (zb := r.zone_b) else None
),
options_fn=lambda r: r.audio_inputs,
entity_category=EntityCategory.DIAGNOSTIC,
),
LyngdorfSensorEntityDescription(
key="zone_b_streaming_source",
translation_key="zone_b_streaming_source",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda r: _known(r.zone_b_streaming_source, r.available_stream_types),
options_fn=lambda r: r.available_stream_types,
value_fn=lambda r: (
_known(zb.streaming_source, r.stream_types) if (zb := r.zone_b) else None
),
options_fn=lambda r: r.stream_types,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
@@ -131,7 +135,7 @@ class LyngdorfSensor(LyngdorfEntity, SensorEntity):
def __init__(
self,
receiver: Receiver,
receiver: LyngdorfReceiver,
config_entry: LyngdorfConfigEntry,
device_info: DeviceInfo,
description: LyngdorfSensorEntityDescription,
+7
View File
@@ -143,8 +143,11 @@ def mock_receiver(mock_create_receiver: MagicMock) -> MagicMock:
receiver.video_input = "hdmi"
receiver.streaming_source = "AirPlay"
receiver.available_audio_inputs = ["optical", "aux"]
receiver.audio_inputs = ["optical", "aux"]
receiver.available_video_inputs = ["hdmi"]
receiver.video_inputs = ["hdmi"]
receiver.available_stream_types = ["AirPlay", "DLNA"]
receiver.stream_types = ["AirPlay", "DLNA"]
receiver.now_playing = None
receiver.has_position = False
@@ -182,6 +185,10 @@ def mock_receiver(mock_create_receiver: MagicMock) -> MagicMock:
receiver.zone_b_source = None
receiver.zone_b_available_sources = []
receiver.zone_b_audio_input = "aux"
zone_b = MagicMock(spec=ZoneB)
zone_b.audio_input = "aux"
zone_b.streaming_source = "DLNA"
receiver.zone_b = zone_b
receiver.zone_b_streaming_source = "DLNA"
receiver.volume = _FloatControl(-40.0, NumericRange(-99.9, 24.0, 0.1))
+53 -3
View File
@@ -52,7 +52,7 @@ async def test_enum_sensor_ignores_unknown_device_value(
mock_receiver: MagicMock,
) -> None:
"""Test an input the library could not name is reported as unknown."""
mock_receiver.available_audio_inputs = ["optical"]
mock_receiver.audio_inputs = ["optical"]
mock_receiver.audio_input = "audio-37"
notify_receiver_update(mock_receiver)
@@ -61,13 +61,63 @@ async def test_enum_sensor_ignores_unknown_device_value(
assert hass.states.get("sensor.mock_lyngdorf_audio_input").state == STATE_UNKNOWN
@pytest.mark.parametrize(
("entity_id", "attribute", "value"),
[
pytest.param(
"sensor.mock_lyngdorf_video_input", "video_inputs", ["DP"], id="video"
),
pytest.param(
"sensor.mock_lyngdorf_streaming_source",
"stream_types",
["Spotify"],
id="stream",
),
],
)
@pytest.mark.usefixtures("init_integration")
async def test_sensors_read_the_current_lists_not_the_deprecated_aliases(
hass: HomeAssistant,
mock_receiver: MagicMock,
entity_id: str,
attribute: str,
value: list[str],
) -> None:
"""Test the lists come from the 2.0 names while the aliases say otherwise."""
setattr(mock_receiver, attribute, value)
notify_receiver_update(mock_receiver)
await hass.async_block_till_done()
assert hass.states.get(entity_id).attributes["options"] == value
@pytest.mark.usefixtures("init_integration")
async def test_zone_b_sensors_read_the_zone_object(
hass: HomeAssistant,
mock_receiver: MagicMock,
) -> None:
"""Test the Zone B sensors follow the zone, not the receiver aliases."""
mock_receiver.zone_b.audio_input = "optical"
mock_receiver.zone_b.streaming_source = "AirPlay"
mock_receiver.zone_b_audio_input = "aux"
mock_receiver.zone_b_streaming_source = "DLNA"
notify_receiver_update(mock_receiver)
await hass.async_block_till_done()
assert hass.states.get("sensor.mock_lyngdorf_zone_b_audio_input").state == "optical"
assert (
hass.states.get("sensor.mock_lyngdorf_zone_b_streaming_source").state
== "AirPlay"
)
@pytest.mark.usefixtures("init_integration")
async def test_enum_options_follow_the_device(
hass: HomeAssistant,
mock_receiver: MagicMock,
) -> None:
"""Test enum options track the lists the device reports."""
mock_receiver.available_audio_inputs = ["HDMI", "optical"]
mock_receiver.audio_inputs = ["HDMI", "optical"]
mock_receiver.audio_input = "HDMI"
notify_receiver_update(mock_receiver)
await hass.async_block_till_done()
@@ -75,7 +125,7 @@ async def test_enum_options_follow_the_device(
state = hass.states.get("sensor.mock_lyngdorf_audio_input")
assert state.attributes["options"] == ["HDMI", "optical"]
mock_receiver.available_audio_inputs = ["HDMI", "optical", "ARC"]
mock_receiver.audio_inputs = ["HDMI", "optical", "ARC"]
notify_receiver_update(mock_receiver)
await hass.async_block_till_done()