230 lines
7.9 KiB
Python
230 lines
7.9 KiB
Python
"""Support for Fibaro lights."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from functools import partial
|
|
|
|
from homeassistant.components.light import (
|
|
ATTR_BRIGHTNESS,
|
|
ATTR_HS_COLOR,
|
|
ATTR_WHITE_VALUE,
|
|
DOMAIN,
|
|
SUPPORT_BRIGHTNESS,
|
|
SUPPORT_COLOR,
|
|
SUPPORT_WHITE_VALUE,
|
|
LightEntity,
|
|
)
|
|
from homeassistant.const import CONF_WHITE_VALUE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
|
import homeassistant.util.color as color_util
|
|
|
|
from . import CONF_COLOR, CONF_DIMMING, CONF_RESET_COLOR, FIBARO_DEVICES, FibaroDevice
|
|
|
|
|
|
def scaleto255(value):
|
|
"""Scale the input value from 0-100 to 0-255."""
|
|
# Fibaro has a funny way of storing brightness either 0-100 or 0-99
|
|
# depending on device type (e.g. dimmer vs led)
|
|
if value > 98:
|
|
value = 100
|
|
return max(0, min(255, ((value * 255.0) / 100.0)))
|
|
|
|
|
|
def scaleto100(value):
|
|
"""Scale the input value from 0-255 to 0-100."""
|
|
# Make sure a low but non-zero value is not rounded down to zero
|
|
if 0 < value < 3:
|
|
return 1
|
|
return max(0, min(100, ((value * 100.0) / 255.0)))
|
|
|
|
|
|
async def async_setup_platform(
|
|
hass: HomeAssistant,
|
|
config: ConfigType,
|
|
async_add_entities: AddEntitiesCallback,
|
|
discovery_info: DiscoveryInfoType | None = None,
|
|
) -> None:
|
|
"""Perform the setup for Fibaro controller devices."""
|
|
if discovery_info is None:
|
|
return
|
|
|
|
async_add_entities(
|
|
[FibaroLight(device) for device in hass.data[FIBARO_DEVICES]["light"]], True
|
|
)
|
|
|
|
|
|
class FibaroLight(FibaroDevice, LightEntity):
|
|
"""Representation of a Fibaro Light, including dimmable."""
|
|
|
|
def __init__(self, fibaro_device):
|
|
"""Initialize the light."""
|
|
self._brightness = None
|
|
self._color = (0, 0)
|
|
self._last_brightness = 0
|
|
self._supported_flags = 0
|
|
self._update_lock = asyncio.Lock()
|
|
self._white = 0
|
|
|
|
devconf = fibaro_device.device_config
|
|
self._reset_color = devconf.get(CONF_RESET_COLOR, False)
|
|
supports_color = (
|
|
"color" in fibaro_device.properties
|
|
or "colorComponents" in fibaro_device.properties
|
|
or "RGB" in fibaro_device.type
|
|
or "rgb" in fibaro_device.type
|
|
or "color" in fibaro_device.baseType
|
|
) and (
|
|
"setColor" in fibaro_device.actions
|
|
or "setColorComponents" in fibaro_device.actions
|
|
)
|
|
supports_white_v = (
|
|
"setW" in fibaro_device.actions
|
|
or "RGBW" in fibaro_device.type
|
|
or "rgbw" in fibaro_device.type
|
|
)
|
|
supports_dimming = (
|
|
"levelChange" in fibaro_device.interfaces
|
|
or supports_color
|
|
or supports_white_v
|
|
)
|
|
|
|
# Configuration can override default capability detection
|
|
if devconf.get(CONF_DIMMING, supports_dimming):
|
|
self._supported_flags |= SUPPORT_BRIGHTNESS
|
|
if devconf.get(CONF_COLOR, supports_color):
|
|
self._supported_flags |= SUPPORT_COLOR
|
|
if devconf.get(CONF_WHITE_VALUE, supports_white_v):
|
|
self._supported_flags |= SUPPORT_WHITE_VALUE
|
|
|
|
super().__init__(fibaro_device)
|
|
self.entity_id = f"{DOMAIN}.{self.ha_id}"
|
|
|
|
@property
|
|
def brightness(self):
|
|
"""Return the brightness of the light."""
|
|
return scaleto255(self._brightness)
|
|
|
|
@property
|
|
def hs_color(self):
|
|
"""Return the color of the light."""
|
|
return self._color
|
|
|
|
@property
|
|
def white_value(self):
|
|
"""Return the white value of this light between 0..255."""
|
|
return self._white
|
|
|
|
@property
|
|
def supported_features(self):
|
|
"""Flag supported features."""
|
|
return self._supported_flags
|
|
|
|
async def async_turn_on(self, **kwargs):
|
|
"""Turn the light on."""
|
|
async with self._update_lock:
|
|
await self.hass.async_add_executor_job(partial(self._turn_on, **kwargs))
|
|
|
|
def _turn_on(self, **kwargs):
|
|
"""Really turn the light on."""
|
|
if self._supported_flags & SUPPORT_BRIGHTNESS:
|
|
target_brightness = kwargs.get(ATTR_BRIGHTNESS)
|
|
|
|
# No brightness specified, so we either restore it to
|
|
# last brightness or switch it on at maximum level
|
|
if target_brightness is None:
|
|
if self._brightness == 0:
|
|
if self._last_brightness:
|
|
self._brightness = self._last_brightness
|
|
else:
|
|
self._brightness = 100
|
|
else:
|
|
# We set it to the target brightness and turn it on
|
|
self._brightness = scaleto100(target_brightness)
|
|
|
|
if self._supported_flags & SUPPORT_COLOR and (
|
|
kwargs.get(ATTR_WHITE_VALUE) is not None
|
|
or kwargs.get(ATTR_HS_COLOR) is not None
|
|
):
|
|
if self._reset_color:
|
|
self._color = (100, 0)
|
|
|
|
# Update based on parameters
|
|
self._white = kwargs.get(ATTR_WHITE_VALUE, self._white)
|
|
self._color = kwargs.get(ATTR_HS_COLOR, self._color)
|
|
rgb = color_util.color_hs_to_RGB(*self._color)
|
|
self.call_set_color(
|
|
round(rgb[0]),
|
|
round(rgb[1]),
|
|
round(rgb[2]),
|
|
round(self._white),
|
|
)
|
|
|
|
if self.state == "off":
|
|
self.set_level(min(int(self._brightness), 99))
|
|
return
|
|
|
|
if self._reset_color:
|
|
bri255 = scaleto255(self._brightness)
|
|
self.call_set_color(bri255, bri255, bri255, bri255)
|
|
|
|
if self._supported_flags & SUPPORT_BRIGHTNESS:
|
|
self.set_level(min(int(self._brightness), 99))
|
|
return
|
|
|
|
# The simplest case is left for last. No dimming, just switch on
|
|
self.call_turn_on()
|
|
|
|
async def async_turn_off(self, **kwargs):
|
|
"""Turn the light off."""
|
|
async with self._update_lock:
|
|
await self.hass.async_add_executor_job(partial(self._turn_off, **kwargs))
|
|
|
|
def _turn_off(self, **kwargs):
|
|
"""Really turn the light off."""
|
|
# Let's save the last brightness level before we switch it off
|
|
if (
|
|
(self._supported_flags & SUPPORT_BRIGHTNESS)
|
|
and self._brightness
|
|
and self._brightness > 0
|
|
):
|
|
self._last_brightness = self._brightness
|
|
self._brightness = 0
|
|
self.call_turn_off()
|
|
|
|
@property
|
|
def is_on(self):
|
|
"""Return true if device is on."""
|
|
return self.current_binary_state
|
|
|
|
async def async_update(self):
|
|
"""Update the state."""
|
|
async with self._update_lock:
|
|
await self.hass.async_add_executor_job(self._update)
|
|
|
|
def _update(self):
|
|
"""Really update the state."""
|
|
# Brightness handling
|
|
if self._supported_flags & SUPPORT_BRIGHTNESS:
|
|
self._brightness = float(self.fibaro_device.properties.value)
|
|
# Fibaro might report 0-99 or 0-100 for brightness,
|
|
# based on device type, so we round up here
|
|
if self._brightness > 99:
|
|
self._brightness = 100
|
|
# Color handling
|
|
if (
|
|
self._supported_flags & SUPPORT_COLOR
|
|
and "color" in self.fibaro_device.properties
|
|
and "," in self.fibaro_device.properties.color
|
|
):
|
|
# Fibaro communicates the color as an 'R, G, B, W' string
|
|
rgbw_s = self.fibaro_device.properties.color
|
|
if rgbw_s == "0,0,0,0" and "lastColorSet" in self.fibaro_device.properties:
|
|
rgbw_s = self.fibaro_device.properties.lastColorSet
|
|
rgbw_list = [int(i) for i in rgbw_s.split(",")][:4]
|
|
if rgbw_list[0] or rgbw_list[1] or rgbw_list[2]:
|
|
self._color = color_util.color_RGB_to_hs(*rgbw_list[:3])
|
|
if (self._supported_flags & SUPPORT_WHITE_VALUE) and self.brightness != 0:
|
|
self._white = min(255, max(0, rgbw_list[3]))
|