diff --git a/homeassistant/components/lifx/util.py b/homeassistant/components/lifx/util.py index 135e1a7e8e9..6a9bff465ee 100644 --- a/homeassistant/components/lifx/util.py +++ b/homeassistant/components/lifx/util.py @@ -14,8 +14,11 @@ from awesomeversion import AwesomeVersion from homeassistant.components.light import ( ATTR_BRIGHTNESS, + ATTR_BRIGHTNESS_PCT, + ATTR_COLOR_NAME, ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, + ATTR_KELVIN, ATTR_RGB_COLOR, ATTR_XY_COLOR, ) @@ -24,7 +27,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr import homeassistant.util.color as color_util -from .const import DOMAIN, INFRARED_BRIGHTNESS_VALUES_MAP, OVERALL_TIMEOUT +from .const import _LOGGER, DOMAIN, INFRARED_BRIGHTNESS_VALUES_MAP, OVERALL_TIMEOUT FIX_MAC_FW = AwesomeVersion("3.70") @@ -80,6 +83,17 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | """ hue, saturation, brightness, kelvin = [None] * 4 + if (color_name := kwargs.get(ATTR_COLOR_NAME)) is not None: + try: + hue, saturation = color_util.color_RGB_to_hs( + *color_util.color_name_to_rgb(color_name) + ) + except ValueError: + _LOGGER.warning( + "Got unknown color %s, falling back to neutral white", color_name + ) + hue, saturation = (0, 0) + if ATTR_HS_COLOR in kwargs: hue, saturation = kwargs[ATTR_HS_COLOR] elif ATTR_RGB_COLOR in kwargs: @@ -93,6 +107,13 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | saturation = int(saturation / 100 * 65535) kelvin = 3500 + if ATTR_KELVIN in kwargs: + _LOGGER.warning( + "The 'kelvin' parameter is deprecated. Please use 'color_temp_kelvin' for all service calls" + ) + kelvin = kwargs.pop(ATTR_KELVIN) + saturation = 0 + if ATTR_COLOR_TEMP_KELVIN in kwargs: kelvin = kwargs.pop(ATTR_COLOR_TEMP_KELVIN) saturation = 0 @@ -100,6 +121,9 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | if ATTR_BRIGHTNESS in kwargs: brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS]) + if ATTR_BRIGHTNESS_PCT in kwargs: + brightness = convert_8_to_16(round(255 * kwargs[ATTR_BRIGHTNESS_PCT] / 100)) + hsbk = [hue, saturation, brightness, kelvin] return None if hsbk == [None] * 4 else hsbk diff --git a/tests/components/lifx/test_light.py b/tests/components/lifx/test_light.py index 6fe63b14b6a..5a9b250034a 100644 --- a/tests/components/lifx/test_light.py +++ b/tests/components/lifx/test_light.py @@ -21,11 +21,14 @@ from homeassistant.components.lifx.manager import ( ) from homeassistant.components.light import ( ATTR_BRIGHTNESS, + ATTR_BRIGHTNESS_PCT, ATTR_COLOR_MODE, + ATTR_COLOR_NAME, ATTR_COLOR_TEMP, ATTR_COLOR_TEMP_KELVIN, ATTR_EFFECT, ATTR_HS_COLOR, + ATTR_KELVIN, ATTR_RGB_COLOR, ATTR_SUPPORTED_COLOR_MODES, ATTR_TRANSITION, @@ -1397,6 +1400,131 @@ async def test_transitions_color_bulb(hass: HomeAssistant) -> None: bulb.set_color.reset_mock() +async def test_lifx_set_state_color(hass: HomeAssistant) -> None: + """Test lifx.set_state works with color names and RGB.""" + config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb_new_firmware() + bulb.power_level = 65535 + bulb.color = [32000, None, 32000, 2700] + with _patch_discovery(device=bulb), _patch_config_flow_try_connect( + device=bulb + ), _patch_device(device=bulb): + await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}}) + await hass.async_block_till_done() + + entity_id = "light.my_bulb" + + # brightness should convert from 8 to 16 bits + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 255}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, None, 65535, 2700] + bulb.set_color.reset_mock() + + # brightness_pct should convert into 16 bit + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS_PCT: 90}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, None, 59110, 2700] + bulb.set_color.reset_mock() + + # color name should turn into hue, saturation + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_NAME: "red", ATTR_BRIGHTNESS_PCT: 100}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [0, 65535, 65535, 3500] + bulb.set_color.reset_mock() + + # unknown color name should reset back to neutral white, i.e. 3500K + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_NAME: "deepblack"}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [0, 0, 32000, 3500] + bulb.set_color.reset_mock() + + # RGB should convert to hue, saturation + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_RGB_COLOR: (0, 255, 0)}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [21845, 65535, 32000, 3500] + bulb.set_color.reset_mock() + + # XY should convert to hue, saturation + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_XY_COLOR: (0.34, 0.339)}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [5461, 5139, 32000, 3500] + bulb.set_color.reset_mock() + + +async def test_lifx_set_state_kelvin(hass: HomeAssistant) -> None: + """Test set_state works with old and new kelvin parameter names.""" + already_migrated_config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL + ) + already_migrated_config_entry.add_to_hass(hass) + bulb = _mocked_bulb_new_firmware() + bulb.power_level = 65535 + bulb.color = [32000, None, 32000, 6000] + with _patch_discovery(device=bulb), _patch_config_flow_try_connect( + device=bulb + ), _patch_device(device=bulb): + await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}}) + await hass.async_block_till_done() + + entity_id = "light.my_bulb" + + state = hass.states.get(entity_id) + assert state.state == "on" + attributes = state.attributes + assert attributes[ATTR_BRIGHTNESS] == 125 + assert attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP + await hass.services.async_call( + LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + assert bulb.set_power.calls[0][0][0] is False + bulb.set_power.reset_mock() + + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 255, ATTR_KELVIN: 3500}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, 0, 65535, 3500] + bulb.set_color.reset_mock() + + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100, ATTR_COLOR_TEMP_KELVIN: 2700}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, 0, 25700, 2700] + bulb.set_color.reset_mock() + + async def test_infrared_color_bulb(hass: HomeAssistant) -> None: """Test setting infrared with a color bulb.""" already_migrated_config_entry = MockConfigEntry(