| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- """
- Platform to control Tuya lights.
- Initially based on the secondary panel lighting control on some climate
- devices, so only providing simple on/off control.
- """
- from homeassistant.components.light import (
- LightEntity,
- ATTR_BRIGHTNESS,
- ATTR_COLOR_MODE,
- ATTR_COLOR_TEMP,
- ATTR_EFFECT,
- ATTR_RGBW_COLOR,
- COLOR_MODE_BRIGHTNESS,
- COLOR_MODE_COLOR_TEMP,
- COLOR_MODE_ONOFF,
- COLOR_MODE_RGBW,
- COLOR_MODE_UNKNOWN,
- SUPPORT_EFFECT,
- VALID_COLOR_MODES,
- )
- import homeassistant.util.color as color_util
- import logging
- from ..device import TuyaLocalDevice
- from ..helpers.device_config import TuyaEntityConfig
- from ..helpers.mixin import TuyaLocalEntity
- _LOGGER = logging.getLogger(__name__)
- class TuyaLocalLight(TuyaLocalEntity, LightEntity):
- """Representation of a Tuya WiFi-connected light."""
- def __init__(self, device: TuyaLocalDevice, config: TuyaEntityConfig):
- """
- Initialize the light.
- Args:
- device (TuyaLocalDevice): The device API instance.
- config (TuyaEntityConfig): The configuration for this entity.
- """
- dps_map = self._init_begin(device, config)
- self._switch_dps = dps_map.pop("switch", None)
- self._brightness_dps = dps_map.pop("brightness", None)
- self._color_mode_dps = dps_map.pop("color_mode", None)
- self._color_temp_dps = dps_map.pop("color_temp", None)
- self._rgbhsv_dps = dps_map.pop("rgbhsv", None)
- self._effect_dps = dps_map.pop("effect", None)
- self._init_end(dps_map)
- @property
- def supported_color_modes(self):
- """Return the supported color modes for this light."""
- if self._color_mode_dps:
- return [
- mode
- for mode in self._color_mode_dps.values(self._device)
- if mode in VALID_COLOR_MODES
- ]
- else:
- mode = self.color_mode
- if mode and mode != COLOR_MODE_UNKNOWN:
- return [mode]
- return []
- @property
- def supported_features(self):
- """Return the supported features for this light."""
- if self.effect_list:
- return SUPPORT_EFFECT
- else:
- return 0
- @property
- def color_mode(self):
- """Return the color mode of the light"""
- if self._color_mode_dps:
- mode = self._color_mode_dps.get_value(self._device)
- if mode in VALID_COLOR_MODES:
- return mode
- if self._rgbhsv_dps:
- return COLOR_MODE_RGBW
- elif self._color_temp_dps:
- return COLOR_MODE_COLOR_TEMP
- elif self._brightness_dps:
- return COLOR_MODE_BRIGHTNESS
- elif self._switch_dps:
- return COLOR_MODE_ONOFF
- else:
- return COLOR_MODE_UNKNOWN
- @property
- def color_temp(self):
- """Return the color temperature in mireds"""
- if self._color_temp_dps:
- unscaled = self._color_temp_dps.get_value(self._device)
- range = self._color_temp_dps.range(self._device)
- if range:
- min = range["min"]
- max = range["max"]
- return unscaled * 347 / (max - min) + 153 - min
- else:
- return unscaled
- return None
- @property
- def is_on(self):
- """Return the current state."""
- if self._switch_dps:
- return self._switch_dps.get_value(self._device)
- elif self._brightness_dps:
- b = self.brightness
- return isinstance(b, int) and b > 0
- else:
- # There shouldn't be lights without control, but if there are,
- # assume always on if they are responding
- return self.available
- @property
- def brightness(self):
- """Get the current brightness of the light"""
- if self._brightness_dps:
- return self._brightness_dps.get_value(self._device)
- @property
- def rgbw_color(self):
- """Get the current RGBW color of the light"""
- if self._rgbhsv_dps and self._rgbhsv_dps.rawtype == "hex":
- # color data in hex format RRGGBBHHHHSSVV (14 digit hex)
- # Either RGB or HSV can be used.
- color = self._rgbhsv_dps.get_value(self._device)
- h = int(color[6:10], 16)
- s = int(color[10:12], 16)
- r, g, b = color_util.color_hs_to_RGB(h, s)
- w = int(color[12:14], 16) * 255 / 100
- return (r, g, b, w)
- @property
- def effect_list(self):
- """Return the list of valid effects for the light"""
- if self._effect_dps:
- return self._effect_dps.values(self._device)
- elif self._color_mode_dps:
- return [
- effect
- for effect in self._color_mode_dps.values(self._device)
- if effect not in VALID_COLOR_MODES
- ]
- @property
- def effect(self):
- """Return the current effect setting of this light"""
- if self._effect_dps:
- return self._effect_dps.get_value(self._device)
- elif self._color_mode_dps:
- mode = self._color_mode_dps.get_value(self._device)
- if mode in VALID_COLOR_MODES:
- return None
- return mode
- async def async_turn_on(self, **params):
- settings = {}
- if self._switch_dps:
- settings = {
- **settings,
- **self._switch_dps.get_values_to_set(self._device, True),
- }
- if self._color_mode_dps:
- color_mode = params.get(ATTR_COLOR_MODE)
- if color_mode:
- color_values = self._color_mode_dps.get_values_to_set(
- self._device, color_mode
- )
- settings = {
- **settings,
- **color_values,
- }
- elif not self._effect_dps:
- effect = params.get(ATTR_EFFECT)
- if effect:
- color_values = self._color_mode_dps.get_values_to_set(
- self._device, effect
- )
- settings = {
- **settings,
- **color_values,
- }
- if self._color_temp_dps:
- color_temp = params.get(ATTR_COLOR_TEMP)
- range = self._color_temp_dps.range(self._device)
- if range and color_temp:
- min = range["min"]
- max = range["max"]
- color_temp = round((color_temp - 153 + min) * (max - min) / 347)
- if color_temp:
- color_values = self._color_temp_dps.get_values_to_set(
- self._device,
- color_temp,
- )
- settings = {
- **settings,
- **color_values,
- }
- if self._brightness_dps:
- bright = params.get(ATTR_BRIGHTNESS, 255)
- bright_values = self._brightness_dps.get_values_to_set(
- self._device,
- bright,
- )
- settings = {
- **settings,
- **bright_values,
- }
- if self._rgbhsv_dps:
- rgbw = params.get(ATTR_RGBW_COLOR, None)
- if rgbw:
- rgb = (rgbw[0], rgbw[1], rgbw[2])
- hs = color_util.color_RGB_to_hs(rgbw[0], rgbw[1], rgbw[2])
- color = "{:02x}{:02x}{:02x}{:04x}{:02x}{:02x}".format(
- round(rgbw[0]),
- round(rgbw[1]),
- round(rgbw[2]),
- round(hs[0]),
- round(hs[1]),
- round(rgbw[3] * 100 / 255),
- )
- color_dps = self._rgbhsv_dps.get_values_to_set(
- self._device,
- color,
- )
- settings = {**settings, **color_dps}
- if self._effect_dps:
- effect = params.get(ATTR_EFFECT, None)
- if effect:
- effect_values = self._effect_dps.get_values_to_set(
- self._device,
- effect,
- )
- settings = {
- **settings,
- **effect_values,
- }
- await self._device.async_set_properties(settings)
- async def async_turn_off(self):
- if self._switch_dps:
- await self._switch_dps.async_set_value(self._device, False)
- elif self._brightness_dps:
- await self._brightness_dps.async_set_value(self._device, 0)
- else:
- raise NotImplementedError()
- async def async_toggle(self):
- disp_on = self.is_on
- await (self.async_turn_on() if not disp_on else self.async_turn_off())
|