1 """Xbox Media Player Support."""
3 from __future__
import annotations
8 from xbox.webapi.api.client
import XboxLiveClient
9 from xbox.webapi.api.provider.catalog.models
import Image
10 from xbox.webapi.api.provider.smartglass.models
import (
14 SmartglassConsoleList,
20 MediaPlayerEntityFeature,
30 from .browse_media
import build_item_response
31 from .const
import DOMAIN
32 from .coordinator
import ConsoleData, XboxUpdateCoordinator
35 MediaPlayerEntityFeature.TURN_ON
36 | MediaPlayerEntityFeature.TURN_OFF
37 | MediaPlayerEntityFeature.PREVIOUS_TRACK
38 | MediaPlayerEntityFeature.NEXT_TRACK
39 | MediaPlayerEntityFeature.PLAY
40 | MediaPlayerEntityFeature.PAUSE
41 | MediaPlayerEntityFeature.VOLUME_STEP
42 | MediaPlayerEntityFeature.VOLUME_MUTE
43 | MediaPlayerEntityFeature.BROWSE_MEDIA
44 | MediaPlayerEntityFeature.PLAY_MEDIA
47 XBOX_STATE_MAP: dict[PlaybackState | PowerState, MediaPlayerState |
None] = {
48 PlaybackState.Playing: MediaPlayerState.PLAYING,
49 PlaybackState.Paused: MediaPlayerState.PAUSED,
50 PowerState.On: MediaPlayerState.ON,
51 PowerState.SystemUpdate: MediaPlayerState.OFF,
52 PowerState.ConnectedStandby: MediaPlayerState.OFF,
53 PowerState.Off: MediaPlayerState.OFF,
54 PowerState.Unknown:
None,
59 hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
61 """Set up Xbox media_player from a config entry."""
62 client: XboxLiveClient = hass.data[DOMAIN][entry.entry_id][
"client"]
63 consoles: SmartglassConsoleList = hass.data[DOMAIN][entry.entry_id][
"consoles"]
64 coordinator: XboxUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
69 [
XboxMediaPlayer(client, console, coordinator)
for console
in consoles.result]
74 """Representation of an Xbox Media Player."""
78 client: XboxLiveClient,
79 console: SmartglassConsole,
80 coordinator: XboxUpdateCoordinator,
82 """Initialize the Xbox Media Player."""
84 self.client: XboxLiveClient = client
85 self._console: SmartglassConsole = console
89 """Return the device name."""
90 return self._console.name
94 """Console device ID."""
95 return self._console.id
98 def data(self) -> ConsoleData:
99 """Return coordinator data for this console."""
100 return self.coordinator.data.consoles[self._console.id]
103 def state(self) -> MediaPlayerState | None:
104 """State of the player."""
106 if status.playback_state
in XBOX_STATE_MAP:
107 return XBOX_STATE_MAP[status.playback_state]
108 return XBOX_STATE_MAP[status.power_state]
112 """Flag media player features that are supported."""
116 & ~MediaPlayerEntityFeature.NEXT_TRACK
117 & ~MediaPlayerEntityFeature.PREVIOUS_TRACK
123 """Media content type."""
125 if app_details
and app_details.product_family ==
"Games":
126 return MediaType.GAME
131 """Title of current playing media."""
132 if not (app_details := self.
datadatadatadata.app_details):
135 app_details.localized_properties[0].product_title
136 or app_details.localized_properties[0].short_title
141 """Image url of current playing media."""
142 if not (app_details := self.
datadatadatadata.app_details):
156 """If the image url is remotely accessible."""
160 """Turn the media player on."""
161 await self.client.smartglass.wake_up(self._console.id)
164 """Turn the media player off."""
165 await self.client.smartglass.turn_off(self._console.id)
168 """Mute the volume."""
170 await self.client.smartglass.mute(self._console.id)
172 await self.client.smartglass.unmute(self._console.id)
175 """Turn volume up for media player."""
176 await self.client.smartglass.volume(self._console.id, VolumeDirection.Up)
179 """Turn volume down for media player."""
180 await self.client.smartglass.volume(self._console.id, VolumeDirection.Down)
183 """Send play command."""
184 await self.client.smartglass.play(self._console.id)
187 """Send pause command."""
188 await self.client.smartglass.pause(self._console.id)
191 """Send previous track command."""
192 await self.client.smartglass.previous(self._console.id)
195 """Send next track command."""
196 await self.client.smartglass.next(self._console.id)
199 """Implement the websocket media browsing helper."""
209 self, media_type: MediaType | str, media_id: str, **kwargs: Any
211 """Launch an app on the Xbox."""
212 if media_id ==
"Home":
213 await self.client.smartglass.go_home(self._console.id)
214 elif media_id ==
"TV":
215 await self.client.smartglass.show_tv_guide(self._console.id)
217 await self.client.smartglass.launch_app(self._console.id, media_id)
221 """Return a device description for device registry."""
223 matches = re.finditer(
224 ".+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)",
225 self._console.console_type,
229 identifiers={(DOMAIN, self._console.id)},
230 manufacturer=
"Microsoft",
231 model=
" ".join([m.group(0)
for m
in matches]),
232 name=self._console.name,
237 purpose_order = [
"FeaturePromotionalSquareArt",
"Tile",
"Logo",
"BoxArt"]
238 for purpose
in purpose_order:
241 image.image_purpose == purpose
242 and image.width == image.height
243 and image.width >= 300