Use HS color instead of RGB color for Tasmota lights ()

pull/52099/head
Erik Montnemery 2021-06-22 20:40:59 +02:00 committed by GitHub
parent e22893a206
commit 04b425ed89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 171 deletions
homeassistant/components/tasmota
tests/components/tasmota

View File

@ -12,13 +12,13 @@ from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
ATTR_EFFECT,
ATTR_RGB_COLOR,
ATTR_HS_COLOR,
ATTR_TRANSITION,
ATTR_WHITE,
COLOR_MODE_BRIGHTNESS,
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_HS,
COLOR_MODE_ONOFF,
COLOR_MODE_RGB,
COLOR_MODE_WHITE,
SUPPORT_EFFECT,
SUPPORT_TRANSITION,
@ -90,7 +90,7 @@ class TasmotaLight(
self._effect = None
self._white_value = None
self._flash_times = None
self._rgb = None
self._hs = None
super().__init__(
**kwds,
@ -111,10 +111,10 @@ class TasmotaLight(
light_type = self._tasmota_entity.light_type
if light_type in [LIGHT_TYPE_RGB, LIGHT_TYPE_RGBW, LIGHT_TYPE_RGBCW]:
# Mark RGB support for RGBW light because we don't have control over the
# Mark HS support for RGBW light because we don't have direct control over the
# white channel, so the base component's RGB->RGBW translation does not work
self._supported_color_modes.add(COLOR_MODE_RGB)
self._color_mode = COLOR_MODE_RGB
self._supported_color_modes.add(COLOR_MODE_HS)
self._color_mode = COLOR_MODE_HS
if light_type == LIGHT_TYPE_RGBW:
self._supported_color_modes.add(COLOR_MODE_WHITE)
@ -149,8 +149,8 @@ class TasmotaLight(
brightness = float(attributes["brightness"])
percent_bright = brightness / TASMOTA_BRIGHTNESS_MAX
self._brightness = percent_bright * 255
if "color" in attributes:
self._rgb = attributes["color"][0:3]
if "color_hs" in attributes:
self._hs = attributes["color_hs"]
if "color_temp" in attributes:
self._color_temp = attributes["color_temp"]
if "effect" in attributes:
@ -160,15 +160,15 @@ class TasmotaLight(
percent_white = white_value / TASMOTA_BRIGHTNESS_MAX
self._white_value = percent_white * 255
if self._tasmota_entity.light_type == LIGHT_TYPE_RGBW:
# Tasmota does not support RGBW mode, set mode to white or rgb
# Tasmota does not support RGBW mode, set mode to white or hs
if self._white_value == 0:
self._color_mode = COLOR_MODE_RGB
self._color_mode = COLOR_MODE_HS
else:
self._color_mode = COLOR_MODE_WHITE
elif self._tasmota_entity.light_type == LIGHT_TYPE_RGBCW:
# Tasmota does not support RGBWW mode, set mode to ct or rgb
# Tasmota does not support RGBWW mode, set mode to ct or hs
if self._white_value == 0:
self._color_mode = COLOR_MODE_RGB
self._color_mode = COLOR_MODE_HS
else:
self._color_mode = COLOR_MODE_COLOR_TEMP
@ -210,21 +210,12 @@ class TasmotaLight(
return self._tasmota_entity.effect_list
@property
def rgb_color(self):
"""Return the rgb color value."""
if self._rgb is None:
def hs_color(self):
"""Return the hs color value."""
if self._hs is None:
return None
rgb = self._rgb
# Tasmota's RGB color is adjusted for brightness, compensate
if self._brightness > 0:
red_compensated = clamp(round(rgb[0] / self._brightness * 255))
green_compensated = clamp(round(rgb[1] / self._brightness * 255))
blue_compensated = clamp(round(rgb[2] / self._brightness * 255))
else:
red_compensated = 0
green_compensated = 0
blue_compensated = 0
return [red_compensated, green_compensated, blue_compensated]
hs_color = self._hs
return [hs_color[0], hs_color[1]]
@property
def force_update(self):
@ -252,9 +243,9 @@ class TasmotaLight(
attributes = {}
if ATTR_RGB_COLOR in kwargs and COLOR_MODE_RGB in supported_color_modes:
rgb = kwargs[ATTR_RGB_COLOR]
attributes["color"] = [rgb[0], rgb[1], rgb[2]]
if ATTR_HS_COLOR in kwargs and COLOR_MODE_HS in supported_color_modes:
hs_color = kwargs[ATTR_HS_COLOR]
attributes["color_hs"] = [hs_color[0], hs_color[1]]
if ATTR_WHITE in kwargs and COLOR_MODE_WHITE in supported_color_modes:
attributes["white_value"] = scale_brightness(kwargs[ATTR_WHITE])

View File

@ -3,7 +3,7 @@
"name": "Tasmota",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tasmota",
"requirements": ["hatasmota==0.2.16"],
"requirements": ["hatasmota==0.2.18"],
"dependencies": ["mqtt"],
"mqtt": ["tasmota/discovery/#"],
"codeowners": ["@emontnemery"],

View File

@ -747,7 +747,7 @@ hass-nabucasa==0.43.0
hass_splunk==0.1.1
# homeassistant.components.tasmota
hatasmota==0.2.16
hatasmota==0.2.18
# homeassistant.components.jewish_calendar
hdate==0.10.2

View File

@ -420,7 +420,7 @@ hangups==0.4.14
hass-nabucasa==0.43.0
# homeassistant.components.tasmota
hatasmota==0.2.16
hatasmota==0.2.18
# homeassistant.components.jewish_calendar
hdate==0.10.2

View File

@ -189,8 +189,8 @@ async def test_attributes_rgb(hass, mqtt_mock, setup_tasmota):
state.attributes.get("supported_features")
== SUPPORT_EFFECT | SUPPORT_TRANSITION
)
assert state.attributes.get("supported_color_modes") == ["rgb"]
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("supported_color_modes") == ["hs"]
assert state.attributes.get("color_mode") == "hs"
async def test_attributes_rgbw(hass, mqtt_mock, setup_tasmota):
@ -223,8 +223,8 @@ async def test_attributes_rgbw(hass, mqtt_mock, setup_tasmota):
state.attributes.get("supported_features")
== SUPPORT_EFFECT | SUPPORT_TRANSITION
)
assert state.attributes.get("supported_color_modes") == ["rgb", "white"]
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("supported_color_modes") == ["hs", "white"]
assert state.attributes.get("color_mode") == "hs"
async def test_attributes_rgbww(hass, mqtt_mock, setup_tasmota):
@ -257,7 +257,7 @@ async def test_attributes_rgbww(hass, mqtt_mock, setup_tasmota):
state.attributes.get("supported_features")
== SUPPORT_EFFECT | SUPPORT_TRANSITION
)
assert state.attributes.get("supported_color_modes") == ["color_temp", "rgb"]
assert state.attributes.get("supported_color_modes") == ["color_temp", "hs"]
assert state.attributes.get("color_mode") == "color_temp"
@ -292,7 +292,7 @@ async def test_attributes_rgbww_reduced(hass, mqtt_mock, setup_tasmota):
state.attributes.get("supported_features")
== SUPPORT_EFFECT | SUPPORT_TRANSITION
)
assert state.attributes.get("supported_color_modes") == ["color_temp", "rgb"]
assert state.attributes.get("supported_color_modes") == ["color_temp", "hs"]
assert state.attributes.get("color_mode") == "color_temp"
@ -434,7 +434,7 @@ async def test_controlling_state_via_mqtt_rgbw(hass, mqtt_mock, setup_tasmota):
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON"}')
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"OFF"}')
state = hass.states.get("light.test")
@ -447,7 +447,7 @@ async def test_controlling_state_via_mqtt_rgbw(hass, mqtt_mock, setup_tasmota):
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("brightness") == 127.5
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Dimmer":75,"White":75}'
@ -460,13 +460,13 @@ async def test_controlling_state_via_mqtt_rgbw(hass, mqtt_mock, setup_tasmota):
async_fire_mqtt_message(
hass,
"tasmota_49A3BC/tele/STATE",
'{"POWER":"ON","Dimmer":50,"Color":"128,64,0","White":0}',
'{"POWER":"ON","Dimmer":50,"HSBColor":"30,100,50","White":0}',
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("brightness") == 127.5
assert state.attributes.get("rgb_color") == (255, 128, 0)
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("hs_color") == (30, 100)
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","White":50}'
@ -550,12 +550,12 @@ async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota):
async_fire_mqtt_message(
hass,
"tasmota_49A3BC/tele/STATE",
'{"POWER":"ON","Color":"128,64,0","White":0}',
'{"POWER":"ON","Dimmer":50,"HSBColor":"30,100,50","White":0}',
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("rgb_color") == (255, 128, 0)
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("hs_color") == (30, 100)
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","White":50}'
@ -583,114 +583,8 @@ async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota):
# Setting white to 0 should clear the color_temp
assert "white_value" not in state.attributes
assert "color_temp" not in state.attributes
assert state.attributes.get("rgb_color") == (255, 128, 0)
assert state.attributes.get("color_mode") == "rgb"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Scheme":3}'
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("effect") == "Cycle down"
async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"ON"}')
state = hass.states.get("light.test")
assert state.state == STATE_ON
async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"OFF"}')
state = hass.states.get("light.test")
assert state.state == STATE_OFF
async def test_controlling_state_via_mqtt_rgbww_hex(hass, mqtt_mock, setup_tasmota):
"""Test state update via MQTT."""
config = copy.deepcopy(DEFAULT_CONFIG)
config["rl"][0] = 2
config["lt_st"] = 5 # 5 channel light (RGBCW)
config["so"]["17"] = 0 # Hex color in state updates
mac = config["mac"]
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{mac}/config",
json.dumps(config),
)
await hass.async_block_till_done()
state = hass.states.get("light.test")
assert state.state == "unavailable"
assert not state.attributes.get(ATTR_ASSUMED_STATE)
assert "color_mode" not in state.attributes
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online")
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert not state.attributes.get(ATTR_ASSUMED_STATE)
assert "color_mode" not in state.attributes
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON"}')
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("color_mode") == "color_temp"
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"OFF"}')
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert "color_mode" not in state.attributes
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Dimmer":50}'
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("brightness") == 127.5
assert state.attributes.get("color_mode") == "color_temp"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Color":"804000","White":0}'
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("rgb_color") == (255, 128, 0)
assert state.attributes.get("color_mode") == "rgb"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Color":"0080400000"}'
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("rgb_color") == (0, 255, 128)
assert state.attributes.get("color_mode") == "rgb"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","White":50}'
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert "white_value" not in state.attributes
# Setting white > 0 should clear the color
assert "rgb_color" not in state.attributes
assert state.attributes.get("color_mode") == "color_temp"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","CT":300}'
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("color_temp") == 300
assert state.attributes.get("color_mode") == "color_temp"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","White":0}'
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
# Setting white to 0 should clear the white_value and color_temp
assert not state.attributes.get("white_value")
assert not state.attributes.get("color_temp")
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("hs_color") == (30, 100)
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Scheme":3}'
@ -757,12 +651,12 @@ async def test_controlling_state_via_mqtt_rgbww_tuya(hass, mqtt_mock, setup_tasm
async_fire_mqtt_message(
hass,
"tasmota_49A3BC/tele/STATE",
'{"POWER":"ON","Color":"128,64,0","White":0}',
'{"POWER":"ON","HSBColor":"30,100,0","White":0}',
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("rgb_color") == (255, 128, 0)
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("hs_color") == (30, 100)
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(
hass,
@ -771,8 +665,8 @@ async def test_controlling_state_via_mqtt_rgbww_tuya(hass, mqtt_mock, setup_tasm
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("rgb_color") == (0, 0, 0)
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("hs_color") == (30, 100)
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Dimmer":50,"White":50}'
@ -800,7 +694,7 @@ async def test_controlling_state_via_mqtt_rgbww_tuya(hass, mqtt_mock, setup_tasm
# Setting white to 0 should clear the white_value and color_temp
assert not state.attributes.get("white_value")
assert not state.attributes.get("color_temp")
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("color_mode") == "hs"
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Scheme":3}'
@ -955,10 +849,10 @@ async def test_sending_mqtt_commands_rgbw_legacy(hass, mqtt_mock, setup_tasmota)
mqtt_mock.async_publish.reset_mock()
# Set color when setting color
await common.async_turn_on(hass, "light.test", rgb_color=[128, 64, 32])
await common.async_turn_on(hass, "light.test", hs_color=[0, 100])
mqtt_mock.async_publish.assert_called_once_with(
"tasmota_49A3BC/cmnd/Backlog",
"NoDelay;Power1 ON;NoDelay;Color2 128,64,32",
"NoDelay;Power1 ON;NoDelay;HsbColor1 0;NoDelay;HsbColor2 100",
0,
False,
)
@ -1061,10 +955,10 @@ async def test_sending_mqtt_commands_rgbw(hass, mqtt_mock, setup_tasmota):
mqtt_mock.async_publish.reset_mock()
# Set color when setting color
await common.async_turn_on(hass, "light.test", rgb_color=[128, 64, 32])
await common.async_turn_on(hass, "light.test", hs_color=[180, 50])
mqtt_mock.async_publish.assert_called_once_with(
"tasmota_49A3BC/cmnd/Backlog",
"NoDelay;Power1 ON;NoDelay;Color2 128,64,32",
"NoDelay;Power1 ON;NoDelay;HsbColor1 180;NoDelay;HsbColor2 50",
0,
False,
)
@ -1166,10 +1060,10 @@ async def test_sending_mqtt_commands_rgbww(hass, mqtt_mock, setup_tasmota):
)
mqtt_mock.async_publish.reset_mock()
await common.async_turn_on(hass, "light.test", rgb_color=[128, 64, 32])
await common.async_turn_on(hass, "light.test", hs_color=[240, 75])
mqtt_mock.async_publish.assert_called_once_with(
"tasmota_49A3BC/cmnd/Backlog",
"NoDelay;Power1 ON;NoDelay;Color2 128,64,32",
"NoDelay;Power1 ON;NoDelay;HsbColor1 240;NoDelay;HsbColor2 75",
0,
False,
)
@ -1356,7 +1250,7 @@ async def test_transition(hass, mqtt_mock, setup_tasmota):
async_fire_mqtt_message(
hass,
"tasmota_49A3BC/tele/STATE",
'{"POWER":"ON","Dimmer":50, "Color":"0,255,0", "White":0}',
'{"POWER":"ON","Dimmer":50, "Color":"0,255,0","HSBColor":"120,100,50","White":0}',
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
@ -1367,7 +1261,7 @@ async def test_transition(hass, mqtt_mock, setup_tasmota):
await common.async_turn_on(hass, "light.test", rgb_color=[255, 0, 0], transition=6)
mqtt_mock.async_publish.assert_called_once_with(
"tasmota_49A3BC/cmnd/Backlog",
"NoDelay;Fade2 1;NoDelay;Speed2 24;NoDelay;Power1 ON;NoDelay;Color2 255,0,0",
"NoDelay;Fade2 1;NoDelay;Speed2 24;NoDelay;Power1 ON;NoDelay;HsbColor1 0;NoDelay;HsbColor2 100",
0,
False,
)
@ -1377,7 +1271,7 @@ async def test_transition(hass, mqtt_mock, setup_tasmota):
async_fire_mqtt_message(
hass,
"tasmota_49A3BC/tele/STATE",
'{"POWER":"ON","Dimmer":100, "Color":"0,255,0"}',
'{"POWER":"ON","Dimmer":100, "Color":"0,255,0","HSBColor":"120,100,50"}',
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
@ -1388,7 +1282,7 @@ async def test_transition(hass, mqtt_mock, setup_tasmota):
await common.async_turn_on(hass, "light.test", rgb_color=[255, 0, 0], transition=6)
mqtt_mock.async_publish.assert_called_once_with(
"tasmota_49A3BC/cmnd/Backlog",
"NoDelay;Fade2 1;NoDelay;Speed2 12;NoDelay;Power1 ON;NoDelay;Color2 255,0,0",
"NoDelay;Fade2 1;NoDelay;Speed2 12;NoDelay;Power1 ON;NoDelay;HsbColor1 0;NoDelay;HsbColor2 100",
0,
False,
)
@ -1693,7 +1587,7 @@ async def test_discovery_update_reconfigure_light(
state.attributes.get("supported_features")
== SUPPORT_EFFECT | SUPPORT_TRANSITION
)
assert state.attributes.get("supported_color_modes") == ["rgb"]
assert state.attributes.get("supported_color_modes") == ["hs"]
async def test_availability_when_connection_lost(