mirror of
https://github.com/home-assistant/core.git
synced 2026-09-06 05:22:44 +01:00
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com> Co-authored-by: Erik Montnemery <erik@montnemery.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Josef Zweck <josef@zweck.dev>
164 lines
5.0 KiB
Python
164 lines
5.0 KiB
Python
"""Support for Collection Image image."""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
import random
|
|
from typing import override
|
|
|
|
from homeassistant.components.image import ImageEntity
|
|
from homeassistant.components.media_player import (
|
|
BrowseError,
|
|
MediaClass,
|
|
async_process_play_media_url,
|
|
)
|
|
from homeassistant.components.media_source import (
|
|
Unresolvable,
|
|
async_browse_media,
|
|
async_resolve_media,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
from homeassistant.helpers.start import async_at_started
|
|
from homeassistant.helpers.typing import UNDEFINED
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
from .const import CONF_MEDIA, DOMAIN
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the Collection Image image entities."""
|
|
media = entry.data[CONF_MEDIA]
|
|
async_add_entities(
|
|
[
|
|
CollectionImageImageEntity(
|
|
name=entry.title,
|
|
media_content_id=media["media_content_id"],
|
|
unique_id=entry.entry_id,
|
|
hass=hass,
|
|
)
|
|
]
|
|
)
|
|
|
|
|
|
class CollectionImageImageEntity(ImageEntity):
|
|
"""Implement the image entity for Collection Image."""
|
|
|
|
_unavailable_logged: bool = False
|
|
|
|
path: Path | None
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
media_content_id: str,
|
|
unique_id: str,
|
|
hass: HomeAssistant,
|
|
) -> None:
|
|
"""Initialize the entity."""
|
|
super().__init__(hass)
|
|
self.path = None
|
|
self._attr_unique_id = unique_id
|
|
self._attr_name = name
|
|
self.media_content_id = media_content_id
|
|
|
|
async def get_next_image(self) -> None:
|
|
"""Update the image entity with the next image from the source media."""
|
|
|
|
self._cached_image = None
|
|
|
|
def set_unavailable() -> None:
|
|
self._unavailable_logged = True
|
|
self._attr_available = False
|
|
self.path = None
|
|
self._attr_image_url = UNDEFINED
|
|
self.async_write_ha_state()
|
|
|
|
try:
|
|
media = await async_browse_media(self.hass, self.media_content_id)
|
|
except BrowseError as err:
|
|
if not self._unavailable_logged:
|
|
_LOGGER.info("%s: %s", self.entity_id, str(err))
|
|
set_unavailable()
|
|
return
|
|
|
|
if media.children and (
|
|
filtered := [
|
|
item for item in media.children if item.media_class == MediaClass.IMAGE
|
|
]
|
|
):
|
|
child = random.choice(filtered)
|
|
try:
|
|
resolved = await async_resolve_media(
|
|
self.hass, child.media_content_id, self.entity_id
|
|
)
|
|
except Unresolvable as err:
|
|
if not self._unavailable_logged:
|
|
_LOGGER.info("%s: %s", self.entity_id, str(err))
|
|
set_unavailable()
|
|
return
|
|
|
|
if resolved.url:
|
|
self.path = None
|
|
self._attr_image_url = async_process_play_media_url(
|
|
self.hass, resolved.url
|
|
)
|
|
else:
|
|
self.path = resolved.path
|
|
self._attr_image_url = UNDEFINED
|
|
|
|
self._attr_content_type = resolved.mime_type
|
|
self._attr_available = True
|
|
self._attr_image_last_updated = dt_util.utcnow()
|
|
if self._unavailable_logged:
|
|
_LOGGER.info(
|
|
"%s: Has become available again",
|
|
self.entity_id,
|
|
)
|
|
self._unavailable_logged = False
|
|
self.async_write_ha_state()
|
|
return
|
|
|
|
if not self._unavailable_logged:
|
|
_LOGGER.info(
|
|
"%s: No valid images in %s",
|
|
self.entity_id,
|
|
self.media_content_id,
|
|
)
|
|
set_unavailable()
|
|
return
|
|
|
|
@override
|
|
async def async_added_to_hass(self) -> None:
|
|
"""Initialize the first image after entity has been created."""
|
|
|
|
async def get_next_image_on_start(_hass: HomeAssistant) -> None:
|
|
await self.get_next_image()
|
|
|
|
self.async_on_remove(async_at_started(self.hass, get_next_image_on_start))
|
|
|
|
@override
|
|
def image(self) -> bytes | None:
|
|
"""Return bytes of image."""
|
|
if self.path:
|
|
try:
|
|
return self.path.read_bytes()
|
|
except OSError as err:
|
|
raise HomeAssistantError(
|
|
translation_domain=DOMAIN,
|
|
translation_key="image_read_error",
|
|
translation_placeholders={
|
|
"path": str(self.path),
|
|
"error": str(err),
|
|
},
|
|
) from err
|
|
|
|
return None
|