1 """Light for Shelly."""
3 from __future__
import annotations
5 from typing
import Any, cast
7 from aioshelly.block_device
import Block
8 from aioshelly.const
import MODEL_BULB, RPC_GENERATIONS
12 ATTR_COLOR_TEMP_KELVIN,
17 DOMAIN
as LIGHT_DOMAIN,
27 BLOCK_MAX_TRANSITION_TIME_MS,
28 DUAL_MODE_LIGHT_MODELS,
30 KELVIN_MIN_VALUE_COLOR,
31 KELVIN_MIN_VALUE_WHITE,
33 MODELS_SUPPORTING_LIGHT_TRANSITION,
35 RPC_MIN_TRANSITION_TIME_SEC,
39 from .coordinator
import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
40 from .entity
import ShellyBlockEntity, ShellyRpcEntity
42 async_remove_orphaned_entities,
43 async_remove_shelly_entity,
44 brightness_to_percentage,
47 is_block_channel_type_light,
48 is_rpc_channel_type_light,
49 percentage_to_brightness,
55 config_entry: ShellyConfigEntry,
56 async_add_entities: AddEntitiesCallback,
58 """Set up lights for device."""
68 config_entry: ShellyConfigEntry,
69 async_add_entities: AddEntitiesCallback,
71 """Set up entities for block device."""
72 coordinator = config_entry.runtime_data.block
75 assert coordinator.device.blocks
76 for block
in coordinator.device.blocks:
77 if block.type ==
"light":
79 elif block.type ==
"relay" and block.channel
is not None:
81 coordinator.device.settings,
int(block.channel)
86 unique_id = f
"{coordinator.mac}-{block.type}_{block.channel}"
98 config_entry: ShellyConfigEntry,
99 async_add_entities: AddEntitiesCallback,
101 """Set up entities for RPC device."""
102 coordinator = config_entry.runtime_data.rpc
107 for id_
in switch_key_ids:
111 switch_ids.append(id_)
112 unique_id = f
"{coordinator.mac}-switch:{id_}"
121 entities: list[RpcShellyLightBase] = []
122 if light_key_ids :=
get_rpc_key_ids(coordinator.device.status,
"light"):
123 entities.extend(
RpcShellyLight(coordinator, id_)
for id_
in light_key_ids)
135 config_entry.entry_id,
138 coordinator.device.status,
143 """Entity that controls a light on block based Shelly devices."""
145 _attr_supported_color_modes: set[str]
147 def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) ->
None:
148 """Initialize light."""
149 super().
__init__(coordinator, block)
155 if hasattr(block,
"red")
and hasattr(block,
"green")
and hasattr(block,
"blue"):
157 if coordinator.model
in RGBW_MODELS:
162 if hasattr(block,
"colorTemp"):
166 if hasattr(block,
"brightness")
or hasattr(block,
"gain"):
171 if hasattr(block,
"effect"):
172 self._attr_supported_features |= LightEntityFeature.EFFECT
174 if coordinator.model
in MODELS_SUPPORTING_LIGHT_TRANSITION:
175 self._attr_supported_features |= LightEntityFeature.TRANSITION
179 """If light is on."""
187 """Return the color mode of the light."""
191 if hasattr(self.
blockblock,
"mode"):
192 return cast(str, self.
blockblock.mode)
195 hasattr(self.
blockblock,
"red")
196 and hasattr(self.
blockblock,
"green")
197 and hasattr(self.
blockblock,
"blue")
205 """Return the brightness of this light between 0..255."""
218 """Return the color mode of the light."""
220 if self.coordinator.model
in RGBW_MODELS:
221 return ColorMode.RGBW
224 if hasattr(self.
blockblock,
"colorTemp"):
225 return ColorMode.COLOR_TEMP
227 if hasattr(self.
blockblock,
"brightness")
or hasattr(self.
blockblock,
"gain"):
228 return ColorMode.BRIGHTNESS
230 return ColorMode.ONOFF
234 """Return the rgb color value [int, int, int]."""
240 red = self.
blockblock.red
241 green = self.
blockblock.green
242 blue = self.
blockblock.blue
243 return (cast(int, red), cast(int, green), cast(int, blue))
247 """Return the rgbw color value [int, int, int, int]."""
251 white = self.
blockblock.white
257 """Return the CT color value in kelvin."""
258 color_temp = cast(int, self.
blockblock.colorTemp)
269 """Return the list of supported effects."""
270 if self.coordinator.model == MODEL_BULB:
271 return list(SHBLB_1_RGB_EFFECTS.values())
273 return list(STANDARD_RGB_EFFECTS.values())
277 """Return the current effect."""
281 effect_index = self.
blockblock.effect
283 if self.coordinator.model == MODEL_BULB:
284 return SHBLB_1_RGB_EFFECTS[cast(int, effect_index)]
286 return STANDARD_RGB_EFFECTS[cast(int, effect_index)]
290 if self.
blockblock.type ==
"relay":
297 params: dict[str, Any] = {
"turn":
"on"}
299 if ATTR_TRANSITION
in kwargs:
300 params[
"transition"] =
min(
301 int(kwargs[ATTR_TRANSITION] * 1000), BLOCK_MAX_TRANSITION_TIME_MS
305 if hasattr(self.
blockblock,
"gain"):
307 if hasattr(self.
blockblock,
"brightness"):
311 ATTR_COLOR_TEMP_KELVIN
in kwargs
312 and ColorMode.COLOR_TEMP
in supported_color_modes
316 color_temp = kwargs[ATTR_COLOR_TEMP_KELVIN]
318 params[
"temp"] =
int(
325 if ATTR_RGB_COLOR
in kwargs
and ColorMode.RGB
in supported_color_modes:
329 (params[
"red"], params[
"green"], params[
"blue"]) = kwargs[ATTR_RGB_COLOR]
331 if ATTR_RGBW_COLOR
in kwargs
and ColorMode.RGBW
in supported_color_modes:
335 (params[
"red"], params[
"green"], params[
"blue"], params[
"white"]) = kwargs[
339 if ATTR_EFFECT
in kwargs
and ATTR_COLOR_TEMP_KELVIN
not in kwargs:
342 if self.coordinator.model == MODEL_BULB:
343 effect_dict = SHBLB_1_RGB_EFFECTS
345 effect_dict = STANDARD_RGB_EFFECTS
346 if kwargs[ATTR_EFFECT]
in effect_dict.values():
348 k
for k, v
in effect_dict.items()
if v == kwargs[ATTR_EFFECT]
352 "Effect '%s' not supported by device %s",
354 self.coordinator.model,
360 and self.coordinator.model
in DUAL_MODE_LIGHT_MODELS
362 params[
"mode"] = set_mode
368 """Turn off light."""
369 params: dict[str, Any] = {
"turn":
"off"}
371 if ATTR_TRANSITION
in kwargs:
372 params[
"transition"] =
min(
373 int(kwargs[ATTR_TRANSITION] * 1000), BLOCK_MAX_TRANSITION_TIME_MS
382 """When device updates, clear control & mode result that overrides state."""
388 """Base Entity for RPC based Shelly devices."""
390 _component: str =
"Light"
392 def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) ->
None:
393 """Initialize light."""
394 super().
__init__(coordinator, f
"{self._component.lower()}:{id_}")
399 """If light is on."""
404 """Return the brightness of this light between 0..255."""
409 """Return the rgb color value [int, int, int]."""
410 return cast(tuple, self.
statusstatus[
"rgb"])
414 """Return the rgbw color value [int, int, int, int]."""
415 return (*self.
statusstatus[
"rgb"], self.
statusstatus[
"white"])
419 params: dict[str, Any] = {
"id": self.
_id_id,
"on":
True}
421 if ATTR_BRIGHTNESS
in kwargs:
424 if ATTR_COLOR_TEMP_KELVIN
in kwargs:
425 params[
"ct"] = kwargs[ATTR_COLOR_TEMP_KELVIN]
427 if ATTR_TRANSITION
in kwargs:
428 params[
"transition_duration"] =
max(
429 kwargs[ATTR_TRANSITION], RPC_MIN_TRANSITION_TIME_SEC
432 if ATTR_RGB_COLOR
in kwargs:
433 params[
"rgb"] =
list(kwargs[ATTR_RGB_COLOR])
435 if ATTR_RGBW_COLOR
in kwargs:
436 params[
"rgb"] =
list(kwargs[ATTR_RGBW_COLOR][:-1])
437 params[
"white"] = kwargs[ATTR_RGBW_COLOR][-1]
439 await self.
call_rpccall_rpc(f
"{self._component}.Set", params)
442 """Turn off light."""
443 params: dict[str, Any] = {
"id": self.
_id_id,
"on":
False}
445 if ATTR_TRANSITION
in kwargs:
446 params[
"transition_duration"] =
max(
447 kwargs[ATTR_TRANSITION], RPC_MIN_TRANSITION_TIME_SEC
450 await self.
call_rpccall_rpc(f
"{self._component}.Set", params)
454 """Entity that controls a relay as light on RPC based Shelly devices."""
456 _component =
"Switch"
458 _attr_color_mode = ColorMode.ONOFF
459 _attr_supported_color_modes = {ColorMode.ONOFF}
463 """Entity that controls a light on RPC based Shelly devices."""
467 _attr_color_mode = ColorMode.BRIGHTNESS
468 _attr_supported_color_modes = {ColorMode.BRIGHTNESS}
469 _attr_supported_features = LightEntityFeature.TRANSITION
473 """Entity that controls a CCT light on RPC based Shelly devices."""
477 _attr_color_mode = ColorMode.COLOR_TEMP
478 _attr_supported_color_modes = {ColorMode.COLOR_TEMP}
479 _attr_supported_features = LightEntityFeature.TRANSITION
481 def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) ->
None:
482 """Initialize light."""
483 color_temp_range = coordinator.device.config[f
"cct:{id_}"][
"ct_range"]
491 """Return the CT color value in Kelvin."""
492 return cast(int, self.
statusstatus[
"ct"])
496 """Entity that controls a RGB light on RPC based Shelly devices."""
500 _attr_color_mode = ColorMode.RGB
501 _attr_supported_color_modes = {ColorMode.RGB}
502 _attr_supported_features = LightEntityFeature.TRANSITION
506 """Entity that controls a RGBW light on RPC based Shelly devices."""
510 _attr_color_mode = ColorMode.RGBW
511 _attr_supported_color_modes = {ColorMode.RGBW}
512 _attr_supported_features = LightEntityFeature.TRANSITION
int min_color_temp_kelvin(self)
int max_color_temp_kelvin(self)
tuple[int, int, int]|None rgb_color(self)
Any set_state(self, **Any kwargs)
Any call_rpc(self, str method, Any params)
_attr_supported_color_modes
_attr_max_color_temp_kelvin
tuple[int, int, int, int] rgbw_color(self)
None async_turn_off(self, **Any kwargs)
int color_temp_kelvin(self)
None async_turn_on(self, **Any kwargs)
None __init__(self, ShellyBlockCoordinator coordinator, Block block)
ColorMode color_mode(self)
_attr_min_color_temp_kelvin
list[str]|None effect_list(self)
None _update_callback(self)
tuple[int, int, int] rgb_color(self)
_attr_min_color_temp_kelvin
None __init__(self, ShellyRpcCoordinator coordinator, int id_)
_attr_max_color_temp_kelvin
int color_temp_kelvin(self)
None async_turn_off(self, **Any kwargs)
tuple[int, int, int] rgb_color(self)
None async_turn_on(self, **Any kwargs)
None __init__(self, ShellyRpcCoordinator coordinator, int id_)
tuple[int, int, int, int] rgbw_color(self)
None async_write_ha_state(self)
bool add(self, _T matcher)
web.Response get(self, web.Request request, str config_key)
bool brightness_supported(Iterable[ColorMode|str]|None color_modes)
None async_setup_block_entry(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities)
None async_setup_entry(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities)
None async_setup_rpc_entry(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities)
bool is_rpc_channel_type_light(dict[str, Any] config, int channel)
int percentage_to_brightness(int percentage)
None async_remove_orphaned_entities(HomeAssistant hass, str config_entry_id, str mac, str platform, Iterable[str] keys, str|None key_suffix=None)
int brightness_to_percentage(int brightness)
list[int] get_rpc_key_ids(dict[str, Any] keys_dict, str key)
int get_device_entry_gen(ConfigEntry entry)
bool is_block_channel_type_light(dict[str, Any] settings, int channel)
None async_remove_shelly_entity(HomeAssistant hass, str domain, str unique_id)