Home Assistant Unofficial Reference 2024.12.1
media_player.py
Go to the documentation of this file.
1 """Support for the DirecTV receivers."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from directv import DIRECTV
9 
11  MediaPlayerDeviceClass,
12  MediaPlayerEntity,
13  MediaPlayerEntityFeature,
14  MediaPlayerState,
15  MediaType,
16 )
17 from homeassistant.config_entries import ConfigEntry
18 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 from homeassistant.util import dt as dt_util
21 
22 from .const import (
23  ATTR_MEDIA_CURRENTLY_RECORDING,
24  ATTR_MEDIA_RATING,
25  ATTR_MEDIA_RECORDED,
26  ATTR_MEDIA_START_TIME,
27  DOMAIN,
28 )
29 from .entity import DIRECTVEntity
30 
31 _LOGGER = logging.getLogger(__name__)
32 
33 KNOWN_MEDIA_TYPES = {MediaType.MOVIE, MediaType.MUSIC, MediaType.TVSHOW}
34 
35 SUPPORT_DTV = (
36  MediaPlayerEntityFeature.PAUSE
37  | MediaPlayerEntityFeature.TURN_ON
38  | MediaPlayerEntityFeature.TURN_OFF
39  | MediaPlayerEntityFeature.PLAY_MEDIA
40  | MediaPlayerEntityFeature.STOP
41  | MediaPlayerEntityFeature.NEXT_TRACK
42  | MediaPlayerEntityFeature.PREVIOUS_TRACK
43  | MediaPlayerEntityFeature.PLAY
44 )
45 
46 SUPPORT_DTV_CLIENT = (
47  MediaPlayerEntityFeature.PAUSE
48  | MediaPlayerEntityFeature.PLAY_MEDIA
49  | MediaPlayerEntityFeature.STOP
50  | MediaPlayerEntityFeature.NEXT_TRACK
51  | MediaPlayerEntityFeature.PREVIOUS_TRACK
52  | MediaPlayerEntityFeature.PLAY
53 )
54 
55 
57  hass: HomeAssistant,
58  entry: ConfigEntry,
59  async_add_entities: AddEntitiesCallback,
60 ) -> None:
61  """Set up the DirecTV config entry."""
62  dtv = hass.data[DOMAIN][entry.entry_id]
63 
65  (
67  dtv=dtv,
68  name=str.title(location.name),
69  address=location.address,
70  )
71  for location in dtv.device.locations
72  ),
73  True,
74  )
75 
76 
78  """Representation of a DirecTV receiver on the network."""
79 
80  def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None:
81  """Initialize DirecTV media player."""
82  super().__init__(
83  dtv=dtv,
84  name=name,
85  address=address,
86  )
87 
88  self._attr_unique_id_attr_unique_id = self._device_id_device_id
89  self._attr_device_class_attr_device_class = MediaPlayerDeviceClass.RECEIVER
90  self._attr_available_attr_available = False
91 
92  self._is_recorded_is_recorded = None
93  self._is_standby_is_standby = True
94  self._last_position_last_position = None
95  self._last_update_last_update = None
96  self._paused_paused = None
97  self._program_program = None
98 
99  async def async_update(self) -> None:
100  """Retrieve latest state."""
101  state = await self.dtvdtv.state(self._address_address)
102  self._attr_available_attr_available = state.available
103  self._is_standby_is_standby = state.standby
104  self._program_program = state.program
105 
106  if self._is_standby_is_standby:
107  self._attr_assumed_state_attr_assumed_state = False
108  self._is_recorded_is_recorded = None
109  self._last_position_last_position = None
110  self._last_update_last_update = None
111  self._paused_paused = None
112  elif self._program_program is not None:
113  self._paused_paused = self._last_position_last_position == self._program_program.position
114  self._is_recorded_is_recorded = self._program_program.recorded
115  self._last_position_last_position = self._program_program.position
116  if not self._paused_paused:
117  self._last_update_last_update = dt_util.utcnow()
118  self._attr_assumed_state_attr_assumed_state = self._is_recorded_is_recorded
119 
120  @property
122  """Return device specific state attributes."""
123  if self._is_standby_is_standby:
124  return {}
125  return {
126  ATTR_MEDIA_CURRENTLY_RECORDING: self.media_currently_recordingmedia_currently_recording,
127  ATTR_MEDIA_RATING: self.media_ratingmedia_rating,
128  ATTR_MEDIA_RECORDED: self.media_recordedmedia_recorded,
129  ATTR_MEDIA_START_TIME: self.media_start_timemedia_start_time,
130  }
131 
132  # MediaPlayerEntity properties and methods
133  @property
134  def state(self) -> MediaPlayerState:
135  """Return the state of the device."""
136  if self._is_standby_is_standby:
137  return MediaPlayerState.OFF
138 
139  # For recorded media we can determine if it is paused or not.
140  # For live media we're unable to determine and will always return
141  # playing instead.
142  if self._paused_paused:
143  return MediaPlayerState.PAUSED
144 
145  return MediaPlayerState.PLAYING
146 
147  @property
148  def media_content_id(self):
149  """Return the content ID of current playing media."""
150  if self._is_standby_is_standby or self._program_program is None:
151  return None
152 
153  return self._program_program.program_id
154 
155  @property
156  def media_content_type(self) -> MediaType | None:
157  """Return the content type of current playing media."""
158  if self._is_standby_is_standby or self._program_program is None:
159  return None
160 
161  if self._program_program.program_type in KNOWN_MEDIA_TYPES:
162  return self._program_program.program_type
163 
164  return MediaType.MOVIE
165 
166  @property
167  def media_duration(self):
168  """Return the duration of current playing media in seconds."""
169  if self._is_standby_is_standby or self._program_program is None:
170  return None
171 
172  return self._program_program.duration
173 
174  @property
175  def media_position(self):
176  """Position of current playing media in seconds."""
177  if self._is_standby_is_standby:
178  return None
179 
180  return self._last_position_last_position
181 
182  @property
184  """When was the position of the current playing media valid."""
185  if self._is_standby_is_standby:
186  return None
187 
188  return self._last_update_last_update
189 
190  @property
191  def media_title(self):
192  """Return the title of current playing media."""
193  if self._is_standby_is_standby or self._program_program is None:
194  return None
195 
196  if self.media_content_typemedia_content_typemedia_content_typemedia_content_type == MediaType.MUSIC:
197  return self._program_program.music_title
198 
199  return self._program_program.title
200 
201  @property
202  def media_artist(self):
203  """Artist of current playing media, music track only."""
204  if self._is_standby_is_standby or self._program_program is None:
205  return None
206 
207  return self._program_program.music_artist
208 
209  @property
210  def media_album_name(self):
211  """Album name of current playing media, music track only."""
212  if self._is_standby_is_standby or self._program_program is None:
213  return None
214 
215  return self._program_program.music_album
216 
217  @property
219  """Return the title of current episode of TV show."""
220  if self._is_standby_is_standby or self._program_program is None:
221  return None
222 
223  return self._program_program.episode_title
224 
225  @property
226  def media_channel(self):
227  """Return the channel current playing media."""
228  if self._is_standby_is_standby or self._program_program is None:
229  return None
230 
231  return f"{self._program.channel_name} ({self._program.channel})"
232 
233  @property
234  def source(self):
235  """Name of the current input source."""
236  if self._is_standby_is_standby or self._program_program is None:
237  return None
238 
239  return self._program_program.channel
240 
241  @property
242  def supported_features(self) -> MediaPlayerEntityFeature:
243  """Flag media player features that are supported."""
244  return SUPPORT_DTV_CLIENT if self._is_client_is_client else SUPPORT_DTV
245 
246  @property
248  """If the media is currently being recorded or not."""
249  if self._is_standby_is_standby or self._program_program is None:
250  return None
251 
252  return self._program_program.recording
253 
254  @property
255  def media_rating(self):
256  """TV Rating of the current playing media."""
257  if self._is_standby_is_standby or self._program_program is None:
258  return None
259 
260  return self._program_program.rating
261 
262  @property
263  def media_recorded(self):
264  """If the media was recorded or live."""
265  if self._is_standby_is_standby:
266  return None
267 
268  return self._is_recorded_is_recorded
269 
270  @property
271  def media_start_time(self):
272  """Start time the program aired."""
273  if self._is_standby_is_standby or self._program_program is None:
274  return None
275 
276  return dt_util.as_local(self._program_program.start_time)
277 
278  async def async_turn_on(self) -> None:
279  """Turn on the receiver."""
280  if self._is_client_is_client:
281  raise NotImplementedError
282 
283  _LOGGER.debug("Turn on %s", self.namename)
284  await self.dtvdtv.remote("poweron", self._address_address)
285 
286  async def async_turn_off(self) -> None:
287  """Turn off the receiver."""
288  if self._is_client_is_client:
289  raise NotImplementedError
290 
291  _LOGGER.debug("Turn off %s", self.namename)
292  await self.dtvdtv.remote("poweroff", self._address_address)
293 
294  async def async_media_play(self) -> None:
295  """Send play command."""
296  _LOGGER.debug("Play on %s", self.namename)
297  await self.dtvdtv.remote("play", self._address_address)
298 
299  async def async_media_pause(self) -> None:
300  """Send pause command."""
301  _LOGGER.debug("Pause on %s", self.namename)
302  await self.dtvdtv.remote("pause", self._address_address)
303 
304  async def async_media_stop(self) -> None:
305  """Send stop command."""
306  _LOGGER.debug("Stop on %s", self.namename)
307  await self.dtvdtv.remote("stop", self._address_address)
308 
309  async def async_media_previous_track(self) -> None:
310  """Send rewind command."""
311  _LOGGER.debug("Rewind on %s", self.namename)
312  await self.dtvdtv.remote("rew", self._address_address)
313 
314  async def async_media_next_track(self) -> None:
315  """Send fast forward command."""
316  _LOGGER.debug("Fast forward on %s", self.namename)
317  await self.dtvdtv.remote("ffwd", self._address_address)
318 
319  async def async_play_media(
320  self, media_type: MediaType | str, media_id: str, **kwargs: Any
321  ) -> None:
322  """Select input source."""
323  if media_type != MediaType.CHANNEL:
324  _LOGGER.error(
325  "Invalid media type %s. Only %s is supported",
326  media_type,
327  MediaType.CHANNEL,
328  )
329  return
330 
331  _LOGGER.debug("Changing channel on %s to %s", self.namename, media_id)
332  await self.dtvdtv.tune(media_id, self._address_address)
None __init__(self, *DIRECTV dtv, str name, str address="0")
Definition: media_player.py:80
None async_play_media(self, MediaType|str media_type, str media_id, **Any kwargs)
str|UndefinedType|None name(self)
Definition: entity.py:738
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: media_player.py:60