Files
core/homeassistant/components/xbox/binary_sensor.py

149 lines
4.3 KiB
Python

"""Xbox friends binary sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from enum import StrEnum
from typing import Any
from pythonxbox.api.provider.people.models import Person
from pythonxbox.api.provider.titlehub.models import Title
from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import XboxConfigEntry
from .entity import (
XboxBaseEntity,
XboxBaseEntityDescription,
check_deprecated_entity,
profile_pic,
)
class XboxBinarySensor(StrEnum):
"""Xbox binary sensor."""
ONLINE = "online"
IN_PARTY = "in_party"
IN_GAME = "in_game"
IN_MULTIPLAYER = "in_multiplayer"
HAS_GAME_PASS = "has_game_pass"
@dataclass(kw_only=True, frozen=True)
class XboxBinarySensorEntityDescription(
XboxBaseEntityDescription, BinarySensorEntityDescription
):
"""Xbox binary sensor description."""
is_on_fn: Callable[[Person], bool | None]
def profile_attributes(person: Person, _: Title | None) -> dict[str, Any]:
"""Attributes for the profile."""
attributes: dict[str, Any] = {}
attributes["display_name"] = person.display_name
attributes["real_name"] = person.real_name or None
attributes["bio"] = person.detail.bio if person.detail else None
attributes["location"] = person.detail.location if person.detail else None
return attributes
def in_game(person: Person) -> bool:
"""True if person is in a game."""
active_app = (
next(
(presence for presence in person.presence_details if presence.is_primary),
None,
)
if person.presence_details
else None
)
return (
active_app is not None and active_app.is_game and active_app.state == "Active"
)
SENSOR_DESCRIPTIONS: tuple[XboxBinarySensorEntityDescription, ...] = (
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.ONLINE,
translation_key=XboxBinarySensor.ONLINE,
is_on_fn=lambda x: x.presence_state == "Online",
name=None,
entity_picture_fn=profile_pic,
attributes_fn=profile_attributes,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.IN_PARTY,
is_on_fn=lambda _: None,
deprecated=True,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.IN_GAME,
translation_key=XboxBinarySensor.IN_GAME,
is_on_fn=in_game,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.IN_MULTIPLAYER,
is_on_fn=lambda _: None,
deprecated=True,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.HAS_GAME_PASS,
translation_key=XboxBinarySensor.HAS_GAME_PASS,
is_on_fn=lambda x: x.detail.has_game_pass if x.detail else None,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: XboxConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Xbox Live friends."""
xuids_added: set[str] = set()
coordinator = entry.runtime_data.status
@callback
def add_entities() -> None:
nonlocal xuids_added
current_xuids = set(coordinator.data.presence)
if new_xuids := current_xuids - xuids_added:
async_add_entities(
[
XboxBinarySensorEntity(coordinator, xuid, description)
for xuid in new_xuids
for description in SENSOR_DESCRIPTIONS
if check_deprecated_entity(
hass, xuid, description, BINARY_SENSOR_DOMAIN
)
]
)
xuids_added |= new_xuids
xuids_added &= current_xuids
coordinator.async_add_listener(add_entities)
add_entities()
class XboxBinarySensorEntity(XboxBaseEntity, BinarySensorEntity):
"""Representation of a Xbox presence state."""
entity_description: XboxBinarySensorEntityDescription
@property
def is_on(self) -> bool | None:
"""Return the status of the requested attribute."""
return self.entity_description.is_on_fn(self.data)