Add control bus mode selector to Cambridge Audio (#139131)

* [CambridgeAudio Integration] Add switch to enable Control Bus Mode

* remove load_fn

* Add import for ControlBusMode

* Add strings for control_bus_mode

* Add icons for control_bus_mode

* Add test case for the select ControlBusMode.Amplifier

* Change the set of icons

* Fix the usage of the wrong property name

* Fix test

* Fix test 2

* add new snapshot

* fix test name

* Fix

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
ichbinsteffen 2025-05-09 15:53:37 +02:00 committed by GitHub
parent 7dad6ebe67
commit 3e0e807c96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 115 additions and 1 deletions

View File

@ -11,6 +11,13 @@
},
"audio_output": {
"default": "mdi:audio-input-stereo-minijack"
},
"control_bus_mode": {
"default": "mdi:audio-video-off",
"state": {
"amplifier": "mdi:speaker",
"receiver": "mdi:audio-video"
}
}
},
"switch": {

View File

@ -11,6 +11,7 @@ from aiostreammagic import (
StreamMagicClient,
TransportControl,
)
from aiostreammagic.models import ControlBusMode
from homeassistant.components.media_player import (
BrowseMedia,
@ -91,6 +92,8 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
features = BASE_FEATURES
if self.client.state.pre_amp_mode:
features |= PREAMP_FEATURES
if self.client.state.control_bus == ControlBusMode.AMPLIFIER:
features |= MediaPlayerEntityFeature.VOLUME_STEP
if TransportControl.PLAY_PAUSE in controls:
features |= MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.PAUSE
for control in controls:

View File

@ -4,7 +4,7 @@ from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from aiostreammagic import StreamMagicClient
from aiostreammagic.models import DisplayBrightness
from aiostreammagic.models import ControlBusMode, DisplayBrightness
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory
@ -76,6 +76,20 @@ CONTROL_ENTITIES: tuple[CambridgeAudioSelectEntityDescription, ...] = (
value_fn=_audio_output_value_fn,
set_value_fn=_audio_output_set_value_fn,
),
CambridgeAudioSelectEntityDescription(
key="control_bus_mode",
translation_key="control_bus_mode",
options=[
ControlBusMode.AMPLIFIER.value,
ControlBusMode.RECEIVER.value,
ControlBusMode.OFF.value,
],
entity_category=EntityCategory.CONFIG,
value_fn=lambda client: client.state.control_bus,
set_value_fn=lambda client, value: client.set_control_bus_mode(
ControlBusMode(value)
),
),
)

View File

@ -46,6 +46,14 @@
},
"audio_output": {
"name": "Audio output"
},
"control_bus_mode": {
"name": "Control Bus mode",
"state": {
"amplifier": "Amplifier",
"receiver": "Receiver",
"off": "[%key:common::state::off%]"
}
}
},
"switch": {

View File

@ -57,6 +57,64 @@
'state': 'unknown',
})
# ---
# name: test_all_entities[select.cambridge_audio_cxnv2_control_bus_mode-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'amplifier',
'receiver',
'off',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'select.cambridge_audio_cxnv2_control_bus_mode',
'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': 'Control Bus mode',
'platform': 'cambridge_audio',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'control_bus_mode',
'unique_id': '0020c2d8-control_bus_mode',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[select.cambridge_audio_cxnv2_control_bus_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Cambridge Audio CXNv2 Control Bus mode',
'options': list([
'amplifier',
'receiver',
'off',
]),
}),
'context': <ANY>,
'entity_id': 'select.cambridge_audio_cxnv2_control_bus_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[select.cambridge_audio_cxnv2_display_brightness-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock
from aiostreammagic import (
ControlBusMode,
RepeatMode as CambridgeRepeatMode,
ShuffleMode,
TransportControl,
@ -129,6 +130,29 @@ async def test_entity_supported_features(
)
async def test_entity_supported_features_with_control_bus(
hass: HomeAssistant,
mock_stream_magic_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test entity attributes with control bus state."""
await setup_integration(hass, mock_config_entry)
mock_stream_magic_client.state.pre_amp_mode = False
mock_stream_magic_client.state.control_bus = ControlBusMode.AMPLIFIER
await mock_state_update(mock_stream_magic_client)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
attrs = state.attributes
assert MediaPlayerEntityFeature.VOLUME_STEP in attrs[ATTR_SUPPORTED_FEATURES]
assert (
MediaPlayerEntityFeature.VOLUME_SET | MediaPlayerEntityFeature.VOLUME_MUTE
not in attrs[ATTR_SUPPORTED_FEATURES]
)
@pytest.mark.parametrize(
("power_state", "play_state", "media_player_state"),
[