285 lines
9.2 KiB
Python
285 lines
9.2 KiB
Python
"""Support for HomematicIP Cloud lights."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from homematicip.aio.device import (
|
|
AsyncBrandDimmer,
|
|
AsyncBrandSwitchMeasuring,
|
|
AsyncBrandSwitchNotificationLight,
|
|
AsyncDimmer,
|
|
AsyncFullFlushDimmer,
|
|
AsyncPluggableDimmer,
|
|
AsyncWiredDimmer3,
|
|
)
|
|
from homematicip.base.enums import RGBColorState
|
|
from homematicip.base.functionalChannels import NotificationLightChannel
|
|
|
|
from homeassistant.components.light import (
|
|
ATTR_BRIGHTNESS,
|
|
ATTR_COLOR_NAME,
|
|
ATTR_HS_COLOR,
|
|
ATTR_TRANSITION,
|
|
COLOR_MODE_BRIGHTNESS,
|
|
COLOR_MODE_HS,
|
|
COLOR_MODE_ONOFF,
|
|
LightEntity,
|
|
LightEntityFeature,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
|
|
from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericEntity
|
|
from .hap import HomematicipHAP
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the HomematicIP Cloud lights from a config entry."""
|
|
hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id]
|
|
entities: list[HomematicipGenericEntity] = []
|
|
for device in hap.home.devices:
|
|
if isinstance(device, AsyncBrandSwitchMeasuring):
|
|
entities.append(HomematicipLightMeasuring(hap, device))
|
|
elif isinstance(device, AsyncBrandSwitchNotificationLight):
|
|
entities.append(HomematicipLight(hap, device))
|
|
entities.append(
|
|
HomematicipNotificationLight(hap, device, device.topLightChannelIndex)
|
|
)
|
|
entities.append(
|
|
HomematicipNotificationLight(
|
|
hap, device, device.bottomLightChannelIndex
|
|
)
|
|
)
|
|
elif isinstance(device, AsyncWiredDimmer3):
|
|
for channel in range(1, 4):
|
|
entities.append(HomematicipMultiDimmer(hap, device, channel=channel))
|
|
elif isinstance(
|
|
device,
|
|
(AsyncDimmer, AsyncPluggableDimmer, AsyncBrandDimmer, AsyncFullFlushDimmer),
|
|
):
|
|
entities.append(HomematicipDimmer(hap, device))
|
|
|
|
if entities:
|
|
async_add_entities(entities)
|
|
|
|
|
|
class HomematicipLight(HomematicipGenericEntity, LightEntity):
|
|
"""Representation of the HomematicIP light."""
|
|
|
|
_attr_color_mode = COLOR_MODE_ONOFF
|
|
_attr_supported_color_modes = {COLOR_MODE_ONOFF}
|
|
|
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
|
"""Initialize the light entity."""
|
|
super().__init__(hap, device)
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return true if light is on."""
|
|
return self._device.on
|
|
|
|
async def async_turn_on(self, **kwargs) -> None:
|
|
"""Turn the light on."""
|
|
await self._device.turn_on()
|
|
|
|
async def async_turn_off(self, **kwargs) -> None:
|
|
"""Turn the light off."""
|
|
await self._device.turn_off()
|
|
|
|
|
|
class HomematicipLightMeasuring(HomematicipLight):
|
|
"""Representation of the HomematicIP measuring light."""
|
|
|
|
|
|
class HomematicipMultiDimmer(HomematicipGenericEntity, LightEntity):
|
|
"""Representation of HomematicIP Cloud dimmer."""
|
|
|
|
_attr_color_mode = COLOR_MODE_BRIGHTNESS
|
|
_attr_supported_color_modes = {COLOR_MODE_BRIGHTNESS}
|
|
|
|
def __init__(
|
|
self,
|
|
hap: HomematicipHAP,
|
|
device,
|
|
channel=1,
|
|
is_multi_channel=True,
|
|
) -> None:
|
|
"""Initialize the dimmer light entity."""
|
|
super().__init__(
|
|
hap, device, channel=channel, is_multi_channel=is_multi_channel
|
|
)
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return true if dimmer is on."""
|
|
func_channel = self._device.functionalChannels[self._channel]
|
|
return func_channel.dimLevel is not None and func_channel.dimLevel > 0.0
|
|
|
|
@property
|
|
def brightness(self) -> int:
|
|
"""Return the brightness of this light between 0..255."""
|
|
return int(
|
|
(self._device.functionalChannels[self._channel].dimLevel or 0.0) * 255
|
|
)
|
|
|
|
async def async_turn_on(self, **kwargs) -> None:
|
|
"""Turn the dimmer on."""
|
|
if ATTR_BRIGHTNESS in kwargs:
|
|
await self._device.set_dim_level(
|
|
kwargs[ATTR_BRIGHTNESS] / 255.0, self._channel
|
|
)
|
|
else:
|
|
await self._device.set_dim_level(1, self._channel)
|
|
|
|
async def async_turn_off(self, **kwargs) -> None:
|
|
"""Turn the dimmer off."""
|
|
await self._device.set_dim_level(0, self._channel)
|
|
|
|
|
|
class HomematicipDimmer(HomematicipMultiDimmer, LightEntity):
|
|
"""Representation of HomematicIP Cloud dimmer."""
|
|
|
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
|
"""Initialize the dimmer light entity."""
|
|
super().__init__(hap, device, is_multi_channel=False)
|
|
|
|
|
|
class HomematicipNotificationLight(HomematicipGenericEntity, LightEntity):
|
|
"""Representation of HomematicIP Cloud notification light."""
|
|
|
|
_attr_color_mode = COLOR_MODE_HS
|
|
_attr_supported_color_modes = {COLOR_MODE_HS}
|
|
_attr_supported_features = LightEntityFeature.TRANSITION
|
|
|
|
def __init__(self, hap: HomematicipHAP, device, channel: int) -> None:
|
|
"""Initialize the notification light entity."""
|
|
if channel == 2:
|
|
super().__init__(
|
|
hap, device, post="Top", channel=channel, is_multi_channel=True
|
|
)
|
|
else:
|
|
super().__init__(
|
|
hap, device, post="Bottom", channel=channel, is_multi_channel=True
|
|
)
|
|
|
|
self._color_switcher: dict[str, tuple[float, float]] = {
|
|
RGBColorState.WHITE: (0.0, 0.0),
|
|
RGBColorState.RED: (0.0, 100.0),
|
|
RGBColorState.YELLOW: (60.0, 100.0),
|
|
RGBColorState.GREEN: (120.0, 100.0),
|
|
RGBColorState.TURQUOISE: (180.0, 100.0),
|
|
RGBColorState.BLUE: (240.0, 100.0),
|
|
RGBColorState.PURPLE: (300.0, 100.0),
|
|
}
|
|
|
|
@property
|
|
def _func_channel(self) -> NotificationLightChannel:
|
|
return self._device.functionalChannels[self._channel]
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return true if light is on."""
|
|
return (
|
|
self._func_channel.dimLevel is not None
|
|
and self._func_channel.dimLevel > 0.0
|
|
)
|
|
|
|
@property
|
|
def brightness(self) -> int:
|
|
"""Return the brightness of this light between 0..255."""
|
|
return int((self._func_channel.dimLevel or 0.0) * 255)
|
|
|
|
@property
|
|
def hs_color(self) -> tuple[float, float]:
|
|
"""Return the hue and saturation color value [float, float]."""
|
|
simple_rgb_color = self._func_channel.simpleRGBColorState
|
|
return self._color_switcher.get(simple_rgb_color, (0.0, 0.0))
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> dict[str, Any]:
|
|
"""Return the state attributes of the notification light sensor."""
|
|
state_attr = super().extra_state_attributes
|
|
|
|
if self.is_on:
|
|
state_attr[ATTR_COLOR_NAME] = self._func_channel.simpleRGBColorState
|
|
|
|
return state_attr
|
|
|
|
@property
|
|
def unique_id(self) -> str:
|
|
"""Return a unique ID."""
|
|
return f"{self.__class__.__name__}_{self._post}_{self._device.id}"
|
|
|
|
async def async_turn_on(self, **kwargs) -> None:
|
|
"""Turn the light on."""
|
|
# Use hs_color from kwargs,
|
|
# if not applicable use current hs_color.
|
|
hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
|
|
simple_rgb_color = _convert_color(hs_color)
|
|
|
|
# Use brightness from kwargs,
|
|
# if not applicable use current brightness.
|
|
brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness)
|
|
|
|
# If no kwargs, use default value.
|
|
if not kwargs:
|
|
brightness = 255
|
|
|
|
# Minimum brightness is 10, otherwise the led is disabled
|
|
brightness = max(10, brightness)
|
|
dim_level = brightness / 255.0
|
|
transition = kwargs.get(ATTR_TRANSITION, 0.5)
|
|
|
|
await self._device.set_rgb_dim_level_with_time(
|
|
channelIndex=self._channel,
|
|
rgb=simple_rgb_color,
|
|
dimLevel=dim_level,
|
|
onTime=0,
|
|
rampTime=transition,
|
|
)
|
|
|
|
async def async_turn_off(self, **kwargs) -> None:
|
|
"""Turn the light off."""
|
|
simple_rgb_color = self._func_channel.simpleRGBColorState
|
|
transition = kwargs.get(ATTR_TRANSITION, 0.5)
|
|
|
|
await self._device.set_rgb_dim_level_with_time(
|
|
channelIndex=self._channel,
|
|
rgb=simple_rgb_color,
|
|
dimLevel=0.0,
|
|
onTime=0,
|
|
rampTime=transition,
|
|
)
|
|
|
|
|
|
def _convert_color(color: tuple) -> RGBColorState:
|
|
"""
|
|
Convert the given color to the reduced RGBColorState color.
|
|
|
|
RGBColorStat contains only 8 colors including white and black,
|
|
so a conversion is required.
|
|
"""
|
|
if color is None:
|
|
return RGBColorState.WHITE
|
|
|
|
hue = int(color[0])
|
|
saturation = int(color[1])
|
|
if saturation < 5:
|
|
return RGBColorState.WHITE
|
|
if 30 < hue <= 90:
|
|
return RGBColorState.YELLOW
|
|
if 90 < hue <= 160:
|
|
return RGBColorState.GREEN
|
|
if 150 < hue <= 210:
|
|
return RGBColorState.TURQUOISE
|
|
if 210 < hue <= 270:
|
|
return RGBColorState.BLUE
|
|
if 270 < hue <= 330:
|
|
return RGBColorState.PURPLE
|
|
return RGBColorState.RED
|