Home Assistant Unofficial Reference 2024.12.1
media_player.py
Go to the documentation of this file.
1 """Support for Devialet speakers."""
2 
3 from __future__ import annotations
4 
5 from devialet.const import NORMAL_INPUTS
6 
8  MediaPlayerEntity,
9  MediaPlayerEntityFeature,
10  MediaPlayerState,
11 )
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import CONF_NAME
14 from homeassistant.core import HomeAssistant, callback
15 from homeassistant.helpers.device_registry import DeviceInfo
16 from homeassistant.helpers.entity_platform import AddEntitiesCallback
17 from homeassistant.helpers.update_coordinator import CoordinatorEntity
18 
19 from .const import DOMAIN, MANUFACTURER, SOUND_MODES
20 from .coordinator import DevialetCoordinator
21 
22 SUPPORT_DEVIALET = (
23  MediaPlayerEntityFeature.VOLUME_SET
24  | MediaPlayerEntityFeature.VOLUME_MUTE
25  | MediaPlayerEntityFeature.TURN_OFF
26  | MediaPlayerEntityFeature.SELECT_SOURCE
27  | MediaPlayerEntityFeature.SELECT_SOUND_MODE
28 )
29 
30 DEVIALET_TO_HA_FEATURE_MAP = {
31  "play": MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.STOP,
32  "pause": MediaPlayerEntityFeature.PAUSE,
33  "previous": MediaPlayerEntityFeature.PREVIOUS_TRACK,
34  "next": MediaPlayerEntityFeature.NEXT_TRACK,
35  "seek": MediaPlayerEntityFeature.SEEK,
36 }
37 
38 
40  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
41 ) -> None:
42  """Set up the Devialet entry."""
43  client = hass.data[DOMAIN][entry.entry_id]
44  coordinator = DevialetCoordinator(hass, client)
45  await coordinator.async_config_entry_first_refresh()
46 
47  async_add_entities([DevialetMediaPlayerEntity(coordinator, entry)])
48 
49 
51  CoordinatorEntity[DevialetCoordinator], MediaPlayerEntity
52 ):
53  """Devialet media player."""
54 
55  _attr_has_entity_name = True
56  _attr_name = None
57 
58  def __init__(self, coordinator: DevialetCoordinator, entry: ConfigEntry) -> None:
59  """Initialize the Devialet device."""
60  self.coordinatorcoordinator = coordinator
61  super().__init__(coordinator)
62 
63  self._attr_unique_id_attr_unique_id = str(entry.unique_id)
64  self._attr_device_info_attr_device_info = DeviceInfo(
65  identifiers={(DOMAIN, self._attr_unique_id_attr_unique_id)},
66  manufacturer=MANUFACTURER,
67  model=self.coordinatorcoordinator.client.model,
68  name=entry.data[CONF_NAME],
69  sw_version=self.coordinatorcoordinator.client.version,
70  )
71 
72  @callback
73  def _handle_coordinator_update(self) -> None:
74  """Handle updated data from the coordinator."""
75  if not self.coordinatorcoordinator.client.is_available:
76  self.async_write_ha_stateasync_write_ha_state()
77  return
78 
79  self._attr_volume_level_attr_volume_level = self.coordinatorcoordinator.client.volume_level
80  self._attr_is_volume_muted_attr_is_volume_muted = self.coordinatorcoordinator.client.is_volume_muted
81  self._attr_source_list_attr_source_list = self.coordinatorcoordinator.client.source_list
82  self._attr_sound_mode_list_attr_sound_mode_list = sorted(SOUND_MODES)
83  self._attr_media_artist_attr_media_artist = self.coordinatorcoordinator.client.media_artist
84  self._attr_media_album_name_attr_media_album_name = self.coordinatorcoordinator.client.media_album_name
85  self._attr_media_artist_attr_media_artist = self.coordinatorcoordinator.client.media_artist
86  self._attr_media_image_url_attr_media_image_url = self.coordinatorcoordinator.client.media_image_url
87  self._attr_media_duration_attr_media_duration = self.coordinatorcoordinator.client.media_duration
88  self._attr_media_position_attr_media_position = self.coordinatorcoordinator.client.current_position
89  self._attr_media_position_updated_at_attr_media_position_updated_at = (
90  self.coordinatorcoordinator.client.position_updated_at
91  )
92  self._attr_media_title_attr_media_title = (
93  self.coordinatorcoordinator.client.media_title
94  if self.coordinatorcoordinator.client.media_title
95  else self.sourcesourcesource
96  )
97  self.async_write_ha_stateasync_write_ha_state()
98 
99  @property
100  def state(self) -> MediaPlayerState | None:
101  """Return the state of the device."""
102  playing_state = self.coordinatorcoordinator.client.playing_state
103 
104  if not playing_state:
105  return MediaPlayerState.IDLE
106  if playing_state == "playing":
107  return MediaPlayerState.PLAYING
108  if playing_state == "paused":
109  return MediaPlayerState.PAUSED
110  return MediaPlayerState.ON
111 
112  @property
113  def available(self) -> bool:
114  """Return if the media player is available."""
115  return self.coordinatorcoordinator.client.is_available
116 
117  @property
118  def supported_features(self) -> MediaPlayerEntityFeature:
119  """Flag media player features that are supported."""
120  features = SUPPORT_DEVIALET
121 
122  if self.coordinatorcoordinator.client.source_state is None:
123  return features
124 
125  if not self.coordinatorcoordinator.client.available_options:
126  return features
127 
128  for option in self.coordinatorcoordinator.client.available_options:
129  features |= DEVIALET_TO_HA_FEATURE_MAP.get(option, 0)
130  return features
131 
132  @property
133  def source(self) -> str | None:
134  """Return the current input source."""
135  source = self.coordinatorcoordinator.client.source
136 
137  for pretty_name, name in NORMAL_INPUTS.items():
138  if source == name:
139  return pretty_name
140  return None
141 
142  @property
143  def sound_mode(self) -> str | None:
144  """Return the current sound mode."""
145  if self.coordinatorcoordinator.client.equalizer is not None:
146  sound_mode = self.coordinatorcoordinator.client.equalizer
147  elif self.coordinatorcoordinator.client.night_mode:
148  sound_mode = "night mode"
149  else:
150  return None
151 
152  for pretty_name, mode in SOUND_MODES.items():
153  if sound_mode == mode:
154  return pretty_name
155  return None
156 
157  async def async_volume_up(self) -> None:
158  """Volume up media player."""
159  await self.coordinatorcoordinator.client.async_volume_up()
160 
161  async def async_volume_down(self) -> None:
162  """Volume down media player."""
163  await self.coordinatorcoordinator.client.async_volume_down()
164 
165  async def async_set_volume_level(self, volume: float) -> None:
166  """Set volume level, range 0..1."""
167  await self.coordinatorcoordinator.client.async_set_volume_level(volume)
168 
169  async def async_mute_volume(self, mute: bool) -> None:
170  """Mute (true) or unmute (false) media player."""
171  await self.coordinatorcoordinator.client.async_mute_volume(mute)
172 
173  async def async_media_play(self) -> None:
174  """Play media player."""
175  await self.coordinatorcoordinator.client.async_media_play()
176 
177  async def async_media_pause(self) -> None:
178  """Pause media player."""
179  await self.coordinatorcoordinator.client.async_media_pause()
180 
181  async def async_media_stop(self) -> None:
182  """Pause media player."""
183  await self.coordinatorcoordinator.client.async_media_stop()
184 
185  async def async_media_next_track(self) -> None:
186  """Send the next track command."""
187  await self.coordinatorcoordinator.client.async_media_next_track()
188 
189  async def async_media_previous_track(self) -> None:
190  """Send the previous track command."""
191  await self.coordinatorcoordinator.client.async_media_previous_track()
192 
193  async def async_media_seek(self, position: float) -> None:
194  """Send seek command."""
195  await self.coordinatorcoordinator.client.async_media_seek(position)
196 
197  async def async_select_sound_mode(self, sound_mode: str) -> None:
198  """Send sound mode command."""
199  for pretty_name, mode in SOUND_MODES.items():
200  if sound_mode == pretty_name:
201  if mode == "night mode":
202  await self.coordinatorcoordinator.client.async_set_night_mode(True)
203  else:
204  await self.coordinatorcoordinator.client.async_set_night_mode(False)
205  await self.coordinatorcoordinator.client.async_set_equalizer(mode)
206 
207  async def async_turn_off(self) -> None:
208  """Turn off media player."""
209  await self.coordinatorcoordinator.client.async_turn_off()
210 
211  async def async_select_source(self, source: str) -> None:
212  """Select input source."""
213  await self.coordinatorcoordinator.client.async_select_source(source)
None __init__(self, DevialetCoordinator coordinator, ConfigEntry entry)
Definition: media_player.py:58
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: media_player.py:41