1 """Support for MQTT lights."""
3 from __future__
import annotations
5 from collections.abc
import Callable
7 from typing
import Any, cast
9 import voluptuous
as vol
23 ATTR_SUPPORTED_COLOR_MODES,
30 valid_supported_color_modes,
46 from ..
import subscription
47 from ..config
import MQTT_RW_SCHEMA
51 CONF_STATE_VALUE_TEMPLATE,
54 from ..entity
import MqttEntity
55 from ..models
import (
63 from ..schemas
import MQTT_ENTITY_COMMON_SCHEMA
64 from ..util
import valid_publish_topic, valid_subscribe_topic
65 from .schema
import MQTT_LIGHT_SCHEMA_SCHEMA
67 _LOGGER = logging.getLogger(__name__)
69 CONF_BRIGHTNESS_COMMAND_TEMPLATE =
"brightness_command_template"
70 CONF_BRIGHTNESS_COMMAND_TOPIC =
"brightness_command_topic"
71 CONF_BRIGHTNESS_SCALE =
"brightness_scale"
72 CONF_BRIGHTNESS_STATE_TOPIC =
"brightness_state_topic"
73 CONF_BRIGHTNESS_VALUE_TEMPLATE =
"brightness_value_template"
74 CONF_COLOR_MODE_STATE_TOPIC =
"color_mode_state_topic"
75 CONF_COLOR_MODE_VALUE_TEMPLATE =
"color_mode_value_template"
76 CONF_COLOR_TEMP_COMMAND_TEMPLATE =
"color_temp_command_template"
77 CONF_COLOR_TEMP_COMMAND_TOPIC =
"color_temp_command_topic"
78 CONF_COLOR_TEMP_STATE_TOPIC =
"color_temp_state_topic"
79 CONF_COLOR_TEMP_VALUE_TEMPLATE =
"color_temp_value_template"
80 CONF_EFFECT_COMMAND_TEMPLATE =
"effect_command_template"
81 CONF_EFFECT_COMMAND_TOPIC =
"effect_command_topic"
82 CONF_EFFECT_LIST =
"effect_list"
83 CONF_EFFECT_STATE_TOPIC =
"effect_state_topic"
84 CONF_EFFECT_VALUE_TEMPLATE =
"effect_value_template"
85 CONF_HS_COMMAND_TEMPLATE =
"hs_command_template"
86 CONF_HS_COMMAND_TOPIC =
"hs_command_topic"
87 CONF_HS_STATE_TOPIC =
"hs_state_topic"
88 CONF_HS_VALUE_TEMPLATE =
"hs_value_template"
89 CONF_MAX_MIREDS =
"max_mireds"
90 CONF_MIN_MIREDS =
"min_mireds"
91 CONF_RGB_COMMAND_TEMPLATE =
"rgb_command_template"
92 CONF_RGB_COMMAND_TOPIC =
"rgb_command_topic"
93 CONF_RGB_STATE_TOPIC =
"rgb_state_topic"
94 CONF_RGB_VALUE_TEMPLATE =
"rgb_value_template"
95 CONF_RGBW_COMMAND_TEMPLATE =
"rgbw_command_template"
96 CONF_RGBW_COMMAND_TOPIC =
"rgbw_command_topic"
97 CONF_RGBW_STATE_TOPIC =
"rgbw_state_topic"
98 CONF_RGBW_VALUE_TEMPLATE =
"rgbw_value_template"
99 CONF_RGBWW_COMMAND_TEMPLATE =
"rgbww_command_template"
100 CONF_RGBWW_COMMAND_TOPIC =
"rgbww_command_topic"
101 CONF_RGBWW_STATE_TOPIC =
"rgbww_state_topic"
102 CONF_RGBWW_VALUE_TEMPLATE =
"rgbww_value_template"
103 CONF_XY_COMMAND_TEMPLATE =
"xy_command_template"
104 CONF_XY_COMMAND_TOPIC =
"xy_command_topic"
105 CONF_XY_STATE_TOPIC =
"xy_state_topic"
106 CONF_XY_VALUE_TEMPLATE =
"xy_value_template"
107 CONF_WHITE_COMMAND_TOPIC =
"white_command_topic"
108 CONF_WHITE_SCALE =
"white_scale"
109 CONF_ON_COMMAND_TYPE =
"on_command_type"
111 MQTT_LIGHT_ATTRIBUTES_BLOCKED = frozenset(
124 ATTR_SUPPORTED_COLOR_MODES,
129 DEFAULT_BRIGHTNESS_SCALE = 255
130 DEFAULT_NAME =
"MQTT LightEntity"
131 DEFAULT_PAYLOAD_OFF =
"OFF"
132 DEFAULT_PAYLOAD_ON =
"ON"
133 DEFAULT_WHITE_SCALE = 255
134 DEFAULT_ON_COMMAND_TYPE =
"last"
136 VALUES_ON_COMMAND_TYPE = [
"first",
"last",
"brightness"]
138 COMMAND_TEMPLATE_KEYS = [
139 CONF_BRIGHTNESS_COMMAND_TEMPLATE,
140 CONF_COLOR_TEMP_COMMAND_TEMPLATE,
141 CONF_EFFECT_COMMAND_TEMPLATE,
142 CONF_HS_COMMAND_TEMPLATE,
143 CONF_RGB_COMMAND_TEMPLATE,
144 CONF_RGBW_COMMAND_TEMPLATE,
145 CONF_RGBWW_COMMAND_TEMPLATE,
146 CONF_XY_COMMAND_TEMPLATE,
148 VALUE_TEMPLATE_KEYS = [
149 CONF_BRIGHTNESS_VALUE_TEMPLATE,
150 CONF_COLOR_MODE_VALUE_TEMPLATE,
151 CONF_COLOR_TEMP_VALUE_TEMPLATE,
152 CONF_EFFECT_VALUE_TEMPLATE,
153 CONF_HS_VALUE_TEMPLATE,
154 CONF_RGB_VALUE_TEMPLATE,
155 CONF_RGBW_VALUE_TEMPLATE,
156 CONF_RGBWW_VALUE_TEMPLATE,
157 CONF_STATE_VALUE_TEMPLATE,
158 CONF_XY_VALUE_TEMPLATE,
161 PLATFORM_SCHEMA_MODERN_BASIC = (
162 MQTT_RW_SCHEMA.extend(
164 vol.Optional(CONF_BRIGHTNESS_COMMAND_TEMPLATE): cv.template,
165 vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): valid_publish_topic,
167 CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE
168 ): vol.All(vol.Coerce(int), vol.Range(min=1)),
169 vol.Optional(CONF_BRIGHTNESS_STATE_TOPIC): valid_subscribe_topic,
170 vol.Optional(CONF_BRIGHTNESS_VALUE_TEMPLATE): cv.template,
171 vol.Optional(CONF_COLOR_MODE_STATE_TOPIC): valid_subscribe_topic,
172 vol.Optional(CONF_COLOR_MODE_VALUE_TEMPLATE): cv.template,
173 vol.Optional(CONF_COLOR_TEMP_COMMAND_TEMPLATE): cv.template,
174 vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): valid_publish_topic,
175 vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): valid_subscribe_topic,
176 vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template,
177 vol.Optional(CONF_EFFECT_COMMAND_TEMPLATE): cv.template,
178 vol.Optional(CONF_EFFECT_COMMAND_TOPIC): valid_publish_topic,
179 vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
180 vol.Optional(CONF_EFFECT_STATE_TOPIC): valid_subscribe_topic,
181 vol.Optional(CONF_EFFECT_VALUE_TEMPLATE): cv.template,
182 vol.Optional(CONF_HS_COMMAND_TEMPLATE): cv.template,
183 vol.Optional(CONF_HS_COMMAND_TOPIC): valid_publish_topic,
184 vol.Optional(CONF_HS_STATE_TOPIC): valid_subscribe_topic,
185 vol.Optional(CONF_HS_VALUE_TEMPLATE): cv.template,
186 vol.Optional(CONF_MAX_MIREDS): cv.positive_int,
187 vol.Optional(CONF_MIN_MIREDS): cv.positive_int,
188 vol.Optional(CONF_NAME): vol.Any(cv.string,
None),
189 vol.Optional(CONF_ON_COMMAND_TYPE, default=DEFAULT_ON_COMMAND_TYPE): vol.In(
190 VALUES_ON_COMMAND_TYPE
192 vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
193 vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
194 vol.Optional(CONF_RGB_COMMAND_TEMPLATE): cv.template,
195 vol.Optional(CONF_RGB_COMMAND_TOPIC): valid_publish_topic,
196 vol.Optional(CONF_RGB_STATE_TOPIC): valid_subscribe_topic,
197 vol.Optional(CONF_RGB_VALUE_TEMPLATE): cv.template,
198 vol.Optional(CONF_RGBW_COMMAND_TEMPLATE): cv.template,
199 vol.Optional(CONF_RGBW_COMMAND_TOPIC): valid_publish_topic,
200 vol.Optional(CONF_RGBW_STATE_TOPIC): valid_subscribe_topic,
201 vol.Optional(CONF_RGBW_VALUE_TEMPLATE): cv.template,
202 vol.Optional(CONF_RGBWW_COMMAND_TEMPLATE): cv.template,
203 vol.Optional(CONF_RGBWW_COMMAND_TOPIC): valid_publish_topic,
204 vol.Optional(CONF_RGBWW_STATE_TOPIC): valid_subscribe_topic,
205 vol.Optional(CONF_RGBWW_VALUE_TEMPLATE): cv.template,
206 vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template,
207 vol.Optional(CONF_WHITE_COMMAND_TOPIC): valid_publish_topic,
208 vol.Optional(CONF_WHITE_SCALE, default=DEFAULT_WHITE_SCALE): vol.All(
209 vol.Coerce(int), vol.Range(min=1)
211 vol.Optional(CONF_XY_COMMAND_TEMPLATE): cv.template,
212 vol.Optional(CONF_XY_COMMAND_TOPIC): valid_publish_topic,
213 vol.Optional(CONF_XY_STATE_TOPIC): valid_subscribe_topic,
214 vol.Optional(CONF_XY_VALUE_TEMPLATE): cv.template,
217 .extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
218 .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
221 DISCOVERY_SCHEMA_BASIC = vol.All(
222 PLATFORM_SCHEMA_MODERN_BASIC.extend({}, extra=vol.REMOVE_EXTRA),
227 """Representation of a MQTT light."""
229 _default_name = DEFAULT_NAME
230 _entity_id_format = ENTITY_ID_FORMAT
231 _attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
232 _topic: dict[str, str |
None]
233 _payload: dict[str, str]
234 _command_templates: dict[
235 str, Callable[[PublishPayloadType, TemplateVarsType], PublishPayloadType]
237 _value_templates: dict[
238 str, Callable[[ReceivePayloadType, ReceivePayloadType], ReceivePayloadType]
241 _optimistic_brightness: bool
242 _optimistic_color_mode: bool
243 _optimistic_color_temp: bool
244 _optimistic_effect: bool
245 _optimistic_hs_color: bool
246 _optimistic_rgb_color: bool
247 _optimistic_rgbw_color: bool
248 _optimistic_rgbww_color: bool
249 _optimistic_xy_color: bool
253 """Return the config schema."""
254 return DISCOVERY_SCHEMA_BASIC
257 """(Re)Setup the entity."""
262 topic: dict[str, str |
None] = {
265 CONF_BRIGHTNESS_COMMAND_TOPIC,
266 CONF_BRIGHTNESS_STATE_TOPIC,
267 CONF_COLOR_MODE_STATE_TOPIC,
268 CONF_COLOR_TEMP_COMMAND_TOPIC,
269 CONF_COLOR_TEMP_STATE_TOPIC,
271 CONF_EFFECT_COMMAND_TOPIC,
272 CONF_EFFECT_STATE_TOPIC,
273 CONF_HS_COMMAND_TOPIC,
275 CONF_RGB_COMMAND_TOPIC,
276 CONF_RGB_STATE_TOPIC,
277 CONF_RGBW_COMMAND_TOPIC,
278 CONF_RGBW_STATE_TOPIC,
279 CONF_RGBWW_COMMAND_TOPIC,
280 CONF_RGBWW_STATE_TOPIC,
282 CONF_WHITE_COMMAND_TOPIC,
283 CONF_XY_COMMAND_TOPIC,
288 self.
_payload_payload = {
"on": config[CONF_PAYLOAD_ON],
"off": config[CONF_PAYLOAD_OFF]}
292 config.get(key), entity=self
293 ).async_render_with_possible_json_value
294 for key
in VALUE_TEMPLATE_KEYS
299 for key
in COMMAND_TEMPLATE_KEYS
302 optimistic: bool = config[CONF_OPTIMISTIC]
304 optimistic
or topic[CONF_COLOR_MODE_STATE_TOPIC]
is None
306 self.
_optimistic_optimistic = optimistic
or topic[CONF_STATE_TOPIC]
is None
311 optimistic
or topic[CONF_RGBWW_STATE_TOPIC]
is None
316 topic[CONF_BRIGHTNESS_COMMAND_TOPIC]
is not None
317 and topic[CONF_BRIGHTNESS_STATE_TOPIC]
is None
320 topic[CONF_BRIGHTNESS_COMMAND_TOPIC]
is None
321 and topic[CONF_RGB_STATE_TOPIC]
is None
325 optimistic
or topic[CONF_COLOR_TEMP_STATE_TOPIC]
is None
330 supported_color_modes: set[ColorMode] = set()
331 if topic[CONF_COLOR_TEMP_COMMAND_TOPIC]
is not None:
332 supported_color_modes.add(ColorMode.COLOR_TEMP)
334 if topic[CONF_HS_COMMAND_TOPIC]
is not None:
335 supported_color_modes.add(ColorMode.HS)
337 if topic[CONF_RGB_COMMAND_TOPIC]
is not None:
338 supported_color_modes.add(ColorMode.RGB)
340 if topic[CONF_RGBW_COMMAND_TOPIC]
is not None:
341 supported_color_modes.add(ColorMode.RGBW)
343 if topic[CONF_RGBWW_COMMAND_TOPIC]
is not None:
344 supported_color_modes.add(ColorMode.RGBWW)
346 if topic[CONF_WHITE_COMMAND_TOPIC]
is not None:
347 supported_color_modes.add(ColorMode.WHITE)
348 if topic[CONF_XY_COMMAND_TOPIC]
is not None:
349 supported_color_modes.add(ColorMode.XY)
351 if len(supported_color_modes) > 1:
354 if not supported_color_modes:
355 if topic[CONF_BRIGHTNESS_COMMAND_TOPIC]
is not None:
357 supported_color_modes.add(ColorMode.BRIGHTNESS)
360 supported_color_modes.add(ColorMode.ONOFF)
364 supported_color_modes
368 if topic[CONF_EFFECT_COMMAND_TOPIC]
is not None:
372 """Return True if the attribute is optimistically updated."""
373 attr: bool = getattr(self, f
"_optimistic_{attribute}")
378 """Handle new MQTT messages."""
380 msg.payload, PayloadSentinel.NONE
383 _LOGGER.debug(
"Ignoring empty state message from '%s'", msg.topic)
386 if payload == self.
_payload_payload[
"on"]:
388 elif payload == self.
_payload_payload[
"off"]:
390 elif payload == PAYLOAD_NONE:
395 """Handle new MQTT messages for the brightness."""
396 payload = self.
_value_templates_value_templates[CONF_BRIGHTNESS_VALUE_TEMPLATE](
397 msg.payload, PayloadSentinel.DEFAULT
399 if payload
is PayloadSentinel.DEFAULT
or not payload:
400 _LOGGER.debug(
"Ignoring empty brightness message from '%s'", msg.topic)
403 device_value =
float(payload)
404 if device_value == 0:
405 _LOGGER.debug(
"Ignoring zero brightness from '%s'", msg.topic)
408 percent_bright = device_value / self.
_config_config[CONF_BRIGHTNESS_SCALE]
416 color_mode: ColorMode,
417 convert_color: Callable[..., tuple[int, ...]],
418 ) -> tuple[int, ...] |
None:
419 """Process MQTT messages for RGBW and RGBWW."""
420 payload = self.
_value_templates_value_templates[template](msg.payload, PayloadSentinel.DEFAULT)
421 if payload
is PayloadSentinel.DEFAULT
or not payload:
422 _LOGGER.debug(
"Ignoring empty %s message from '%s'", color_mode, msg.topic)
424 color =
tuple(
int(val)
for val
in str(payload).split(
","))
427 if self.
_topic_topic[CONF_BRIGHTNESS_STATE_TOPIC]
is None:
428 rgb = convert_color(*color)
429 brightness =
max(rgb)
432 "Ignoring %s message with zero rgb brightness from '%s'",
440 min(round(channel / brightness * 255), 255)
for channel
in color
446 """Handle new MQTT messages for RGB."""
448 msg, CONF_RGB_VALUE_TEMPLATE, ColorMode.RGB,
lambda *x: x
456 """Handle new MQTT messages for RGBW."""
459 CONF_RGBW_VALUE_TEMPLATE,
461 color_util.color_rgbw_to_rgb,
469 """Handle new MQTT messages for RGBWW."""
473 r: int, g: int, b: int, cw: int, ww: int
474 ) -> tuple[int, int, int]:
475 min_kelvin = color_util.color_temperature_mired_to_kelvin(self.
max_miredsmax_mireds)
476 max_kelvin = color_util.color_temperature_mired_to_kelvin(self.
min_miredsmin_mireds)
477 return color_util.color_rgbww_to_rgb(
478 r, g, b, cw, ww, min_kelvin, max_kelvin
483 CONF_RGBWW_VALUE_TEMPLATE,
493 """Handle new MQTT messages for color mode."""
494 payload = self.
_value_templates_value_templates[CONF_COLOR_MODE_VALUE_TEMPLATE](
495 msg.payload, PayloadSentinel.DEFAULT
497 if payload
is PayloadSentinel.DEFAULT
or not payload:
498 _LOGGER.debug(
"Ignoring empty color mode message from '%s'", msg.topic)
505 """Handle new MQTT messages for color temperature."""
506 payload = self.
_value_templates_value_templates[CONF_COLOR_TEMP_VALUE_TEMPLATE](
507 msg.payload, PayloadSentinel.DEFAULT
509 if payload
is PayloadSentinel.DEFAULT
or not payload:
510 _LOGGER.debug(
"Ignoring empty color temp message from '%s'", msg.topic)
519 """Handle new MQTT messages for effect."""
520 payload = self.
_value_templates_value_templates[CONF_EFFECT_VALUE_TEMPLATE](
521 msg.payload, PayloadSentinel.DEFAULT
523 if payload
is PayloadSentinel.DEFAULT
or not payload:
524 _LOGGER.debug(
"Ignoring empty effect message from '%s'", msg.topic)
531 """Handle new MQTT messages for hs color."""
533 msg.payload, PayloadSentinel.DEFAULT
535 if payload
is PayloadSentinel.DEFAULT
or not payload:
536 _LOGGER.debug(
"Ignoring empty hs message from '%s'", msg.topic)
539 hs_color =
tuple(
float(val)
for val
in str(payload).split(
",", 2))
544 _LOGGER.warning(
"Failed to parse hs state update: '%s'", payload)
548 """Handle new MQTT messages for xy color."""
550 msg.payload, PayloadSentinel.DEFAULT
552 if payload
is PayloadSentinel.DEFAULT
or not payload:
553 _LOGGER.debug(
"Ignoring empty xy-color message from '%s'", msg.topic)
556 xy_color =
tuple(
float(val)
for val
in str(payload).split(
",", 2))
563 """(Re)Subscribe to topics."""
566 CONF_BRIGHTNESS_STATE_TOPIC, self.
_brightness_received_brightness_received, {
"_attr_brightness"}
569 CONF_RGB_STATE_TOPIC,
571 {
"_attr_brightness",
"_attr_color_mode",
"_attr_rgb_color"},
574 CONF_RGBW_STATE_TOPIC,
576 {
"_attr_brightness",
"_attr_color_mode",
"_attr_rgbw_color"},
579 CONF_RGBWW_STATE_TOPIC,
581 {
"_attr_brightness",
"_attr_color_mode",
"_attr_rgbww_color"},
584 CONF_COLOR_MODE_STATE_TOPIC, self.
_color_mode_received_color_mode_received, {
"_attr_color_mode"}
587 CONF_COLOR_TEMP_STATE_TOPIC,
589 {
"_attr_color_mode",
"_attr_color_temp"},
592 CONF_EFFECT_STATE_TOPIC, self.
_effect_received_effect_received, {
"_attr_effect"}
597 {
"_attr_color_mode",
"_attr_hs_color"},
602 {
"_attr_color_mode",
"_attr_xy_color"},
606 """(Re)Subscribe to topics."""
607 subscription.async_subscribe_topics_internal(self.
hasshasshass, self.
_sub_state_sub_state)
611 attribute: str, condition_attribute: str |
None =
None
613 """Restore a state attribute."""
614 if condition_attribute
is None:
615 condition_attribute = attribute
616 optimistic = self.
_is_optimistic_is_optimistic(condition_attribute)
617 if optimistic
and last_state
and last_state.attributes.get(attribute):
618 setattr(self, f
"_attr_{attribute}", last_state.attributes[attribute])
620 if self.
_topic_topic[CONF_STATE_TOPIC]
is None and self.
_optimistic_optimistic
and last_state:
621 self.
_attr_is_on_attr_is_on = last_state.state == STATE_ON
635 """Turn the device on.
637 This method is a coroutine.
639 should_update =
False
640 on_command_type: str = self.
_config_config[CONF_ON_COMMAND_TYPE]
642 async
def publish(topic: str, payload: PublishPayloadType) ->
None:
643 """Publish an MQTT message."""
647 color: tuple[int, ...],
648 brightness: int |
None =
None,
649 ) -> tuple[int, ...]:
650 """Scale RGBx for brightness."""
651 if brightness
is None:
654 if self.
_topic_topic[CONF_BRIGHTNESS_COMMAND_TOPIC]
is not None:
657 brightness = kwargs.get(ATTR_BRIGHTNESS)
or self.
brightnessbrightness
or 255
658 return tuple(
int(channel * brightness / 255)
for channel
in color)
661 color: tuple[int, ...],
663 color_mode: ColorMode,
664 ) -> PublishPayloadType:
665 """Render RGBx payload."""
666 rgb_color_str =
",".join(
str(channel)
for channel
in color)
667 keys = [
"red",
"green",
"blue"]
668 if color_mode == ColorMode.RGBW:
670 elif color_mode == ColorMode.RGBWW:
671 keys.extend([
"cold_white",
"warm_white"])
672 variables =
dict(zip(keys, color, strict=
False))
678 color_mode: ColorMode |
None =
None,
679 condition_attribute: str |
None =
None,
681 """Optimistically update a state attribute."""
682 if condition_attribute
is None:
683 condition_attribute = attribute
689 setattr(self, f
"_attr_{attribute}", value)
692 if on_command_type ==
"first":
700 on_command_type ==
"brightness"
701 and ATTR_BRIGHTNESS
not in kwargs
702 and ATTR_WHITE
not in kwargs
704 kwargs[ATTR_BRIGHTNESS] = self.
brightnessbrightness
or 255
706 hs_color: str |
None = kwargs.get(ATTR_HS_COLOR)
708 if hs_color
and self.
_topic_topic[CONF_HS_COMMAND_TOPIC]
is not None:
709 device_hs_payload = self.
_command_templates_command_templates[CONF_HS_COMMAND_TEMPLATE](
710 f
"{hs_color[0]},{hs_color[1]}",
711 {
"hue": hs_color[0],
"sat": hs_color[1]},
713 await
publish(CONF_HS_COMMAND_TOPIC, device_hs_payload)
714 should_update |= set_optimistic(ATTR_HS_COLOR, hs_color, ColorMode.HS)
716 rgb: tuple[int, int, int] |
None
717 if (rgb := kwargs.get(ATTR_RGB_COLOR))
and self.
_topic_topic[
718 CONF_RGB_COMMAND_TOPIC
720 scaled = scale_rgbx(rgb)
721 rgb_s = render_rgbx(scaled, CONF_RGB_COMMAND_TEMPLATE, ColorMode.RGB)
722 await
publish(CONF_RGB_COMMAND_TOPIC, rgb_s)
723 should_update |= set_optimistic(ATTR_RGB_COLOR, rgb, ColorMode.RGB)
725 rgbw: tuple[int, int, int, int] |
None
726 if (rgbw := kwargs.get(ATTR_RGBW_COLOR))
and self.
_topic_topic[
727 CONF_RGBW_COMMAND_TOPIC
729 scaled = scale_rgbx(rgbw)
730 rgbw_s = render_rgbx(scaled, CONF_RGBW_COMMAND_TEMPLATE, ColorMode.RGBW)
731 await
publish(CONF_RGBW_COMMAND_TOPIC, rgbw_s)
732 should_update |= set_optimistic(ATTR_RGBW_COLOR, rgbw, ColorMode.RGBW)
734 rgbww: tuple[int, int, int, int, int] |
None
735 if (rgbww := kwargs.get(ATTR_RGBWW_COLOR))
and self.
_topic_topic[
736 CONF_RGBWW_COMMAND_TOPIC
738 scaled = scale_rgbx(rgbww)
739 rgbww_s = render_rgbx(scaled, CONF_RGBWW_COMMAND_TEMPLATE, ColorMode.RGBWW)
740 await
publish(CONF_RGBWW_COMMAND_TOPIC, rgbww_s)
741 should_update |= set_optimistic(ATTR_RGBWW_COLOR, rgbww, ColorMode.RGBWW)
743 xy_color: tuple[float, float] |
None
744 if (xy_color := kwargs.get(ATTR_XY_COLOR))
and self.
_topic_topic[
745 CONF_XY_COMMAND_TOPIC
747 device_xy_payload = self.
_command_templates_command_templates[CONF_XY_COMMAND_TEMPLATE](
748 f
"{xy_color[0]},{xy_color[1]}",
749 {
"x": xy_color[0],
"y": xy_color[1]},
751 await
publish(CONF_XY_COMMAND_TOPIC, device_xy_payload)
752 should_update |= set_optimistic(ATTR_XY_COLOR, xy_color, ColorMode.XY)
755 ATTR_BRIGHTNESS
in kwargs
756 and self.
_topic_topic[CONF_BRIGHTNESS_COMMAND_TOPIC]
is not None
758 brightness_normalized: float = kwargs[ATTR_BRIGHTNESS] / 255
759 brightness_scale: int = self.
_config_config[CONF_BRIGHTNESS_SCALE]
760 device_brightness =
min(
761 round(brightness_normalized * brightness_scale), brightness_scale
764 device_brightness =
max(device_brightness, 1)
765 command_tpl = self.
_command_templates_command_templates[CONF_BRIGHTNESS_COMMAND_TEMPLATE]
766 device_brightness_payload = command_tpl(device_brightness,
None)
767 await
publish(CONF_BRIGHTNESS_COMMAND_TOPIC, device_brightness_payload)
768 should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS])
770 ATTR_BRIGHTNESS
in kwargs
771 and ATTR_RGB_COLOR
not in kwargs
772 and self.
_topic_topic[CONF_RGB_COMMAND_TOPIC]
is not None
774 rgb_color = self.
rgb_colorrgb_color
or (255,) * 3
775 rgb_scaled = scale_rgbx(rgb_color, kwargs[ATTR_BRIGHTNESS])
776 rgb_s = render_rgbx(rgb_scaled, CONF_RGB_COMMAND_TEMPLATE, ColorMode.RGB)
777 await
publish(CONF_RGB_COMMAND_TOPIC, rgb_s)
778 should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS])
780 ATTR_BRIGHTNESS
in kwargs
781 and ATTR_RGBW_COLOR
not in kwargs
782 and self.
_topic_topic[CONF_RGBW_COMMAND_TOPIC]
is not None
784 rgbw_color = self.
rgbw_colorrgbw_color
or (255,) * 4
785 rgbw_b = scale_rgbx(rgbw_color, kwargs[ATTR_BRIGHTNESS])
786 rgbw_s = render_rgbx(rgbw_b, CONF_RGBW_COMMAND_TEMPLATE, ColorMode.RGBW)
787 await
publish(CONF_RGBW_COMMAND_TOPIC, rgbw_s)
788 should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS])
790 ATTR_BRIGHTNESS
in kwargs
791 and ATTR_RGBWW_COLOR
not in kwargs
792 and self.
_topic_topic[CONF_RGBWW_COMMAND_TOPIC]
is not None
794 rgbww_color = self.
rgbww_colorrgbww_color
or (255,) * 5
795 rgbww_b = scale_rgbx(rgbww_color, kwargs[ATTR_BRIGHTNESS])
796 rgbww_s = render_rgbx(rgbww_b, CONF_RGBWW_COMMAND_TEMPLATE, ColorMode.RGBWW)
797 await
publish(CONF_RGBWW_COMMAND_TOPIC, rgbww_s)
798 should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS])
800 ATTR_COLOR_TEMP
in kwargs
801 and self.
_topic_topic[CONF_COLOR_TEMP_COMMAND_TOPIC]
is not None
803 ct_command_tpl = self.
_command_templates_command_templates[CONF_COLOR_TEMP_COMMAND_TEMPLATE]
804 color_temp = ct_command_tpl(
int(kwargs[ATTR_COLOR_TEMP]),
None)
805 await
publish(CONF_COLOR_TEMP_COMMAND_TOPIC, color_temp)
806 should_update |= set_optimistic(
807 ATTR_COLOR_TEMP, kwargs[ATTR_COLOR_TEMP], ColorMode.COLOR_TEMP
811 ATTR_EFFECT
in kwargs
812 and self.
_topic_topic[CONF_EFFECT_COMMAND_TOPIC]
is not None
813 and CONF_EFFECT_LIST
in self.
_config_config
815 if kwargs[ATTR_EFFECT]
in self.
_config_config[CONF_EFFECT_LIST]:
816 eff_command_tpl = self.
_command_templates_command_templates[CONF_EFFECT_COMMAND_TEMPLATE]
817 effect = eff_command_tpl(kwargs[ATTR_EFFECT],
None)
818 await
publish(CONF_EFFECT_COMMAND_TOPIC, effect)
819 should_update |= set_optimistic(ATTR_EFFECT, kwargs[ATTR_EFFECT])
821 if ATTR_WHITE
in kwargs
and self.
_topic_topic[CONF_WHITE_COMMAND_TOPIC]
is not None:
822 percent_white =
float(kwargs[ATTR_WHITE]) / 255
823 white_scale: int = self.
_config_config[CONF_WHITE_SCALE]
824 device_white_value =
min(round(percent_white * white_scale), white_scale)
825 await
publish(CONF_WHITE_COMMAND_TOPIC, device_white_value)
826 should_update |= set_optimistic(
832 if on_command_type ==
"last":
845 """Turn the device off.
847 This method is a coroutine.
int|None brightness(self)
tuple[int, int, int]|None rgb_color(self)
tuple[int, int, int, int]|None rgbw_color(self)
tuple[int, int, int, int, int]|None rgbww_color(self)
None async_publish_with_config(self, str topic, PublishPayloadType payload)
bool add_subscription(self, str state_topic_config_key, Callable[[ReceiveMessage], None] msg_callback, set[str]|None tracked_attributes, bool disable_encoding=False)
None _rgbww_received(self, ReceiveMessage msg)
None async_turn_off(self, **Any kwargs)
bool _is_optimistic(self, str attribute)
_attr_supported_color_modes
VolSchemaType config_schema()
tuple[int,...]|None _rgbx_received(self, ReceiveMessage msg, str template, ColorMode color_mode, Callable[..., tuple[int,...]] convert_color)
None _brightness_received(self, ReceiveMessage msg)
None _subscribe_topics(self)
None _color_mode_received(self, ReceiveMessage msg)
None _state_received(self, ReceiveMessage msg)
None async_turn_on(self, **Any kwargs)
None _rgbw_received(self, ReceiveMessage msg)
None _effect_received(self, ReceiveMessage msg)
None _prepare_subscribe_topics(self)
None _color_temp_received(self, ReceiveMessage msg)
None _hs_received(self, ReceiveMessage msg)
None _xy_received(self, ReceiveMessage msg)
None _rgb_received(self, ReceiveMessage msg)
None _setup_from_config(self, ConfigType config)
None async_write_ha_state(self)
State|None async_get_last_state(self)
set[ColorMode|str] valid_supported_color_modes(Iterable[ColorMode|str] color_modes)
None publish(HomeAssistant hass, str topic, PublishPayloadType payload, int|None qos=0, bool|None retain=False, str|None encoding=DEFAULT_ENCODING)