From 556e40add58cb3fd009e4b976870ee804204f895 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 22 Sep 2023 13:31:29 +0200 Subject: [PATCH] Fix mqtt light rgbww update without state topic (#100707) * Fix mqtt light rgbww update without state topic * Add @callback decprator and correct mired conv --- .../components/mqtt/light/schema_basic.py | 18 +- tests/components/mqtt/test_light.py | 171 ++++++++++++++++++ 2 files changed, 188 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 2a726075bb0da2..9a1600f5865d8e 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -462,6 +462,7 @@ def brightness_received(msg: ReceiveMessage) -> None: add_topic(CONF_BRIGHTNESS_STATE_TOPIC, brightness_received) + @callback def _rgbx_received( msg: ReceiveMessage, template: str, @@ -532,11 +533,26 @@ def rgbw_received(msg: ReceiveMessage) -> None: @log_messages(self.hass, self.entity_id) def rgbww_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for RGBWW.""" + + @callback + def _converter( + r: int, g: int, b: int, cw: int, ww: int + ) -> tuple[int, int, int]: + min_kelvin = color_util.color_temperature_mired_to_kelvin( + self.max_mireds + ) + max_kelvin = color_util.color_temperature_mired_to_kelvin( + self.min_mireds + ) + return color_util.color_rgbww_to_rgb( + r, g, b, cw, ww, min_kelvin, max_kelvin + ) + rgbww = _rgbx_received( msg, CONF_RGBWW_VALUE_TEMPLATE, ColorMode.RGBWW, - color_util.color_rgbww_to_rgb, + _converter, ) if rgbww is None: return diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 08def9a923ee6c..133f38c1a56f41 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -198,6 +198,7 @@ from homeassistant.core import HomeAssistant, State from .test_common import ( + help_custom_config, help_test_availability_when_connection_lost, help_test_availability_without_topic, help_test_custom_availability_payload, @@ -441,6 +442,176 @@ async def test_controlling_state_via_topic( assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes +@pytest.mark.parametrize( + "hass_config", + [ + help_custom_config( + light.DOMAIN, + DEFAULT_CONFIG, + ( + { + "state_topic": "test-topic", + "optimistic": True, + "brightness_command_topic": "test_light_rgb/brightness/set", + "color_mode_state_topic": "color-mode-state-topic", + "rgb_command_topic": "test_light_rgb/rgb/set", + "rgb_state_topic": "rgb-state-topic", + "rgbw_command_topic": "test_light_rgb/rgbw/set", + "rgbw_state_topic": "rgbw-state-topic", + "rgbww_command_topic": "test_light_rgb/rgbww/set", + "rgbww_state_topic": "rgbww-state-topic", + }, + ), + ) + ], +) +async def test_received_rgbx_values_set_state_optimistic( + hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator +) -> None: + """Test the state is set correctly when an rgbx update is received.""" + await mqtt_mock_entry() + state = hass.states.get("light.test") + assert state and state.state is not None + async_fire_mqtt_message(hass, "test-topic", "ON") + ## Test rgb processing + async_fire_mqtt_message(hass, "rgb-state-topic", "255,255,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgb" + assert state.attributes["rgb_color"] == (255, 255, 255) + + # Only update color mode + async_fire_mqtt_message(hass, "color-mode-state-topic", "rgbww") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbww" + + # Resending same rgb value should restore color mode + async_fire_mqtt_message(hass, "rgb-state-topic", "255,255,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgb" + assert state.attributes["rgb_color"] == (255, 255, 255) + + # Only update brightness + await common.async_turn_on(hass, "light.test", brightness=128) + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 128 + assert state.attributes["color_mode"] == "rgb" + assert state.attributes["rgb_color"] == (255, 255, 255) + + # Resending same rgb value should restore brightness + async_fire_mqtt_message(hass, "rgb-state-topic", "255,255,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgb" + assert state.attributes["rgb_color"] == (255, 255, 255) + + # Only change rgb value + async_fire_mqtt_message(hass, "rgb-state-topic", "255,255,0") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgb" + assert state.attributes["rgb_color"] == (255, 255, 0) + + ## Test rgbw processing + async_fire_mqtt_message(hass, "rgbw-state-topic", "255,255,255,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbw" + assert state.attributes["rgbw_color"] == (255, 255, 255, 255) + + # Only update color mode + async_fire_mqtt_message(hass, "color-mode-state-topic", "rgb") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgb" + + # Resending same rgbw value should restore color mode + async_fire_mqtt_message(hass, "rgbw-state-topic", "255,255,255,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbw" + assert state.attributes["rgbw_color"] == (255, 255, 255, 255) + + # Only update brightness + await common.async_turn_on(hass, "light.test", brightness=128) + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 128 + assert state.attributes["color_mode"] == "rgbw" + assert state.attributes["rgbw_color"] == (255, 255, 255, 255) + + # Resending same rgbw value should restore brightness + async_fire_mqtt_message(hass, "rgbw-state-topic", "255,255,255,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbw" + assert state.attributes["rgbw_color"] == (255, 255, 255, 255) + + # Only change rgbw value + async_fire_mqtt_message(hass, "rgbw-state-topic", "255,255,128,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbw" + assert state.attributes["rgbw_color"] == (255, 255, 128, 255) + + ## Test rgbww processing + async_fire_mqtt_message(hass, "rgbww-state-topic", "255,255,255,32,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbww" + assert state.attributes["rgbww_color"] == (255, 255, 255, 32, 255) + + # Only update color mode + async_fire_mqtt_message(hass, "color-mode-state-topic", "rgb") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgb" + + # Resending same rgbw value should restore color mode + async_fire_mqtt_message(hass, "rgbww-state-topic", "255,255,255,32,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbww" + assert state.attributes["rgbww_color"] == (255, 255, 255, 32, 255) + + # Only update brightness + await common.async_turn_on(hass, "light.test", brightness=128) + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 128 + assert state.attributes["color_mode"] == "rgbww" + assert state.attributes["rgbww_color"] == (255, 255, 255, 32, 255) + + # Resending same rgbww value should restore brightness + async_fire_mqtt_message(hass, "rgbww-state-topic", "255,255,255,32,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbww" + assert state.attributes["rgbww_color"] == (255, 255, 255, 32, 255) + + # Only change rgbww value + async_fire_mqtt_message(hass, "rgbww-state-topic", "255,255,128,32,255") + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert state.attributes["brightness"] == 255 + assert state.attributes["color_mode"] == "rgbww" + assert state.attributes["rgbww_color"] == (255, 255, 128, 32, 255) + + @pytest.mark.parametrize( "hass_config", [