1 """Support for the Tuya lights."""
3 from __future__
import annotations
5 from dataclasses
import dataclass, field
7 from typing
import Any, cast
9 from tuya_sharing
import CustomerDevice, Manager
17 LightEntityDescription,
18 filter_supported_color_modes,
25 from .
import TuyaConfigEntry
26 from .const
import TUYA_DISCOVERY_NEW, DPCode, DPType, WorkMode
27 from .entity
import IntegerTypeData, TuyaEntity
28 from .util
import remap_value
33 """Color Type Data."""
35 h_type: IntegerTypeData
36 s_type: IntegerTypeData
37 v_type: IntegerTypeData
41 h_type=
IntegerTypeData(DPCode.COLOUR_DATA_HSV, min=1, scale=0, max=360, step=1),
42 s_type=
IntegerTypeData(DPCode.COLOUR_DATA_HSV, min=1, scale=0, max=255, step=1),
43 v_type=
IntegerTypeData(DPCode.COLOUR_DATA_HSV, min=1, scale=0, max=255, step=1),
47 h_type=
IntegerTypeData(DPCode.COLOUR_DATA_HSV, min=1, scale=0, max=360, step=1),
48 s_type=
IntegerTypeData(DPCode.COLOUR_DATA_HSV, min=1, scale=0, max=1000, step=1),
49 v_type=
IntegerTypeData(DPCode.COLOUR_DATA_HSV, min=1, scale=0, max=1000, step=1),
53 @dataclass(frozen=True)
55 """Describe an Tuya light entity."""
57 brightness_max: DPCode |
None =
None
58 brightness_min: DPCode |
None =
None
59 brightness: DPCode | tuple[DPCode, ...] |
None =
None
60 color_data: DPCode | tuple[DPCode, ...] |
None =
None
61 color_mode: DPCode |
None =
None
62 color_temp: DPCode | tuple[DPCode, ...] |
None =
None
63 default_color_type: ColorTypeData = field(
64 default_factory=
lambda: DEFAULT_COLOR_TYPE_DATA
68 LIGHTS: dict[str, tuple[TuyaLightEntityDescription, ...]] = {
73 key=DPCode.SWITCH_BACKLIGHT,
74 translation_key=
"backlight",
75 entity_category=EntityCategory.CONFIG,
82 key=DPCode.SWITCH_LED,
84 color_mode=DPCode.WORK_MODE,
85 brightness=DPCode.BRIGHT_VALUE,
86 color_temp=DPCode.TEMP_VALUE,
87 color_data=DPCode.COLOUR_DATA,
94 key=DPCode.SWITCH_LED,
96 color_mode=DPCode.WORK_MODE,
97 brightness=DPCode.BRIGHT_VALUE,
98 color_temp=DPCode.TEMP_VALUE,
99 color_data=DPCode.COLOUR_DATA,
100 default_color_type=DEFAULT_COLOR_TYPE_DATA_V2,
107 key=DPCode.SWITCH_LED,
109 color_mode=DPCode.WORK_MODE,
110 brightness=(DPCode.BRIGHT_VALUE_V2, DPCode.BRIGHT_VALUE),
111 color_temp=(DPCode.TEMP_VALUE_V2, DPCode.TEMP_VALUE),
112 color_data=(DPCode.COLOUR_DATA_V2, DPCode.COLOUR_DATA),
118 translation_key=
"light",
119 brightness=DPCode.BRIGHT_VALUE_1,
128 key=DPCode.SWITCH_LED,
130 color_mode=DPCode.WORK_MODE,
131 brightness=DPCode.BRIGHT_VALUE,
138 key=DPCode.SWITCH_LED,
140 color_mode=DPCode.WORK_MODE,
141 brightness=DPCode.BRIGHT_VALUE,
142 color_temp=DPCode.TEMP_VALUE,
143 color_data=DPCode.COLOUR_DATA,
155 key=DPCode.SWITCH_LED,
157 color_mode=DPCode.WORK_MODE,
158 brightness=DPCode.BRIGHT_VALUE,
159 color_temp=DPCode.TEMP_VALUE,
160 color_data=DPCode.COLOUR_DATA,
167 key=DPCode.SWITCH_LED,
169 color_mode=DPCode.WORK_MODE,
170 brightness=DPCode.BRIGHT_VALUE,
171 color_temp=DPCode.TEMP_VALUE,
172 color_data=DPCode.COLOUR_DATA,
179 key=DPCode.SWITCH_LED,
181 color_mode=DPCode.WORK_MODE,
182 brightness=DPCode.BRIGHT_VALUE,
183 color_data=DPCode.COLOUR_DATA_HSV,
190 key=DPCode.SWITCH_BACKLIGHT,
191 translation_key=
"backlight",
192 entity_category=EntityCategory.CONFIG,
200 translation_key=
"backlight",
201 entity_category=EntityCategory.CONFIG,
209 translation_key=
"backlight",
210 entity_category=EntityCategory.CONFIG,
218 key=DPCode.SWITCH_LED,
220 color_mode=DPCode.WORK_MODE,
221 brightness=DPCode.BRIGHT_VALUE,
222 color_data=DPCode.COLOUR_DATA,
230 key=DPCode.SWITCH_LED,
232 color_mode=DPCode.WORK_MODE,
233 brightness=DPCode.BRIGHT_VALUE,
234 color_data=DPCode.COLOUR_DATA,
242 translation_key=
"backlight",
243 entity_category=EntityCategory.CONFIG,
250 key=DPCode.FLOODLIGHT_SWITCH,
251 brightness=DPCode.FLOODLIGHT_LIGHTNESS,
255 key=DPCode.BASIC_INDICATOR,
256 name=
"Indicator light",
257 entity_category=EntityCategory.CONFIG,
265 brightness=DPCode.BRIGHT_VALUE,
266 translation_key=
"light",
273 key=DPCode.SWITCH_LED_1,
274 translation_key=
"light",
275 brightness=DPCode.BRIGHT_VALUE_1,
276 brightness_max=DPCode.BRIGHTNESS_MAX_1,
277 brightness_min=DPCode.BRIGHTNESS_MIN_1,
280 key=DPCode.SWITCH_LED_2,
281 translation_key=
"light_2",
282 brightness=DPCode.BRIGHT_VALUE_2,
283 brightness_max=DPCode.BRIGHTNESS_MAX_2,
284 brightness_min=DPCode.BRIGHTNESS_MIN_2,
287 key=DPCode.SWITCH_LED_3,
288 translation_key=
"light_3",
289 brightness=DPCode.BRIGHT_VALUE_3,
290 brightness_max=DPCode.BRIGHTNESS_MAX_3,
291 brightness_min=DPCode.BRIGHTNESS_MIN_3,
298 key=DPCode.SWITCH_LED,
299 translation_key=
"light",
300 brightness=(DPCode.BRIGHT_VALUE_V2, DPCode.BRIGHT_VALUE),
301 brightness_max=DPCode.BRIGHTNESS_MAX_1,
302 brightness_min=DPCode.BRIGHTNESS_MIN_1,
305 key=DPCode.SWITCH_LED_1,
306 translation_key=
"light",
307 brightness=DPCode.BRIGHT_VALUE_1,
310 key=DPCode.SWITCH_LED_2,
311 translation_key=
"light_2",
312 brightness=DPCode.BRIGHT_VALUE_2,
319 key=DPCode.SWITCH_LED,
320 translation_key=
"light",
321 brightness=(DPCode.BRIGHT_VALUE_V2, DPCode.BRIGHT_VALUE),
322 brightness_max=DPCode.BRIGHTNESS_MAX_1,
323 brightness_min=DPCode.BRIGHTNESS_MIN_1,
330 key=DPCode.SWITCH_LED,
332 color_mode=DPCode.WORK_MODE,
333 brightness=DPCode.BRIGHT_VALUE,
334 color_temp=DPCode.TEMP_VALUE,
335 color_data=DPCode.COLOUR_DATA,
342 key=DPCode.SWITCH_LED,
344 color_mode=DPCode.WORK_MODE,
345 brightness=DPCode.BRIGHT_VALUE,
346 color_temp=DPCode.TEMP_VALUE,
347 color_data=DPCode.COLOUR_DATA,
350 key=DPCode.SWITCH_NIGHT_LIGHT,
351 translation_key=
"night_light",
358 key=DPCode.SWITCH_CONTROLLER,
360 color_mode=DPCode.WORK_MODE,
361 brightness=DPCode.BRIGHT_CONTROLLER,
362 color_temp=DPCode.TEMP_CONTROLLER,
371 color_mode=DPCode.WORK_MODE,
372 brightness=DPCode.BRIGHT_VALUE,
373 color_temp=DPCode.TEMP_VALUE,
376 key=DPCode.SWITCH_LED,
377 translation_key=
"light_2",
378 brightness=DPCode.BRIGHT_VALUE_1,
385 LIGHTS[
"cz"] = LIGHTS[
"kg"]
389 LIGHTS[
"pc"] = LIGHTS[
"kg"]
393 LIGHTS[
"tdq"] = LIGHTS[
"tgq"]
400 type_data: ColorTypeData
407 """Get the HS value from this color data."""
409 self.type_data.h_type.remap_value_to(self.h_value, 0, 360),
410 self.type_data.s_type.remap_value_to(self.s_value, 0, 100),
415 """Get the brightness value from this color data."""
416 return round(self.type_data.v_type.remap_value_to(self.v_value, 0, 255))
420 hass: HomeAssistant, entry: TuyaConfigEntry, async_add_entities: AddEntitiesCallback
422 """Set up tuya light dynamically through tuya discovery."""
423 hass_data = entry.runtime_data
427 """Discover and add a discovered tuya light."""
428 entities: list[TuyaLightEntity] = []
429 for device_id
in device_ids:
430 device = hass_data.manager.device_map[device_id]
431 if descriptions := LIGHTS.get(device.category):
434 for description
in descriptions
435 if description.key
in device.status
442 entry.async_on_unload(
448 """Tuya light device."""
450 entity_description: TuyaLightEntityDescription
452 _brightness_max: IntegerTypeData |
None =
None
453 _brightness_min: IntegerTypeData |
None =
None
454 _brightness: IntegerTypeData |
None =
None
455 _color_data_dpcode: DPCode |
None =
None
456 _color_data_type: ColorTypeData |
None =
None
457 _color_mode: DPCode |
None =
None
458 _color_temp: IntegerTypeData |
None =
None
459 _fixed_color_mode: ColorMode |
None =
None
463 device: CustomerDevice,
464 device_manager: Manager,
465 description: TuyaLightEntityDescription,
467 """Init TuyaHaLight."""
468 super().
__init__(device, device_manager)
471 color_modes: set[ColorMode] = {ColorMode.ONOFF}
475 description.color_mode, prefer_function=
True
479 description.brightness, dptype=DPType.INTEGER, prefer_function=
True
482 color_modes.add(ColorMode.BRIGHTNESS)
484 description.brightness_max, dptype=DPType.INTEGER
487 description.brightness_min, dptype=DPType.INTEGER
491 description.color_temp, dptype=DPType.INTEGER, prefer_function=
True
494 color_modes.add(ColorMode.COLOR_TEMP)
498 )
and self.
get_dptypeget_dptype(dpcode) == DPType.JSON:
500 color_modes.add(ColorMode.HS)
501 if dpcode
in self.
devicedevice.function:
502 values = cast(str, self.
devicedevice.function[dpcode].values)
504 values = self.
devicedevice.status_range[dpcode].values
507 if function_data := json.loads(values):
528 """Return true if light is on."""
532 """Turn on or control the light."""
535 if self.
_color_temp_color_temp
and ATTR_COLOR_TEMP
in kwargs:
540 "value": WorkMode.WHITE,
549 kwargs[ATTR_COLOR_TEMP],
559 ATTR_HS_COLOR
in kwargs
561 ATTR_BRIGHTNESS
in kwargs
563 and ATTR_COLOR_TEMP
not in kwargs
570 "value": WorkMode.COLOUR,
574 if not (brightness := kwargs.get(ATTR_BRIGHTNESS)):
577 if not (color := kwargs.get(ATTR_HS_COLOR)):
605 elif ATTR_BRIGHTNESS
in kwargs
and self.
_brightness_brightness:
606 brightness = kwargs[ATTR_BRIGHTNESS]
614 brightness_max := self.
devicedevice.status.get(
620 brightness_min := self.
devicedevice.status.get(
627 brightness_max = self.
_brightness_max_brightness_max.remap_value_to(brightness_max)
628 brightness_min = self.
_brightness_min_brightness_min.remap_value_to(brightness_min)
633 to_min=brightness_min,
634 to_max=brightness_max,
640 "value": round(self.
_brightness_brightness.remap_value_from(brightness)),
647 """Instruct the light to turn off."""
652 """Return the brightness of the light."""
655 return color_data.brightness
661 if brightness
is None:
665 brightness = self.
_brightness_brightness.remap_value_to(brightness)
678 brightness_max = self.
_brightness_max_brightness_max.remap_value_to(brightness_max)
679 brightness_min = self.
_brightness_min_brightness_min.remap_value_to(brightness_min)
684 from_min=brightness_min,
685 from_max=brightness_max,
688 return round(brightness)
692 """Return the color_temp of the light."""
697 if temperature
is None:
708 """Return the hs_color of the light."""
713 return color_data.hs_color
717 """Return the color_mode of the light."""
730 return ColorMode.COLOR_TEMP
733 """Get current color data from device."""
744 if not (status := json.loads(status_data)):
int|None brightness(self)
tuple[float, float]|None hs_color(self)
ColorMode|str|None color_mode(self)
None _send_command(self, list[dict[str, Any]] commands)
DPCode|EnumTypeData|IntegerTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, DPType|None dptype=None)
IntegerTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, Literal[DPType.INTEGER] dptype)
DPCode|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False)
EnumTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, Literal[DPType.ENUM] dptype)
DPType|None get_dptype(self, DPCode|None dpcode, bool prefer_function=False)
tuple[float, float] hs_color(self)
ColorMode color_mode(self)
None turn_on(self, **Any kwargs)
tuple[float, float]|None hs_color(self)
ColorData|None _get_color_data(self)
int|None brightness(self)
_attr_supported_color_modes
int|None color_temp(self)
None turn_off(self, **Any kwargs)
None __init__(self, CustomerDevice device, Manager device_manager, TuyaLightEntityDescription description)
ElkSystem|None async_discover_device(HomeAssistant hass, str host)
set[ColorMode] filter_supported_color_modes(Iterable[ColorMode] color_modes)
None async_setup_entry(HomeAssistant hass, TuyaConfigEntry entry, AddEntitiesCallback async_add_entities)
float remap_value(float value, float from_min=0, float from_max=255, float to_min=0, float to_max=255, bool reverse=False)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)