mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Add entity available attribute to Cambridge Audio (#125831)
* Bump aiostreammagic to 2.2.4 * Move callback handling to entity class * Wrap all module exceptions in HA errors for Cambridge Audio
This commit is contained in:
parent
4afc472068
commit
a4c88a8591
@ -1,13 +1,38 @@
|
|||||||
"""Base class for Cambridge Audio entities."""
|
"""Base class for Cambridge Audio entities."""
|
||||||
|
|
||||||
|
from collections.abc import Awaitable, Callable, Coroutine
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Any, Concatenate
|
||||||
|
|
||||||
from aiostreammagic import StreamMagicClient
|
from aiostreammagic import StreamMagicClient
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
from . import STREAM_MAGIC_EXCEPTIONS
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
def command[_EntityT: CambridgeAudioEntity, **_P](
|
||||||
|
func: Callable[Concatenate[_EntityT, _P], Awaitable[None]],
|
||||||
|
) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]:
|
||||||
|
"""Wrap async calls to raise on request error."""
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
async def decorator(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||||
|
"""Wrap all command methods."""
|
||||||
|
try:
|
||||||
|
await func(self, *args, **kwargs)
|
||||||
|
except STREAM_MAGIC_EXCEPTIONS as exc:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
f"Error executing {func.__name__} on entity {self.entity_id},"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
class CambridgeAudioEntity(Entity):
|
class CambridgeAudioEntity(Entity):
|
||||||
"""Defines a base Cambridge Audio entity."""
|
"""Defines a base Cambridge Audio entity."""
|
||||||
|
|
||||||
@ -24,3 +49,17 @@ class CambridgeAudioEntity(Entity):
|
|||||||
serial_number=client.info.unit_id,
|
serial_number=client.info.unit_id,
|
||||||
configuration_url=f"http://{client.host}",
|
configuration_url=f"http://{client.host}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
async def _state_update_callback(self, _client: StreamMagicClient) -> None:
|
||||||
|
"""Call when the device is notified of changes."""
|
||||||
|
self._attr_available = _client.is_connected()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Register callback handlers."""
|
||||||
|
await self.client.register_state_update_callbacks(self._state_update_callback)
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Remove callbacks."""
|
||||||
|
await self.client.unregister_state_update_callbacks(self._state_update_callback)
|
||||||
|
@ -23,7 +23,7 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .entity import CambridgeAudioEntity
|
from .entity import CambridgeAudioEntity, command
|
||||||
|
|
||||||
BASE_FEATURES = (
|
BASE_FEATURES = (
|
||||||
MediaPlayerEntityFeature.SELECT_SOURCE
|
MediaPlayerEntityFeature.SELECT_SOURCE
|
||||||
@ -70,18 +70,6 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
|||||||
super().__init__(client)
|
super().__init__(client)
|
||||||
self._attr_unique_id = client.info.unit_id
|
self._attr_unique_id = client.info.unit_id
|
||||||
|
|
||||||
async def _state_update_callback(self, _client: StreamMagicClient) -> None:
|
|
||||||
"""Call when the device is notified of changes."""
|
|
||||||
self.schedule_update_ha_state()
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""Register callback handlers."""
|
|
||||||
await self.client.register_state_update_callbacks(self._state_update_callback)
|
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
|
||||||
"""Remove callbacks."""
|
|
||||||
await self.client.unregister_state_update_callbacks(self._state_update_callback)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> MediaPlayerEntityFeature:
|
def supported_features(self) -> MediaPlayerEntityFeature:
|
||||||
"""Supported features for the media player."""
|
"""Supported features for the media player."""
|
||||||
@ -194,10 +182,12 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
|||||||
mode_repeat = RepeatMode.ALL
|
mode_repeat = RepeatMode.ALL
|
||||||
return mode_repeat
|
return mode_repeat
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_media_play_pause(self) -> None:
|
async def async_media_play_pause(self) -> None:
|
||||||
"""Toggle play/pause the current media."""
|
"""Toggle play/pause the current media."""
|
||||||
await self.client.play_pause()
|
await self.client.play_pause()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_media_pause(self) -> None:
|
async def async_media_pause(self) -> None:
|
||||||
"""Pause the current media."""
|
"""Pause the current media."""
|
||||||
controls = self.client.now_playing.controls
|
controls = self.client.now_playing.controls
|
||||||
@ -209,10 +199,12 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
|||||||
else:
|
else:
|
||||||
await self.client.pause()
|
await self.client.pause()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_media_stop(self) -> None:
|
async def async_media_stop(self) -> None:
|
||||||
"""Stop the current media."""
|
"""Stop the current media."""
|
||||||
await self.client.stop()
|
await self.client.stop()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_media_play(self) -> None:
|
async def async_media_play(self) -> None:
|
||||||
"""Play the current media."""
|
"""Play the current media."""
|
||||||
controls = self.client.now_playing.controls
|
controls = self.client.now_playing.controls
|
||||||
@ -224,14 +216,17 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
|||||||
else:
|
else:
|
||||||
await self.client.play()
|
await self.client.play()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_media_next_track(self) -> None:
|
async def async_media_next_track(self) -> None:
|
||||||
"""Skip to the next track."""
|
"""Skip to the next track."""
|
||||||
await self.client.next_track()
|
await self.client.next_track()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_media_previous_track(self) -> None:
|
async def async_media_previous_track(self) -> None:
|
||||||
"""Skip to the previous track."""
|
"""Skip to the previous track."""
|
||||||
await self.client.previous_track()
|
await self.client.previous_track()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_select_source(self, source: str) -> None:
|
async def async_select_source(self, source: str) -> None:
|
||||||
"""Select the source."""
|
"""Select the source."""
|
||||||
for src in self.client.sources:
|
for src in self.client.sources:
|
||||||
@ -239,34 +234,42 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
|||||||
await self.client.set_source_by_id(src.id)
|
await self.client.set_source_by_id(src.id)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_turn_on(self) -> None:
|
async def async_turn_on(self) -> None:
|
||||||
"""Power on the device."""
|
"""Power on the device."""
|
||||||
await self.client.power_on()
|
await self.client.power_on()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_turn_off(self) -> None:
|
async def async_turn_off(self) -> None:
|
||||||
"""Power off the device."""
|
"""Power off the device."""
|
||||||
await self.client.power_off()
|
await self.client.power_off()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_volume_up(self) -> None:
|
async def async_volume_up(self) -> None:
|
||||||
"""Step the volume up."""
|
"""Step the volume up."""
|
||||||
await self.client.volume_up()
|
await self.client.volume_up()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_volume_down(self) -> None:
|
async def async_volume_down(self) -> None:
|
||||||
"""Step the volume down."""
|
"""Step the volume down."""
|
||||||
await self.client.volume_down()
|
await self.client.volume_down()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_set_volume_level(self, volume: float) -> None:
|
async def async_set_volume_level(self, volume: float) -> None:
|
||||||
"""Set the volume level."""
|
"""Set the volume level."""
|
||||||
await self.client.set_volume(int(volume * 100))
|
await self.client.set_volume(int(volume * 100))
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_mute_volume(self, mute: bool) -> None:
|
async def async_mute_volume(self, mute: bool) -> None:
|
||||||
"""Set the mute state."""
|
"""Set the mute state."""
|
||||||
await self.client.set_mute(mute)
|
await self.client.set_mute(mute)
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_media_seek(self, position: float) -> None:
|
async def async_media_seek(self, position: float) -> None:
|
||||||
"""Seek to a position in the current media."""
|
"""Seek to a position in the current media."""
|
||||||
await self.client.media_seek(int(position))
|
await self.client.media_seek(int(position))
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_set_shuffle(self, shuffle: bool) -> None:
|
async def async_set_shuffle(self, shuffle: bool) -> None:
|
||||||
"""Set the shuffle mode for the current queue."""
|
"""Set the shuffle mode for the current queue."""
|
||||||
shuffle_mode = ShuffleMode.OFF
|
shuffle_mode = ShuffleMode.OFF
|
||||||
@ -274,6 +277,7 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
|
|||||||
shuffle_mode = ShuffleMode.ALL
|
shuffle_mode = ShuffleMode.ALL
|
||||||
await self.client.set_shuffle(shuffle_mode)
|
await self.client.set_shuffle(shuffle_mode)
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_set_repeat(self, repeat: RepeatMode) -> None:
|
async def async_set_repeat(self, repeat: RepeatMode) -> None:
|
||||||
"""Set the repeat mode for the current queue."""
|
"""Set the repeat mode for the current queue."""
|
||||||
repeat_mode = CambridgeRepeatMode.OFF
|
repeat_mode = CambridgeRepeatMode.OFF
|
||||||
|
Loading…
x
Reference in New Issue
Block a user