579553a850
HACS-compatible custom component split out from the main LedGrab repo. Creates light, switch, sensor, number, and select entities for each configured LedGrab device.
179 lines
6.2 KiB
Python
179 lines
6.2 KiB
Python
"""Select platform for LED Screen Controller (CSS source & brightness source)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from homeassistant.components.select import SelectEntity
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
|
|
from .const import DOMAIN, DATA_COORDINATOR, TARGET_TYPE_HA_LIGHT
|
|
from .coordinator import LedGrabCoordinator
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
NONE_OPTION = "\u2014 None \u2014"
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up LED Screen Controller select entities."""
|
|
data = hass.data[DOMAIN][entry.entry_id]
|
|
coordinator: LedGrabCoordinator = data[DATA_COORDINATOR]
|
|
|
|
entities: list[SelectEntity] = []
|
|
if coordinator.data and "targets" in coordinator.data:
|
|
for target_id, target_data in coordinator.data["targets"].items():
|
|
info = target_data["info"]
|
|
target_type = info.get("target_type", "led")
|
|
|
|
# Both LED and HA Light targets have a CSS source
|
|
entities.append(CSSSourceSelect(coordinator, target_id, entry.entry_id))
|
|
|
|
# Only LED targets have a brightness value source
|
|
if target_type != TARGET_TYPE_HA_LIGHT:
|
|
entities.append(BrightnessSourceSelect(coordinator, target_id, entry.entry_id))
|
|
|
|
async_add_entities(entities)
|
|
|
|
|
|
class CSSSourceSelect(CoordinatorEntity, SelectEntity):
|
|
"""Select entity for choosing a color strip source for a target."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_icon = "mdi:palette"
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
target_id: str,
|
|
entry_id: str,
|
|
) -> None:
|
|
super().__init__(coordinator)
|
|
self._target_id = target_id
|
|
self._entry_id = entry_id
|
|
self._attr_unique_id = f"{target_id}_css_source"
|
|
self._attr_translation_key = "color_strip_source"
|
|
|
|
@property
|
|
def device_info(self) -> dict[str, Any]:
|
|
return {"identifiers": {(DOMAIN, self._target_id)}}
|
|
|
|
@property
|
|
def options(self) -> list[str]:
|
|
if not self.coordinator.data:
|
|
return []
|
|
sources = self.coordinator.data.get("css_sources") or []
|
|
return [s["name"] for s in sources]
|
|
|
|
@property
|
|
def current_option(self) -> str | None:
|
|
if not self.coordinator.data:
|
|
return None
|
|
target_data = self.coordinator.data.get("targets", {}).get(self._target_id)
|
|
if not target_data:
|
|
return None
|
|
current_id = target_data["info"].get("color_strip_source_id", "")
|
|
sources = self.coordinator.data.get("css_sources") or []
|
|
for s in sources:
|
|
if s["id"] == current_id:
|
|
return s["name"]
|
|
return None
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
if not self.coordinator.data:
|
|
return False
|
|
return self._target_id in self.coordinator.data.get("targets", {})
|
|
|
|
async def async_select_option(self, option: str) -> None:
|
|
source_id = self._name_to_id_map().get(option)
|
|
if source_id is None:
|
|
_LOGGER.error("CSS source not found: %s", option)
|
|
return
|
|
await self.coordinator.update_target(self._target_id, color_strip_source_id=source_id)
|
|
|
|
def _name_to_id_map(self) -> dict[str, str]:
|
|
sources = (self.coordinator.data or {}).get("css_sources") or []
|
|
return {s["name"]: s["id"] for s in sources}
|
|
|
|
|
|
class BrightnessSourceSelect(CoordinatorEntity, SelectEntity):
|
|
"""Select entity for choosing a brightness value source for an LED target."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_icon = "mdi:brightness-auto"
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
target_id: str,
|
|
entry_id: str,
|
|
) -> None:
|
|
super().__init__(coordinator)
|
|
self._target_id = target_id
|
|
self._entry_id = entry_id
|
|
self._attr_unique_id = f"{target_id}_brightness_source"
|
|
self._attr_translation_key = "brightness_source"
|
|
|
|
@property
|
|
def device_info(self) -> dict[str, Any]:
|
|
return {"identifiers": {(DOMAIN, self._target_id)}}
|
|
|
|
@property
|
|
def options(self) -> list[str]:
|
|
if not self.coordinator.data:
|
|
return [NONE_OPTION]
|
|
sources = self.coordinator.data.get("value_sources") or []
|
|
return [NONE_OPTION] + [s["name"] for s in sources]
|
|
|
|
@property
|
|
def current_option(self) -> str | None:
|
|
if not self.coordinator.data:
|
|
return None
|
|
target_data = self.coordinator.data.get("targets", {}).get(self._target_id)
|
|
if not target_data:
|
|
return None
|
|
# BindableFloat: brightness is either a plain float or {"value": float, "source_id": str}
|
|
brightness = target_data["info"].get("brightness", "")
|
|
if isinstance(brightness, dict):
|
|
current_id = brightness.get("source_id", "")
|
|
else:
|
|
current_id = target_data["info"].get("brightness_value_source_id", "")
|
|
if not current_id:
|
|
return NONE_OPTION
|
|
sources = self.coordinator.data.get("value_sources") or []
|
|
for s in sources:
|
|
if s["id"] == current_id:
|
|
return s["name"]
|
|
return NONE_OPTION
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
if not self.coordinator.data:
|
|
return False
|
|
return self._target_id in self.coordinator.data.get("targets", {})
|
|
|
|
async def async_select_option(self, option: str) -> None:
|
|
if option == NONE_OPTION:
|
|
source_id = ""
|
|
else:
|
|
name_map = {
|
|
s["name"]: s["id"] for s in (self.coordinator.data or {}).get("value_sources") or []
|
|
}
|
|
source_id = name_map.get(option)
|
|
if source_id is None:
|
|
_LOGGER.error("Value source not found: %s", option)
|
|
return
|
|
await self.coordinator.update_target(
|
|
self._target_id,
|
|
brightness={"value": 1.0, "source_id": source_id} if source_id else 1.0,
|
|
)
|