"""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)