diff --git a/homeassistant/components/tasmota/light.py b/homeassistant/components/tasmota/light.py index edbb01eefe6..9af95049f79 100644 --- a/homeassistant/components/tasmota/light.py +++ b/homeassistant/components/tasmota/light.py @@ -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]) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 13248cba47d..59b91b75903 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -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"], diff --git a/requirements_all.txt b/requirements_all.txt index ff5a7d69792..1fe6fa8f2ce 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b7ad93c1b37..175c1fc2d77 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index 0c0e0a4e566..e1ba2615742 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -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(