Add support for color_mode white to MQTT light basic schema (#51484)
* Add support for color_mode white to MQTT light basic schema * Add missing abbreviationspull/52091/head
parent
39bf304031
commit
52c142a82d
|
@ -220,6 +220,8 @@ ABBREVIATIONS = {
|
|||
"uniq_id": "unique_id",
|
||||
"unit_of_meas": "unit_of_measurement",
|
||||
"val_tpl": "value_template",
|
||||
"whit_cmd_t": "white_command_topic",
|
||||
"whit_scl": "white_scale",
|
||||
"whit_val_cmd_t": "white_value_command_topic",
|
||||
"whit_val_scl": "white_value_scale",
|
||||
"whit_val_stat_t": "white_value_state_topic",
|
||||
|
|
|
@ -12,6 +12,7 @@ from homeassistant.components.light import (
|
|||
ATTR_RGB_COLOR,
|
||||
ATTR_RGBW_COLOR,
|
||||
ATTR_RGBWW_COLOR,
|
||||
ATTR_WHITE,
|
||||
ATTR_WHITE_VALUE,
|
||||
ATTR_XY_COLOR,
|
||||
COLOR_MODE_BRIGHTNESS,
|
||||
|
@ -22,6 +23,7 @@ from homeassistant.components.light import (
|
|||
COLOR_MODE_RGBW,
|
||||
COLOR_MODE_RGBWW,
|
||||
COLOR_MODE_UNKNOWN,
|
||||
COLOR_MODE_WHITE,
|
||||
COLOR_MODE_XY,
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
|
@ -29,6 +31,7 @@ from homeassistant.components.light import (
|
|||
SUPPORT_EFFECT,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
LightEntity,
|
||||
valid_supported_color_modes,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
|
@ -86,6 +89,8 @@ CONF_STATE_VALUE_TEMPLATE = "state_value_template"
|
|||
CONF_XY_COMMAND_TOPIC = "xy_command_topic"
|
||||
CONF_XY_STATE_TOPIC = "xy_state_topic"
|
||||
CONF_XY_VALUE_TEMPLATE = "xy_value_template"
|
||||
CONF_WHITE_COMMAND_TOPIC = "white_command_topic"
|
||||
CONF_WHITE_SCALE = "white_scale"
|
||||
CONF_WHITE_VALUE_COMMAND_TOPIC = "white_value_command_topic"
|
||||
CONF_WHITE_VALUE_SCALE = "white_value_scale"
|
||||
CONF_WHITE_VALUE_STATE_TOPIC = "white_value_state_topic"
|
||||
|
@ -98,6 +103,7 @@ DEFAULT_OPTIMISTIC = False
|
|||
DEFAULT_PAYLOAD_OFF = "OFF"
|
||||
DEFAULT_PAYLOAD_ON = "ON"
|
||||
DEFAULT_WHITE_VALUE_SCALE = 255
|
||||
DEFAULT_WHITE_SCALE = 255
|
||||
DEFAULT_ON_COMMAND_TYPE = "last"
|
||||
|
||||
VALUES_ON_COMMAND_TYPE = ["first", "last", "brightness"]
|
||||
|
@ -168,6 +174,10 @@ PLATFORM_SCHEMA_BASIC = vol.All(
|
|||
vol.Optional(CONF_RGBWW_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||
vol.Optional(CONF_RGBWW_VALUE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_WHITE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||
vol.Optional(CONF_WHITE_SCALE, default=DEFAULT_WHITE_SCALE): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1)
|
||||
),
|
||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_WHITE_VALUE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||
vol.Optional(
|
||||
|
@ -259,6 +269,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||
CONF_RGBWW_COMMAND_TOPIC,
|
||||
CONF_RGBWW_STATE_TOPIC,
|
||||
CONF_STATE_TOPIC,
|
||||
CONF_WHITE_COMMAND_TOPIC,
|
||||
CONF_WHITE_VALUE_COMMAND_TOPIC,
|
||||
CONF_WHITE_VALUE_STATE_TOPIC,
|
||||
CONF_XY_COMMAND_TOPIC,
|
||||
|
@ -316,35 +327,40 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||
optimistic or topic[CONF_WHITE_VALUE_STATE_TOPIC] is None
|
||||
)
|
||||
self._optimistic_xy_color = optimistic or topic[CONF_XY_STATE_TOPIC] is None
|
||||
self._supported_color_modes = set()
|
||||
supported_color_modes = set()
|
||||
if topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None:
|
||||
self._supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
|
||||
supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
|
||||
self._color_mode = COLOR_MODE_COLOR_TEMP
|
||||
if topic[CONF_HS_COMMAND_TOPIC] is not None:
|
||||
self._supported_color_modes.add(COLOR_MODE_HS)
|
||||
supported_color_modes.add(COLOR_MODE_HS)
|
||||
self._color_mode = COLOR_MODE_HS
|
||||
if topic[CONF_RGB_COMMAND_TOPIC] is not None:
|
||||
self._supported_color_modes.add(COLOR_MODE_RGB)
|
||||
supported_color_modes.add(COLOR_MODE_RGB)
|
||||
self._color_mode = COLOR_MODE_RGB
|
||||
if topic[CONF_RGBW_COMMAND_TOPIC] is not None:
|
||||
self._supported_color_modes.add(COLOR_MODE_RGBW)
|
||||
supported_color_modes.add(COLOR_MODE_RGBW)
|
||||
self._color_mode = COLOR_MODE_RGBW
|
||||
if topic[CONF_RGBWW_COMMAND_TOPIC] is not None:
|
||||
self._supported_color_modes.add(COLOR_MODE_RGBWW)
|
||||
supported_color_modes.add(COLOR_MODE_RGBWW)
|
||||
self._color_mode = COLOR_MODE_RGBWW
|
||||
if topic[CONF_WHITE_COMMAND_TOPIC] is not None:
|
||||
supported_color_modes.add(COLOR_MODE_WHITE)
|
||||
if topic[CONF_XY_COMMAND_TOPIC] is not None:
|
||||
self._supported_color_modes.add(COLOR_MODE_XY)
|
||||
supported_color_modes.add(COLOR_MODE_XY)
|
||||
self._color_mode = COLOR_MODE_XY
|
||||
if len(self._supported_color_modes) > 1:
|
||||
if len(supported_color_modes) > 1:
|
||||
self._color_mode = COLOR_MODE_UNKNOWN
|
||||
|
||||
if not self._supported_color_modes:
|
||||
if not supported_color_modes:
|
||||
if topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None:
|
||||
self._color_mode = COLOR_MODE_BRIGHTNESS
|
||||
self._supported_color_modes.add(COLOR_MODE_BRIGHTNESS)
|
||||
supported_color_modes.add(COLOR_MODE_BRIGHTNESS)
|
||||
else:
|
||||
self._color_mode = COLOR_MODE_ONOFF
|
||||
self._supported_color_modes.add(COLOR_MODE_ONOFF)
|
||||
supported_color_modes.add(COLOR_MODE_ONOFF)
|
||||
|
||||
# Validate the color_modes configuration
|
||||
self._supported_color_modes = valid_supported_color_modes(supported_color_modes)
|
||||
|
||||
if topic[CONF_WHITE_VALUE_COMMAND_TOPIC] is not None:
|
||||
self._legacy_mode = True
|
||||
|
@ -817,7 +833,11 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||
# If brightness is being used instead of an on command, make sure
|
||||
# there is a brightness input. Either set the brightness to our
|
||||
# saved value or the maximum value if this is the first call
|
||||
elif on_command_type == "brightness" and ATTR_BRIGHTNESS not in kwargs:
|
||||
elif (
|
||||
on_command_type == "brightness"
|
||||
and ATTR_BRIGHTNESS not in kwargs
|
||||
and ATTR_WHITE not in kwargs
|
||||
):
|
||||
kwargs[ATTR_BRIGHTNESS] = self._brightness if self._brightness else 255
|
||||
|
||||
hs_color = kwargs.get(ATTR_HS_COLOR)
|
||||
|
@ -971,6 +991,17 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||
publish(CONF_EFFECT_COMMAND_TOPIC, effect)
|
||||
should_update |= set_optimistic(ATTR_EFFECT, effect)
|
||||
|
||||
if ATTR_WHITE in kwargs and self._topic[CONF_WHITE_COMMAND_TOPIC] is not None:
|
||||
percent_white = float(kwargs[ATTR_WHITE]) / 255
|
||||
white_scale = self._config[CONF_WHITE_SCALE]
|
||||
device_white_value = min(round(percent_white * white_scale), white_scale)
|
||||
publish(CONF_WHITE_COMMAND_TOPIC, device_white_value)
|
||||
should_update |= set_optimistic(
|
||||
ATTR_BRIGHTNESS,
|
||||
kwargs[ATTR_WHITE],
|
||||
COLOR_MODE_WHITE,
|
||||
)
|
||||
|
||||
if (
|
||||
ATTR_WHITE_VALUE in kwargs
|
||||
and self._topic[CONF_WHITE_VALUE_COMMAND_TOPIC] is not None
|
||||
|
|
|
@ -2268,6 +2268,83 @@ async def test_on_command_rgbww_template(hass, mqtt_mock):
|
|||
mqtt_mock.async_publish.assert_called_once_with("test_light/set", "OFF", 0, False)
|
||||
|
||||
|
||||
async def test_on_command_white(hass, mqtt_mock):
|
||||
"""Test sending commands for RGB + white light."""
|
||||
config = {
|
||||
light.DOMAIN: {
|
||||
"platform": "mqtt",
|
||||
"name": "test",
|
||||
"command_topic": "tasmota_B94927/cmnd/POWER",
|
||||
"value_template": "{{ value_json.POWER }}",
|
||||
"payload_off": "OFF",
|
||||
"payload_on": "ON",
|
||||
"brightness_command_topic": "tasmota_B94927/cmnd/Dimmer",
|
||||
"brightness_scale": 100,
|
||||
"on_command_type": "brightness",
|
||||
"brightness_value_template": "{{ value_json.Dimmer }}",
|
||||
"rgb_command_topic": "tasmota_B94927/cmnd/Color2",
|
||||
"rgb_value_template": "{{value_json.Color.split(',')[0:3]|join(',')}}",
|
||||
"white_command_topic": "tasmota_B94927/cmnd/White",
|
||||
"white_scale": 100,
|
||||
"color_mode_value_template": "{% if value_json.White %} white {% else %} rgb {% endif %}",
|
||||
"qos": "0",
|
||||
}
|
||||
}
|
||||
color_modes = ["rgb", "white"]
|
||||
|
||||
assert await async_setup_component(hass, light.DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get("brightness") is None
|
||||
assert state.attributes.get("rgb_color") is None
|
||||
assert state.attributes.get(light.ATTR_COLOR_MODE) is None
|
||||
assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes
|
||||
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||
|
||||
await common.async_turn_on(hass, "light.test", brightness=192)
|
||||
mqtt_mock.async_publish.assert_has_calls(
|
||||
[
|
||||
call("tasmota_B94927/cmnd/Dimmer", "75", 0, False),
|
||||
],
|
||||
any_order=True,
|
||||
)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_turn_on(hass, "light.test", white=255)
|
||||
mqtt_mock.async_publish.assert_has_calls(
|
||||
[
|
||||
call("tasmota_B94927/cmnd/White", "100", 0, False),
|
||||
],
|
||||
any_order=True,
|
||||
)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_turn_on(hass, "light.test", white=64)
|
||||
mqtt_mock.async_publish.assert_has_calls(
|
||||
[
|
||||
call("tasmota_B94927/cmnd/White", "25", 0, False),
|
||||
],
|
||||
any_order=True,
|
||||
)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_turn_on(hass, "light.test")
|
||||
mqtt_mock.async_publish.assert_has_calls(
|
||||
[
|
||||
call("tasmota_B94927/cmnd/Dimmer", "25", 0, False),
|
||||
],
|
||||
any_order=True,
|
||||
)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_turn_off(hass, "light.test")
|
||||
mqtt_mock.async_publish.assert_called_once_with(
|
||||
"tasmota_B94927/cmnd/POWER", "OFF", 0, False
|
||||
)
|
||||
|
||||
|
||||
async def test_explicit_color_mode(hass, mqtt_mock):
|
||||
"""Test explicit color mode over mqtt."""
|
||||
config = {
|
||||
|
@ -2499,6 +2576,70 @@ async def test_explicit_color_mode_templated(hass, mqtt_mock):
|
|||
assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes
|
||||
|
||||
|
||||
async def test_white_state_update(hass, mqtt_mock):
|
||||
"""Test state updates for RGB + white light."""
|
||||
config = {
|
||||
light.DOMAIN: {
|
||||
"platform": "mqtt",
|
||||
"name": "test",
|
||||
"state_topic": "tasmota_B94927/tele/STATE",
|
||||
"command_topic": "tasmota_B94927/cmnd/POWER",
|
||||
"value_template": "{{ value_json.POWER }}",
|
||||
"payload_off": "OFF",
|
||||
"payload_on": "ON",
|
||||
"brightness_command_topic": "tasmota_B94927/cmnd/Dimmer",
|
||||
"brightness_state_topic": "tasmota_B94927/tele/STATE",
|
||||
"brightness_scale": 100,
|
||||
"on_command_type": "brightness",
|
||||
"brightness_value_template": "{{ value_json.Dimmer }}",
|
||||
"rgb_command_topic": "tasmota_B94927/cmnd/Color2",
|
||||
"rgb_state_topic": "tasmota_B94927/tele/STATE",
|
||||
"rgb_value_template": "{{value_json.Color.split(',')[0:3]|join(',')}}",
|
||||
"white_command_topic": "tasmota_B94927/cmnd/White",
|
||||
"white_scale": 100,
|
||||
"color_mode_state_topic": "tasmota_B94927/tele/STATE",
|
||||
"color_mode_value_template": "{% if value_json.White %} white {% else %} rgb {% endif %}",
|
||||
"qos": "0",
|
||||
}
|
||||
}
|
||||
color_modes = ["rgb", "white"]
|
||||
|
||||
assert await async_setup_component(hass, light.DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get("brightness") is None
|
||||
assert state.attributes.get("rgb_color") is None
|
||||
assert state.attributes.get(light.ATTR_COLOR_MODE) is None
|
||||
assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes
|
||||
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
"tasmota_B94927/tele/STATE",
|
||||
'{"POWER":"ON","Dimmer":50,"Color":"0,0,0,128","White":50}',
|
||||
)
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("brightness") == 128
|
||||
assert state.attributes.get("rgb_color") is None
|
||||
assert state.attributes.get(light.ATTR_COLOR_MODE) == "white"
|
||||
assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
"tasmota_B94927/tele/STATE",
|
||||
'{"POWER":"ON","Dimmer":50,"Color":"128,64,32,0","White":0}',
|
||||
)
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("brightness") == 128
|
||||
assert state.attributes.get("rgb_color") == (128, 64, 32)
|
||||
assert state.attributes.get(light.ATTR_COLOR_MODE) == "rgb"
|
||||
assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes
|
||||
|
||||
|
||||
async def test_effect(hass, mqtt_mock):
|
||||
"""Test effect."""
|
||||
config = {
|
||||
|
|
Loading…
Reference in New Issue