diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 43b0cbf77b3..14e21e61d48 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -2,7 +2,6 @@ from __future__ import annotations -from collections.abc import Callable from contextlib import suppress import logging from typing import TYPE_CHECKING, Any, cast @@ -24,7 +23,6 @@ from homeassistant.components.light import ( ATTR_XY_COLOR, DEFAULT_MAX_KELVIN, DEFAULT_MIN_KELVIN, - DOMAIN as LIGHT_DOMAIN, ENTITY_ID_FORMAT, FLASH_LONG, FLASH_SHORT, @@ -34,7 +32,6 @@ from homeassistant.components.light import ( LightEntityFeature, brightness_supported, color_supported, - filter_supported_color_modes, valid_supported_color_modes, ) from homeassistant.const import ( @@ -48,15 +45,13 @@ from homeassistant.const import ( CONF_XY, STATE_ON, ) -from homeassistant.core import async_get_hass, callback +from homeassistant.core import callback from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.json import json_dumps from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, VolSchemaType from homeassistant.util import color as color_util from homeassistant.util.json import json_loads_object -from homeassistant.util.yaml import dump as yaml_dump from .. import subscription from ..config import DEFAULT_QOS, DEFAULT_RETAIN, MQTT_RW_SCHEMA @@ -68,7 +63,6 @@ from ..const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - DOMAIN as MQTT_DOMAIN, ) from ..entity import MqttEntity from ..models import ReceiveMessage @@ -86,15 +80,10 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = "mqtt_json" DEFAULT_BRIGHTNESS = False -DEFAULT_COLOR_MODE = False -DEFAULT_COLOR_TEMP = False DEFAULT_EFFECT = False DEFAULT_FLASH_TIME_LONG = 10 DEFAULT_FLASH_TIME_SHORT = 2 DEFAULT_NAME = "MQTT JSON Light" -DEFAULT_RGB = False -DEFAULT_XY = False -DEFAULT_HS = False DEFAULT_BRIGHTNESS_SCALE = 255 DEFAULT_WHITE_SCALE = 255 @@ -110,89 +99,6 @@ CONF_MAX_MIREDS = "max_mireds" CONF_MIN_MIREDS = "min_mireds" -def valid_color_configuration( - setup_from_yaml: bool, -) -> Callable[[dict[str, Any]], dict[str, Any]]: - """Test color_mode is not combined with deprecated config.""" - - def _valid_color_configuration(config: ConfigType) -> ConfigType: - deprecated = {CONF_COLOR_TEMP, CONF_HS, CONF_RGB, CONF_XY} - deprecated_flags_used = any(config.get(key) for key in deprecated) - if config.get(CONF_SUPPORTED_COLOR_MODES): - if deprecated_flags_used: - raise vol.Invalid( - "supported_color_modes must not " - f"be combined with any of {deprecated}" - ) - elif deprecated_flags_used: - deprecated_flags = ", ".join(key for key in deprecated if key in config) - _LOGGER.warning( - "Deprecated flags [%s] used in MQTT JSON light config " - "for handling color mode, please use `supported_color_modes` instead. " - "Got: %s. This will stop working in Home Assistant Core 2025.3", - deprecated_flags, - config, - ) - if not setup_from_yaml: - return config - issue_id = hex(hash(frozenset(config))) - yaml_config_str = yaml_dump(config) - learn_more_url = ( - "https://www.home-assistant.io/integrations/" - f"{LIGHT_DOMAIN}.mqtt/#json-schema" - ) - hass = async_get_hass() - async_create_issue( - hass, - MQTT_DOMAIN, - issue_id, - issue_domain=LIGHT_DOMAIN, - is_fixable=False, - severity=IssueSeverity.WARNING, - learn_more_url=learn_more_url, - translation_placeholders={ - "deprecated_flags": deprecated_flags, - "config": yaml_config_str, - }, - translation_key="deprecated_color_handling", - ) - - if CONF_COLOR_MODE in config: - _LOGGER.warning( - "Deprecated flag `color_mode` used in MQTT JSON light config " - ", the `color_mode` flag is not used anymore and should be removed. " - "Got: %s. This will stop working in Home Assistant Core 2025.3", - config, - ) - if not setup_from_yaml: - return config - issue_id = hex(hash(frozenset(config))) - yaml_config_str = yaml_dump(config) - learn_more_url = ( - "https://www.home-assistant.io/integrations/" - f"{LIGHT_DOMAIN}.mqtt/#json-schema" - ) - hass = async_get_hass() - async_create_issue( - hass, - MQTT_DOMAIN, - issue_id, - breaks_in_ha_version="2025.3.0", - issue_domain=LIGHT_DOMAIN, - is_fixable=False, - severity=IssueSeverity.WARNING, - learn_more_url=learn_more_url, - translation_placeholders={ - "config": yaml_config_str, - }, - translation_key="deprecated_color_mode_flag", - ) - - return config - - return _valid_color_configuration - - _PLATFORM_SCHEMA_BASE = ( MQTT_RW_SCHEMA.extend( { @@ -200,12 +106,6 @@ _PLATFORM_SCHEMA_BASE = ( vol.Optional( CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE ): vol.All(vol.Coerce(int), vol.Range(min=1)), - # CONF_COLOR_MODE was deprecated with HA Core 2024.4 and will be - # removed with HA Core 2025.3 - vol.Optional(CONF_COLOR_MODE): cv.boolean, - # CONF_COLOR_TEMP was deprecated with HA Core 2024.4 and will be - # removed with HA Core 2025.3 - vol.Optional(CONF_COLOR_TEMP, default=DEFAULT_COLOR_TEMP): cv.boolean, vol.Optional(CONF_COLOR_TEMP_KELVIN, default=False): cv.boolean, vol.Optional(CONF_EFFECT, default=DEFAULT_EFFECT): cv.boolean, vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), @@ -215,9 +115,6 @@ _PLATFORM_SCHEMA_BASE = ( vol.Optional( CONF_FLASH_TIME_SHORT, default=DEFAULT_FLASH_TIME_SHORT ): cv.positive_int, - # CONF_HS was deprecated with HA Core 2024.4 and will be - # removed with HA Core 2025.3 - vol.Optional(CONF_HS, default=DEFAULT_HS): cv.boolean, vol.Optional(CONF_MAX_MIREDS): cv.positive_int, vol.Optional(CONF_MIN_MIREDS): cv.positive_int, vol.Optional(CONF_MAX_KELVIN): cv.positive_int, @@ -227,9 +124,6 @@ _PLATFORM_SCHEMA_BASE = ( vol.Coerce(int), vol.In([0, 1, 2]) ), vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, - # CONF_RGB was deprecated with HA Core 2024.4 and will be - # removed with HA Core 2025.3 - vol.Optional(CONF_RGB, default=DEFAULT_RGB): cv.boolean, vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_SUPPORTED_COLOR_MODES): vol.All( cv.ensure_list, @@ -240,22 +134,29 @@ _PLATFORM_SCHEMA_BASE = ( vol.Optional(CONF_WHITE_SCALE, default=DEFAULT_WHITE_SCALE): vol.All( vol.Coerce(int), vol.Range(min=1) ), - # CONF_XY was deprecated with HA Core 2024.4 and will be - # removed with HA Core 2025.3 - vol.Optional(CONF_XY, default=DEFAULT_XY): cv.boolean, }, ) .extend(MQTT_ENTITY_COMMON_SCHEMA.schema) .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) +# Support for legacy color_mode handling was removed with HA Core 2025.3 +# The removed attributes can be removed from the schema's from HA Core 2026.3 DISCOVERY_SCHEMA_JSON = vol.All( - valid_color_configuration(False), + cv.removed(CONF_COLOR_MODE, raise_if_present=False), + cv.removed(CONF_COLOR_TEMP, raise_if_present=False), + cv.removed(CONF_HS, raise_if_present=False), + cv.removed(CONF_RGB, raise_if_present=False), + cv.removed(CONF_XY, raise_if_present=False), _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) PLATFORM_SCHEMA_MODERN_JSON = vol.All( - valid_color_configuration(True), + cv.removed(CONF_COLOR_MODE), + cv.removed(CONF_COLOR_TEMP), + cv.removed(CONF_HS), + cv.removed(CONF_RGB), + cv.removed(CONF_XY), _PLATFORM_SCHEMA_BASE, ) @@ -272,8 +173,6 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): _topic: dict[str, str | None] _optimistic: bool - _deprecated_color_handling: bool = False - @staticmethod def config_schema() -> VolSchemaType: """Return the config schema.""" @@ -318,122 +217,65 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self._attr_color_mode = next(iter(self.supported_color_modes)) else: self._attr_color_mode = ColorMode.UNKNOWN - else: - self._deprecated_color_handling = True - 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._attr_supported_color_modes = filter_supported_color_modes(color_modes) - if self.supported_color_modes and len(self.supported_color_modes) == 1: - self._fixed_color_mode = next(iter(self.supported_color_modes)) def _update_color(self, values: dict[str, Any]) -> None: - if self._deprecated_color_handling: - # Deprecated color handling - try: - red = int(values["color"]["r"]) - green = int(values["color"]["g"]) - blue = int(values["color"]["b"]) - self._attr_hs_color = color_util.color_RGB_to_hs(red, green, blue) - except KeyError: - pass - except ValueError: - _LOGGER.warning( - "Invalid RGB color value '%s' received for entity %s", - values, - self.entity_id, + color_mode: str = values["color_mode"] + if not self._supports_color_mode(color_mode): + _LOGGER.warning( + "Invalid color mode '%s' received for entity %s", + color_mode, + self.entity_id, + ) + return + try: + if color_mode == ColorMode.COLOR_TEMP: + self._attr_color_temp_kelvin = ( + values["color_temp"] + if self._color_temp_kelvin + else color_util.color_temperature_mired_to_kelvin( + values["color_temp"] + ) ) - return - - try: - x_color = float(values["color"]["x"]) - y_color = float(values["color"]["y"]) - self._attr_hs_color = color_util.color_xy_to_hs(x_color, y_color) - except KeyError: - pass - except ValueError: - _LOGGER.warning( - "Invalid XY color value '%s' received for entity %s", - values, - self.entity_id, - ) - return - - try: + self._attr_color_mode = ColorMode.COLOR_TEMP + elif color_mode == ColorMode.HS: hue = float(values["color"]["h"]) saturation = float(values["color"]["s"]) + self._attr_color_mode = ColorMode.HS self._attr_hs_color = (hue, saturation) - except KeyError: - pass - except ValueError: - _LOGGER.warning( - "Invalid HS color value '%s' received for entity %s", - values, - self.entity_id, - ) - return - else: - color_mode: str = values["color_mode"] - if not self._supports_color_mode(color_mode): - _LOGGER.warning( - "Invalid color mode '%s' received for entity %s", - color_mode, - self.entity_id, - ) - return - try: - if color_mode == ColorMode.COLOR_TEMP: - self._attr_color_temp_kelvin = ( - values["color_temp"] - if self._color_temp_kelvin - else color_util.color_temperature_mired_to_kelvin( - values["color_temp"] - ) - ) - self._attr_color_mode = ColorMode.COLOR_TEMP - elif color_mode == ColorMode.HS: - hue = float(values["color"]["h"]) - saturation = float(values["color"]["s"]) - self._attr_color_mode = ColorMode.HS - self._attr_hs_color = (hue, saturation) - elif color_mode == ColorMode.RGB: - r = int(values["color"]["r"]) - g = int(values["color"]["g"]) - b = int(values["color"]["b"]) - self._attr_color_mode = ColorMode.RGB - self._attr_rgb_color = (r, g, b) - elif color_mode == ColorMode.RGBW: - r = int(values["color"]["r"]) - g = int(values["color"]["g"]) - b = int(values["color"]["b"]) - w = int(values["color"]["w"]) - self._attr_color_mode = ColorMode.RGBW - self._attr_rgbw_color = (r, g, b, w) - elif color_mode == ColorMode.RGBWW: - r = int(values["color"]["r"]) - g = int(values["color"]["g"]) - b = int(values["color"]["b"]) - c = int(values["color"]["c"]) - w = int(values["color"]["w"]) - self._attr_color_mode = ColorMode.RGBWW - self._attr_rgbww_color = (r, g, b, c, w) - elif color_mode == ColorMode.WHITE: - self._attr_color_mode = ColorMode.WHITE - elif color_mode == ColorMode.XY: - x = float(values["color"]["x"]) - y = float(values["color"]["y"]) - self._attr_color_mode = ColorMode.XY - self._attr_xy_color = (x, y) - except (KeyError, ValueError): - _LOGGER.warning( - "Invalid or incomplete color value '%s' received for entity %s", - values, - self.entity_id, - ) + elif color_mode == ColorMode.RGB: + r = int(values["color"]["r"]) + g = int(values["color"]["g"]) + b = int(values["color"]["b"]) + self._attr_color_mode = ColorMode.RGB + self._attr_rgb_color = (r, g, b) + elif color_mode == ColorMode.RGBW: + r = int(values["color"]["r"]) + g = int(values["color"]["g"]) + b = int(values["color"]["b"]) + w = int(values["color"]["w"]) + self._attr_color_mode = ColorMode.RGBW + self._attr_rgbw_color = (r, g, b, w) + elif color_mode == ColorMode.RGBWW: + r = int(values["color"]["r"]) + g = int(values["color"]["g"]) + b = int(values["color"]["b"]) + c = int(values["color"]["c"]) + w = int(values["color"]["w"]) + self._attr_color_mode = ColorMode.RGBWW + self._attr_rgbww_color = (r, g, b, c, w) + elif color_mode == ColorMode.WHITE: + self._attr_color_mode = ColorMode.WHITE + elif color_mode == ColorMode.XY: + x = float(values["color"]["x"]) + y = float(values["color"]["y"]) + self._attr_color_mode = ColorMode.XY + self._attr_xy_color = (x, y) + except (KeyError, TypeError, ValueError): + _LOGGER.warning( + "Invalid or incomplete color value '%s' received for entity %s", + values, + self.entity_id, + ) @callback def _state_received(self, msg: ReceiveMessage) -> None: @@ -447,18 +289,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): elif values["state"] is None: self._attr_is_on = None - if ( - self._deprecated_color_handling - and color_supported(self.supported_color_modes) - and "color" in values - ): - # Deprecated color handling - if values["color"] is None: - self._attr_hs_color = None - else: - self._update_color(values) - - if not self._deprecated_color_handling and "color_mode" in values: + if color_supported(self.supported_color_modes) and "color_mode" in values: self._update_color(values) if brightness_supported(self.supported_color_modes): @@ -484,35 +315,6 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self.entity_id, ) - if ( - self._deprecated_color_handling - and self.supported_color_modes - and ColorMode.COLOR_TEMP in self.supported_color_modes - ): - # Deprecated color handling - try: - if values["color_temp"] is None: - self._attr_color_temp_kelvin = None - else: - self._attr_color_temp_kelvin = ( - values["color_temp"] # type: ignore[assignment] - if self._color_temp_kelvin - else color_util.color_temperature_mired_to_kelvin( - values["color_temp"] # type: ignore[arg-type] - ) - ) - except KeyError: - pass - except (TypeError, ValueError): - _LOGGER.warning( - "Invalid color temp value '%s' received for entity %s", - values["color_temp"], - self.entity_id, - ) - # Allow to switch back to color_temp - if "color" not in values: - self._attr_hs_color = None - if self.supported_features and LightEntityFeature.EFFECT: with suppress(KeyError): self._attr_effect = cast(str, values["effect"]) @@ -565,19 +367,6 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): ) self._attr_xy_color = last_attributes.get(ATTR_XY_COLOR, self.xy_color) - @property - def color_mode(self) -> ColorMode | str | None: - """Return current color mode.""" - if not self._deprecated_color_handling: - return self._attr_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_color is not None: - return ColorMode.HS - return ColorMode.COLOR_TEMP - def _set_flash_and_transition(self, message: dict[str, Any], **kwargs: Any) -> None: if ATTR_TRANSITION in kwargs: message["transition"] = kwargs[ATTR_TRANSITION] @@ -604,17 +393,15 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): def _supports_color_mode(self, color_mode: ColorMode | str) -> bool: """Return True if the light natively supports a color mode.""" return ( - not self._deprecated_color_handling - and self.supported_color_modes is not None + self.supported_color_modes is not None and color_mode in self.supported_color_modes ) - async def async_turn_on(self, **kwargs: Any) -> None: # noqa: C901 + async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on. This method is a coroutine. """ - brightness: int should_update = False hs_color: tuple[float, float] message: dict[str, Any] = {"state": "ON"} @@ -623,39 +410,6 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): rgbcw: tuple[int, ...] xy_color: tuple[float, float] - 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]: - # If brightness is supported, we don't want to scale the - # RGB values given using the brightness. - if self._config[CONF_BRIGHTNESS]: - brightness = 255 - else: - # We pop the brightness, to omit it from the payload - brightness = kwargs.pop(ATTR_BRIGHTNESS, 255) - rgb = color_util.color_hsv_to_RGB( - hs_color[0], hs_color[1], brightness / 255 * 100 - ) - message["color"]["r"] = rgb[0] - message["color"]["g"] = rgb[1] - message["color"]["b"] = rgb[2] - if self._config[CONF_XY]: - xy_color = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR]) - message["color"]["x"] = xy_color[0] - message["color"]["y"] = xy_color[1] - if self._config[CONF_HS]: - message["color"]["h"] = hs_color[0] - message["color"]["s"] = hs_color[1] - - if self._optimistic: - self._attr_color_temp_kelvin = None - self._attr_hs_color = kwargs[ATTR_HS_COLOR] - should_update = True - if ATTR_HS_COLOR in kwargs and self._supports_color_mode(ColorMode.HS): hs_color = kwargs[ATTR_HS_COLOR] message["color"] = {"h": hs_color[0], "s": hs_color[1]} diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index 3815b6adbd5..bf0bd594ea4 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -1,13 +1,5 @@ { "issues": { - "deprecated_color_handling": { - "title": "Deprecated color handling used for MQTT light", - "description": "An MQTT light config (with `json` schema) found in `configuration.yaml` uses deprecated color handling flags.\n\nConfiguration found:\n```yaml\n{config}\n```\nDeprecated flags: **{deprecated_flags}**.\n\nUse the `supported_color_modes` option instead and [reload](/developer-tools/yaml) the manually configured MQTT items or restart Home Assistant to fix this issue." - }, - "deprecated_color_mode_flag": { - "title": "Deprecated color_mode option flag used for MQTT light", - "description": "An MQTT light config (with `json` schema) found in `configuration.yaml` uses a deprecated `color_mode` flag.\n\nConfiguration found:\n```yaml\n{config}\n```\n\nRemove the option from your config and [reload](/developer-tools/yaml) the manually configured MQTT items or restart Home Assistant to fix this issue." - }, "invalid_platform_config": { "title": "Invalid config found for mqtt {domain} item", "description": "Home Assistant detected an invalid config for a manually configured item.\n\nPlatform domain: **{domain}**\nConfiguration file: **{config_file}**\nNear line: **{line}**\nConfiguration found:\n```yaml\n{config}\n```\nError: **{error}**.\n\nMake sure the configuration is valid and [reload](/developer-tools/yaml) the manually configured MQTT items or restart Home Assistant to fix this issue." diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 512e4091438..7ddd04a09a6 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -100,7 +100,6 @@ from homeassistant.const import ( STATE_UNKNOWN, ) from homeassistant.core import HomeAssistant, State -from homeassistant.helpers.json import json_dumps from homeassistant.util.json import json_loads from .test_common import ( @@ -195,172 +194,6 @@ async def test_fail_setup_if_no_command_topic( assert "required key not provided" in caplog.text -@pytest.mark.parametrize( - "hass_config", - [ - help_custom_config(light.DOMAIN, COLOR_MODES_CONFIG, ({"color_temp": True},)), - help_custom_config(light.DOMAIN, COLOR_MODES_CONFIG, ({"hs": True},)), - help_custom_config(light.DOMAIN, COLOR_MODES_CONFIG, ({"rgb": True},)), - help_custom_config(light.DOMAIN, COLOR_MODES_CONFIG, ({"xy": True},)), - ], -) -async def test_fail_setup_if_color_mode_deprecated( - mqtt_mock_entry: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test if setup fails if color mode is combined with deprecated config keys.""" - assert await mqtt_mock_entry() - assert "supported_color_modes must not be combined with any of" in caplog.text - - -@pytest.mark.parametrize( - ("hass_config", "color_modes"), - [ - ( - help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"color_temp": True},)), - ("color_temp",), - ), - (help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"hs": True},)), ("hs",)), - (help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"rgb": True},)), ("rgb",)), - (help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"xy": True},)), ("xy",)), - ( - help_custom_config( - light.DOMAIN, DEFAULT_CONFIG, ({"color_temp": True, "rgb": True},) - ), - ("color_temp, rgb", "rgb, color_temp"), - ), - ], - ids=["color_temp", "hs", "rgb", "xy", "color_temp, rgb"], -) -async def test_warning_if_color_mode_flags_are_used( - mqtt_mock_entry: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, - color_modes: tuple[str, ...], -) -> None: - """Test warnings deprecated config keys without supported color modes defined.""" - with patch( - "homeassistant.components.mqtt.light.schema_json.async_create_issue" - ) as mock_async_create_issue: - assert await mqtt_mock_entry() - assert any( - ( - f"Deprecated flags [{color_modes_case}] used in MQTT JSON light config " - "for handling color mode, please use `supported_color_modes` instead." - in caplog.text - ) - for color_modes_case in color_modes - ) - mock_async_create_issue.assert_called_once() - - -@pytest.mark.parametrize( - ("config", "color_modes"), - [ - ( - help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"color_temp": True},)), - ("color_temp",), - ), - (help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"hs": True},)), ("hs",)), - (help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"rgb": True},)), ("rgb",)), - (help_custom_config(light.DOMAIN, DEFAULT_CONFIG, ({"xy": True},)), ("xy",)), - ( - help_custom_config( - light.DOMAIN, DEFAULT_CONFIG, ({"color_temp": True, "rgb": True},) - ), - ("color_temp, rgb", "rgb, color_temp"), - ), - ], - ids=["color_temp", "hs", "rgb", "xy", "color_temp, rgb"], -) -async def test_warning_on_discovery_if_color_mode_flags_are_used( - hass: HomeAssistant, - mqtt_mock_entry: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, - config: dict[str, Any], - color_modes: tuple[str, ...], -) -> None: - """Test warnings deprecated config keys with discovery.""" - with patch( - "homeassistant.components.mqtt.light.schema_json.async_create_issue" - ) as mock_async_create_issue: - assert await mqtt_mock_entry() - - config_payload = json_dumps(config[mqtt.DOMAIN][light.DOMAIN][0]) - async_fire_mqtt_message( - hass, - "homeassistant/light/bla/config", - config_payload, - ) - await hass.async_block_till_done() - assert any( - ( - f"Deprecated flags [{color_modes_case}] used in MQTT JSON light config " - "for handling color mode, please " - "use `supported_color_modes` instead" in caplog.text - ) - for color_modes_case in color_modes - ) - mock_async_create_issue.assert_not_called() - - -@pytest.mark.parametrize( - "hass_config", - [ - help_custom_config( - light.DOMAIN, - DEFAULT_CONFIG, - ({"color_mode": True, "supported_color_modes": ["color_temp"]},), - ), - ], - ids=["color_temp"], -) -async def test_warning_if_color_mode_option_flag_is_used( - mqtt_mock_entry: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test warning deprecated color_mode option flag is used.""" - with patch( - "homeassistant.components.mqtt.light.schema_json.async_create_issue" - ) as mock_async_create_issue: - assert await mqtt_mock_entry() - assert "Deprecated flag `color_mode` used in MQTT JSON light config" in caplog.text - mock_async_create_issue.assert_called_once() - - -@pytest.mark.parametrize( - "config", - [ - help_custom_config( - light.DOMAIN, - DEFAULT_CONFIG, - ({"color_mode": True, "supported_color_modes": ["color_temp"]},), - ), - ], - ids=["color_temp"], -) -async def test_warning_on_discovery_if_color_mode_option_flag_is_used( - hass: HomeAssistant, - mqtt_mock_entry: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, - config: dict[str, Any], -) -> None: - """Test warning deprecated color_mode option flag is used.""" - with patch( - "homeassistant.components.mqtt.light.schema_json.async_create_issue" - ) as mock_async_create_issue: - assert await mqtt_mock_entry() - - config_payload = json_dumps(config[mqtt.DOMAIN][light.DOMAIN][0]) - async_fire_mqtt_message( - hass, - "homeassistant/light/bla/config", - config_payload, - ) - await hass.async_block_till_done() - assert "Deprecated flag `color_mode` used in MQTT JSON light config" in caplog.text - mock_async_create_issue.assert_not_called() - - @pytest.mark.parametrize( ("hass_config", "error"), [ @@ -400,82 +233,6 @@ async def test_fail_setup_if_color_modes_invalid( assert error in caplog.text -@pytest.mark.parametrize( - ("hass_config", "kelvin", "color_temp_payload_value"), - [ - ( - { - mqtt.DOMAIN: { - light.DOMAIN: { - "schema": "json", - "name": "test", - "command_topic": "test_light/set", - "state_topic": "test_light", - "color_mode": True, - "color_temp_kelvin": False, - "supported_color_modes": "color_temp", - } - } - }, - 5208, - 192, - ), - ( - { - mqtt.DOMAIN: { - light.DOMAIN: { - "schema": "json", - "name": "test", - "command_topic": "test_light/set", - "state_topic": "test_light", - "color_mode": True, - "color_temp_kelvin": True, - "supported_color_modes": "color_temp", - } - } - }, - 5208, - 5208, - ), - ], - ids=["mireds", "kelvin"], -) -async def test_single_color_mode( - hass: HomeAssistant, - mqtt_mock_entry: MqttMockHAClientGenerator, - kelvin: int, - color_temp_payload_value: int, -) -> None: - """Test setup with single color_mode.""" - await mqtt_mock_entry() - state = hass.states.get("light.test") - assert state.state == STATE_UNKNOWN - - await common.async_turn_on( - hass, "light.test", brightness=50, color_temp_kelvin=kelvin - ) - - payload = { - "state": "ON", - "brightness": 50, - "color_mode": "color_temp", - "color_temp": color_temp_payload_value, - } - async_fire_mqtt_message( - hass, - "test_light", - json_dumps(payload), - ) - color_modes = [light.ColorMode.COLOR_TEMP] - state = hass.states.get("light.test") - assert state.state == STATE_ON - - assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes - assert state.attributes.get(light.ATTR_COLOR_TEMP_KELVIN) == 5208 - assert state.attributes.get(light.ATTR_BRIGHTNESS) == 50 - assert state.attributes.get(light.ATTR_COLOR_MODE) == color_modes[0] - - @pytest.mark.parametrize("hass_config", [COLOR_MODES_CONFIG]) async def test_turn_on_with_unknown_color_mode_optimistic( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator @@ -550,34 +307,6 @@ async def test_controlling_state_with_unknown_color_mode( assert state.attributes.get(light.ATTR_COLOR_MODE) == light.ColorMode.COLOR_TEMP -@pytest.mark.parametrize( - "hass_config", - [ - { - mqtt.DOMAIN: { - light.DOMAIN: { - "schema": "json", - "name": "test", - "command_topic": "test_light_rgb/set", - "rgb": True, - } - } - } - ], -) -async def test_legacy_rgb_light( - hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator -) -> None: - """Test legacy RGB light flags expected features and color modes.""" - await mqtt_mock_entry() - - state = hass.states.get("light.test") - 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 - - @pytest.mark.parametrize( "hass_config", [ @@ -642,203 +371,9 @@ async def test_no_color_brightness_color_temp_if_no_topics( "name": "test", "state_topic": "test_light_rgb", "command_topic": "test_light_rgb/set", - "brightness": True, - "color_temp": True, - "effect": True, - "rgb": True, - "xy": True, - "hs": True, - "qos": "0", - } - } - } - ], -) -async def test_controlling_state_via_topic( - hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator -) -> None: - """Test the controlling of the state via topic.""" - await mqtt_mock_entry() - - 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_EFFECT | 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 - assert state.attributes.get("color_temp_kelvin") is None - assert state.attributes.get("effect") is None - assert state.attributes.get("xy_color") is None - assert state.attributes.get("hs_color") is None - assert not state.attributes.get(ATTR_ASSUMED_STATE) - - # Turn on the light - async_fire_mqtt_message( - hass, - "test_light_rgb", - '{"state":"ON",' - '"color":{"r":255,"g":255,"b":255},' - '"brightness":255,' - '"color_temp":155,' - '"effect":"colorloop"}', - ) - - state = hass.states.get("light.test") - 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_kelvin") 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, - 249, - ) # temp converted to color - assert state.attributes.get("brightness") == 255 - assert state.attributes.get("color_temp_kelvin") == 6451 - assert state.attributes.get("effect") == "colorloop" - assert state.attributes.get("xy_color") == (0.328, 0.333) # 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"}') - - state = hass.states.get("light.test") - assert state.state == STATE_OFF - - async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "brightness":100}') - - light_state = hass.states.get("light.test") - - assert light_state.attributes["brightness"] == 100 - - async_fire_mqtt_message( - hass, "test_light_rgb", '{"state":"ON", "color":{"r":125,"g":125,"b":125}}' - ) - - light_state = hass.states.get("light.test") - assert light_state.attributes.get("rgb_color") == (255, 255, 255) - - async_fire_mqtt_message( - hass, "test_light_rgb", '{"state":"ON", "color":{"x":0.135,"y":0.135}}' - ) - - light_state = hass.states.get("light.test") - assert light_state.attributes.get("xy_color") == (0.141, 0.141) - - async_fire_mqtt_message( - hass, "test_light_rgb", '{"state":"ON", "color":{"h":180,"s":50}}' - ) - - light_state = hass.states.get("light.test") - assert light_state.attributes.get("hs_color") == (180.0, 50.0) - - 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 # Color temp approximation - - async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color_temp":155}') - - light_state = hass.states.get("light.test") - assert light_state.attributes.get("color_temp_kelvin") == 6451 # 155 mired - - async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color_temp":null}') - - light_state = hass.states.get("light.test") - assert light_state.attributes.get("color_temp_kelvin") is None - - async_fire_mqtt_message( - hass, "test_light_rgb", '{"state":"ON", "effect":"colorloop"}' - ) - - light_state = hass.states.get("light.test") - assert light_state.attributes.get("effect") == "colorloop" - - async_fire_mqtt_message( - hass, - "test_light_rgb", - '{"state":"ON",' - '"color":{"r":255,"g":255,"b":255},' - '"brightness":128,' - '"color_temp":155,' - '"effect":"colorloop"}', - ) - light_state = hass.states.get("light.test") - assert light_state.state == STATE_ON - assert light_state.attributes.get("brightness") == 128 - - async_fire_mqtt_message( - hass, - "test_light_rgb", - '{"state":"OFF","brightness":0}', - ) - light_state = hass.states.get("light.test") - assert light_state.state == STATE_OFF - assert light_state.attributes.get("brightness") is None - - # Simulate the lights color temp has been changed - # while it was switched off - async_fire_mqtt_message( - hass, - "test_light_rgb", - '{"state":"OFF","color_temp":201}', - ) - light_state = hass.states.get("light.test") - assert light_state.state == STATE_OFF - # Color temp attribute is not exposed while the lamp is off - assert light_state.attributes.get("color_temp_kelvin") is None - - # test previous zero brightness received was ignored and brightness is restored - # see if the latest color_temp value received is restored - async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON"}') - light_state = hass.states.get("light.test") - assert light_state.attributes.get("brightness") == 128 - assert light_state.attributes.get("color_temp_kelvin") == 4975 # 201 mired - - # A `0` brightness value is ignored when a light is turned on - async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON","brightness":0}') - light_state = hass.states.get("light.test") - assert light_state.attributes.get("brightness") == 128 - - -@pytest.mark.parametrize( - "hass_config", - [ - { - mqtt.DOMAIN: { - light.DOMAIN: { - "schema": "json", - "name": "test", - "state_topic": "test_light_rgb", - "command_topic": "test_light_rgb/set", - "brightness": True, - "color_temp": True, "color_temp_kelvin": True, "effect": True, - "rgb": True, - "xy": True, - "hs": True, + "supported_color_modes": ["color_temp", "hs"], "qos": "0", } } @@ -856,9 +391,11 @@ async def test_controlling_state_color_temp_kelvin( color_modes = [light.ColorMode.COLOR_TEMP, light.ColorMode.HS] assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes expected_features = ( - light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION + light.LightEntityFeature.EFFECT + | light.LightEntityFeature.FLASH + | light.LightEntityFeature.TRANSITION ) - assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features + assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features assert state.attributes.get("rgb_color") is None assert state.attributes.get("brightness") is None assert state.attributes.get("color_temp_kelvin") is None @@ -872,7 +409,8 @@ async def test_controlling_state_color_temp_kelvin( hass, "test_light_rgb", '{"state":"ON",' - '"color":{"r":255,"g":255,"b":255},' + '"color":{"h": 44.098, "s": 2.43},' + '"color_mode": "hs",' '"brightness":255,' '"color_temp":155,' '"effect":"colorloop"}', @@ -880,12 +418,12 @@ async def test_controlling_state_color_temp_kelvin( state = hass.states.get("light.test") assert state.state == STATE_ON - assert state.attributes.get("rgb_color") == (255, 255, 255) + assert state.attributes.get("rgb_color") == (255, 253, 249) assert state.attributes.get("brightness") == 255 assert state.attributes.get("color_temp_kelvin") 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) + assert state.attributes.get("xy_color") == (0.328, 0.333) + assert state.attributes.get("hs_color") == (44.098, 2.43) # Turn on the light async_fire_mqtt_message( @@ -894,6 +432,7 @@ async def test_controlling_state_color_temp_kelvin( '{"state":"ON",' '"brightness":255,' '"color":null,' + '"color_mode":"color_temp",' '"color_temp":6451,' # Kelvin '"effect":"colorloop"}', ) @@ -920,7 +459,7 @@ async def test_controlling_state_color_temp_kelvin( ) ], ) -async def test_controlling_state_via_topic2( +async def test_controlling_state_via_topic( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator, caplog: pytest.LogCaptureFixture, @@ -981,6 +520,11 @@ async def test_controlling_state_via_topic2( state = hass.states.get("light.test") assert state.attributes["brightness"] == 100 + # Zero brightness value is ignored + async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "brightness":0}') + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 100 + # RGB color async_fire_mqtt_message( hass, @@ -1083,242 +627,6 @@ async def test_controlling_state_via_topic2( { mqtt.DOMAIN: { light.DOMAIN: { - "schema": "json", - "name": "test", - "command_topic": "test_light_rgb/set", - "state_topic": "test_light_rgb/set", - "rgb": True, - "color_temp": True, - "brightness": True, - } - } - } - ], -) -async def test_controlling_the_state_with_legacy_color_handling( - hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator -) -> None: - """Test state updates for lights with a legacy color handling.""" - supported_color_modes = ["color_temp", "hs"] - await mqtt_mock_entry() - - state = hass.states.get("light.test") - assert state.state == STATE_UNKNOWN - expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION - assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features - assert state.attributes.get("brightness") is None - assert state.attributes.get("color_mode") is None - assert state.attributes.get("color_temp_kelvin") is None - assert state.attributes.get("effect") is None - assert state.attributes.get("hs_color") is None - assert state.attributes.get("rgb_color") is None - assert state.attributes.get("rgbw_color") is None - assert state.attributes.get("rgbww_color") is None - assert state.attributes.get("supported_color_modes") == supported_color_modes - assert state.attributes.get("xy_color") is None - assert not state.attributes.get(ATTR_ASSUMED_STATE) - - for _ in range(2): - # Returned state after the light was turned on - # Receiving legacy color mode: rgb. - async_fire_mqtt_message( - hass, - "test_light_rgb/set", - '{ "state": "ON", "brightness": 255, "level": 100, "hue": 16,' - '"saturation": 100, "color": { "r": 255, "g": 67, "b": 0 }, ' - '"bulb_mode": "color", "color_mode": "rgb" }', - ) - - state = hass.states.get("light.test") - assert state.state == STATE_ON - assert state.attributes.get("brightness") == 255 - assert state.attributes.get("color_mode") == "hs" - assert state.attributes.get("color_temp_kelvin") is None - assert state.attributes.get("effect") is None - assert state.attributes.get("hs_color") == (15.765, 100.0) - assert state.attributes.get("rgb_color") == (255, 67, 0) - assert state.attributes.get("rgbw_color") is None - assert state.attributes.get("rgbww_color") is None - assert state.attributes.get("xy_color") == (0.674, 0.322) - - # Returned state after the lights color mode was changed - # Receiving legacy color mode: color_temp - async_fire_mqtt_message( - hass, - "test_light_rgb/set", - '{ "state": "ON", "brightness": 255, "level": 100, ' - '"kelvin": 92, "color_temp": 353, "bulb_mode": "white", ' - '"color_mode": "color_temp" }', - ) - - state = hass.states.get("light.test") - assert state.state == STATE_ON - assert state.attributes.get("brightness") == 255 - assert state.attributes.get("color_mode") == "color_temp" - assert state.attributes.get("color_temp_kelvin") == 2832 - assert state.attributes.get("effect") is None - assert state.attributes.get("hs_color") == (28.125, 61.661) - assert state.attributes.get("rgb_color") == (255, 171, 98) - assert state.attributes.get("rgbw_color") is None - assert state.attributes.get("rgbww_color") is None - assert state.attributes.get("xy_color") == (0.512, 0.385) - - -@pytest.mark.parametrize( - "hass_config", - [ - { - mqtt.DOMAIN: { - light.DOMAIN: { - "schema": "json", - "name": "test", - "command_topic": "test_light_rgb/set", - "brightness": True, - "color_temp": True, - "effect": True, - "hs": True, - "rgb": True, - "xy": True, - "qos": 2, - } - } - } - ], -) -async def test_sending_mqtt_commands_and_optimistic( - hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator -) -> None: - """Test the sending of command in optimistic mode.""" - fake_state = State( - "light.test", - "on", - { - "brightness": 95, - "hs_color": [100, 100], - "effect": "random", - "color_temp_kelvin": 10000, - }, - ) - mock_restore_cache(hass, (fake_state,)) - - mqtt_mock = await mqtt_mock_entry() - - state = hass.states.get("light.test") - assert state.state == STATE_ON - 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_kelvin") 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_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION - ) - assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features - assert state.attributes.get(ATTR_ASSUMED_STATE) - - await common.async_turn_on(hass, "light.test") - - mqtt_mock.async_publish.assert_called_once_with( - "test_light_rgb/set", '{"state":"ON"}', 2, False - ) - mqtt_mock.async_publish.reset_mock() - state = hass.states.get("light.test") - assert state.state == STATE_ON - - await common.async_turn_on(hass, "light.test", color_temp_kelvin=11111) - - mqtt_mock.async_publish.assert_called_once_with( - "test_light_rgb/set", - JsonValidator('{"state": "ON", "color_temp": 90}'), - 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.COLOR_TEMP - assert state.attributes.get("color_temp_kelvin") == 11111 - - await common.async_turn_off(hass, "light.test") - - mqtt_mock.async_publish.assert_called_once_with( - "test_light_rgb/set", '{"state":"OFF"}', 2, False - ) - mqtt_mock.async_publish.reset_mock() - state = hass.states.get("light.test") - assert state.state == STATE_OFF - - mqtt_mock.reset_mock() - await common.async_turn_on( - hass, "light.test", brightness=50, xy_color=(0.123, 0.123) - ) - mqtt_mock.async_publish.assert_called_once_with( - "test_light_rgb/set", - JsonValidator( - '{"state": "ON", "color": {"r": 0, "g": 124, "b": 255,' - ' "x": 0.14, "y": 0.133, "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, 124, 255) - assert state.attributes["xy_color"] == (0.14, 0.133) - - 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.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) - - -@pytest.mark.parametrize( - "hass_config", - [ - { - mqtt.DOMAIN: { - light.DOMAIN: { - "brightness": True, - "color_mode": True, "command_topic": "test_light_rgb/set", "effect": True, "name": "test", @@ -1338,7 +646,7 @@ async def test_sending_mqtt_commands_and_optimistic( } ], ) -async def test_sending_mqtt_commands_and_optimistic2( +async def test_sending_mqtt_commands_and_optimistic( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test the sending of command in optimistic mode for a light supporting color mode.""" @@ -1560,8 +868,7 @@ async def test_sending_mqtt_commands_and_optimistic2( "schema": "json", "name": "test", "command_topic": "test_light_rgb/set", - "brightness": True, - "hs": True, + "supported_color_modes": ["hs"], } } } @@ -1623,7 +930,7 @@ async def test_sending_hs_color( "schema": "json", "name": "test", "command_topic": "test_light_rgb/set", - "rgb": True, + "supported_color_modes": ["rgb"], } } } @@ -1678,7 +985,6 @@ async def test_sending_rgb_color_no_brightness( { mqtt.DOMAIN: { light.DOMAIN: { - "color_mode": True, "command_topic": "test_light_rgb/set", "name": "test", "schema": "json", @@ -1761,8 +1067,8 @@ async def test_sending_rgb_color_no_brightness2( "schema": "json", "name": "test", "command_topic": "test_light_rgb/set", + "supported_color_modes": ["rgb"], "brightness": True, - "rgb": True, } } } @@ -1829,9 +1135,9 @@ async def test_sending_rgb_color_with_brightness( "schema": "json", "name": "test", "command_topic": "test_light_rgb/set", + "supported_color_modes": ["rgb"], "brightness": True, "brightness_scale": 100, - "rgb": True, } } } @@ -1899,9 +1205,7 @@ async def test_sending_rgb_color_with_scaled_brightness( "schema": "json", "name": "test", "command_topic": "test_light_rgb/set", - "brightness": True, "brightness_scale": 100, - "color_mode": True, "supported_color_modes": ["hs", "white"], "white_scale": 50, } @@ -1946,8 +1250,7 @@ async def test_sending_scaled_white( "schema": "json", "name": "test", "command_topic": "test_light_rgb/set", - "brightness": True, - "xy": True, + "supported_color_modes": ["xy"], } } } @@ -1973,7 +1276,7 @@ async def test_sending_xy_color( call( "test_light_rgb/set", JsonValidator( - '{"state": "ON", "color": {"x": 0.14, "y": 0.133},' + '{"state": "ON", "color": {"x": 0.123, "y": 0.123},' ' "brightness": 50}' ), 0, @@ -2190,7 +1493,7 @@ async def test_transition( "name": "test", "state_topic": "test_light_bright_scale", "command_topic": "test_light_bright_scale/set", - "brightness": True, + "supported_color_modes": ["brightness"], "brightness_scale": 99, } } @@ -2255,7 +1558,6 @@ async def test_brightness_scale( "command_topic": "test_light_bright_scale/set", "brightness": True, "brightness_scale": 99, - "color_mode": True, "supported_color_modes": ["hs", "white"], "white_scale": 50, } @@ -2315,8 +1617,7 @@ async def test_white_scale( "state_topic": "test_light_rgb", "command_topic": "test_light_rgb/set", "brightness": True, - "color_temp": True, - "rgb": True, + "supported_color_modes": ["hs", "color_temp"], "qos": "0", } } @@ -2349,62 +1650,64 @@ async def test_invalid_values( '{"state":"ON",' '"color":{"r":255,"g":255,"b":255},' '"brightness": 255,' + '"color_mode": "color_temp",' '"color_temp": 100,' '"effect": "rainbow"}', ) state = hass.states.get("light.test") assert state.state == STATE_ON - assert state.attributes.get("rgb_color") == (255, 255, 255) + # Color converttrd from color_temp to rgb + assert state.attributes.get("rgb_color") == (202, 218, 255) assert state.attributes.get("brightness") == 255 - assert state.attributes.get("color_temp_kelvin") is None + assert state.attributes.get("color_temp_kelvin") == 10000 # Empty color value async_fire_mqtt_message( hass, "test_light_rgb", - '{"state":"ON", "color":{}}', + '{"state":"ON", "color":{}, "color_mode": "rgb"}', ) # Color should not have changed state = hass.states.get("light.test") assert state.state == STATE_ON - assert state.attributes.get("rgb_color") == (255, 255, 255) + assert state.attributes.get("rgb_color") == (202, 218, 255) # Bad HS color values async_fire_mqtt_message( hass, "test_light_rgb", - '{"state":"ON", "color":{"h":"bad","s":"val"}}', + '{"state":"ON", "color":{"h":"bad","s":"val"}, "color_mode": "hs"}', ) # Color should not have changed state = hass.states.get("light.test") assert state.state == STATE_ON - assert state.attributes.get("rgb_color") == (255, 255, 255) + assert state.attributes.get("rgb_color") == (202, 218, 255) # Bad RGB color values async_fire_mqtt_message( hass, "test_light_rgb", - '{"state":"ON", "color":{"r":"bad","g":"val","b":"test"}}', + '{"state":"ON", "color":{"r":"bad","g":"val","b":"test"}, "color_mode": "rgb"}', ) # Color should not have changed state = hass.states.get("light.test") assert state.state == STATE_ON - assert state.attributes.get("rgb_color") == (255, 255, 255) + assert state.attributes.get("rgb_color") == (202, 218, 255) # Bad XY color values async_fire_mqtt_message( hass, "test_light_rgb", - '{"state":"ON", "color":{"x":"bad","y":"val"}}', + '{"state":"ON", "color":{"x":"bad","y":"val"}, "color_mode": "xy"}', ) # Color should not have changed state = hass.states.get("light.test") assert state.state == STATE_ON - assert state.attributes.get("rgb_color") == (255, 255, 255) + assert state.attributes.get("rgb_color") == (202, 218, 255) # Bad brightness values async_fire_mqtt_message( @@ -2418,7 +1721,9 @@ async def test_invalid_values( # Unset color and set a valid color temperature async_fire_mqtt_message( - hass, "test_light_rgb", '{"state":"ON", "color": null, "color_temp": 100}' + hass, + "test_light_rgb", + '{"state":"ON", "color": null, "color_temp": 100, "color_mode": "color_temp"}', ) state = hass.states.get("light.test") assert state.state == STATE_ON @@ -2426,11 +1731,14 @@ async def test_invalid_values( # Bad color temperature async_fire_mqtt_message( - hass, "test_light_rgb", '{"state":"ON", "color_temp": "badValue"}' + hass, + "test_light_rgb", + '{"state":"ON", "color_temp": "badValue", "color_mode": "color_temp"}', ) assert ( - "Invalid color temp value 'badValue' received for entity light.test" - in caplog.text + "Invalid or incomplete color value '{'state': 'ON', 'color_temp': " + "'badValue', 'color_mode': 'color_temp'}' " + "received for entity light.test" in caplog.text ) # Color temperature should not have changed @@ -2927,7 +2235,6 @@ async def test_setup_manual_entity_from_yaml( DEFAULT_CONFIG, ( { - "color_mode": True, "effect": True, "supported_color_modes": [ "color_temp",