From abc7dcf6bf45f5266a350ee78b5d63f0054ebb77 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Dec 2021 06:45:34 -0600 Subject: [PATCH] Add zones support to flux_led (#61072) --- homeassistant/components/flux_led/const.py | 1 + homeassistant/components/flux_led/light.py | 39 +++++++++++++++++- .../components/flux_led/services.yaml | 41 +++++++++++++++++++ homeassistant/components/flux_led/util.py | 11 ++++- tests/components/flux_led/test_light.py | 32 +++++++++++++++ 5 files changed, 122 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/flux_led/const.py b/homeassistant/components/flux_led/const.py index be100e4141c..430c5a0e38a 100644 --- a/homeassistant/components/flux_led/const.py +++ b/homeassistant/components/flux_led/const.py @@ -62,6 +62,7 @@ TRANSITION_STROBE: Final = "strobe" CONF_COLORS: Final = "colors" CONF_SPEED_PCT: Final = "speed_pct" CONF_TRANSITION: Final = "transition" +CONF_EFFECT: Final = "effect" EFFECT_SPEED_SUPPORT_MODES: Final = {COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW} diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index 58d98bf2462..6bde2ec3b31 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -5,6 +5,7 @@ import ast import logging from typing import Any, Final +from flux_led.const import MultiColorEffects from flux_led.utils import ( color_temp_to_white_levels, rgbcw_brightness, @@ -43,6 +44,7 @@ from .const import ( CONF_CUSTOM_EFFECT_COLORS, CONF_CUSTOM_EFFECT_SPEED_PCT, CONF_CUSTOM_EFFECT_TRANSITION, + CONF_EFFECT, CONF_SPEED_PCT, CONF_TRANSITION, DEFAULT_EFFECT_SPEED, @@ -53,7 +55,12 @@ from .const import ( ) from .coordinator import FluxLedUpdateCoordinator from .entity import FluxOnOffEntity -from .util import _effect_brightness, _flux_color_mode_to_hass, _hass_color_modes +from .util import ( + _effect_brightness, + _flux_color_mode_to_hass, + _hass_color_modes, + _str_to_multi_color_effect, +) _LOGGER = logging.getLogger(__name__) @@ -73,6 +80,7 @@ COLOR_TEMP_WARM_VS_COLD_WHITE_CUT_OFF: Final = 285 EFFECT_CUSTOM: Final = "custom" SERVICE_CUSTOM_EFFECT: Final = "set_custom_effect" +SERVICE_SET_ZONES: Final = "set_zones" CUSTOM_EFFECT_DICT: Final = { vol.Required(CONF_COLORS): vol.All( @@ -88,6 +96,20 @@ CUSTOM_EFFECT_DICT: Final = { ), } +SET_ZONES_DICT: Final = { + vol.Required(CONF_COLORS): vol.All( + cv.ensure_list, + vol.Length(min=1, max=2048), + [vol.All(vol.Coerce(tuple), vol.ExactSequence((cv.byte, cv.byte, cv.byte)))], + ), + vol.Optional(CONF_SPEED_PCT, default=50): vol.All( + vol.Range(min=0, max=100), vol.Coerce(int) + ), + vol.Optional(CONF_EFFECT, default=MultiColorEffects.STATIC.name.lower()): vol.All( + cv.string, vol.In([effect.name.lower() for effect in MultiColorEffects]) + ), +} + async def async_setup_entry( hass: HomeAssistant, @@ -103,6 +125,11 @@ async def async_setup_entry( CUSTOM_EFFECT_DICT, "async_set_custom_effect", ) + platform.async_register_entity_service( + SERVICE_SET_ZONES, + SET_ZONES_DICT, + "async_set_zones", + ) options = entry.options try: @@ -293,3 +320,13 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): speed_pct, transition, ) + + async def async_set_zones( + self, colors: list[tuple[int, int, int]], speed_pct: int, effect: str + ) -> None: + """Set a colors for zones.""" + await self._device.async_set_zones( + colors, + speed_pct, + _str_to_multi_color_effect(effect), + ) diff --git a/homeassistant/components/flux_led/services.yaml b/homeassistant/components/flux_led/services.yaml index f1dae55560b..ff1e0679807 100644 --- a/homeassistant/components/flux_led/services.yaml +++ b/homeassistant/components/flux_led/services.yaml @@ -36,3 +36,44 @@ set_custom_effect: - "gradual" - "jump" - "strobe" +set_zones: + description: Set strip zones for Addressable v3 controllers (0xA3). + target: + entity: + integration: flux_led + domain: light + fields: + colors: + description: List of colors for each zone (RGB). The length of each zone is the number of pixels per segment divided by the number of colors. (Max 2048 Colors) + example: | + - [255,0,0] + - [0,255,0] + - [0,0,255] + - [255,255,255] + required: true + selector: + object: + speed_pct: + description: Effect speed for the custom effect (0-100) + example: 80 + default: 50 + required: false + selector: + number: + min: 1 + step: 1 + max: 100 + unit_of_measurement: "%" + effect: + description: Effect + example: 'running_water' + default: 'static' + required: false + selector: + select: + options: + - "static" + - "running_water" + - "strobe" + - "jump" + - "breathing" diff --git a/homeassistant/components/flux_led/util.py b/homeassistant/components/flux_led/util.py index 818b5c45506..8e1e387cafb 100644 --- a/homeassistant/components/flux_led/util.py +++ b/homeassistant/components/flux_led/util.py @@ -2,7 +2,7 @@ from __future__ import annotations from flux_led.aio import AIOWifiLedBulb -from flux_led.const import COLOR_MODE_DIM as FLUX_COLOR_MODE_DIM +from flux_led.const import COLOR_MODE_DIM as FLUX_COLOR_MODE_DIM, MultiColorEffects from homeassistant.components.light import ( COLOR_MODE_BRIGHTNESS, @@ -34,3 +34,12 @@ def _flux_color_mode_to_hass( def _effect_brightness(brightness: int) -> int: """Convert hass brightness to effect brightness.""" return round(brightness / 255 * 100) + + +def _str_to_multi_color_effect(effect_str: str) -> MultiColorEffects: + """Convert an multicolor effect string to MultiColorEffects.""" + for effect in MultiColorEffects: + if effect.name.lower() == effect_str: + return effect + # unreachable due to schema validation + assert False # pragma: no cover diff --git a/tests/components/flux_led/test_light.py b/tests/components/flux_led/test_light.py index 554a4eeedf8..cdf11a19a0b 100644 --- a/tests/components/flux_led/test_light.py +++ b/tests/components/flux_led/test_light.py @@ -10,6 +10,7 @@ from flux_led.const import ( COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW, COLOR_MODE_RGBWW as FLUX_COLOR_MODE_RGBWW, COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W, + MultiColorEffects, ) import pytest @@ -19,6 +20,7 @@ from homeassistant.components.flux_led.const import ( CONF_CUSTOM_EFFECT_COLORS, CONF_CUSTOM_EFFECT_SPEED_PCT, CONF_CUSTOM_EFFECT_TRANSITION, + CONF_EFFECT, CONF_SPEED_PCT, CONF_TRANSITION, DOMAIN, @@ -1119,6 +1121,36 @@ async def test_rgb_light_custom_effect_via_service( ) bulb.async_set_custom_pattern.reset_mock() + await hass.services.async_call( + DOMAIN, + "set_zones", + { + ATTR_ENTITY_ID: entity_id, + CONF_COLORS: [[0, 0, 255], [255, 0, 0]], + CONF_EFFECT: "running_water", + }, + blocking=True, + ) + bulb.async_set_zones.assert_called_with( + [(0, 0, 255), (255, 0, 0)], 50, MultiColorEffects.RUNNING_WATER + ) + bulb.async_set_zones.reset_mock() + + await hass.services.async_call( + DOMAIN, + "set_zones", + { + ATTR_ENTITY_ID: entity_id, + CONF_COLORS: [[0, 0, 255], [255, 0, 0]], + CONF_SPEED_PCT: 30, + }, + blocking=True, + ) + bulb.async_set_zones.assert_called_with( + [(0, 0, 255), (255, 0, 0)], 30, MultiColorEffects.STATIC + ) + bulb.async_set_zones.reset_mock() + async def test_addressable_light(hass: HomeAssistant) -> None: """Test an addressable light."""