diff --git a/immich_album_watcher/__init__.py b/immich_album_watcher/__init__.py index 5e0c8ef..e96ae60 100644 --- a/immich_album_watcher/__init__.py +++ b/immich_album_watcher/__init__.py @@ -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 diff --git a/immich_album_watcher/coordinator.py b/immich_album_watcher/coordinator.py index 5b298d0..f467941 100644 --- a/immich_album_watcher/coordinator.py +++ b/immich_album_watcher/coordinator.py @@ -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]]: diff --git a/immich_album_watcher/sensor.py b/immich_album_watcher/sensor.py index 7e7aa42..7df7364 100644 --- a/immich_album_watcher/sensor.py +++ b/immich_album_watcher/sensor.py @@ -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.""" diff --git a/immich_album_watcher/services.yaml b/immich_album_watcher/services.yaml index c13206e..456b58a 100644 --- a/immich_album_watcher/services.yaml +++ b/immich_album_watcher/services.yaml @@ -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). diff --git a/immich_album_watcher/translations/en.json b/immich_album_watcher/translations/en.json index 791c4b5..b3a0082 100644 --- a/immich_album_watcher/translations/en.json +++ b/immich_album_watcher/translations/en.json @@ -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)." diff --git a/immich_album_watcher/translations/ru.json b/immich_album_watcher/translations/ru.json index b3cdf35..b7bad69 100644 --- a/immich_album_watcher/translations/ru.json +++ b/immich_album_watcher/translations/ru.json @@ -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)."