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.
234 lines
7.8 KiB
Python
234 lines
7.8 KiB
Python
"""Number platform for LED Screen Controller (device brightness & HA light settings)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from homeassistant.components.number import NumberEntity, NumberMode
|
|
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__)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up LED Screen Controller number entities."""
|
|
data = hass.data[DOMAIN][entry.entry_id]
|
|
coordinator: LedGrabCoordinator = data[DATA_COORDINATOR]
|
|
|
|
entities: list[NumberEntity] = []
|
|
if coordinator.data and "targets" in coordinator.data:
|
|
devices = coordinator.data.get("devices") or {}
|
|
|
|
for target_id, target_data in coordinator.data["targets"].items():
|
|
info = target_data["info"]
|
|
target_type = info.get("target_type", "led")
|
|
|
|
if target_type == TARGET_TYPE_HA_LIGHT:
|
|
# HA Light target — expose tunable settings
|
|
entities.append(HALightUpdateRate(coordinator, target_id, entry.entry_id))
|
|
entities.append(HALightTransition(coordinator, target_id, entry.entry_id))
|
|
entities.append(HALightMinBrightness(coordinator, target_id, entry.entry_id))
|
|
entities.append(HALightColorTolerance(coordinator, target_id, entry.entry_id))
|
|
continue
|
|
|
|
# LED target — brightness lives on the device
|
|
device_id = info.get("device_id", "")
|
|
if not device_id:
|
|
continue
|
|
|
|
device_data = devices.get(device_id)
|
|
if not device_data:
|
|
continue
|
|
|
|
capabilities = device_data.get("info", {}).get("capabilities") or []
|
|
if "brightness_control" not in capabilities or "static_color" in capabilities:
|
|
continue
|
|
|
|
entities.append(
|
|
LedGrabBrightness(
|
|
coordinator,
|
|
target_id,
|
|
device_id,
|
|
entry.entry_id,
|
|
)
|
|
)
|
|
|
|
async_add_entities(entities)
|
|
|
|
|
|
class LedGrabBrightness(CoordinatorEntity, NumberEntity):
|
|
"""Brightness control for an LED device associated with a target."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_native_min_value = 0
|
|
_attr_native_max_value = 255
|
|
_attr_native_step = 1
|
|
_attr_mode = NumberMode.SLIDER
|
|
_attr_icon = "mdi:brightness-6"
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
target_id: str,
|
|
device_id: str,
|
|
entry_id: str,
|
|
) -> None:
|
|
"""Initialize the brightness number."""
|
|
super().__init__(coordinator)
|
|
self._target_id = target_id
|
|
self._device_id = device_id
|
|
self._entry_id = entry_id
|
|
self._attr_unique_id = f"{target_id}_brightness"
|
|
self._attr_translation_key = "brightness"
|
|
|
|
@property
|
|
def device_info(self) -> dict[str, Any]:
|
|
"""Return device information."""
|
|
return {"identifiers": {(DOMAIN, self._target_id)}}
|
|
|
|
@property
|
|
def native_value(self) -> float | None:
|
|
"""Return the current brightness value."""
|
|
if not self.coordinator.data:
|
|
return None
|
|
device_data = self.coordinator.data.get("devices", {}).get(self._device_id)
|
|
if not device_data:
|
|
return None
|
|
return device_data.get("brightness")
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return if entity is available."""
|
|
if not self.coordinator.data:
|
|
return False
|
|
targets = self.coordinator.data.get("targets", {})
|
|
devices = self.coordinator.data.get("devices", {})
|
|
return self._target_id in targets and self._device_id in devices
|
|
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
"""Set brightness value."""
|
|
await self.coordinator.set_brightness(self._device_id, int(value))
|
|
|
|
|
|
# --- HA Light target number entities ---
|
|
|
|
|
|
class _HALightNumberBase(CoordinatorEntity, NumberEntity):
|
|
"""Base class for HA Light target number entities."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_mode = NumberMode.SLIDER
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
target_id: str,
|
|
entry_id: str,
|
|
*,
|
|
field_name: str,
|
|
) -> None:
|
|
super().__init__(coordinator)
|
|
self._target_id = target_id
|
|
self._entry_id = entry_id
|
|
self._field_name = field_name
|
|
|
|
@property
|
|
def device_info(self) -> dict[str, Any]:
|
|
return {"identifiers": {(DOMAIN, self._target_id)}}
|
|
|
|
@property
|
|
def native_value(self) -> float | None:
|
|
target_data = self._get_target_data()
|
|
if not target_data:
|
|
return None
|
|
return target_data.get("info", {}).get(self._field_name)
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
return self._get_target_data() is not None
|
|
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
await self.coordinator.update_target(self._target_id, **{self._field_name: round(value, 2)})
|
|
|
|
def _get_target_data(self) -> dict[str, Any] | None:
|
|
if not self.coordinator.data:
|
|
return None
|
|
return self.coordinator.data.get("targets", {}).get(self._target_id)
|
|
|
|
|
|
class HALightUpdateRate(_HALightNumberBase):
|
|
"""Update rate (Hz) for an HA Light target."""
|
|
|
|
_attr_native_min_value = 0.5
|
|
_attr_native_max_value = 5.0
|
|
_attr_native_step = 0.5
|
|
_attr_native_unit_of_measurement = "Hz"
|
|
_attr_icon = "mdi:update"
|
|
|
|
def __init__(
|
|
self, coordinator: LedGrabCoordinator, target_id: str, entry_id: str
|
|
) -> None:
|
|
super().__init__(coordinator, target_id, entry_id, field_name="update_rate")
|
|
self._attr_unique_id = f"{target_id}_update_rate"
|
|
self._attr_translation_key = "ha_light_update_rate"
|
|
|
|
|
|
class HALightTransition(_HALightNumberBase):
|
|
"""Transition time (seconds) for an HA Light target."""
|
|
|
|
_attr_native_min_value = 0.0
|
|
_attr_native_max_value = 10.0
|
|
_attr_native_step = 0.1
|
|
_attr_native_unit_of_measurement = "s"
|
|
_attr_icon = "mdi:transition-masked"
|
|
|
|
def __init__(
|
|
self, coordinator: LedGrabCoordinator, target_id: str, entry_id: str
|
|
) -> None:
|
|
super().__init__(coordinator, target_id, entry_id, field_name="transition")
|
|
self._attr_unique_id = f"{target_id}_transition"
|
|
self._attr_translation_key = "ha_light_transition"
|
|
|
|
|
|
class HALightMinBrightness(_HALightNumberBase):
|
|
"""Minimum brightness threshold for an HA Light target."""
|
|
|
|
_attr_native_min_value = 0
|
|
_attr_native_max_value = 255
|
|
_attr_native_step = 1
|
|
_attr_icon = "mdi:brightness-4"
|
|
|
|
def __init__(
|
|
self, coordinator: LedGrabCoordinator, target_id: str, entry_id: str
|
|
) -> None:
|
|
super().__init__(coordinator, target_id, entry_id, field_name="min_brightness_threshold")
|
|
self._attr_unique_id = f"{target_id}_min_brightness"
|
|
self._attr_translation_key = "ha_light_min_brightness"
|
|
|
|
|
|
class HALightColorTolerance(_HALightNumberBase):
|
|
"""Color tolerance (RGB delta skip threshold) for an HA Light target."""
|
|
|
|
_attr_native_min_value = 0
|
|
_attr_native_max_value = 50
|
|
_attr_native_step = 1
|
|
_attr_icon = "mdi:palette-outline"
|
|
|
|
def __init__(
|
|
self, coordinator: LedGrabCoordinator, target_id: str, entry_id: str
|
|
) -> None:
|
|
super().__init__(coordinator, target_id, entry_id, field_name="color_tolerance")
|
|
self._attr_unique_id = f"{target_id}_color_tolerance"
|
|
self._attr_translation_key = "ha_light_color_tolerance"
|