mirror of
https://github.com/home-assistant/core.git
synced 2025-12-21 19:39:06 +00:00
180 lines
5.6 KiB
Python
180 lines
5.6 KiB
Python
"""Base Sensor for the Xbox Integration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Mapping
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from pythonxbox.api.provider.people.models import Person
|
|
from pythonxbox.api.provider.smartglass.models import ConsoleType, SmartglassConsole
|
|
from pythonxbox.api.provider.titlehub.models import Title
|
|
from yarl import URL
|
|
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import entity_registry as er
|
|
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
|
from homeassistant.helpers.entity import EntityDescription
|
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
|
|
from .const import DOMAIN
|
|
from .coordinator import ConsoleData, XboxUpdateCoordinator
|
|
|
|
MAP_MODEL = {
|
|
ConsoleType.XboxOne: "Xbox One",
|
|
ConsoleType.XboxOneS: "Xbox One S",
|
|
ConsoleType.XboxOneSDigital: "Xbox One S All-Digital",
|
|
ConsoleType.XboxOneX: "Xbox One X",
|
|
ConsoleType.XboxSeriesS: "Xbox Series S",
|
|
ConsoleType.XboxSeriesX: "Xbox Series X",
|
|
}
|
|
|
|
|
|
@dataclass(kw_only=True, frozen=True)
|
|
class XboxBaseEntityDescription(EntityDescription):
|
|
"""Xbox base entity description."""
|
|
|
|
entity_picture_fn: Callable[[Person, Title | None], str | None] | None = None
|
|
attributes_fn: Callable[[Person, Title | None], Mapping[str, Any] | None] | None = (
|
|
None
|
|
)
|
|
deprecated: bool | None = None
|
|
|
|
|
|
class XboxBaseEntity(CoordinatorEntity[XboxUpdateCoordinator]):
|
|
"""Base Sensor for the Xbox Integration."""
|
|
|
|
_attr_has_entity_name = True
|
|
entity_description: XboxBaseEntityDescription
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: XboxUpdateCoordinator,
|
|
xuid: str,
|
|
entity_description: XboxBaseEntityDescription,
|
|
) -> None:
|
|
"""Initialize Xbox entity."""
|
|
super().__init__(coordinator)
|
|
self.xuid = xuid
|
|
self.entity_description = entity_description
|
|
|
|
self._attr_unique_id = f"{xuid}_{entity_description.key}"
|
|
|
|
self._attr_device_info = DeviceInfo(
|
|
entry_type=DeviceEntryType.SERVICE,
|
|
identifiers={(DOMAIN, xuid)},
|
|
manufacturer="Microsoft",
|
|
model="Xbox Network",
|
|
name=self.data.gamertag,
|
|
)
|
|
|
|
@property
|
|
def data(self) -> Person:
|
|
"""Return coordinator data for this person."""
|
|
return self.coordinator.data.presence[self.xuid]
|
|
|
|
@property
|
|
def title_info(self) -> Title | None:
|
|
"""Return title info."""
|
|
return self.coordinator.data.title_info.get(self.xuid)
|
|
|
|
@property
|
|
def entity_picture(self) -> str | None:
|
|
"""Return the entity picture."""
|
|
|
|
return (
|
|
entity_picture
|
|
if self.available
|
|
and (fn := self.entity_description.entity_picture_fn) is not None
|
|
and (entity_picture := fn(self.data, self.title_info)) is not None
|
|
else super().entity_picture
|
|
)
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> Mapping[str, float | None] | None:
|
|
"""Return entity specific state attributes."""
|
|
return (
|
|
fn(self.data, self.title_info)
|
|
if (fn := self.entity_description.attributes_fn)
|
|
else super().extra_state_attributes
|
|
)
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return True if entity is available."""
|
|
|
|
return super().available and self.xuid in self.coordinator.data.presence
|
|
|
|
|
|
class XboxConsoleBaseEntity(CoordinatorEntity[XboxUpdateCoordinator]):
|
|
"""Console base entity for the Xbox integration."""
|
|
|
|
_attr_has_entity_name = True
|
|
|
|
def __init__(
|
|
self,
|
|
console: SmartglassConsole,
|
|
coordinator: XboxUpdateCoordinator,
|
|
) -> None:
|
|
"""Initialize the Xbox Console entity."""
|
|
|
|
super().__init__(coordinator)
|
|
self.client = coordinator.client
|
|
self._console = console
|
|
|
|
self._attr_name = None
|
|
self._attr_unique_id = console.id
|
|
|
|
self._attr_device_info = DeviceInfo(
|
|
identifiers={(DOMAIN, console.id)},
|
|
manufacturer="Microsoft",
|
|
model=MAP_MODEL.get(self._console.console_type),
|
|
name=console.name,
|
|
)
|
|
|
|
@property
|
|
def data(self) -> ConsoleData:
|
|
"""Return coordinator data for this console."""
|
|
return self.coordinator.data.consoles[self._console.id]
|
|
|
|
|
|
def check_deprecated_entity(
|
|
hass: HomeAssistant,
|
|
xuid: str,
|
|
entity_description: XboxBaseEntityDescription,
|
|
entity_domain: str,
|
|
) -> bool:
|
|
"""Check for deprecated entity and remove it."""
|
|
if not entity_description.deprecated:
|
|
return True
|
|
ent_reg = er.async_get(hass)
|
|
if entity_id := ent_reg.async_get_entity_id(
|
|
entity_domain,
|
|
DOMAIN,
|
|
f"{xuid}_{entity_description.key}",
|
|
):
|
|
ent_reg.async_remove(entity_id)
|
|
|
|
return False
|
|
|
|
|
|
def to_https(image_url: str) -> str:
|
|
"""Convert image URLs to secure URLs."""
|
|
|
|
url = URL(image_url)
|
|
if url.host == "images-eds.xboxlive.com":
|
|
url = url.with_host("images-eds-ssl.xboxlive.com")
|
|
return str(url.with_scheme("https"))
|
|
|
|
|
|
def profile_pic(person: Person, _: Title | None = None) -> str | None:
|
|
"""Return the gamer pic."""
|
|
|
|
# Xbox sometimes returns a domain that uses a wrong certificate which
|
|
# creates issues with loading the image.
|
|
# The correct domain is images-eds-ssl which can just be replaced
|
|
# to point to the correct image, with the correct domain and certificate.
|
|
# We need to also remove the 'mode=Padding' query because with it,
|
|
# it results in an error 400.
|
|
return str(URL(to_https(person.display_pic_raw)).without_query_params("mode"))
|