Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Coordinator for the xbox integration."""
2 
3 from __future__ import annotations
4 
5 from contextlib import suppress
6 from dataclasses import dataclass
7 from datetime import timedelta
8 import logging
9 
10 from xbox.webapi.api.client import XboxLiveClient
11 from xbox.webapi.api.provider.catalog.const import SYSTEM_PFN_ID_MAP
12 from xbox.webapi.api.provider.catalog.models import AlternateIdType, Product
13 from xbox.webapi.api.provider.people.models import (
14  PeopleResponse,
15  Person,
16  PresenceDetail,
17 )
18 from xbox.webapi.api.provider.smartglass.models import (
19  SmartglassConsoleList,
20  SmartglassConsoleStatus,
21 )
22 
23 from homeassistant.core import HomeAssistant
24 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
25 
26 from .const import DOMAIN
27 
28 _LOGGER = logging.getLogger(__name__)
29 
30 
31 @dataclass
33  """Xbox console status data."""
34 
35  status: SmartglassConsoleStatus
36  app_details: Product | None
37 
38 
39 @dataclass
41  """Xbox user presence data."""
42 
43  xuid: str
44  gamertag: str
45  display_pic: str
46  online: bool
47  status: str
48  in_party: bool
49  in_game: bool
50  in_multiplayer: bool
51  gamer_score: str
52  gold_tenure: str | None
53  account_tier: str
54 
55 
56 @dataclass
57 class XboxData:
58  """Xbox dataclass for update coordinator."""
59 
60  consoles: dict[str, ConsoleData]
61  presence: dict[str, PresenceData]
62 
63 
65  """Store Xbox Console Status."""
66 
67  def __init__(
68  self,
69  hass: HomeAssistant,
70  client: XboxLiveClient,
71  consoles: SmartglassConsoleList,
72  ) -> None:
73  """Initialize."""
74  super().__init__(
75  hass,
76  _LOGGER,
77  name=DOMAIN,
78  update_interval=timedelta(seconds=10),
79  )
80  self.datadatadata = XboxData({}, {})
81  self.client: XboxLiveClient = client
82  self.consoles: SmartglassConsoleList = consoles
83 
84  async def _async_update_data(self) -> XboxData:
85  """Fetch the latest console status."""
86  # Update Console Status
87  new_console_data: dict[str, ConsoleData] = {}
88  for console in self.consoles.result:
89  current_state: ConsoleData | None = self.datadatadata.consoles.get(console.id)
90  status: SmartglassConsoleStatus = (
91  await self.client.smartglass.get_console_status(console.id)
92  )
93 
94  _LOGGER.debug(
95  "%s status: %s",
96  console.name,
97  status.dict(),
98  )
99 
100  # Setup focus app
101  app_details: Product | None = None
102  if current_state is not None:
103  app_details = current_state.app_details
104 
105  if status.focus_app_aumid:
106  if (
107  not current_state
108  or status.focus_app_aumid != current_state.status.focus_app_aumid
109  ):
110  app_id = status.focus_app_aumid.split("!")[0]
111  id_type = AlternateIdType.PACKAGE_FAMILY_NAME
112  if app_id in SYSTEM_PFN_ID_MAP:
113  id_type = AlternateIdType.LEGACY_XBOX_PRODUCT_ID
114  app_id = SYSTEM_PFN_ID_MAP[app_id][id_type]
115  catalog_result = (
116  await self.client.catalog.get_product_from_alternate_id(
117  app_id, id_type
118  )
119  )
120  if catalog_result and catalog_result.products:
121  app_details = catalog_result.products[0]
122  else:
123  app_details = None
124 
125  new_console_data[console.id] = ConsoleData(
126  status=status, app_details=app_details
127  )
128 
129  # Update user presence
130  presence_data: dict[str, PresenceData] = {}
131  batch: PeopleResponse = await self.client.people.get_friends_own_batch(
132  [self.client.xuid]
133  )
134  own_presence: Person = batch.people[0]
135  presence_data[own_presence.xuid] = _build_presence_data(own_presence)
136 
137  friends: PeopleResponse = await self.client.people.get_friends_own()
138  for friend in friends.people:
139  if not friend.is_favorite:
140  continue
141 
142  presence_data[friend.xuid] = _build_presence_data(friend)
143 
144  return XboxData(new_console_data, presence_data)
145 
146 
147 def _build_presence_data(person: Person) -> PresenceData:
148  """Build presence data from a person."""
149  active_app: PresenceDetail | None = None
150  with suppress(StopIteration):
151  active_app = next(
152  presence for presence in person.presence_details if presence.is_primary
153  )
154 
155  return PresenceData(
156  xuid=person.xuid,
157  gamertag=person.gamertag,
158  display_pic=person.display_pic_raw,
159  online=person.presence_state == "Online",
160  status=person.presence_text,
161  in_party=person.multiplayer_summary.in_party > 0,
162  in_game=active_app is not None and active_app.is_game,
163  in_multiplayer=person.multiplayer_summary.in_multiplayer_session,
164  gamer_score=person.gamer_score,
165  gold_tenure=person.detail.tenure,
166  account_tier=person.detail.account_tier,
167  )
None __init__(self, HomeAssistant hass, XboxLiveClient client, SmartglassConsoleList consoles)
Definition: coordinator.py:72
PresenceData _build_presence_data(Person person)
Definition: coordinator.py:147