Home Assistant Unofficial Reference 2024.12.1
media_player.py
Go to the documentation of this file.
1 """Combination of multiple media players for a universal controller."""
2 
3 from __future__ import annotations
4 
5 from copy import copy
6 from typing import Any
7 
8 import voluptuous as vol
9 
11  ATTR_APP_ID,
12  ATTR_APP_NAME,
13  ATTR_INPUT_SOURCE,
14  ATTR_INPUT_SOURCE_LIST,
15  ATTR_MEDIA_ALBUM_ARTIST,
16  ATTR_MEDIA_ALBUM_NAME,
17  ATTR_MEDIA_ARTIST,
18  ATTR_MEDIA_CHANNEL,
19  ATTR_MEDIA_CONTENT_ID,
20  ATTR_MEDIA_CONTENT_TYPE,
21  ATTR_MEDIA_DURATION,
22  ATTR_MEDIA_EPISODE,
23  ATTR_MEDIA_PLAYLIST,
24  ATTR_MEDIA_POSITION,
25  ATTR_MEDIA_POSITION_UPDATED_AT,
26  ATTR_MEDIA_REPEAT,
27  ATTR_MEDIA_SEASON,
28  ATTR_MEDIA_SEEK_POSITION,
29  ATTR_MEDIA_SERIES_TITLE,
30  ATTR_MEDIA_SHUFFLE,
31  ATTR_MEDIA_TITLE,
32  ATTR_MEDIA_TRACK,
33  ATTR_MEDIA_VOLUME_LEVEL,
34  ATTR_MEDIA_VOLUME_MUTED,
35  ATTR_SOUND_MODE,
36  ATTR_SOUND_MODE_LIST,
37  DEVICE_CLASSES_SCHEMA,
38  DOMAIN as MEDIA_PLAYER_DOMAIN,
39  PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
40  SERVICE_CLEAR_PLAYLIST,
41  SERVICE_PLAY_MEDIA,
42  SERVICE_SELECT_SOUND_MODE,
43  SERVICE_SELECT_SOURCE,
44  BrowseMedia,
45  MediaPlayerEntity,
46  MediaPlayerEntityFeature,
47  MediaPlayerState,
48  MediaType,
49  RepeatMode,
50 )
51 from homeassistant.const import (
52  ATTR_ASSUMED_STATE,
53  ATTR_ENTITY_ID,
54  ATTR_ENTITY_PICTURE,
55  ATTR_SUPPORTED_FEATURES,
56  CONF_DEVICE_CLASS,
57  CONF_NAME,
58  CONF_STATE,
59  CONF_STATE_TEMPLATE,
60  CONF_UNIQUE_ID,
61  EVENT_HOMEASSISTANT_START,
62  SERVICE_MEDIA_NEXT_TRACK,
63  SERVICE_MEDIA_PAUSE,
64  SERVICE_MEDIA_PLAY,
65  SERVICE_MEDIA_PLAY_PAUSE,
66  SERVICE_MEDIA_PREVIOUS_TRACK,
67  SERVICE_MEDIA_SEEK,
68  SERVICE_MEDIA_STOP,
69  SERVICE_REPEAT_SET,
70  SERVICE_SHUFFLE_SET,
71  SERVICE_TOGGLE,
72  SERVICE_TURN_OFF,
73  SERVICE_TURN_ON,
74  SERVICE_VOLUME_DOWN,
75  SERVICE_VOLUME_MUTE,
76  SERVICE_VOLUME_SET,
77  SERVICE_VOLUME_UP,
78  STATE_ON,
79  STATE_UNAVAILABLE,
80  STATE_UNKNOWN,
81 )
82 from homeassistant.core import Event, EventStateChangedData, HomeAssistant, callback
83 from homeassistant.exceptions import TemplateError
84 from homeassistant.helpers import config_validation as cv
85 from homeassistant.helpers.entity_component import EntityComponent
86 from homeassistant.helpers.entity_platform import AddEntitiesCallback
87 from homeassistant.helpers.event import (
88  TrackTemplate,
89  TrackTemplateResult,
90  async_track_state_change_event,
91  async_track_template_result,
92 )
93 from homeassistant.helpers.reload import async_setup_reload_service
94 from homeassistant.helpers.service import async_call_from_config
95 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
96 
97 ATTR_ACTIVE_CHILD = "active_child"
98 
99 CONF_ACTIVE_CHILD_TEMPLATE = "active_child_template"
100 CONF_ATTRS = "attributes"
101 CONF_CHILDREN = "children"
102 CONF_COMMANDS = "commands"
103 CONF_BROWSE_MEDIA_ENTITY = "browse_media_entity"
104 
105 STATES_ORDER = [
106  STATE_UNKNOWN,
107  STATE_UNAVAILABLE,
108  MediaPlayerState.OFF,
109  MediaPlayerState.IDLE,
110  MediaPlayerState.STANDBY,
111  MediaPlayerState.ON,
112  MediaPlayerState.PAUSED,
113  MediaPlayerState.BUFFERING,
114  MediaPlayerState.PLAYING,
115 ]
116 STATES_ORDER_LOOKUP = {state: idx for idx, state in enumerate(STATES_ORDER)}
117 STATES_ORDER_IDLE = STATES_ORDER_LOOKUP[MediaPlayerState.IDLE]
118 
119 ATTRS_SCHEMA = cv.schema_with_slug_keys(cv.string)
120 CMD_SCHEMA = cv.schema_with_slug_keys(cv.SERVICE_SCHEMA)
121 
122 PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
123  {
124  vol.Required(CONF_NAME): cv.string,
125  vol.Optional(CONF_CHILDREN, default=[]): cv.entity_ids,
126  vol.Optional(CONF_COMMANDS, default={}): CMD_SCHEMA,
127  vol.Optional(CONF_ATTRS, default={}): vol.Or(
128  cv.ensure_list(ATTRS_SCHEMA), ATTRS_SCHEMA
129  ),
130  vol.Optional(CONF_BROWSE_MEDIA_ENTITY): cv.string,
131  vol.Optional(CONF_UNIQUE_ID): cv.string,
132  vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
133  vol.Optional(CONF_ACTIVE_CHILD_TEMPLATE): cv.template,
134  vol.Optional(CONF_STATE_TEMPLATE): cv.template,
135  },
136  extra=vol.REMOVE_EXTRA,
137 )
138 
139 
141  hass: HomeAssistant,
142  config: ConfigType,
143  async_add_entities: AddEntitiesCallback,
144  discovery_info: DiscoveryInfoType | None = None,
145 ) -> None:
146  """Set up the universal media players."""
147  await async_setup_reload_service(hass, "universal", ["media_player"])
148 
149  player = UniversalMediaPlayer(hass, config)
150  async_add_entities([player])
151 
152 
154  """Representation of an universal media player."""
155 
156  _attr_should_poll = False
157 
158  def __init__(
159  self,
160  hass,
161  config,
162  ):
163  """Initialize the Universal media device."""
164  self.hasshasshass = hass
165  self._attr_name_attr_name = config.get(CONF_NAME)
166  self._children_children = config.get(CONF_CHILDREN)
167  self._active_child_template_active_child_template = config.get(CONF_ACTIVE_CHILD_TEMPLATE)
168  self._active_child_template_result_active_child_template_result = None
169  self._cmds_cmds = config.get(CONF_COMMANDS)
170  self._attrs_attrs = {}
171  for key, val in config.get(CONF_ATTRS).items():
172  attr = list(map(str.strip, val.split("|", 1)))
173  if len(attr) == 1:
174  attr.append(None)
175  self._attrs_attrs[key] = attr
176  self._child_state_child_state = None
177  self._state_template_result_state_template_result = None
178  self._state_template_state_template = config.get(CONF_STATE_TEMPLATE)
179  self._attr_device_class_attr_device_class = config.get(CONF_DEVICE_CLASS)
180  self._attr_unique_id_attr_unique_id = config.get(CONF_UNIQUE_ID)
181  self._browse_media_entity_browse_media_entity = config.get(CONF_BROWSE_MEDIA_ENTITY)
182 
183  async def async_added_to_hass(self) -> None:
184  """Subscribe to children and template state changes."""
185 
186  @callback
187  def _async_on_dependency_update(
188  event: Event[EventStateChangedData],
189  ) -> None:
190  """Update ha state when dependencies update."""
191  self.async_set_contextasync_set_context(event.context)
192  self._async_update_async_update()
193  self.async_write_ha_stateasync_write_ha_state()
194 
195  @callback
196  def _async_on_template_update(
197  event: Event[EventStateChangedData] | None,
198  updates: list[TrackTemplateResult],
199  ) -> None:
200  """Update state when template state changes."""
201  for data in updates:
202  template = data.template
203  result = data.result
204 
205  if template == self._state_template_state_template:
206  self._state_template_result_state_template_result = (
207  None if isinstance(result, TemplateError) else result
208  )
209  if template == self._active_child_template_active_child_template:
210  self._active_child_template_result_active_child_template_result = (
211  None if isinstance(result, TemplateError) else result
212  )
213 
214  if event:
215  self.async_set_contextasync_set_context(event.context)
216 
217  self._async_update_async_update()
218  self.async_write_ha_stateasync_write_ha_state()
219 
220  track_templates: list[TrackTemplate] = []
221  if self._state_template_state_template:
222  track_templates.append(TrackTemplate(self._state_template_state_template, None))
223  if self._active_child_template_active_child_template:
224  track_templates.append(TrackTemplate(self._active_child_template_active_child_template, None))
225 
226  if track_templates:
228  self.hasshasshass,
229  track_templates,
230  _async_on_template_update,
231  )
232  self.hasshasshass.bus.async_listen_once(
233  EVENT_HOMEASSISTANT_START, callback(lambda _: result.async_refresh())
234  )
235 
236  self.async_on_removeasync_on_remove(result.async_remove)
237 
238  depend = copy(self._children_children)
239  for entity in self._attrs_attrs.values():
240  depend.append(entity[0])
241 
242  self.async_on_removeasync_on_remove(
244  self.hasshasshass, list(set(depend)), _async_on_dependency_update
245  )
246  )
247 
248  def _entity_lkp(self, entity_id, state_attr=None):
249  """Look up an entity state."""
250  if (state_obj := self.hasshasshass.states.get(entity_id)) is None:
251  return None
252 
253  if state_attr:
254  return state_obj.attributes.get(state_attr)
255  return state_obj.state
256 
257  def _override_or_child_attr(self, attr_name):
258  """Return either the override or the active child for attr_name."""
259  if attr_name in self._attrs_attrs:
260  return self._entity_lkp_entity_lkp(
261  self._attrs_attrs[attr_name][0], self._attrs_attrs[attr_name][1]
262  )
263 
264  return self._child_attr_child_attr(attr_name)
265 
266  def _child_attr(self, attr_name):
267  """Return the active child's attributes."""
268  active_child = self._child_state_child_state
269  return active_child.attributes.get(attr_name) if active_child else None
270 
272  self, service_name, service_data=None, allow_override=False
273  ):
274  """Call either a specified or active child's service."""
275  if service_data is None:
276  service_data = {}
277 
278  if allow_override and service_name in self._cmds_cmds:
280  self.hasshasshass,
281  self._cmds_cmds[service_name],
282  variables=service_data,
283  blocking=True,
284  validate_config=False,
285  )
286  return
287 
288  if (active_child := self._child_state_child_state) is None:
289  # No child to call service on
290  return
291 
292  service_data[ATTR_ENTITY_ID] = active_child.entity_id
293 
294  await self.hasshasshass.services.async_call(
295  MEDIA_PLAYER_DOMAIN,
296  service_name,
297  service_data,
298  blocking=True,
299  context=self._context_context,
300  )
301 
302  @property
303  def master_state(self):
304  """Return the master state for entity or None."""
305  if self._state_template_state_template is not None:
306  return self._state_template_result_state_template_result
307  if CONF_STATE in self._attrs_attrs:
308  master_state = self._entity_lkp_entity_lkp(
309  self._attrs_attrs[CONF_STATE][0], self._attrs_attrs[CONF_STATE][1]
310  )
311  return master_state if master_state else MediaPlayerState.OFF
312 
313  return None
314 
315  @property
316  def assumed_state(self) -> bool:
317  """Return True if unable to access real state of the entity."""
318  return self._child_attr_child_attr(ATTR_ASSUMED_STATE)
319 
320  @property
321  def state(self):
322  """Return the current state of media player.
323 
324  Off if master state is off
325  else Status of first active child
326  else master state or off
327  """
328  master_state = self.master_statemaster_state # avoid multiple lookups
329  if (master_state == MediaPlayerState.OFF) or (self._state_template_state_template is not None):
330  return master_state
331 
332  if active_child := self._child_state_child_state:
333  return active_child.state
334 
335  return master_state if master_state else MediaPlayerState.OFF
336 
337  @property
338  def volume_level(self):
339  """Volume level of entity specified in attributes or active child."""
340  try:
341  return float(self._override_or_child_attr_override_or_child_attr(ATTR_MEDIA_VOLUME_LEVEL))
342  except (TypeError, ValueError):
343  return None
344 
345  @property
346  def is_volume_muted(self):
347  """Boolean if volume is muted."""
348  return self._override_or_child_attr_override_or_child_attr(ATTR_MEDIA_VOLUME_MUTED) in [True, STATE_ON]
349 
350  @property
351  def media_content_id(self):
352  """Return the content ID of current playing media."""
353  return self._child_attr_child_attr(ATTR_MEDIA_CONTENT_ID)
354 
355  @property
357  """Return the content type of current playing media."""
358  return self._child_attr_child_attr(ATTR_MEDIA_CONTENT_TYPE)
359 
360  @property
361  def media_duration(self):
362  """Return the duration of current playing media in seconds."""
363  return self._child_attr_child_attr(ATTR_MEDIA_DURATION)
364 
365  @property
366  def media_image_url(self):
367  """Image url of current playing media."""
368  return self._child_attr_child_attr(ATTR_ENTITY_PICTURE)
369 
370  @property
371  def entity_picture(self):
372  """Return image of the media playing.
373 
374  The universal media player doesn't use the parent class logic, since
375  the url is coming from child entity pictures which have already been
376  sent through the API proxy.
377  """
378  return self.media_image_urlmedia_image_urlmedia_image_url
379 
380  @property
381  def media_title(self):
382  """Title of current playing media."""
383  return self._child_attr_child_attr(ATTR_MEDIA_TITLE)
384 
385  @property
386  def media_artist(self):
387  """Artist of current playing media (Music track only)."""
388  return self._child_attr_child_attr(ATTR_MEDIA_ARTIST)
389 
390  @property
391  def media_album_name(self):
392  """Album name of current playing media (Music track only)."""
393  return self._child_attr_child_attr(ATTR_MEDIA_ALBUM_NAME)
394 
395  @property
397  """Album artist of current playing media (Music track only)."""
398  return self._child_attr_child_attr(ATTR_MEDIA_ALBUM_ARTIST)
399 
400  @property
401  def media_track(self):
402  """Track number of current playing media (Music track only)."""
403  return self._child_attr_child_attr(ATTR_MEDIA_TRACK)
404 
405  @property
407  """Return the title of the series of current playing media (TV)."""
408  return self._child_attr_child_attr(ATTR_MEDIA_SERIES_TITLE)
409 
410  @property
411  def media_season(self):
412  """Season of current playing media (TV Show only)."""
413  return self._child_attr_child_attr(ATTR_MEDIA_SEASON)
414 
415  @property
416  def media_episode(self):
417  """Episode of current playing media (TV Show only)."""
418  return self._child_attr_child_attr(ATTR_MEDIA_EPISODE)
419 
420  @property
421  def media_channel(self):
422  """Channel currently playing."""
423  return self._child_attr_child_attr(ATTR_MEDIA_CHANNEL)
424 
425  @property
426  def media_playlist(self):
427  """Title of Playlist currently playing."""
428  return self._child_attr_child_attr(ATTR_MEDIA_PLAYLIST)
429 
430  @property
431  def app_id(self):
432  """ID of the current running app."""
433  return self._child_attr_child_attr(ATTR_APP_ID)
434 
435  @property
436  def app_name(self):
437  """Name of the current running app."""
438  return self._child_attr_child_attr(ATTR_APP_NAME)
439 
440  @property
441  def sound_mode(self):
442  """Return the current sound mode of the device."""
443  return self._override_or_child_attr_override_or_child_attr(ATTR_SOUND_MODE)
444 
445  @property
446  def sound_mode_list(self):
447  """List of available sound modes."""
448  return self._override_or_child_attr_override_or_child_attr(ATTR_SOUND_MODE_LIST)
449 
450  @property
451  def source(self):
452  """Return the current input source of the device."""
453  return self._override_or_child_attr_override_or_child_attr(ATTR_INPUT_SOURCE)
454 
455  @property
456  def source_list(self):
457  """List of available input sources."""
458  return self._override_or_child_attr_override_or_child_attr(ATTR_INPUT_SOURCE_LIST)
459 
460  @property
461  def repeat(self):
462  """Boolean if repeating is enabled."""
463  return self._override_or_child_attr_override_or_child_attr(ATTR_MEDIA_REPEAT)
464 
465  @property
466  def shuffle(self):
467  """Boolean if shuffling is enabled."""
468  return self._override_or_child_attr_override_or_child_attr(ATTR_MEDIA_SHUFFLE)
469 
470  @property
471  def supported_features(self) -> MediaPlayerEntityFeature:
472  """Flag media player features that are supported."""
473  flags: MediaPlayerEntityFeature = self._child_attr_child_attr(
474  ATTR_SUPPORTED_FEATURES
476 
477  if SERVICE_TURN_ON in self._cmds_cmds:
478  flags |= MediaPlayerEntityFeature.TURN_ON
479  if SERVICE_TURN_OFF in self._cmds_cmds:
480  flags |= MediaPlayerEntityFeature.TURN_OFF
481 
482  if SERVICE_MEDIA_PLAY_PAUSE in self._cmds_cmds:
483  flags |= MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.PAUSE
484  else:
485  if SERVICE_MEDIA_PLAY in self._cmds_cmds:
486  flags |= MediaPlayerEntityFeature.PLAY
487  if SERVICE_MEDIA_PAUSE in self._cmds_cmds:
488  flags |= MediaPlayerEntityFeature.PAUSE
489 
490  if SERVICE_MEDIA_STOP in self._cmds_cmds:
491  flags |= MediaPlayerEntityFeature.STOP
492 
493  if SERVICE_MEDIA_NEXT_TRACK in self._cmds_cmds:
494  flags |= MediaPlayerEntityFeature.NEXT_TRACK
495  if SERVICE_MEDIA_PREVIOUS_TRACK in self._cmds_cmds:
496  flags |= MediaPlayerEntityFeature.PREVIOUS_TRACK
497 
498  if any(cmd in self._cmds_cmds for cmd in (SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN)):
499  flags |= MediaPlayerEntityFeature.VOLUME_STEP
500  if SERVICE_VOLUME_SET in self._cmds_cmds:
501  flags |= MediaPlayerEntityFeature.VOLUME_SET
502 
503  if SERVICE_VOLUME_MUTE in self._cmds_cmds and ATTR_MEDIA_VOLUME_MUTED in self._attrs_attrs:
504  flags |= MediaPlayerEntityFeature.VOLUME_MUTE
505 
506  if (
507  SERVICE_SELECT_SOURCE in self._cmds_cmds
508  and ATTR_INPUT_SOURCE_LIST in self._attrs_attrs
509  ):
510  flags |= MediaPlayerEntityFeature.SELECT_SOURCE
511 
512  if SERVICE_PLAY_MEDIA in self._cmds_cmds:
513  flags |= MediaPlayerEntityFeature.PLAY_MEDIA
514 
515  if self._browse_media_entity_browse_media_entity:
516  flags |= MediaPlayerEntityFeature.BROWSE_MEDIA
517 
518  if SERVICE_CLEAR_PLAYLIST in self._cmds_cmds:
519  flags |= MediaPlayerEntityFeature.CLEAR_PLAYLIST
520 
521  if SERVICE_SHUFFLE_SET in self._cmds_cmds and ATTR_MEDIA_SHUFFLE in self._attrs_attrs:
522  flags |= MediaPlayerEntityFeature.SHUFFLE_SET
523 
524  if SERVICE_REPEAT_SET in self._cmds_cmds and ATTR_MEDIA_REPEAT in self._attrs_attrs:
525  flags |= MediaPlayerEntityFeature.REPEAT_SET
526 
527  if (
528  SERVICE_SELECT_SOUND_MODE in self._cmds_cmds
529  and ATTR_SOUND_MODE_LIST in self._attrs_attrs
530  ):
531  flags |= MediaPlayerEntityFeature.SELECT_SOUND_MODE
532 
533  return flags
534 
535  @property
537  """Return device specific state attributes."""
538  active_child = self._child_state_child_state
539  return {ATTR_ACTIVE_CHILD: active_child.entity_id} if active_child else {}
540 
541  @property
542  def media_position(self):
543  """Position of current playing media in seconds."""
544  return self._child_attr_child_attr(ATTR_MEDIA_POSITION)
545 
546  @property
548  """When was the position of the current playing media valid."""
549  return self._child_attr_child_attr(ATTR_MEDIA_POSITION_UPDATED_AT)
550 
551  async def async_turn_on(self) -> None:
552  """Turn the media player on."""
553  await self._async_call_service_async_call_service(SERVICE_TURN_ON, allow_override=True)
554 
555  async def async_turn_off(self) -> None:
556  """Turn the media player off."""
557  await self._async_call_service_async_call_service(SERVICE_TURN_OFF, allow_override=True)
558 
559  async def async_mute_volume(self, mute: bool) -> None:
560  """Mute the volume."""
561  data = {ATTR_MEDIA_VOLUME_MUTED: mute}
562  await self._async_call_service_async_call_service(SERVICE_VOLUME_MUTE, data, allow_override=True)
563 
564  async def async_set_volume_level(self, volume: float) -> None:
565  """Set volume level, range 0..1."""
566  data = {ATTR_MEDIA_VOLUME_LEVEL: volume}
567  await self._async_call_service_async_call_service(SERVICE_VOLUME_SET, data, allow_override=True)
568 
569  async def async_media_play(self) -> None:
570  """Send play command."""
571  await self._async_call_service_async_call_service(SERVICE_MEDIA_PLAY, allow_override=True)
572 
573  async def async_media_pause(self) -> None:
574  """Send pause command."""
575  await self._async_call_service_async_call_service(SERVICE_MEDIA_PAUSE, allow_override=True)
576 
577  async def async_media_stop(self) -> None:
578  """Send stop command."""
579  await self._async_call_service_async_call_service(SERVICE_MEDIA_STOP, allow_override=True)
580 
581  async def async_media_previous_track(self) -> None:
582  """Send previous track command."""
583  await self._async_call_service_async_call_service(
584  SERVICE_MEDIA_PREVIOUS_TRACK, allow_override=True
585  )
586 
587  async def async_media_next_track(self) -> None:
588  """Send next track command."""
589  await self._async_call_service_async_call_service(SERVICE_MEDIA_NEXT_TRACK, allow_override=True)
590 
591  async def async_media_seek(self, position: float) -> None:
592  """Send seek command."""
593  data = {ATTR_MEDIA_SEEK_POSITION: position}
594  await self._async_call_service_async_call_service(SERVICE_MEDIA_SEEK, data)
595 
596  async def async_play_media(
597  self, media_type: MediaType | str, media_id: str, **kwargs: Any
598  ) -> None:
599  """Play a piece of media."""
600  data = {ATTR_MEDIA_CONTENT_TYPE: media_type, ATTR_MEDIA_CONTENT_ID: media_id}
601  await self._async_call_service_async_call_service(SERVICE_PLAY_MEDIA, data, allow_override=True)
602 
603  async def async_volume_up(self) -> None:
604  """Turn volume up for media player."""
605  await self._async_call_service_async_call_service(SERVICE_VOLUME_UP, allow_override=True)
606 
607  async def async_volume_down(self) -> None:
608  """Turn volume down for media player."""
609  await self._async_call_service_async_call_service(SERVICE_VOLUME_DOWN, allow_override=True)
610 
611  async def async_media_play_pause(self) -> None:
612  """Play or pause the media player."""
613  await self._async_call_service_async_call_service(SERVICE_MEDIA_PLAY_PAUSE, allow_override=True)
614 
615  async def async_select_sound_mode(self, sound_mode: str) -> None:
616  """Select sound mode."""
617  data = {ATTR_SOUND_MODE: sound_mode}
618  await self._async_call_service_async_call_service(
619  SERVICE_SELECT_SOUND_MODE, data, allow_override=True
620  )
621 
622  async def async_select_source(self, source: str) -> None:
623  """Set the input source."""
624  data = {ATTR_INPUT_SOURCE: source}
625  await self._async_call_service_async_call_service(SERVICE_SELECT_SOURCE, data, allow_override=True)
626 
627  async def async_clear_playlist(self) -> None:
628  """Clear players playlist."""
629  await self._async_call_service_async_call_service(SERVICE_CLEAR_PLAYLIST, allow_override=True)
630 
631  async def async_set_shuffle(self, shuffle: bool) -> None:
632  """Enable/disable shuffling."""
633  data = {ATTR_MEDIA_SHUFFLE: shuffle}
634  await self._async_call_service_async_call_service(SERVICE_SHUFFLE_SET, data, allow_override=True)
635 
636  async def async_set_repeat(self, repeat: RepeatMode) -> None:
637  """Set repeat mode."""
638  data = {ATTR_MEDIA_REPEAT: repeat}
639  await self._async_call_service_async_call_service(SERVICE_REPEAT_SET, data, allow_override=True)
640 
641  async def async_toggle(self) -> None:
642  """Toggle the power on the media player."""
643  if SERVICE_TOGGLE in self._cmds_cmds:
644  await self._async_call_service_async_call_service(SERVICE_TOGGLE, allow_override=True)
645  else:
646  # Delegate to turn_on or turn_off by default
647  await super().async_toggle()
648 
650  self,
651  media_content_type: MediaType | str | None = None,
652  media_content_id: str | None = None,
653  ) -> BrowseMedia:
654  """Return a BrowseMedia instance."""
655  entity_id = self._browse_media_entity_browse_media_entity
656  if not entity_id and self._child_state_child_state:
657  entity_id = self._child_state_child_state.entity_id
658  component: EntityComponent[MediaPlayerEntity] = self.hasshasshass.data[
659  MEDIA_PLAYER_DOMAIN
660  ]
661  if entity_id and (entity := component.get_entity(entity_id)):
662  return await entity.async_browse_media(media_content_type, media_content_id)
663  raise NotImplementedError
664 
665  @callback
666  def _async_update(self) -> None:
667  """Update state in HA."""
668  if self._active_child_template_result_active_child_template_result:
669  self._child_state_child_state = self.hasshasshass.states.get(self._active_child_template_result_active_child_template_result)
670  return
671  self._child_state_child_state = None
672  for child_name in self._children_children:
673  if (child_state := self.hasshasshass.states.get(child_name)) and (
674  child_state_order := STATES_ORDER_LOOKUP.get(child_state.state, 0)
675  ) >= STATES_ORDER_IDLE:
676  if self._child_state_child_state:
677  if child_state_order > STATES_ORDER_LOOKUP.get(
678  self._child_state_child_state.state, 0
679  ):
680  self._child_state_child_state = child_state
681  else:
682  self._child_state_child_state = child_state
683 
684  async def async_update(self) -> None:
685  """Manual update from API."""
686  self._async_update_async_update()
def _async_call_service(self, service_name, service_data=None, allow_override=False)
None async_play_media(self, MediaType|str media_type, str media_id, **Any kwargs)
BrowseMedia async_browse_media(self, MediaType|str|None media_content_type=None, str|None media_content_id=None)
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None async_set_context(self, Context context)
Definition: entity.py:937
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
CALLBACK_TYPE async_track_state_change_event(HomeAssistant hass, str|Iterable[str] entity_ids, Callable[[Event[EventStateChangedData]], Any] action, HassJobType|None job_type=None)
Definition: event.py:314
TrackTemplateResultInfo async_track_template_result(HomeAssistant hass, Sequence[TrackTemplate] track_templates, TrackTemplateResultListener action, bool strict=False, Callable[[int, str], None]|None log_fn=None, bool has_super_template=False)
Definition: event.py:1345
None async_setup_reload_service(HomeAssistant hass, str domain, Iterable[str] platforms)
Definition: reload.py:191
None async_call_from_config(HomeAssistant hass, ConfigType config, bool blocking=False, TemplateVarsType variables=None, bool validate_config=True, Context|None context=None)
Definition: service.py:331