|  | 
|  | 1 | +"""Image platform for the Xbox integration.""" | 
|  | 2 | + | 
|  | 3 | +from __future__ import annotations | 
|  | 4 | + | 
|  | 5 | +from collections.abc import Callable | 
|  | 6 | +from dataclasses import dataclass | 
|  | 7 | +from enum import StrEnum | 
|  | 8 | + | 
|  | 9 | +from xbox.webapi.api.provider.people.models import Person | 
|  | 10 | +from xbox.webapi.api.provider.titlehub.models import Title | 
|  | 11 | + | 
|  | 12 | +from homeassistant.components.image import ImageEntity, ImageEntityDescription | 
|  | 13 | +from homeassistant.core import HomeAssistant, callback | 
|  | 14 | +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback | 
|  | 15 | +from homeassistant.util import dt as dt_util | 
|  | 16 | + | 
|  | 17 | +from .coordinator import XboxConfigEntry, XboxUpdateCoordinator | 
|  | 18 | +from .entity import XboxBaseEntity, XboxBaseEntityDescription, profile_pic | 
|  | 19 | + | 
|  | 20 | +PARALLEL_UPDATES = 0 | 
|  | 21 | + | 
|  | 22 | + | 
|  | 23 | +class XboxImage(StrEnum): | 
|  | 24 | +    """Xbox image.""" | 
|  | 25 | + | 
|  | 26 | +    NOW_PLAYING = "now_playing" | 
|  | 27 | +    GAMERPIC = "gamerpic" | 
|  | 28 | +    AVATAR = "avatar" | 
|  | 29 | + | 
|  | 30 | + | 
|  | 31 | +@dataclass(kw_only=True, frozen=True) | 
|  | 32 | +class XboxImageEntityDescription(XboxBaseEntityDescription, ImageEntityDescription): | 
|  | 33 | +    """Xbox image description.""" | 
|  | 34 | + | 
|  | 35 | +    image_url_fn: Callable[[Person, Title | None], str | None] | 
|  | 36 | + | 
|  | 37 | + | 
|  | 38 | +IMAGE_DESCRIPTIONS: tuple[XboxImageEntityDescription, ...] = ( | 
|  | 39 | +    XboxImageEntityDescription( | 
|  | 40 | +        key=XboxImage.GAMERPIC, | 
|  | 41 | +        translation_key=XboxImage.GAMERPIC, | 
|  | 42 | +        image_url_fn=profile_pic, | 
|  | 43 | +    ), | 
|  | 44 | +    XboxImageEntityDescription( | 
|  | 45 | +        key=XboxImage.NOW_PLAYING, | 
|  | 46 | +        translation_key=XboxImage.NOW_PLAYING, | 
|  | 47 | +        image_url_fn=lambda _, title: title.display_image if title else None, | 
|  | 48 | +    ), | 
|  | 49 | +    XboxImageEntityDescription( | 
|  | 50 | +        key=XboxImage.AVATAR, | 
|  | 51 | +        translation_key=XboxImage.AVATAR, | 
|  | 52 | +        image_url_fn=( | 
|  | 53 | +            lambda person, | 
|  | 54 | +            _: f"https://avatar-ssl.xboxlive.com/avatar/{person.gamertag}/avatar-body.png" | 
|  | 55 | +        ), | 
|  | 56 | +    ), | 
|  | 57 | +) | 
|  | 58 | + | 
|  | 59 | + | 
|  | 60 | +async def async_setup_entry( | 
|  | 61 | +    hass: HomeAssistant, | 
|  | 62 | +    config_entry: XboxConfigEntry, | 
|  | 63 | +    async_add_entities: AddConfigEntryEntitiesCallback, | 
|  | 64 | +) -> None: | 
|  | 65 | +    """Set up Xbox images.""" | 
|  | 66 | + | 
|  | 67 | +    coordinator = config_entry.runtime_data | 
|  | 68 | + | 
|  | 69 | +    xuids_added: set[str] = set() | 
|  | 70 | + | 
|  | 71 | +    @callback | 
|  | 72 | +    def add_entities() -> None: | 
|  | 73 | +        """Add image entities.""" | 
|  | 74 | +        nonlocal xuids_added | 
|  | 75 | + | 
|  | 76 | +        current_xuids = set(coordinator.data.presence) | 
|  | 77 | +        if new_xuids := current_xuids - xuids_added: | 
|  | 78 | +            async_add_entities( | 
|  | 79 | +                [ | 
|  | 80 | +                    XboxImageEntity(hass, coordinator, xuid, description) | 
|  | 81 | +                    for xuid in new_xuids | 
|  | 82 | +                    for description in IMAGE_DESCRIPTIONS | 
|  | 83 | +                ] | 
|  | 84 | +            ) | 
|  | 85 | +            xuids_added |= new_xuids | 
|  | 86 | +        xuids_added &= current_xuids | 
|  | 87 | + | 
|  | 88 | +    coordinator.async_add_listener(add_entities) | 
|  | 89 | +    add_entities() | 
|  | 90 | + | 
|  | 91 | + | 
|  | 92 | +class XboxImageEntity(XboxBaseEntity, ImageEntity): | 
|  | 93 | +    """An image entity.""" | 
|  | 94 | + | 
|  | 95 | +    entity_description: XboxImageEntityDescription | 
|  | 96 | + | 
|  | 97 | +    def __init__( | 
|  | 98 | +        self, | 
|  | 99 | +        hass: HomeAssistant, | 
|  | 100 | +        coordinator: XboxUpdateCoordinator, | 
|  | 101 | +        xuid: str, | 
|  | 102 | +        entity_description: XboxImageEntityDescription, | 
|  | 103 | +    ) -> None: | 
|  | 104 | +        """Initialize the image entity.""" | 
|  | 105 | +        super().__init__(coordinator, xuid, entity_description) | 
|  | 106 | +        ImageEntity.__init__(self, hass) | 
|  | 107 | + | 
|  | 108 | +        self._attr_image_url = self.entity_description.image_url_fn( | 
|  | 109 | +            self.data, self.title_info | 
|  | 110 | +        ) | 
|  | 111 | +        self._attr_image_last_updated = dt_util.utcnow() | 
|  | 112 | + | 
|  | 113 | +    def _handle_coordinator_update(self) -> None: | 
|  | 114 | +        """Handle updated data from the coordinator.""" | 
|  | 115 | + | 
|  | 116 | +        url = self.entity_description.image_url_fn(self.data, self.title_info) | 
|  | 117 | + | 
|  | 118 | +        if url != self._attr_image_url: | 
|  | 119 | +            self._attr_image_url = url | 
|  | 120 | +            self._cached_image = None | 
|  | 121 | +            self._attr_image_last_updated = dt_util.utcnow() | 
|  | 122 | + | 
|  | 123 | +        super()._handle_coordinator_update() | 
0 commit comments