Skip to content

Commit 5c070c8

Browse files
authored
Add new entities to Xbox integration (home-assistant#154911)
1 parent 854882d commit 5c070c8

File tree

8 files changed

+658
-5
lines changed

8 files changed

+658
-5
lines changed

homeassistant/components/xbox/binary_sensor.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class XboxBinarySensor(StrEnum):
2727
IN_PARTY = "in_party"
2828
IN_GAME = "in_game"
2929
IN_MULTIPLAYER = "in_multiplayer"
30+
HAS_GAME_PASS = "has_game_pass"
3031

3132

3233
@dataclass(kw_only=True, frozen=True)
@@ -79,6 +80,11 @@ def profile_pic(data: PresenceData) -> str | None:
7980
is_on_fn=lambda x: x.in_multiplayer,
8081
entity_registry_enabled_default=False,
8182
),
83+
XboxBinarySensorEntityDescription(
84+
key=XboxBinarySensor.HAS_GAME_PASS,
85+
translation_key=XboxBinarySensor.HAS_GAME_PASS,
86+
is_on_fn=lambda x: x.has_game_pass,
87+
),
8288
)
8389

8490

homeassistant/components/xbox/coordinator.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from dataclasses import dataclass, field
6-
from datetime import timedelta
6+
from datetime import UTC, datetime, timedelta
77
import logging
88

99
from httpx import HTTPStatusError, RequestError, TimeoutException
@@ -58,6 +58,10 @@ class PresenceData:
5858
gamer_score: str
5959
gold_tenure: str | None
6060
account_tier: str
61+
last_seen: datetime | None
62+
following_count: int
63+
follower_count: int
64+
has_game_pass: bool
6165

6266

6367
@dataclass
@@ -246,4 +250,12 @@ def _build_presence_data(person: Person) -> PresenceData:
246250
gamer_score=person.gamer_score,
247251
gold_tenure=person.detail.tenure,
248252
account_tier=person.detail.account_tier,
253+
last_seen=(
254+
person.last_seen_date_time_utc.replace(tzinfo=UTC)
255+
if person.last_seen_date_time_utc
256+
else None
257+
),
258+
follower_count=person.detail.follower_count,
259+
following_count=person.detail.following_count,
260+
has_game_pass=person.detail.has_game_pass,
249261
)

homeassistant/components/xbox/icons.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212
},
1313
"gold_tenure": {
1414
"default": "mdi:microsoft-xbox"
15+
},
16+
"last_online": {
17+
"default": "mdi:account-clock"
18+
},
19+
"follower": {
20+
"default": "mdi:account-arrow-left"
21+
},
22+
"following": {
23+
"default": "mdi:account-arrow-right"
1524
}
1625
},
1726
"binary_sensor": {
@@ -26,6 +35,9 @@
2635
},
2736
"in_multiplayer": {
2837
"default": "mdi:account-multiple"
38+
},
39+
"has_game_pass": {
40+
"default": "mdi:microsoft-xbox"
2941
}
3042
}
3143
}

homeassistant/components/xbox/sensor.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
from collections.abc import Callable
66
from dataclasses import dataclass
7+
from datetime import datetime
78
from enum import StrEnum
89
from functools import partial
910

10-
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
11+
from homeassistant.components.sensor import (
12+
SensorDeviceClass,
13+
SensorEntity,
14+
SensorEntityDescription,
15+
)
1116
from homeassistant.core import HomeAssistant, callback
1217
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1318
from homeassistant.helpers.typing import StateType
@@ -23,13 +28,16 @@ class XboxSensor(StrEnum):
2328
GAMER_SCORE = "gamer_score"
2429
ACCOUNT_TIER = "account_tier"
2530
GOLD_TENURE = "gold_tenure"
31+
LAST_ONLINE = "last_online"
32+
FOLLOWING = "following"
33+
FOLLOWER = "follower"
2634

2735

2836
@dataclass(kw_only=True, frozen=True)
2937
class XboxSensorEntityDescription(SensorEntityDescription):
3038
"""Xbox sensor description."""
3139

32-
value_fn: Callable[[PresenceData], StateType]
40+
value_fn: Callable[[PresenceData], StateType | datetime]
3341

3442

3543
SENSOR_DESCRIPTIONS: tuple[XboxSensorEntityDescription, ...] = (
@@ -55,6 +63,22 @@ class XboxSensorEntityDescription(SensorEntityDescription):
5563
entity_registry_enabled_default=False,
5664
value_fn=lambda x: x.gold_tenure,
5765
),
66+
XboxSensorEntityDescription(
67+
key=XboxSensor.LAST_ONLINE,
68+
translation_key=XboxSensor.LAST_ONLINE,
69+
value_fn=(lambda x: x.last_seen),
70+
device_class=SensorDeviceClass.TIMESTAMP,
71+
),
72+
XboxSensorEntityDescription(
73+
key=XboxSensor.FOLLOWING,
74+
translation_key=XboxSensor.FOLLOWING,
75+
value_fn=lambda x: x.following_count,
76+
),
77+
XboxSensorEntityDescription(
78+
key=XboxSensor.FOLLOWER,
79+
translation_key=XboxSensor.FOLLOWER,
80+
value_fn=lambda x: x.follower_count,
81+
),
5882
)
5983

6084

@@ -78,7 +102,7 @@ class XboxSensorEntity(XboxBaseEntity, SensorEntity):
78102
entity_description: XboxSensorEntityDescription
79103

80104
@property
81-
def native_value(self) -> StateType:
105+
def native_value(self) -> StateType | datetime:
82106
"""Return the state of the requested attribute."""
83107
return self.entity_description.value_fn(self.data)
84108

homeassistant/components/xbox/strings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@
3838
},
3939
"gold_tenure": {
4040
"name": "Gold tenure"
41+
},
42+
"last_online": {
43+
"name": "Last online"
44+
},
45+
"following": {
46+
"name": "Following",
47+
"unit_of_measurement": "people"
48+
},
49+
"follower": {
50+
"name": "Follower",
51+
"unit_of_measurement": "[%key:component::xbox::entity::sensor::following::unit_of_measurement%]"
4152
}
4253
},
4354
"binary_sensor": {
@@ -49,6 +60,9 @@
4960
},
5061
"in_multiplayer": {
5162
"name": "In multiplayer"
63+
},
64+
"has_game_pass": {
65+
"name": "Subscribed to Xbox Game Pass"
5266
}
5367
}
5468
},

tests/components/xbox/fixtures/people_batch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"mute": false,
6868
"followerCount": 105,
6969
"followingCount": 121,
70-
"hasGamePass": false
70+
"hasGamePass": true
7171
},
7272
"communityManagerTitles": null,
7373
"socialManager": null,

tests/components/xbox/snapshots/test_binary_sensor.ambr

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,54 @@
192192
'state': 'off',
193193
})
194194
# ---
195+
# name: test_binary_sensors[binary_sensor.erics273_subscribed_to_xbox_game_pass-entry]
196+
EntityRegistryEntrySnapshot({
197+
'aliases': set({
198+
}),
199+
'area_id': None,
200+
'capabilities': None,
201+
'config_entry_id': <ANY>,
202+
'config_subentry_id': <ANY>,
203+
'device_class': None,
204+
'device_id': <ANY>,
205+
'disabled_by': None,
206+
'domain': 'binary_sensor',
207+
'entity_category': None,
208+
'entity_id': 'binary_sensor.erics273_subscribed_to_xbox_game_pass',
209+
'has_entity_name': True,
210+
'hidden_by': None,
211+
'icon': None,
212+
'id': <ANY>,
213+
'labels': set({
214+
}),
215+
'name': None,
216+
'options': dict({
217+
}),
218+
'original_device_class': None,
219+
'original_icon': None,
220+
'original_name': 'Subscribed to Xbox Game Pass',
221+
'platform': 'xbox',
222+
'previous_unique_id': None,
223+
'suggested_object_id': None,
224+
'supported_features': 0,
225+
'translation_key': <XboxBinarySensor.HAS_GAME_PASS: 'has_game_pass'>,
226+
'unique_id': '2533274913657542_has_game_pass',
227+
'unit_of_measurement': None,
228+
})
229+
# ---
230+
# name: test_binary_sensors[binary_sensor.erics273_subscribed_to_xbox_game_pass-state]
231+
StateSnapshot({
232+
'attributes': ReadOnlyDict({
233+
'friendly_name': 'erics273 Subscribed to Xbox Game Pass',
234+
}),
235+
'context': <ANY>,
236+
'entity_id': 'binary_sensor.erics273_subscribed_to_xbox_game_pass',
237+
'last_changed': <ANY>,
238+
'last_reported': <ANY>,
239+
'last_updated': <ANY>,
240+
'state': 'off',
241+
})
242+
# ---
195243
# name: test_binary_sensors[binary_sensor.gsr_ae-entry]
196244
EntityRegistryEntrySnapshot({
197245
'aliases': set({
@@ -385,6 +433,54 @@
385433
'state': 'off',
386434
})
387435
# ---
436+
# name: test_binary_sensors[binary_sensor.gsr_ae_subscribed_to_xbox_game_pass-entry]
437+
EntityRegistryEntrySnapshot({
438+
'aliases': set({
439+
}),
440+
'area_id': None,
441+
'capabilities': None,
442+
'config_entry_id': <ANY>,
443+
'config_subentry_id': <ANY>,
444+
'device_class': None,
445+
'device_id': <ANY>,
446+
'disabled_by': None,
447+
'domain': 'binary_sensor',
448+
'entity_category': None,
449+
'entity_id': 'binary_sensor.gsr_ae_subscribed_to_xbox_game_pass',
450+
'has_entity_name': True,
451+
'hidden_by': None,
452+
'icon': None,
453+
'id': <ANY>,
454+
'labels': set({
455+
}),
456+
'name': None,
457+
'options': dict({
458+
}),
459+
'original_device_class': None,
460+
'original_icon': None,
461+
'original_name': 'Subscribed to Xbox Game Pass',
462+
'platform': 'xbox',
463+
'previous_unique_id': None,
464+
'suggested_object_id': None,
465+
'supported_features': 0,
466+
'translation_key': <XboxBinarySensor.HAS_GAME_PASS: 'has_game_pass'>,
467+
'unique_id': '271958441785640_has_game_pass',
468+
'unit_of_measurement': None,
469+
})
470+
# ---
471+
# name: test_binary_sensors[binary_sensor.gsr_ae_subscribed_to_xbox_game_pass-state]
472+
StateSnapshot({
473+
'attributes': ReadOnlyDict({
474+
'friendly_name': 'GSR Ae Subscribed to Xbox Game Pass',
475+
}),
476+
'context': <ANY>,
477+
'entity_id': 'binary_sensor.gsr_ae_subscribed_to_xbox_game_pass',
478+
'last_changed': <ANY>,
479+
'last_reported': <ANY>,
480+
'last_updated': <ANY>,
481+
'state': 'on',
482+
})
483+
# ---
388484
# name: test_binary_sensors[binary_sensor.ikken_hissatsuu-entry]
389485
EntityRegistryEntrySnapshot({
390486
'aliases': set({
@@ -578,3 +674,51 @@
578674
'state': 'off',
579675
})
580676
# ---
677+
# name: test_binary_sensors[binary_sensor.ikken_hissatsuu_subscribed_to_xbox_game_pass-entry]
678+
EntityRegistryEntrySnapshot({
679+
'aliases': set({
680+
}),
681+
'area_id': None,
682+
'capabilities': None,
683+
'config_entry_id': <ANY>,
684+
'config_subentry_id': <ANY>,
685+
'device_class': None,
686+
'device_id': <ANY>,
687+
'disabled_by': None,
688+
'domain': 'binary_sensor',
689+
'entity_category': None,
690+
'entity_id': 'binary_sensor.ikken_hissatsuu_subscribed_to_xbox_game_pass',
691+
'has_entity_name': True,
692+
'hidden_by': None,
693+
'icon': None,
694+
'id': <ANY>,
695+
'labels': set({
696+
}),
697+
'name': None,
698+
'options': dict({
699+
}),
700+
'original_device_class': None,
701+
'original_icon': None,
702+
'original_name': 'Subscribed to Xbox Game Pass',
703+
'platform': 'xbox',
704+
'previous_unique_id': None,
705+
'suggested_object_id': None,
706+
'supported_features': 0,
707+
'translation_key': <XboxBinarySensor.HAS_GAME_PASS: 'has_game_pass'>,
708+
'unique_id': '2533274838782903_has_game_pass',
709+
'unit_of_measurement': None,
710+
})
711+
# ---
712+
# name: test_binary_sensors[binary_sensor.ikken_hissatsuu_subscribed_to_xbox_game_pass-state]
713+
StateSnapshot({
714+
'attributes': ReadOnlyDict({
715+
'friendly_name': 'Ikken Hissatsuu Subscribed to Xbox Game Pass',
716+
}),
717+
'context': <ANY>,
718+
'entity_id': 'binary_sensor.ikken_hissatsuu_subscribed_to_xbox_game_pass',
719+
'last_changed': <ANY>,
720+
'last_reported': <ANY>,
721+
'last_updated': <ANY>,
722+
'state': 'off',
723+
})
724+
# ---

0 commit comments

Comments
 (0)