From b21f8c9ea813fbe3e721497d93608977d4f43d1e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 8 Sep 2022 11:21:46 +0200 Subject: [PATCH] Remove use of deprecated SUPPORT_* constants from MQTT light (#77828) * Remove use of deprecated SUPPORT_* constants from MQTT light * Refactor --- homeassistant/components/light/__init__.py | 17 -- .../components/mqtt/light/schema_json.py | 67 ++++-- .../components/mqtt/light/schema_template.py | 88 ++++---- tests/components/mqtt/test_light_json.py | 190 ++++++++++-------- tests/components/mqtt/test_light_template.py | 52 +++-- 5 files changed, 244 insertions(+), 170 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 33ec3119b95..5089f454ac8 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -977,20 +977,3 @@ class LightEntity(ToggleEntity): def supported_features(self) -> int: """Flag supported features.""" return self._attr_supported_features - - -def legacy_supported_features( - supported_features: int, supported_color_modes: list[str] | None -) -> int: - """Calculate supported features with backwards compatibility.""" - # Backwards compatibility for supported_color_modes added in 2021.4 - if supported_color_modes is None: - return supported_features - if any(mode in supported_color_modes for mode in COLOR_MODES_COLOR): - supported_features |= SUPPORT_COLOR - if any(mode in supported_color_modes for mode in COLOR_MODES_BRIGHTNESS): - supported_features |= SUPPORT_BRIGHTNESS - if ColorMode.COLOR_TEMP in supported_color_modes: - supported_features |= SUPPORT_COLOR_TEMP - - return supported_features diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 295b43120d4..f7703b0f1a4 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -20,14 +20,13 @@ from homeassistant.components.light import ( ENTITY_ID_FORMAT, FLASH_LONG, FLASH_SHORT, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, VALID_COLOR_MODES, ColorMode, LightEntity, LightEntityFeature, - legacy_supported_features, + brightness_supported, + color_supported, + filter_supported_color_modes, valid_supported_color_modes, ) from homeassistant.const import ( @@ -198,6 +197,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self._color_mode = None self._color_temp = None self._effect = None + self._fixed_color_mode = None self._flash_times = None self._hs = None self._rgb = None @@ -230,13 +230,20 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): ) self._supported_features |= config[CONF_EFFECT] and LightEntityFeature.EFFECT if not self._config[CONF_COLOR_MODE]: - self._supported_features |= config[CONF_BRIGHTNESS] and SUPPORT_BRIGHTNESS - self._supported_features |= config[CONF_COLOR_TEMP] and SUPPORT_COLOR_TEMP - self._supported_features |= config[CONF_HS] and SUPPORT_COLOR - self._supported_features |= config[CONF_RGB] and ( - SUPPORT_COLOR | SUPPORT_BRIGHTNESS - ) - self._supported_features |= config[CONF_XY] and SUPPORT_COLOR + color_modes = {ColorMode.ONOFF} + if config[CONF_BRIGHTNESS]: + color_modes.add(ColorMode.BRIGHTNESS) + if config[CONF_COLOR_TEMP]: + color_modes.add(ColorMode.COLOR_TEMP) + if config[CONF_HS] or config[CONF_RGB] or config[CONF_XY]: + color_modes.add(ColorMode.HS) + self._supported_color_modes = filter_supported_color_modes(color_modes) + if len(self._supported_color_modes) == 1: + self._fixed_color_mode = next(iter(self._supported_color_modes)) + else: + self._supported_color_modes = self._config[CONF_SUPPORTED_COLOR_MODES] + if len(self._supported_color_modes) == 1: + self._color_mode = next(iter(self._supported_color_modes)) def _update_color(self, values): if not self._config[CONF_COLOR_MODE]: @@ -332,7 +339,12 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): elif values["state"] is None: self._state = None - if self._supported_features and SUPPORT_COLOR and "color" in values: + if ( + not self._config[CONF_COLOR_MODE] + and color_supported(self._supported_color_modes) + and "color" in values + ): + # Deprecated color handling if values["color"] is None: self._hs = None else: @@ -341,7 +353,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): if self._config[CONF_COLOR_MODE] and "color_mode" in values: self._update_color(values) - if self._supported_features and SUPPORT_BRIGHTNESS: + if brightness_supported(self._supported_color_modes): try: self._brightness = int( values["brightness"] @@ -354,10 +366,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): _LOGGER.warning("Invalid brightness value received") if ( - self._supported_features - and SUPPORT_COLOR_TEMP + ColorMode.COLOR_TEMP in self._supported_color_modes and not self._config[CONF_COLOR_MODE] ): + # Deprecated color handling try: if values["color_temp"] is None: self._color_temp = None @@ -474,19 +486,25 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): @property def color_mode(self): """Return current color mode.""" - return self._color_mode + if self._config[CONF_COLOR_MODE]: + return self._color_mode + if self._fixed_color_mode: + # Legacy light with support for a single color mode + return self._fixed_color_mode + # Legacy light with support for ct + hs, prioritize hs + if self._hs is not None: + return ColorMode.HS + return ColorMode.COLOR_TEMP @property def supported_color_modes(self): """Flag supported color modes.""" - return self._config.get(CONF_SUPPORTED_COLOR_MODES) + return self._supported_color_modes @property def supported_features(self): """Flag supported features.""" - return legacy_supported_features( - self._supported_features, self._config.get(CONF_SUPPORTED_COLOR_MODES) - ) + return self._supported_features def _set_flash_and_transition(self, message, **kwargs): if ATTR_TRANSITION in kwargs: @@ -510,7 +528,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): return tuple(round(i / 255 * brightness) for i in rgbxx) def _supports_color_mode(self, color_mode): - return self.supported_color_modes and color_mode in self.supported_color_modes + """Return True if the light natively supports a color mode.""" + return ( + self._config[CONF_COLOR_MODE] and color_mode in self.supported_color_modes + ) async def async_turn_on(self, **kwargs): # noqa: C901 """Turn the device on. @@ -524,6 +545,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): if ATTR_HS_COLOR in kwargs and ( self._config[CONF_HS] or self._config[CONF_RGB] or self._config[CONF_XY] ): + # Legacy color handling hs_color = kwargs[ATTR_HS_COLOR] message["color"] = {} if self._config[CONF_RGB]: @@ -548,6 +570,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): message["color"]["s"] = hs_color[1] if self._optimistic: + self._color_temp = None self._hs = kwargs[ATTR_HS_COLOR] should_update = True @@ -617,7 +640,9 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): message["color_temp"] = int(kwargs[ATTR_COLOR_TEMP]) if self._optimistic: + self._color_mode = ColorMode.COLOR_TEMP self._color_temp = kwargs[ATTR_COLOR_TEMP] + self._hs = None should_update = True if ATTR_EFFECT in kwargs: diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 73f2786ad12..dacc977a036 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -11,11 +11,10 @@ from homeassistant.components.light import ( ATTR_HS_COLOR, ATTR_TRANSITION, ENTITY_ID_FORMAT, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, + ColorMode, LightEntity, LightEntityFeature, + filter_supported_color_modes, ) from homeassistant.const import ( CONF_NAME, @@ -129,6 +128,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): # features self._brightness = None + self._fixed_color_mode = None self._color_temp = None self._hs = None self._effect = None @@ -166,6 +166,21 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): or self._templates[CONF_STATE_TEMPLATE] is None ) + color_modes = {ColorMode.ONOFF} + if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None: + color_modes.add(ColorMode.BRIGHTNESS) + if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: + color_modes.add(ColorMode.COLOR_TEMP) + if ( + self._templates[CONF_RED_TEMPLATE] is not None + and self._templates[CONF_GREEN_TEMPLATE] is not None + and self._templates[CONF_BLUE_TEMPLATE] is not None + ): + color_modes.add(ColorMode.HS) + self._supported_color_modes = filter_supported_color_modes(color_modes) + if len(self._supported_color_modes) == 1: + self._fixed_color_mode = next(iter(self._supported_color_modes)) + def _prepare_subscribe_topics(self): """(Re)Subscribe to topics.""" for tpl in self._templates.values(): @@ -200,11 +215,10 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: try: - self._color_temp = int( - self._templates[ - CONF_COLOR_TEMP_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) - ) + color_temp = self._templates[ + CONF_COLOR_TEMP_TEMPLATE + ].async_render_with_possible_json_value(msg.payload) + self._color_temp = int(color_temp) if color_temp != "None" else None except ValueError: _LOGGER.warning("Invalid color temperature value received") @@ -214,22 +228,21 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): and self._templates[CONF_BLUE_TEMPLATE] is not None ): try: - red = int( - self._templates[ - CONF_RED_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) - ) - green = int( - self._templates[ - CONF_GREEN_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) - ) - blue = int( - self._templates[ - CONF_BLUE_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) - ) - self._hs = color_util.color_RGB_to_hs(red, green, blue) + red = self._templates[ + CONF_RED_TEMPLATE + ].async_render_with_possible_json_value(msg.payload) + green = self._templates[ + CONF_GREEN_TEMPLATE + ].async_render_with_possible_json_value(msg.payload) + blue = self._templates[ + CONF_BLUE_TEMPLATE + ].async_render_with_possible_json_value(msg.payload) + if red == "None" and green == "None" and blue == "None": + self._hs = None + else: + self._hs = color_util.color_RGB_to_hs( + int(red), int(green), int(blue) + ) except ValueError: _LOGGER.warning("Invalid color value received") @@ -340,6 +353,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: self._color_temp = kwargs[ATTR_COLOR_TEMP] + self._hs = None if ATTR_HS_COLOR in kwargs: hs_color = kwargs[ATTR_HS_COLOR] @@ -363,6 +377,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): values["sat"] = hs_color[1] if self._optimistic: + self._color_temp = None self._hs = kwargs[ATTR_HS_COLOR] if ATTR_EFFECT in kwargs: @@ -415,21 +430,26 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: self.async_write_ha_state() + @property + def color_mode(self): + """Return current color mode.""" + if self._fixed_color_mode: + return self._fixed_color_mode + # Support for ct + hs, prioritize hs + if self._hs is not None: + return ColorMode.HS + return ColorMode.COLOR_TEMP + + @property + def supported_color_modes(self): + """Flag supported color modes.""" + return self._supported_color_modes + @property def supported_features(self): """Flag supported features.""" features = LightEntityFeature.FLASH | LightEntityFeature.TRANSITION - if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None: - features = features | SUPPORT_BRIGHTNESS - if ( - self._templates[CONF_RED_TEMPLATE] is not None - and self._templates[CONF_GREEN_TEMPLATE] is not None - and self._templates[CONF_BLUE_TEMPLATE] is not None - ): - features = features | SUPPORT_COLOR | SUPPORT_BRIGHTNESS if self._config.get(CONF_EFFECT_LIST) is not None: features = features | LightEntityFeature.EFFECT - if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: - features = features | SUPPORT_COLOR_TEMP return features diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index e61d4e77286..af6daf6b7e4 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -237,8 +237,8 @@ async def test_fail_setup_if_color_modes_invalid( assert error in caplog.text -async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config): - """Test RGB light flags brightness support.""" +async def test_legacy_rgb_light(hass, mqtt_mock_entry_with_yaml_config): + """Test legacy RGB light flags expected features and color modes.""" assert await async_setup_component( hass, mqtt.DOMAIN, @@ -257,12 +257,9 @@ async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config): await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") - expected_features = ( - light.SUPPORT_BRIGHTNESS - | light.SUPPORT_COLOR - | light.SUPPORT_FLASH - | light.SUPPORT_TRANSITION - ) + color_modes = [light.ColorMode.HS] + assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes + expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features @@ -348,13 +345,10 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN + color_modes = [light.ColorMode.COLOR_TEMP, light.ColorMode.HS] + assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes expected_features = ( - light.SUPPORT_BRIGHTNESS - | light.SUPPORT_COLOR - | light.SUPPORT_COLOR_TEMP - | light.SUPPORT_EFFECT - | light.SUPPORT_FLASH - | light.SUPPORT_TRANSITION + light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION ) assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features assert state.attributes.get("rgb_color") is None @@ -380,11 +374,35 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi assert state.state == STATE_ON assert state.attributes.get("rgb_color") == (255, 255, 255) assert state.attributes.get("brightness") == 255 - assert state.attributes.get("color_temp") == 155 + assert state.attributes.get("color_temp") is None # rgb color has priority assert state.attributes.get("effect") == "colorloop" assert state.attributes.get("xy_color") == (0.323, 0.329) assert state.attributes.get("hs_color") == (0.0, 0.0) + # Turn on the light + async_fire_mqtt_message( + hass, + "test_light_rgb", + '{"state":"ON",' + '"brightness":255,' + '"color":null,' + '"color_temp":155,' + '"effect":"colorloop"}', + ) + + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("rgb_color") == ( + 255, + 253, + 248, + ) # temp converted to color + assert state.attributes.get("brightness") == 255 + assert state.attributes.get("color_temp") == 155 + assert state.attributes.get("effect") == "colorloop" + assert state.attributes.get("xy_color") == (0.328, 0.334) # temp converted to color + assert state.attributes.get("hs_color") == (44.098, 2.43) # temp converted to color + # Turn the light off async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"OFF"}') @@ -421,7 +439,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color":null}') light_state = hass.states.get("light.test") - assert "hs_color" in light_state.attributes + assert "hs_color" in light_state.attributes # Color temp approximation async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color_temp":155}') @@ -472,12 +490,7 @@ async def test_controlling_state_via_topic2( state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN expected_features = ( - light.SUPPORT_BRIGHTNESS - | light.SUPPORT_COLOR - | light.SUPPORT_COLOR_TEMP - | light.SUPPORT_EFFECT - | light.SUPPORT_FLASH - | light.SUPPORT_TRANSITION + light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION ) assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features assert state.attributes.get("brightness") is None @@ -660,14 +673,11 @@ async def test_sending_mqtt_commands_and_optimistic( assert state.attributes.get("brightness") == 95 assert state.attributes.get("hs_color") == (100, 100) assert state.attributes.get("effect") == "random" - assert state.attributes.get("color_temp") == 100 + assert state.attributes.get("color_temp") is None # hs_color has priority + color_modes = [light.ColorMode.COLOR_TEMP, light.ColorMode.HS] + assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes expected_features = ( - light.SUPPORT_BRIGHTNESS - | light.SUPPORT_COLOR - | light.SUPPORT_COLOR_TEMP - | light.SUPPORT_EFFECT - | light.SUPPORT_FLASH - | light.SUPPORT_TRANSITION + light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION ) assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features assert state.attributes.get(ATTR_ASSUMED_STATE) @@ -692,6 +702,8 @@ async def test_sending_mqtt_commands_and_optimistic( mqtt_mock.async_publish.reset_mock() state = hass.states.get("light.test") assert state.state == STATE_ON + assert state.attributes.get("color_mode") == light.ColorMode.COLOR_TEMP + assert state.attributes.get("color_temp") == 90 await common.async_turn_off(hass, "light.test") @@ -706,49 +718,61 @@ async def test_sending_mqtt_commands_and_optimistic( await common.async_turn_on( hass, "light.test", brightness=50, xy_color=[0.123, 0.123] ) - await common.async_turn_on(hass, "light.test", brightness=50, hs_color=[359, 78]) - await common.async_turn_on(hass, "light.test", rgb_color=[255, 128, 0]) - - mqtt_mock.async_publish.assert_has_calls( - [ - call( - "test_light_rgb/set", - JsonValidator( - '{"state": "ON", "color": {"r": 0, "g": 123, "b": 255,' - ' "x": 0.14, "y": 0.131, "h": 210.824, "s": 100.0},' - ' "brightness": 50}' - ), - 2, - False, - ), - call( - "test_light_rgb/set", - JsonValidator( - '{"state": "ON", "color": {"r": 255, "g": 56, "b": 59,' - ' "x": 0.654, "y": 0.301, "h": 359.0, "s": 78.0},' - ' "brightness": 50}' - ), - 2, - False, - ), - call( - "test_light_rgb/set", - JsonValidator( - '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0,' - ' "x": 0.611, "y": 0.375, "h": 30.118, "s": 100.0}}' - ), - 2, - False, - ), - ], - any_order=True, + mqtt_mock.async_publish.assert_called_once_with( + "test_light_rgb/set", + JsonValidator( + '{"state": "ON", "color": {"r": 0, "g": 123, "b": 255,' + ' "x": 0.14, "y": 0.131, "h": 210.824, "s": 100.0},' + ' "brightness": 50}' + ), + 2, + False, ) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get("light.test") + assert state.attributes.get("color_mode") == light.ColorMode.HS + assert state.attributes["brightness"] == 50 + assert state.attributes["hs_color"] == (210.824, 100.0) + assert state.attributes["rgb_color"] == (0, 123, 255) + assert state.attributes["xy_color"] == (0.14, 0.131) + await common.async_turn_on(hass, "light.test", brightness=50, hs_color=[359, 78]) + mqtt_mock.async_publish.assert_called_once_with( + "test_light_rgb/set", + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 56, "b": 59,' + ' "x": 0.654, "y": 0.301, "h": 359.0, "s": 78.0},' + ' "brightness": 50}' + ), + 2, + False, + ) + mqtt_mock.async_publish.reset_mock() state = hass.states.get("light.test") assert state.state == STATE_ON - assert state.attributes["rgb_color"] == (255, 128, 0) + assert state.attributes.get("color_mode") == light.ColorMode.HS + assert state.attributes["brightness"] == 50 + assert state.attributes["hs_color"] == (359.0, 78.0) + assert state.attributes["rgb_color"] == (255, 56, 59) + assert state.attributes["xy_color"] == (0.654, 0.301) + + await common.async_turn_on(hass, "light.test", rgb_color=[255, 128, 0]) + mqtt_mock.async_publish.assert_called_once_with( + "test_light_rgb/set", + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0,' + ' "x": 0.611, "y": 0.375, "h": 30.118, "s": 100.0}}' + ), + 2, + False, + ) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("color_mode") == light.ColorMode.HS assert state.attributes["brightness"] == 50 assert state.attributes["hs_color"] == (30.118, 100) + assert state.attributes["rgb_color"] == (255, 128, 0) assert state.attributes["xy_color"] == (0.611, 0.375) @@ -794,12 +818,7 @@ async def test_sending_mqtt_commands_and_optimistic2( state = hass.states.get("light.test") assert state.state == STATE_ON expected_features = ( - light.SUPPORT_BRIGHTNESS - | light.SUPPORT_COLOR - | light.SUPPORT_COLOR_TEMP - | light.SUPPORT_EFFECT - | light.SUPPORT_FLASH - | light.SUPPORT_TRANSITION + light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION ) assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features assert state.attributes.get("brightness") == 95 @@ -1682,7 +1701,9 @@ async def test_white_scale(hass, mqtt_mock_entry_with_yaml_config): # Turn on the light with brightness async_fire_mqtt_message( - hass, "test_light_bright_scale", '{"state":"ON", "brightness": 99}' + hass, + "test_light_bright_scale", + '{"state":"ON", "brightness": 99, "color_mode":"hs", "color":{"h":180,"s":50}}', ) state = hass.states.get("light.test") @@ -1726,13 +1747,9 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN - expected_features = ( - light.SUPPORT_BRIGHTNESS - | light.SUPPORT_COLOR - | light.SUPPORT_COLOR_TEMP - | light.SUPPORT_FLASH - | light.SUPPORT_TRANSITION - ) + color_modes = [light.ColorMode.COLOR_TEMP, light.ColorMode.HS] + assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes + expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features assert state.attributes.get("rgb_color") is None assert state.attributes.get("brightness") is None @@ -1754,8 +1771,7 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): assert state.state == STATE_ON assert state.attributes.get("rgb_color") == (255, 255, 255) assert state.attributes.get("brightness") == 255 - assert state.attributes.get("color_temp") == 100 - + assert state.attributes.get("color_temp") is None # Empty color value async_fire_mqtt_message( hass, @@ -1814,6 +1830,14 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): assert state.state == STATE_ON assert state.attributes.get("brightness") == 255 + # Unset color and set a valid color temperature + async_fire_mqtt_message( + hass, "test_light_rgb", '{"state":"ON", "color": null, "color_temp": 100}' + ) + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("color_temp") == 100 + # Bad color temperature async_fire_mqtt_message( hass, "test_light_rgb", '{"state":"ON", "color_temp": "badValue"}' @@ -2199,7 +2223,7 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): [ ( "state_topic", - '{ "state": "ON", "brightness": 200 }', + '{ "state": "ON", "brightness": 200, "color_mode":"hs", "color":{"h":180,"s":50} }', "brightness", 200, None, diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index d88480f9f5a..8ecdcc2c872 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -167,12 +167,9 @@ async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config): state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN - expected_features = ( - light.SUPPORT_TRANSITION - | light.SUPPORT_COLOR - | light.SUPPORT_FLASH - | light.SUPPORT_BRIGHTNESS - ) + color_modes = [light.ColorMode.HS] + assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes + expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features @@ -281,8 +278,27 @@ async def test_state_brightness_color_effect_temp_change_via_topic( assert state.state == STATE_ON assert state.attributes.get("rgb_color") == (255, 128, 63) assert state.attributes.get("brightness") == 255 + assert state.attributes.get("color_temp") is None # rgb color has priority + assert state.attributes.get("effect") is None + + # turn on the light + async_fire_mqtt_message(hass, "test_light_rgb", "on,255,145,None-None-None,") + + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("rgb_color") == ( + 246, + 244, + 255, + ) # temp converted to color + assert state.attributes.get("brightness") == 255 assert state.attributes.get("color_temp") == 145 assert state.attributes.get("effect") is None + assert state.attributes.get("xy_color") == (0.317, 0.317) # temp converted to color + assert state.attributes.get("hs_color") == ( + 251.249, + 4.253, + ) # temp converted to color # make the light state unknown async_fire_mqtt_message(hass, "test_light_rgb", "None") @@ -375,7 +391,7 @@ async def test_sending_mqtt_commands_and_optimistic( assert state.state == STATE_ON assert state.attributes.get("hs_color") == (100, 100) assert state.attributes.get("effect") == "random" - assert state.attributes.get("color_temp") == 100 + assert state.attributes.get("color_temp") is None # hs_color has priority assert state.attributes.get(ATTR_ASSUMED_STATE) await common.async_turn_off(hass, "light.test") @@ -779,7 +795,7 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): state = hass.states.get("light.test") assert state.state == STATE_ON assert state.attributes.get("brightness") == 255 - assert state.attributes.get("color_temp") == 215 + assert state.attributes.get("color_temp") is None # hs_color has priority assert state.attributes.get("rgb_color") == (255, 255, 255) assert state.attributes.get("effect") == "rainbow" @@ -797,13 +813,6 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): state = hass.states.get("light.test") assert state.attributes.get("brightness") == 255 - # bad color temp values - async_fire_mqtt_message(hass, "test_light_rgb", "on,,off,255-255-255") - - # color temp should not have changed - state = hass.states.get("light.test") - assert state.attributes.get("color_temp") == 215 - # bad color values async_fire_mqtt_message(hass, "test_light_rgb", "on,255,a-b-c") @@ -811,6 +820,19 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): state = hass.states.get("light.test") assert state.attributes.get("rgb_color") == (255, 255, 255) + # Unset color and set a valid color temperature + async_fire_mqtt_message(hass, "test_light_rgb", "on,,215,None-None-None") + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("color_temp") == 215 + + # bad color temp values + async_fire_mqtt_message(hass, "test_light_rgb", "on,,off,") + + # color temp should not have changed + state = hass.states.get("light.test") + assert state.attributes.get("color_temp") == 215 + # bad effect value async_fire_mqtt_message(hass, "test_light_rgb", "on,255,a-b-c,white")