1 """Support for interfacing to iTunes API."""
3 from __future__
import annotations
8 import voluptuous
as vol
11 PLATFORM_SCHEMA
as MEDIA_PLAYER_PLATFORM_SCHEMA,
13 MediaPlayerEntityFeature,
23 DEFAULT_NAME =
"iTunes"
30 PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
32 vol.Required(CONF_HOST): cv.string,
33 vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
34 vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
35 vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
41 """The iTunes API client."""
44 """Initialize the iTunes device."""
51 """Return the base URL for endpoints."""
53 uri_scheme =
"https://"
55 uri_scheme =
"http://"
58 return f
"{uri_scheme}{self.host}:{self.port}"
60 return f
"{uri_scheme}{self.host}"
63 """Make the actual request and return the parsed response."""
64 url = f
"{self._base_url}{path}"
68 response = requests.get(url, timeout=DEFAULT_TIMEOUT)
69 elif method
in (
"POST",
"PUT"):
70 response = requests.put(url, params, timeout=DEFAULT_TIMEOUT)
71 elif method ==
"DELETE":
72 response = requests.delete(url, timeout=DEFAULT_TIMEOUT)
74 return response.json()
75 except requests.exceptions.HTTPError:
76 return {
"player_state":
"error"}
77 except requests.exceptions.RequestException:
78 return {
"player_state":
"offline"}
81 """Make a request for a controlling command."""
82 return self.
_request_request(
"PUT", f
"/{named_command}")
85 """Return the current state."""
86 return self.
_request_request(
"GET",
"/now_playing")
89 """Set the volume and returns the current state, level 0-100."""
90 return self.
_request_request(
"PUT",
"/volume", {
"level": level})
93 """Mute and returns the current state, muted True or False."""
94 return self.
_request_request(
"PUT",
"/mute", {
"muted": muted})
97 """Set the shuffle mode, shuffle True or False."""
99 "PUT",
"/shuffle", {
"mode": (
"songs" if shuffle
else "off")}
103 """Set playback to play and returns the current state."""
104 return self.
_command_command(
"play")
107 """Set playback to paused and returns the current state."""
108 return self.
_command_command(
"pause")
111 """Skip to the next track and returns the current state."""
112 return self.
_command_command(
"next")
115 """Skip back and returns the current state."""
116 return self.
_command_command(
"previous")
119 """Stop playback and return the current state."""
120 return self.
_command_command(
"stop")
123 """Set a playlist to be current and returns the current state."""
124 response = self.
_request_request(
"GET",
"/playlists")
125 playlists = response.get(
"playlists", [])
129 for playlist
in playlists
130 if (playlist_id_or_name
in [playlist[
"name"], playlist[
"id"]])
134 playlist = found_playlists[0]
135 path = f
"/playlists/{playlist['id']}/play"
136 return self.
_request_request(
"PUT", path)
138 raise ValueError(f
"Playlist {playlist_id_or_name} not found")
141 """Return a URL of the current track's album art."""
142 return f
"{self._base_url}/artwork"
145 """Return a list of AirPlay devices."""
146 return self.
_request_request(
"GET",
"/airplay_devices")
149 """Return an AirPlay device."""
150 return self.
_request_request(
"GET", f
"/airplay_devices/{device_id}")
153 """Toggle airplay device on or off, id, toggle True or False."""
154 command =
"on" if toggle
else "off"
155 path = f
"/airplay_devices/{device_id}/{command}"
156 return self.
_request_request(
"PUT", path)
159 """Set volume, returns current state of device, id,level 0-100."""
160 path = f
"/airplay_devices/{device_id}/volume"
161 return self.
_request_request(
"PUT", path, {
"level": level})
167 add_entities: AddEntitiesCallback,
168 discovery_info: DiscoveryInfoType |
None =
None,
170 """Set up the iTunes platform."""
174 config.get(CONF_NAME),
175 config.get(CONF_HOST),
176 config.get(CONF_PORT),
185 """Representation of an iTunes API instance."""
187 _attr_media_content_type = MediaType.MUSIC
188 _attr_supported_features = (
189 MediaPlayerEntityFeature.PAUSE
190 | MediaPlayerEntityFeature.VOLUME_SET
191 | MediaPlayerEntityFeature.VOLUME_MUTE
192 | MediaPlayerEntityFeature.PREVIOUS_TRACK
193 | MediaPlayerEntityFeature.NEXT_TRACK
194 | MediaPlayerEntityFeature.SEEK
195 | MediaPlayerEntityFeature.PLAY_MEDIA
196 | MediaPlayerEntityFeature.PLAY
197 | MediaPlayerEntityFeature.TURN_OFF
198 | MediaPlayerEntityFeature.SHUFFLE_SET
201 def __init__(self, name, host, port, use_ssl, add_entities):
202 """Initialize the iTunes device."""
227 """Update all the state properties with the passed in dictionary."""
228 self.
player_stateplayer_state = state_hash.get(
"player_state",
None)
231 self.
mutedmuted = state_hash.get(
"muted",
None)
232 self.
current_titlecurrent_title = state_hash.get(
"name",
None)
233 self.
current_albumcurrent_album = state_hash.get(
"album",
None)
234 self.
current_artistcurrent_artist = state_hash.get(
"artist",
None)
236 self.
content_idcontent_id = state_hash.get(
"id",
None)
238 _shuffle = state_hash.get(
"shuffle",
None)
239 self.
shuffledshuffled = _shuffle ==
"songs"
243 """Return the name of the device."""
244 return self.
_name_name
248 """Return the state of the device."""
256 return MediaPlayerState.IDLE
259 return MediaPlayerState.PAUSED
261 return MediaPlayerState.PLAYING
264 """Retrieve latest state."""
265 now_playing = self.
clientclient.now_playing()
269 found_devices = found_devices.get(
"airplay_devices", [])
273 for device_data
in found_devices:
274 device_id = device_data.get(
"id")
279 airplay_device.update_state(device_data)
283 airplay_device.update_state(device_data)
285 new_devices.append(airplay_device)
292 """Boolean if volume is currently muted."""
293 return self.
mutedmuted
297 """Volume level of the media player (0..1)."""
302 """Content ID of current playing media."""
307 """Image url of current playing media."""
311 MediaPlayerState.PLAYING,
312 MediaPlayerState.IDLE,
313 MediaPlayerState.PAUSED,
317 return f
"{self.client.artwork_url()}?id={self.content_id}"
320 "https://cloud.githubusercontent.com/assets/260/9829355"
321 "/33fab972-58cf-11e5-8ea2-2ca74bdaae40.png"
326 """Title of current playing media."""
331 """Artist of current playing media (Music track only)."""
336 """Album of current playing media (Music track only)."""
341 """Title of the currently playing playlist."""
346 """Boolean if shuffle is enabled."""
350 """Set volume level, range 0..1."""
351 response = self.
clientclient.set_volume(
int(volume * 100))
355 """Mute (true) or unmute (false) media player."""
356 response = self.
clientclient.set_muted(mute)
360 """Shuffle (true) or no shuffle (false) media player."""
365 """Send media_play command to media player."""
366 response = self.
clientclient.play()
370 """Send media_pause command to media player."""
371 response = self.
clientclient.pause()
375 """Send media_next command to media player."""
376 response = self.
clientclient.next()
380 """Send media_previous command media player."""
381 response = self.
clientclient.previous()
385 self, media_type: MediaType | str, media_id: str, **kwargs: Any
387 """Send the play_media command to the media player."""
388 if media_type == MediaType.PLAYLIST:
389 response = self.
clientclient.play_playlist(media_id)
393 """Turn the media player off."""
394 response = self.
clientclient.stop()
399 """Representation an AirPlay device via an iTunes API instance."""
401 _attr_media_content_type = MediaType.MUSIC
402 _attr_supported_features = (
403 MediaPlayerEntityFeature.VOLUME_SET
404 | MediaPlayerEntityFeature.TURN_ON
405 | MediaPlayerEntityFeature.TURN_OFF
409 """Initialize the AirPlay device."""
422 """Update all the state properties with the passed in dictionary."""
423 if "player_state" in state_hash:
424 self.
player_stateplayer_state = state_hash.get(
"player_state",
None)
426 if "name" in state_hash:
427 name = state_hash.get(
"name",
"")
428 self.
device_namedevice_name = f
"{name} AirTunes Speaker".strip()
430 if "kind" in state_hash:
431 self.
kindkind = state_hash.get(
"kind",
None)
433 if "active" in state_hash:
434 self.
activeactive = state_hash.get(
"active",
None)
436 if "selected" in state_hash:
437 self.
selectedselected = state_hash.get(
"selected",
None)
439 if "sound_volume" in state_hash:
440 self.
volumevolume = state_hash.get(
"sound_volume", 0)
442 if "supports_audio" in state_hash:
443 self.
supports_audiosupports_audio = state_hash.get(
"supports_audio",
None)
445 if "supports_video" in state_hash:
446 self.
supports_videosupports_video = state_hash.get(
"supports_video",
None)
450 """Return the name of the device."""
455 """Return the icon to use in the frontend, if any."""
457 return "mdi:volume-high"
459 return "mdi:volume-off"
462 def state(self) -> MediaPlayerState:
463 """Return the state of the device."""
465 return MediaPlayerState.ON
467 return MediaPlayerState.OFF
470 """Retrieve latest state."""
474 """Return the volume."""
478 """Set volume level, range 0..1."""
479 volume =
int(volume * 100)
480 response = self.
clientclient.set_volume_airplay_device(self.
_id_id, volume)
484 """Select AirPlay."""
487 response = self.
clientclient.toggle_airplay_device(self.
_id_id,
True)
491 """Deselect AirPlay."""
494 response = self.
clientclient.toggle_airplay_device(self.
_id_id,
False)
None schedule_update_ha_state(self, bool force_refresh=False)
None add_entities(AsusWrtRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
web.Response get(self, web.Request request, str config_key)