224 lines
7.5 KiB
Python
224 lines
7.5 KiB
Python
"""Lights on Zigbee Home Automation networks."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
import functools
|
|
import logging
|
|
from typing import Any
|
|
|
|
from zha.application.platforms.light.const import (
|
|
ColorMode as ZhaColorMode,
|
|
LightEntityFeature as ZhaLightEntityFeature,
|
|
)
|
|
|
|
from homeassistant.components.light import (
|
|
ATTR_BRIGHTNESS,
|
|
ATTR_COLOR_MODE,
|
|
ATTR_COLOR_TEMP_KELVIN,
|
|
ATTR_EFFECT,
|
|
ATTR_FLASH,
|
|
ATTR_TRANSITION,
|
|
ATTR_XY_COLOR,
|
|
ColorMode,
|
|
LightEntity,
|
|
LightEntityFeature,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import STATE_ON, Platform
|
|
from homeassistant.core import HomeAssistant, State, callback
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.util import color as color_util
|
|
|
|
from .entity import ZHAEntity
|
|
from .helpers import (
|
|
SIGNAL_ADD_ENTITIES,
|
|
EntityData,
|
|
async_add_entities as zha_async_add_entities,
|
|
convert_zha_error_to_ha_error,
|
|
get_zha_data,
|
|
)
|
|
|
|
ZHA_TO_HA_COLOR_MODE = {
|
|
ZhaColorMode.UNKNOWN: ColorMode.UNKNOWN,
|
|
ZhaColorMode.ONOFF: ColorMode.ONOFF,
|
|
ZhaColorMode.BRIGHTNESS: ColorMode.BRIGHTNESS,
|
|
ZhaColorMode.COLOR_TEMP: ColorMode.COLOR_TEMP,
|
|
ZhaColorMode.XY: ColorMode.XY,
|
|
}
|
|
|
|
HA_TO_ZHA_COLOR_MODE = {v: k for k, v in ZHA_TO_HA_COLOR_MODE.items()}
|
|
|
|
OFF_BRIGHTNESS = "off_brightness"
|
|
OFF_WITH_TRANSITION = "off_with_transition"
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the Zigbee Home Automation light from config entry."""
|
|
zha_data = get_zha_data(hass)
|
|
entities_to_create = zha_data.platforms[Platform.LIGHT]
|
|
|
|
unsub = async_dispatcher_connect(
|
|
hass,
|
|
SIGNAL_ADD_ENTITIES,
|
|
functools.partial(
|
|
zha_async_add_entities, async_add_entities, Light, entities_to_create
|
|
),
|
|
)
|
|
config_entry.async_on_unload(unsub)
|
|
|
|
|
|
class Light(LightEntity, ZHAEntity):
|
|
"""Representation of a ZHA or ZLL light."""
|
|
|
|
def __init__(self, entity_data: EntityData) -> None:
|
|
"""Initialize the ZHA light."""
|
|
super().__init__(entity_data)
|
|
color_modes: set[ColorMode] = set()
|
|
has_brightness = False
|
|
for color_mode in self.entity_data.entity.supported_color_modes:
|
|
if color_mode == ZhaColorMode.BRIGHTNESS:
|
|
has_brightness = True
|
|
if color_mode not in (ZhaColorMode.BRIGHTNESS, ZhaColorMode.ONOFF):
|
|
color_modes.add(ZHA_TO_HA_COLOR_MODE[color_mode])
|
|
if color_modes:
|
|
self._attr_supported_color_modes = color_modes
|
|
elif has_brightness:
|
|
color_modes.add(ColorMode.BRIGHTNESS)
|
|
self._attr_supported_color_modes = color_modes
|
|
else:
|
|
color_modes.add(ColorMode.ONOFF)
|
|
self._attr_supported_color_modes = color_modes
|
|
|
|
features = LightEntityFeature(0)
|
|
zha_features: ZhaLightEntityFeature = self.entity_data.entity.supported_features
|
|
|
|
if ZhaLightEntityFeature.EFFECT in zha_features:
|
|
features |= LightEntityFeature.EFFECT
|
|
if ZhaLightEntityFeature.FLASH in zha_features:
|
|
features |= LightEntityFeature.FLASH
|
|
if ZhaLightEntityFeature.TRANSITION in zha_features:
|
|
features |= LightEntityFeature.TRANSITION
|
|
|
|
self._attr_supported_features = features
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
|
"""Return entity specific state attributes."""
|
|
state = self.entity_data.entity.state
|
|
return {
|
|
"off_with_transition": state.get("off_with_transition"),
|
|
"off_brightness": state.get("off_brightness"),
|
|
}
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return true if entity is on."""
|
|
return self.entity_data.entity.is_on
|
|
|
|
@property
|
|
def brightness(self) -> int:
|
|
"""Return the brightness of this light."""
|
|
return self.entity_data.entity.brightness
|
|
|
|
@property
|
|
def max_color_temp_kelvin(self) -> int:
|
|
"""Return the coldest color_temp_kelvin that this light supports."""
|
|
return color_util.color_temperature_mired_to_kelvin(
|
|
self.entity_data.entity.min_mireds
|
|
)
|
|
|
|
@property
|
|
def min_color_temp_kelvin(self) -> int:
|
|
"""Return the warmest color_temp_kelvin that this light supports."""
|
|
return color_util.color_temperature_mired_to_kelvin(
|
|
self.entity_data.entity.max_mireds
|
|
)
|
|
|
|
@property
|
|
def xy_color(self) -> tuple[float, float] | None:
|
|
"""Return the xy color value [float, float]."""
|
|
return self.entity_data.entity.xy_color
|
|
|
|
@property
|
|
def color_temp_kelvin(self) -> int | None:
|
|
"""Return the color temperature value in Kelvin."""
|
|
return (
|
|
color_util.color_temperature_mired_to_kelvin(mireds)
|
|
if (mireds := self.entity_data.entity.color_temp)
|
|
else None
|
|
)
|
|
|
|
@property
|
|
def color_mode(self) -> ColorMode | None:
|
|
"""Return the color mode."""
|
|
if self.entity_data.entity.color_mode is None:
|
|
return None
|
|
return ZHA_TO_HA_COLOR_MODE[self.entity_data.entity.color_mode]
|
|
|
|
@property
|
|
def effect_list(self) -> list[str] | None:
|
|
"""Return the list of supported effects."""
|
|
return self.entity_data.entity.effect_list
|
|
|
|
@property
|
|
def effect(self) -> str | None:
|
|
"""Return the current effect."""
|
|
return self.entity_data.entity.effect
|
|
|
|
@convert_zha_error_to_ha_error
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn the entity on."""
|
|
color_temp = (
|
|
color_util.color_temperature_kelvin_to_mired(color_temp_k)
|
|
if (color_temp_k := kwargs.get(ATTR_COLOR_TEMP_KELVIN))
|
|
else None
|
|
)
|
|
await self.entity_data.entity.async_turn_on(
|
|
transition=kwargs.get(ATTR_TRANSITION),
|
|
brightness=kwargs.get(ATTR_BRIGHTNESS),
|
|
effect=kwargs.get(ATTR_EFFECT),
|
|
flash=kwargs.get(ATTR_FLASH),
|
|
color_temp=color_temp,
|
|
xy_color=kwargs.get(ATTR_XY_COLOR),
|
|
)
|
|
self.async_write_ha_state()
|
|
|
|
@convert_zha_error_to_ha_error
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn the entity off."""
|
|
await self.entity_data.entity.async_turn_off(
|
|
transition=kwargs.get(ATTR_TRANSITION)
|
|
)
|
|
self.async_write_ha_state()
|
|
|
|
@callback
|
|
def restore_external_state_attributes(self, state: State) -> None:
|
|
"""Restore entity state."""
|
|
color_temp = (
|
|
color_util.color_temperature_kelvin_to_mired(color_temp_k)
|
|
if (color_temp_k := state.attributes.get(ATTR_COLOR_TEMP_KELVIN))
|
|
else None
|
|
)
|
|
self.entity_data.entity.restore_external_state_attributes(
|
|
state=(state.state == STATE_ON),
|
|
off_with_transition=state.attributes.get(OFF_WITH_TRANSITION),
|
|
off_brightness=state.attributes.get(OFF_BRIGHTNESS),
|
|
brightness=state.attributes.get(ATTR_BRIGHTNESS),
|
|
color_temp=color_temp,
|
|
xy_color=state.attributes.get(ATTR_XY_COLOR),
|
|
color_mode=(
|
|
HA_TO_ZHA_COLOR_MODE[ColorMode(state.attributes[ATTR_COLOR_MODE])]
|
|
if state.attributes.get(ATTR_COLOR_MODE) is not None
|
|
else None
|
|
),
|
|
effect=state.attributes.get(ATTR_EFFECT),
|
|
)
|