From fe7ffe9519406b3490e9bccba2a56a634a5927d0 Mon Sep 17 00:00:00 2001 From: Avi Miller Date: Mon, 14 Nov 2022 03:59:47 +1100 Subject: [PATCH] Enable more customization of the LIFX pulse and color loop effects (#81699) --- homeassistant/components/lifx/manager.py | 41 +++++++++++++------ homeassistant/components/lifx/services.yaml | 44 ++++++++++++++++++--- tests/components/lifx/test_light.py | 17 +++++++- 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/lifx/manager.py b/homeassistant/components/lifx/manager.py index f91ed761e44..1eb80c0c5a2 100644 --- a/homeassistant/components/lifx/manager.py +++ b/homeassistant/components/lifx/manager.py @@ -42,15 +42,17 @@ SERVICE_EFFECT_MOVE = "effect_move" SERVICE_EFFECT_PULSE = "effect_pulse" SERVICE_EFFECT_STOP = "effect_stop" +ATTR_CHANGE = "change" +ATTR_CYCLES = "cycles" +ATTR_DIRECTION = "direction" +ATTR_PALETTE = "palette" +ATTR_PERIOD = "period" ATTR_POWER_OFF = "power_off" ATTR_POWER_ON = "power_on" -ATTR_PERIOD = "period" -ATTR_CYCLES = "cycles" -ATTR_SPREAD = "spread" -ATTR_CHANGE = "change" -ATTR_DIRECTION = "direction" +ATTR_SATURATION_MAX = "saturation_max" +ATTR_SATURATION_MIN = "saturation_min" ATTR_SPEED = "speed" -ATTR_PALETTE = "palette" +ATTR_SPREAD = "spread" EFFECT_FLAME = "FLAME" EFFECT_MORPH = "MORPH" @@ -72,8 +74,8 @@ EFFECT_MOVE_DIRECTIONS = [EFFECT_MOVE_DIRECTION_LEFT, EFFECT_MOVE_DIRECTION_RIGH PULSE_MODE_BLINK = "blink" PULSE_MODE_BREATHE = "breathe" PULSE_MODE_PING = "ping" -PULSE_MODE_STROBE = "strobe" PULSE_MODE_SOLID = "solid" +PULSE_MODE_STROBE = "strobe" PULSE_MODES = [ PULSE_MODE_BLINK, @@ -90,8 +92,8 @@ LIFX_EFFECT_SCHEMA = { LIFX_EFFECT_PULSE_SCHEMA = cv.make_entity_service_schema( { **LIFX_EFFECT_SCHEMA, - ATTR_BRIGHTNESS: VALID_BRIGHTNESS, - ATTR_BRIGHTNESS_PCT: VALID_BRIGHTNESS_PCT, + vol.Exclusive(ATTR_BRIGHTNESS, ATTR_BRIGHTNESS): VALID_BRIGHTNESS, + vol.Exclusive(ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_PCT, vol.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string, vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All( vol.Coerce(tuple), vol.ExactSequence((cv.byte, cv.byte, cv.byte)) @@ -121,8 +123,10 @@ LIFX_EFFECT_PULSE_SCHEMA = cv.make_entity_service_schema( LIFX_EFFECT_COLORLOOP_SCHEMA = cv.make_entity_service_schema( { **LIFX_EFFECT_SCHEMA, - ATTR_BRIGHTNESS: VALID_BRIGHTNESS, - ATTR_BRIGHTNESS_PCT: VALID_BRIGHTNESS_PCT, + vol.Exclusive(ATTR_BRIGHTNESS, ATTR_BRIGHTNESS): VALID_BRIGHTNESS, + vol.Exclusive(ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_PCT, + ATTR_SATURATION_MAX: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=100)), + ATTR_SATURATION_MIN: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=100)), ATTR_PERIOD: vol.All(vol.Coerce(float), vol.Clamp(min=0.05)), ATTR_CHANGE: vol.All(vol.Coerce(float), vol.Clamp(min=0, max=360)), ATTR_SPREAD: vol.All(vol.Coerce(float), vol.Clamp(min=0, max=360)), @@ -345,8 +349,21 @@ class LIFXManager: elif service == SERVICE_EFFECT_COLORLOOP: brightness = None + saturation_max = None + saturation_min = None + if ATTR_BRIGHTNESS in kwargs: brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS]) + elif ATTR_BRIGHTNESS_PCT in kwargs: + brightness = convert_8_to_16( + round(255 * kwargs[ATTR_BRIGHTNESS_PCT] / 100) + ) + + if ATTR_SATURATION_MAX in kwargs: + saturation_max = int(kwargs[ATTR_SATURATION_MAX] / 100 * 65535) + + if ATTR_SATURATION_MIN in kwargs: + saturation_min = int(kwargs[ATTR_SATURATION_MIN] / 100 * 65535) effect = aiolifx_effects.EffectColorloop( power_on=kwargs.get(ATTR_POWER_ON), @@ -355,6 +372,8 @@ class LIFXManager: spread=kwargs.get(ATTR_SPREAD), transition=kwargs.get(ATTR_TRANSITION), brightness=brightness, + saturation_max=saturation_max, + saturation_min=saturation_min, ) await self.effects_conductor.start(effect, bulbs) diff --git a/homeassistant/components/lifx/services.yaml b/homeassistant/components/lifx/services.yaml index 976d4ff5623..6613bb6a329 100644 --- a/homeassistant/components/lifx/services.yaml +++ b/homeassistant/components/lifx/services.yaml @@ -79,12 +79,20 @@ effect_pulse: - "strobe" - "solid" brightness: - name: Brightness - description: Number indicating brightness of the temporary color. + name: Brightness value + description: Number indicating brightness of the temporary color, where 1 is the minimum brightness and 255 is the maximum brightness supported by the light. selector: number: - min: 0 + min: 1 max: 255 + brightness_pct: + name: Brightness + description: Percentage indicating the brightness of the temporary color, where 1 is the minimum brightness and 100 is the maximum brightness supported by the light. + selector: + number: + min: 1 + max: 100 + unit_of_measurement: "%" color_name: name: Color name description: A human readable color name. @@ -131,12 +139,38 @@ effect_colorloop: domain: light fields: brightness: - name: Brightness - description: Number indicating brightness of the effect. Leave this out to maintain the current brightness of each participating light. + name: Brightness value + description: Number indicating brightness of the color loop, where 1 is the minimum brightness and 255 is the maximum brightness supported by the light. selector: number: min: 0 max: 255 + brightness_pct: + name: Brightness + description: Percentage indicating the brightness of the color loop, where 1 is the minimum brightness and 100 is the maximum brightness supported by the light. + selector: + number: + min: 0 + max: 100 + unit_of_measurement: "%" + saturation_min: + name: Minimum saturation + description: Percentage indicating the minimum saturation of the colors in the loop. + default: 80 + selector: + number: + min: 1 + max: 100 + unit_of_measurement: "%" + saturation_max: + name: Maximum saturation + description: Percentage indicating the maximum saturation of the colors in the loop. + default: 100 + selector: + number: + min: 1 + max: 100 + unit_of_measurement: "%" period: name: Period description: Duration between color changes. diff --git a/tests/components/lifx/test_light.py b/tests/components/lifx/test_light.py index 5a9b250034a..866095709ce 100644 --- a/tests/components/lifx/test_light.py +++ b/tests/components/lifx/test_light.py @@ -13,6 +13,8 @@ from homeassistant.components.lifx.light import ATTR_INFRARED, ATTR_ZONES from homeassistant.components.lifx.manager import ( ATTR_DIRECTION, ATTR_PALETTE, + ATTR_SATURATION_MAX, + ATTR_SATURATION_MIN, ATTR_SPEED, ATTR_THEME, SERVICE_EFFECT_COLORLOOP, @@ -1009,7 +1011,20 @@ async def test_color_light_with_temp( await hass.services.async_call( DOMAIN, SERVICE_EFFECT_COLORLOOP, - {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128}, + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS_PCT: 50, ATTR_SATURATION_MAX: 90}, + blocking=True, + ) + start_call = mock_effect_conductor.start.mock_calls + first_call = start_call[0][1] + assert isinstance(first_call[0], aiolifx_effects.EffectColorloop) + assert first_call[1][0] == bulb + mock_effect_conductor.start.reset_mock() + mock_effect_conductor.stop.reset_mock() + + await hass.services.async_call( + DOMAIN, + SERVICE_EFFECT_COLORLOOP, + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128, ATTR_SATURATION_MIN: 90}, blocking=True, ) start_call = mock_effect_conductor.start.mock_calls