Files
ha-ninlight/custom_components/ninlight/light.py
T

127 lines
4.0 KiB
Python

"""Light platform for ninlight lamps."""
from __future__ import annotations
from typing import Any
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP_KELVIN,
ATTR_RGB_COLOR,
ATTR_TRANSITION,
ColorMode,
LightEntity,
LightEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import MAX_TRANSITION_MS
from .coordinator import NinlightConfigEntry, NinlightCoordinator
from .entity import NinlightEntity
PARALLEL_UPDATES = 0 # the coordinator lock serializes per lamp
_FALLBACK_KELVIN = (2700, 6500)
def ha_to_lamp_brightness(value: int) -> int:
"""HA 1-255 -> lamp 1-100. Never 0: 0 would switch the lamp off."""
return max(1, min(100, round(value * 100 / 255)))
def lamp_to_ha_brightness(value: int) -> int:
"""Lamp 1-100 -> HA 1-255; round-trips back to the same lamp value."""
return max(1, min(255, round(value * 255 / 100)))
def transition_ms(seconds: float) -> int:
return max(0, min(MAX_TRANSITION_MS, round(seconds * 1000)))
async def async_setup_entry(
hass: HomeAssistant,
entry: NinlightConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
async_add_entities([NinlightLight(entry.runtime_data)])
class NinlightLight(NinlightEntity, LightEntity):
"""The lamp itself."""
_attr_name = None
_attr_supported_features = LightEntityFeature.TRANSITION
def __init__(self, coordinator: NinlightCoordinator) -> None:
super().__init__(coordinator, "light")
@property
def _state(self) -> dict[str, Any]:
return self.coordinator.data.state
@property
def is_on(self) -> bool:
return bool(self._state["on"])
@property
def brightness(self) -> int:
return lamp_to_ha_brightness(int(self._state["brightness"]))
@property
def supported_color_modes(self) -> set[ColorMode]:
if self._state["capabilities"].get("color"):
return {ColorMode.RGB, ColorMode.COLOR_TEMP}
return {ColorMode.COLOR_TEMP}
@property
def color_mode(self) -> ColorMode:
if self._state.get("mode") == "color" and ColorMode.RGB in self.supported_color_modes:
return ColorMode.RGB
return ColorMode.COLOR_TEMP
@property
def rgb_color(self) -> tuple[int, int, int] | None:
color = self._state.get("color")
if not isinstance(color, dict):
return None
return (int(color["r"]), int(color["g"]), int(color["b"]))
@property
def color_temp_kelvin(self) -> int | None:
value = self._state.get("color_temp")
return int(value) if value is not None else None
def _kelvin_range(self) -> tuple[int, int]:
value = self._state["capabilities"].get("color_temp")
if isinstance(value, list) and len(value) == 2:
return (int(value[0]), int(value[1]))
return _FALLBACK_KELVIN
@property
def min_color_temp_kelvin(self) -> int:
return self._kelvin_range()[0]
@property
def max_color_temp_kelvin(self) -> int:
return self._kelvin_range()[1]
async def async_turn_on(self, **kwargs: Any) -> None:
fields: dict[str, Any] = {"on": True}
if ATTR_BRIGHTNESS in kwargs:
fields["brightness"] = ha_to_lamp_brightness(kwargs[ATTR_BRIGHTNESS])
if ATTR_RGB_COLOR in kwargs:
red, green, blue = kwargs[ATTR_RGB_COLOR]
fields["color"] = {"r": red, "g": green, "b": blue}
if ATTR_COLOR_TEMP_KELVIN in kwargs:
fields["color_temp"] = int(kwargs[ATTR_COLOR_TEMP_KELVIN])
if ATTR_TRANSITION in kwargs:
fields["transition"] = transition_ms(kwargs[ATTR_TRANSITION])
await self.coordinator.async_set_state(**fields)
async def async_turn_off(self, **kwargs: Any) -> None:
fields: dict[str, Any] = {"on": False}
if ATTR_TRANSITION in kwargs:
fields["transition"] = transition_ms(kwargs[ATTR_TRANSITION])
await self.coordinator.async_set_state(**fields)