Add entity picker support
This commit is contained in:
@@ -3,14 +3,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall, ServiceResponse, SupportsResponse
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_validation as cv
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_ALBUM_ID,
|
|
||||||
CONF_ALBUMS,
|
CONF_ALBUMS,
|
||||||
CONF_API_KEY,
|
CONF_API_KEY,
|
||||||
CONF_IMMICH_URL,
|
CONF_IMMICH_URL,
|
||||||
@@ -18,25 +15,11 @@ from .const import (
|
|||||||
DEFAULT_SCAN_INTERVAL,
|
DEFAULT_SCAN_INTERVAL,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
SERVICE_GET_RECENT_ASSETS,
|
|
||||||
SERVICE_REFRESH,
|
|
||||||
)
|
)
|
||||||
from .coordinator import ImmichAlbumWatcherCoordinator
|
from .coordinator import ImmichAlbumWatcherCoordinator
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Immich Album Watcher from a config entry."""
|
"""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
|
# Register update listener for options changes
|
||||||
entry.async_on_unload(entry.add_update_listener(async_update_options))
|
entry.async_on_unload(entry.add_update_listener(async_update_options))
|
||||||
|
|
||||||
# Register services (only once)
|
|
||||||
await async_setup_services(hass)
|
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Immich Album Watcher set up successfully, watching %d albums",
|
"Immich Album Watcher set up successfully, watching %d albums",
|
||||||
len(album_ids),
|
len(album_ids),
|
||||||
@@ -77,46 +57,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
return True
|
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:
|
async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
"""Handle options update."""
|
"""Handle options update."""
|
||||||
coordinator: ImmichAlbumWatcherCoordinator = hass.data[DOMAIN][entry.entry_id]
|
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:
|
if unload_ok:
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
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
|
return unload_ok
|
||||||
|
|||||||
@@ -252,6 +252,15 @@ class ImmichAlbumWatcherCoordinator(DataUpdateCoordinator[dict[str, AlbumData]])
|
|||||||
"""Force an immediate refresh."""
|
"""Force an immediate refresh."""
|
||||||
await self.async_request_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(
|
async def async_get_recent_assets(
|
||||||
self, album_id: str, count: int = 10
|
self, album_id: str, count: int = 10
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
|
|||||||
@@ -6,13 +6,16 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
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 import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
@@ -34,6 +37,8 @@ from .const import (
|
|||||||
ATTR_VIDEO_COUNT,
|
ATTR_VIDEO_COUNT,
|
||||||
CONF_ALBUMS,
|
CONF_ALBUMS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
SERVICE_GET_RECENT_ASSETS,
|
||||||
|
SERVICE_REFRESH,
|
||||||
)
|
)
|
||||||
from .coordinator import AlbumData, ImmichAlbumWatcherCoordinator
|
from .coordinator import AlbumData, ImmichAlbumWatcherCoordinator
|
||||||
|
|
||||||
@@ -63,6 +68,26 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
async_add_entities(entities)
|
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):
|
class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], SensorEntity):
|
||||||
"""Base sensor for Immich album."""
|
"""Base sensor for Immich album."""
|
||||||
@@ -114,6 +139,15 @@ class ImmichAlbumBaseSensor(CoordinatorEntity[ImmichAlbumWatcherCoordinator], Se
|
|||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
self.async_write_ha_state()
|
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):
|
class ImmichAlbumAssetCountSensor(ImmichAlbumBaseSensor):
|
||||||
"""Sensor representing an Immich album asset count."""
|
"""Sensor representing an Immich album asset count."""
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
refresh:
|
refresh:
|
||||||
name: 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:
|
get_recent_assets:
|
||||||
name: 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:
|
fields:
|
||||||
album_id:
|
|
||||||
name: Album ID
|
|
||||||
description: The ID of the album to get recent assets from.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
count:
|
count:
|
||||||
name: Count
|
name: Count
|
||||||
description: Number of recent assets to return (1-100).
|
description: Number of recent assets to return (1-100).
|
||||||
|
|||||||
@@ -99,16 +99,12 @@
|
|||||||
"services": {
|
"services": {
|
||||||
"refresh": {
|
"refresh": {
|
||||||
"name": "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": {
|
"get_recent_assets": {
|
||||||
"name": "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": {
|
"fields": {
|
||||||
"album_id": {
|
|
||||||
"name": "Album ID",
|
|
||||||
"description": "The ID of the album to get recent assets from."
|
|
||||||
},
|
|
||||||
"count": {
|
"count": {
|
||||||
"name": "Count",
|
"name": "Count",
|
||||||
"description": "Number of recent assets to return (1-100)."
|
"description": "Number of recent assets to return (1-100)."
|
||||||
|
|||||||
@@ -99,16 +99,12 @@
|
|||||||
"services": {
|
"services": {
|
||||||
"refresh": {
|
"refresh": {
|
||||||
"name": "Обновить",
|
"name": "Обновить",
|
||||||
"description": "Принудительно обновить данные всех альбомов из Immich."
|
"description": "Принудительно обновить данные альбома из Immich."
|
||||||
},
|
},
|
||||||
"get_recent_assets": {
|
"get_recent_assets": {
|
||||||
"name": "Получить последние файлы",
|
"name": "Получить последние файлы",
|
||||||
"description": "Получить последние файлы из указанного альбома.",
|
"description": "Получить последние файлы из выбранного альбома.",
|
||||||
"fields": {
|
"fields": {
|
||||||
"album_id": {
|
|
||||||
"name": "ID альбома",
|
|
||||||
"description": "ID альбома для получения последних файлов."
|
|
||||||
},
|
|
||||||
"count": {
|
"count": {
|
||||||
"name": "Количество",
|
"name": "Количество",
|
||||||
"description": "Количество возвращаемых файлов (1-100)."
|
"description": "Количество возвращаемых файлов (1-100)."
|
||||||
|
|||||||
Reference in New Issue
Block a user