mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Add audio output select to Cambridge Audio (#129366)
This commit is contained in:
parent
6c047e2678
commit
0cd5deaa3f
@ -8,6 +8,9 @@
|
||||
"dim": "mdi:brightness-6",
|
||||
"off": "mdi:brightness-3"
|
||||
}
|
||||
},
|
||||
"audio_output": {
|
||||
"default": "mdi:audio-input-stereo-minijack"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Support for Cambridge Audio select entities."""
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from aiostreammagic import StreamMagicClient
|
||||
from aiostreammagic.models import DisplayBrightness
|
||||
@ -19,10 +19,34 @@ from .entity import CambridgeAudioEntity
|
||||
class CambridgeAudioSelectEntityDescription(SelectEntityDescription):
|
||||
"""Describes Cambridge Audio select entity."""
|
||||
|
||||
options_fn: Callable[[StreamMagicClient], list[str]] = field(default=lambda _: [])
|
||||
load_fn: Callable[[StreamMagicClient], bool] = field(default=lambda _: True)
|
||||
value_fn: Callable[[StreamMagicClient], str | None]
|
||||
set_value_fn: Callable[[StreamMagicClient, str], Awaitable[None]]
|
||||
|
||||
|
||||
async def _audio_output_set_value_fn(client: StreamMagicClient, value: str) -> None:
|
||||
"""Set the audio output using the display name."""
|
||||
audio_output_id = next(
|
||||
(output.id for output in client.audio_output.outputs if value == output.name),
|
||||
None,
|
||||
)
|
||||
assert audio_output_id is not None
|
||||
await client.set_audio_output(audio_output_id)
|
||||
|
||||
|
||||
def _audio_output_value_fn(client: StreamMagicClient) -> str | None:
|
||||
"""Convert the current audio output id to name."""
|
||||
return next(
|
||||
(
|
||||
output.name
|
||||
for output in client.audio_output.outputs
|
||||
if client.state.audio_output == output.id
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
CONTROL_ENTITIES: tuple[CambridgeAudioSelectEntityDescription, ...] = (
|
||||
CambridgeAudioSelectEntityDescription(
|
||||
key="display_brightness",
|
||||
@ -34,6 +58,17 @@ CONTROL_ENTITIES: tuple[CambridgeAudioSelectEntityDescription, ...] = (
|
||||
DisplayBrightness(value)
|
||||
),
|
||||
),
|
||||
CambridgeAudioSelectEntityDescription(
|
||||
key="audio_output",
|
||||
translation_key="audio_output",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
options_fn=lambda client: [
|
||||
output.name for output in client.audio_output.outputs
|
||||
],
|
||||
load_fn=lambda client: len(client.audio_output.outputs) > 0,
|
||||
value_fn=_audio_output_value_fn,
|
||||
set_value_fn=_audio_output_set_value_fn,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@ -46,7 +81,9 @@ async def async_setup_entry(
|
||||
|
||||
client: StreamMagicClient = entry.runtime_data
|
||||
entities: list[CambridgeAudioSelect] = [
|
||||
CambridgeAudioSelect(client, description) for description in CONTROL_ENTITIES
|
||||
CambridgeAudioSelect(client, description)
|
||||
for description in CONTROL_ENTITIES
|
||||
if description.load_fn(client)
|
||||
]
|
||||
async_add_entities(entities)
|
||||
|
||||
@ -65,6 +102,9 @@ class CambridgeAudioSelect(CambridgeAudioEntity, SelectEntity):
|
||||
super().__init__(client)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{client.info.unit_id}-{description.key}"
|
||||
options_fn = description.options_fn(client)
|
||||
if options_fn:
|
||||
self._attr_options = options_fn
|
||||
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
|
@ -32,6 +32,9 @@
|
||||
"dim": "Dim",
|
||||
"off": "[%key:common::state::off%]"
|
||||
}
|
||||
},
|
||||
"audio_output": {
|
||||
"name": "Audio output"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
@ -4,6 +4,7 @@ from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from aiostreammagic.models import (
|
||||
AudioOutput,
|
||||
Display,
|
||||
Info,
|
||||
NowPlaying,
|
||||
@ -63,6 +64,9 @@ def mock_stream_magic_client() -> Generator[AsyncMock]:
|
||||
client.preset_list = PresetList.from_json(
|
||||
load_fixture("get_presets_list.json", DOMAIN)
|
||||
)
|
||||
client.audio_output = AudioOutput.from_json(
|
||||
load_fixture("get_audio_output.json", DOMAIN)
|
||||
)
|
||||
client.is_connected = Mock(return_value=True)
|
||||
client.position_last_updated = client.play_state.position
|
||||
client.unregister_state_update_callbacks.return_value = True
|
||||
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"outputs": [
|
||||
{
|
||||
"id": "speaker_a",
|
||||
"name": "Speaker A"
|
||||
},
|
||||
{
|
||||
"id": "speaker_b",
|
||||
"name": "Speaker B"
|
||||
},
|
||||
{
|
||||
"id": "headphones",
|
||||
"name": "Headphones"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,4 +1,61 @@
|
||||
# serializer version: 1
|
||||
# name: test_all_entities[select.cambridge_audio_cxnv2_audio_output-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'Speaker A',
|
||||
'Speaker B',
|
||||
'Headphones',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.cambridge_audio_cxnv2_audio_output',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Audio output',
|
||||
'platform': 'cambridge_audio',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'audio_output',
|
||||
'unique_id': '0020c2d8-audio_output',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[select.cambridge_audio_cxnv2_audio_output-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Cambridge Audio CXNv2 Audio output',
|
||||
'options': list([
|
||||
'Speaker A',
|
||||
'Speaker B',
|
||||
'Headphones',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.cambridge_audio_cxnv2_audio_output',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[select.cambridge_audio_cxnv2_display_brightness-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
@ -51,3 +51,14 @@ async def test_setting_value(
|
||||
blocking=True,
|
||||
)
|
||||
mock_stream_magic_client.set_display_brightness.assert_called_once_with("dim")
|
||||
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: "select.cambridge_audio_cxnv2_audio_output",
|
||||
ATTR_OPTION: "Speaker A",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
mock_stream_magic_client.set_audio_output.assert_called_once_with("speaker_a")
|
||||
|
Loading…
x
Reference in New Issue
Block a user