Add entity picker support

This commit is contained in:
2026-01-30 02:05:10 +03:00
parent 17c1792637
commit c8bd475a52
6 changed files with 59 additions and 87 deletions

View File

@@ -3,14 +3,11 @@
from __future__ import annotations
import logging
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, ServiceCall, ServiceResponse, SupportsResponse
from homeassistant.helpers import config_validation as cv
from homeassistant.core import HomeAssistant
from .const import (
ATTR_ALBUM_ID,
CONF_ALBUMS,
CONF_API_KEY,
CONF_IMMICH_URL,
@@ -18,25 +15,11 @@ from .const import (
DEFAULT_SCAN_INTERVAL,
DOMAIN,
PLATFORMS,
SERVICE_GET_RECENT_ASSETS,
SERVICE_REFRESH,
)
from .coordinator import ImmichAlbumWatcherCoordinator
_LOGGER = logging.getLogger(__name__)
# Service schemas
SERVICE_REFRESH_SCHEMA = vol.Schema({})
SERVICE_GET_RECENT_ASSETS_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ALBUM_ID): cv.string,
vol.Optional("count", default=10): vol.All(
vol.Coerce(int), vol.Range(min=1, max=100)
),
}
)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Immich Album Watcher from a config entry."""
@@ -66,9 +49,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Register update listener for options changes
entry.async_on_unload(entry.add_update_listener(async_update_options))
# Register services (only once)
await async_setup_services(hass)
_LOGGER.info(
"Immich Album Watcher set up successfully, watching %d albums",
len(album_ids),
@@ -77,46 +57,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True
async def async_setup_services(hass: HomeAssistant) -> None:
"""Set up services for Immich Album Watcher."""
if hass.services.has_service(DOMAIN, SERVICE_REFRESH):
return # Services already registered
async def handle_refresh(call: ServiceCall) -> None:
"""Handle the refresh service call."""
for coordinator in hass.data[DOMAIN].values():
if isinstance(coordinator, ImmichAlbumWatcherCoordinator):
await coordinator.async_refresh_now()
async def handle_get_recent_assets(call: ServiceCall) -> ServiceResponse:
"""Handle the get_recent_assets service call."""
album_id = call.data[ATTR_ALBUM_ID]
count = call.data.get("count", 10)
for coordinator in hass.data[DOMAIN].values():
if isinstance(coordinator, ImmichAlbumWatcherCoordinator):
if coordinator.data and album_id in coordinator.data:
assets = await coordinator.async_get_recent_assets(album_id, count)
return {"assets": assets}
return {"assets": [], "error": f"Album {album_id} not found"}
hass.services.async_register(
DOMAIN,
SERVICE_REFRESH,
handle_refresh,
schema=SERVICE_REFRESH_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_GET_RECENT_ASSETS,
handle_get_recent_assets,
schema=SERVICE_GET_RECENT_ASSETS_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
coordinator: ImmichAlbumWatcherCoordinator = hass.data[DOMAIN][entry.entry_id]
@@ -137,9 +77,4 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
# Unregister services if no more entries
if not hass.data[DOMAIN]:
hass.services.async_remove(DOMAIN, SERVICE_REFRESH)
hass.services.async_remove(DOMAIN, SERVICE_GET_RECENT_ASSETS)
return unload_ok

View File

@@ -252,6 +252,15 @@ class ImmichAlbumWatcherCoordinator(DataUpdateCoordinator[dict[str, AlbumData]])
"""Force an immediate refresh."""
await self.async_request_refresh()
async def async_refresh_album(self, album_id: str) -> None:
"""Force an immediate refresh of a specific album.
Currently refreshes all albums as they share the same coordinator,
but the method signature allows for future optimization.
"""
if album_id in self._album_ids:
await self.async_request_refresh()
async def async_get_recent_assets(
self, album_id: str, count: int = 10
) -> list[dict[str, Any]]:

View File

@@ -6,13 +6,16 @@ import logging
from datetime import datetime
from typing import Any
import voluptuous as vol
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant, ServiceResponse, SupportsResponse, callback
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -34,6 +37,8 @@ from .const import (
ATTR_VIDEO_COUNT,
CONF_ALBUMS,
DOMAIN,
SERVICE_GET_RECENT_ASSETS,
SERVICE_REFRESH,
)
from .coordinator import AlbumData, ImmichAlbumWatcherCoordinator
@@ -63,6 +68,26 @@ async def async_setup_entry(
async_add_entities(entities)
# Register entity services
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
SERVICE_REFRESH,
{},
"async_refresh_album",
)
platform.async_register_entity_service(
SERVICE_GET_RECENT_ASSETS,
{
vol.Optional("count", default=10): vol.All(
vol.Coerce(int), vol.Range(min=1, max=100)
),
},
"async_get_recent_assets",
supports_response=SupportsResponse.ONLY,
)
class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], SensorEntity):
"""Base sensor for Immich album."""
@@ -114,6 +139,15 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
"""Handle updated data from the coordinator."""
self.async_write_ha_state()
async def async_refresh_album(self) -> None:
"""Refresh data for this album."""
await self.coordinator.async_refresh_album(self._album_id)
async def async_get_recent_assets(self, count: int = 10) -> ServiceResponse:
"""Get recent assets for this album."""
assets = await self.coordinator.async_get_recent_assets(self._album_id, count)
return {"assets": assets}
class ImmichAlbumAssetCountSensor(ImmichAlbumBaseSensor):
"""Sensor representing an Immich album asset count."""

View File

@@ -1,17 +1,19 @@
refresh:
name: Refresh
description: Force an immediate refresh of all album data from Immich.
description: Force an immediate refresh of album data from Immich.
target:
entity:
integration: immich_album_watcher
domain: sensor
get_recent_assets:
name: Get Recent Assets
description: Get the most recent assets from a specific album.
description: Get the most recent assets from the targeted album.
target:
entity:
integration: immich_album_watcher
domain: sensor
fields:
album_id:
name: Album ID
description: The ID of the album to get recent assets from.
required: true
selector:
text:
count:
name: Count
description: Number of recent assets to return (1-100).

View File

@@ -99,16 +99,12 @@
"services": {
"refresh": {
"name": "Refresh",
"description": "Force an immediate refresh of all album data from Immich."
"description": "Force an immediate refresh of album data from Immich."
},
"get_recent_assets": {
"name": "Get Recent Assets",
"description": "Get the most recent assets from a specific album.",
"description": "Get the most recent assets from the targeted album.",
"fields": {
"album_id": {
"name": "Album ID",
"description": "The ID of the album to get recent assets from."
},
"count": {
"name": "Count",
"description": "Number of recent assets to return (1-100)."

View File

@@ -99,16 +99,12 @@
"services": {
"refresh": {
"name": "Обновить",
"description": "Принудительно обновить данные всех альбомов из Immich."
"description": "Принудительно обновить данные альбома из Immich."
},
"get_recent_assets": {
"name": "Получить последние файлы",
"description": "Получить последние файлы из указанного альбома.",
"description": "Получить последние файлы из выбранного альбома.",
"fields": {
"album_id": {
"name": "ID альбома",
"description": "ID альбома для получения последних файлов."
},
"count": {
"name": "Количество",
"description": "Количество возвращаемых файлов (1-100)."