381ee75371
Lint & Test / test (push) Successful in 1m13s
- Add brightness_value_source_id to HALightOutputTarget model, to_dict,
from_dict, update_fields, register_with_manager, API response
- Wire value stream in HALightTargetProcessor: acquire/release on
start/stop, multiply brightness in _update_lights loop
- Fix transition=0 not saving (parseFloat("0") || 0.5 was falsy)
- Fix dashboard showing "Key Colors" for HA targets — now "Home Assistant"
- Fix dashboard FPS showing 0/2 — HA targets show target/target
- Add CSS source subtitle to HA target dashboard cards
234 lines
7.9 KiB
Python
234 lines
7.9 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 WLEDScreenControllerCoordinator
|
|
|
|
_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: WLEDScreenControllerCoordinator = 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(
|
|
WLEDScreenControllerBrightness(
|
|
coordinator,
|
|
target_id,
|
|
device_id,
|
|
entry.entry_id,
|
|
)
|
|
)
|
|
|
|
async_add_entities(entities)
|
|
|
|
|
|
class WLEDScreenControllerBrightness(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: WLEDScreenControllerCoordinator,
|
|
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: WLEDScreenControllerCoordinator,
|
|
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: WLEDScreenControllerCoordinator, 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: WLEDScreenControllerCoordinator, 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: WLEDScreenControllerCoordinator, 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: WLEDScreenControllerCoordinator, 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"
|