705616f8b0
One device per scene playlist (model "Scene Playlist"), each with: - a switch (start/stop; the server cycles one playlist at a time, so starting one stops any other) - a "Current Scene" sensor that resolves the active preset name from scene_presets while this playlist is cycling - an "Items" diagnostic sensor (configured scene count; no state_class) The coordinator reads scene_playlists plus the flat playlist_state from the /api/v1/snapshot payload and gains start_playlist()/stop_playlist(); __init__ registers and prunes per-playlist devices and reloads on playlist-id changes; the event listener also refreshes on the playlist_state_changed WS event. Shared device/lookup/running-state plumbing lives in a new LedGrabPlaylistEntity base (entity.py) used by both the switch and the sensors. Adds en/ru translation keys. Note: this also lands the in-progress coordinator migration from the per-request fan-out to the single /api/v1/snapshot poll that was already present in the working tree. Requires the led-grab server /api/v1/snapshot to emit scene_playlists + playlist_state (companion server change, tracked separately).
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""Shared base entity for scene-playlist platforms.
|
|
|
|
The playlist switch and the playlist stats sensors live on the same device and
|
|
share the same lookup, availability, and running-state logic. Keeping that in
|
|
one place means the snapshot key names (``scene_playlists`` / ``playlist_state``)
|
|
and the "is this the active playlist?" match only ever live once, so the switch
|
|
and sensors can't silently drift apart.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
|
|
from .const import DOMAIN
|
|
from .coordinator import LedGrabCoordinator
|
|
|
|
|
|
class LedGrabPlaylistEntity(CoordinatorEntity):
|
|
"""Common plumbing for entities attached to a scene-playlist device."""
|
|
|
|
_attr_has_entity_name = True
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
playlist_id: str,
|
|
entry_id: str,
|
|
) -> None:
|
|
super().__init__(coordinator)
|
|
self._playlist_id = playlist_id
|
|
self._entry_id = entry_id
|
|
|
|
@property
|
|
def device_info(self) -> dict[str, Any]:
|
|
return {"identifiers": {(DOMAIN, self._playlist_id)}}
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
return self._get_playlist() is not None
|
|
|
|
def _get_playlist(self) -> dict[str, Any] | None:
|
|
if not self.coordinator.data:
|
|
return None
|
|
for playlist in self.coordinator.data.get("scene_playlists", []):
|
|
if playlist.get("id") == self._playlist_id:
|
|
return playlist
|
|
return None
|
|
|
|
def _running_state(self) -> dict[str, Any] | None:
|
|
"""Return the global cycling state if this playlist is the active one."""
|
|
if not self.coordinator.data:
|
|
return None
|
|
state = self.coordinator.data.get("playlist_state") or {}
|
|
if state.get("is_running") and state.get("playlist_id") == self._playlist_id:
|
|
return state
|
|
return None
|