diff --git a/homeassistant/components/habitica/__init__.py b/homeassistant/components/habitica/__init__.py index 91411e18b16..9a9d689bedc 100644 --- a/homeassistant/components/habitica/__init__.py +++ b/homeassistant/components/habitica/__init__.py @@ -21,6 +21,7 @@ PLATFORMS = [ Platform.BINARY_SENSOR, Platform.BUTTON, Platform.CALENDAR, + Platform.IMAGE, Platform.SENSOR, Platform.SWITCH, Platform.TODO, diff --git a/homeassistant/components/habitica/coordinator.py b/homeassistant/components/habitica/coordinator.py index 3cef1fed837..587f8148398 100644 --- a/homeassistant/components/habitica/coordinator.py +++ b/homeassistant/components/habitica/coordinator.py @@ -5,6 +5,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass from datetime import timedelta +from io import BytesIO import logging from typing import Any @@ -18,6 +19,7 @@ from habiticalib import ( TaskFilter, TooManyRequestsError, UserData, + UserStyles, ) from homeassistant.config_entries import ConfigEntry @@ -130,3 +132,13 @@ class HabiticaDataUpdateCoordinator(DataUpdateCoordinator[HabiticaData]): ) from e else: await self.async_request_refresh() + + async def generate_avatar(self, user_styles: UserStyles) -> bytes: + """Generate Avatar.""" + + avatar = BytesIO() + await self.habitica.generate_avatar( + fp=avatar, user_styles=user_styles, fmt="PNG" + ) + + return avatar.getvalue() diff --git a/homeassistant/components/habitica/image.py b/homeassistant/components/habitica/image.py new file mode 100644 index 00000000000..27b406c475c --- /dev/null +++ b/homeassistant/components/habitica/image.py @@ -0,0 +1,76 @@ +"""Image platform for Habitica integration.""" + +from __future__ import annotations + +from dataclasses import asdict +from enum import StrEnum + +from habiticalib import UserStyles + +from homeassistant.components.image import ImageEntity, ImageEntityDescription +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util import dt as dt_util + +from . import HabiticaConfigEntry +from .coordinator import HabiticaDataUpdateCoordinator +from .entity import HabiticaBase + + +class HabiticaImageEntity(StrEnum): + """Image entities.""" + + AVATAR = "avatar" + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HabiticaConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the habitica image platform.""" + + coordinator = config_entry.runtime_data + + async_add_entities([HabiticaImage(hass, coordinator)]) + + +class HabiticaImage(HabiticaBase, ImageEntity): + """A Habitica image entity.""" + + entity_description = ImageEntityDescription( + key=HabiticaImageEntity.AVATAR, + translation_key=HabiticaImageEntity.AVATAR, + ) + _attr_content_type = "image/png" + _current_appearance: UserStyles | None = None + _cache: bytes | None = None + + def __init__( + self, + hass: HomeAssistant, + coordinator: HabiticaDataUpdateCoordinator, + ) -> None: + """Initialize the image entity.""" + super().__init__(coordinator, self.entity_description) + ImageEntity.__init__(self, hass) + self._attr_image_last_updated = dt_util.utcnow() + + def _handle_coordinator_update(self) -> None: + """Check if equipped gear and other things have changed since last avatar image generation.""" + new_appearance = UserStyles.from_dict(asdict(self.coordinator.data.user)) + + if self._current_appearance != new_appearance: + self._current_appearance = new_appearance + self._attr_image_last_updated = dt_util.utcnow() + self._cache = None + + return super()._handle_coordinator_update() + + async def async_image(self) -> bytes | None: + """Return cached bytes, otherwise generate new avatar.""" + if not self._cache and self._current_appearance: + self._cache = await self.coordinator.generate_avatar( + self._current_appearance + ) + return self._cache diff --git a/homeassistant/components/habitica/strings.json b/homeassistant/components/habitica/strings.json index a99eb2407a8..b4925861d67 100644 --- a/homeassistant/components/habitica/strings.json +++ b/homeassistant/components/habitica/strings.json @@ -167,6 +167,11 @@ "name": "Daily reminders" } }, + "image": { + "avatar": { + "name": "Avatar" + } + }, "sensor": { "display_name": { "name": "Display name"