Add zones support to flux_led (#61072)
This commit is contained in:
parent
3d75befe0a
commit
abc7dcf6bf
5 changed files with 122 additions and 2 deletions
|
@ -62,6 +62,7 @@ TRANSITION_STROBE: Final = "strobe"
|
||||||
CONF_COLORS: Final = "colors"
|
CONF_COLORS: Final = "colors"
|
||||||
CONF_SPEED_PCT: Final = "speed_pct"
|
CONF_SPEED_PCT: Final = "speed_pct"
|
||||||
CONF_TRANSITION: Final = "transition"
|
CONF_TRANSITION: Final = "transition"
|
||||||
|
CONF_EFFECT: Final = "effect"
|
||||||
|
|
||||||
|
|
||||||
EFFECT_SPEED_SUPPORT_MODES: Final = {COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW}
|
EFFECT_SPEED_SUPPORT_MODES: Final = {COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ast
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Final
|
from typing import Any, Final
|
||||||
|
|
||||||
|
from flux_led.const import MultiColorEffects
|
||||||
from flux_led.utils import (
|
from flux_led.utils import (
|
||||||
color_temp_to_white_levels,
|
color_temp_to_white_levels,
|
||||||
rgbcw_brightness,
|
rgbcw_brightness,
|
||||||
|
@ -43,6 +44,7 @@ from .const import (
|
||||||
CONF_CUSTOM_EFFECT_COLORS,
|
CONF_CUSTOM_EFFECT_COLORS,
|
||||||
CONF_CUSTOM_EFFECT_SPEED_PCT,
|
CONF_CUSTOM_EFFECT_SPEED_PCT,
|
||||||
CONF_CUSTOM_EFFECT_TRANSITION,
|
CONF_CUSTOM_EFFECT_TRANSITION,
|
||||||
|
CONF_EFFECT,
|
||||||
CONF_SPEED_PCT,
|
CONF_SPEED_PCT,
|
||||||
CONF_TRANSITION,
|
CONF_TRANSITION,
|
||||||
DEFAULT_EFFECT_SPEED,
|
DEFAULT_EFFECT_SPEED,
|
||||||
|
@ -53,7 +55,12 @@ from .const import (
|
||||||
)
|
)
|
||||||
from .coordinator import FluxLedUpdateCoordinator
|
from .coordinator import FluxLedUpdateCoordinator
|
||||||
from .entity import FluxOnOffEntity
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -73,6 +80,7 @@ COLOR_TEMP_WARM_VS_COLD_WHITE_CUT_OFF: Final = 285
|
||||||
EFFECT_CUSTOM: Final = "custom"
|
EFFECT_CUSTOM: Final = "custom"
|
||||||
|
|
||||||
SERVICE_CUSTOM_EFFECT: Final = "set_custom_effect"
|
SERVICE_CUSTOM_EFFECT: Final = "set_custom_effect"
|
||||||
|
SERVICE_SET_ZONES: Final = "set_zones"
|
||||||
|
|
||||||
CUSTOM_EFFECT_DICT: Final = {
|
CUSTOM_EFFECT_DICT: Final = {
|
||||||
vol.Required(CONF_COLORS): vol.All(
|
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(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -103,6 +125,11 @@ async def async_setup_entry(
|
||||||
CUSTOM_EFFECT_DICT,
|
CUSTOM_EFFECT_DICT,
|
||||||
"async_set_custom_effect",
|
"async_set_custom_effect",
|
||||||
)
|
)
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_SET_ZONES,
|
||||||
|
SET_ZONES_DICT,
|
||||||
|
"async_set_zones",
|
||||||
|
)
|
||||||
options = entry.options
|
options = entry.options
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -293,3 +320,13 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity):
|
||||||
speed_pct,
|
speed_pct,
|
||||||
transition,
|
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),
|
||||||
|
)
|
||||||
|
|
|
@ -36,3 +36,44 @@ set_custom_effect:
|
||||||
- "gradual"
|
- "gradual"
|
||||||
- "jump"
|
- "jump"
|
||||||
- "strobe"
|
- "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"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from flux_led.aio import AIOWifiLedBulb
|
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 (
|
from homeassistant.components.light import (
|
||||||
COLOR_MODE_BRIGHTNESS,
|
COLOR_MODE_BRIGHTNESS,
|
||||||
|
@ -34,3 +34,12 @@ def _flux_color_mode_to_hass(
|
||||||
def _effect_brightness(brightness: int) -> int:
|
def _effect_brightness(brightness: int) -> int:
|
||||||
"""Convert hass brightness to effect brightness."""
|
"""Convert hass brightness to effect brightness."""
|
||||||
return round(brightness / 255 * 100)
|
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
|
||||||
|
|
|
@ -10,6 +10,7 @@ from flux_led.const import (
|
||||||
COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW,
|
COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW,
|
||||||
COLOR_MODE_RGBWW as FLUX_COLOR_MODE_RGBWW,
|
COLOR_MODE_RGBWW as FLUX_COLOR_MODE_RGBWW,
|
||||||
COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W,
|
COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W,
|
||||||
|
MultiColorEffects,
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ from homeassistant.components.flux_led.const import (
|
||||||
CONF_CUSTOM_EFFECT_COLORS,
|
CONF_CUSTOM_EFFECT_COLORS,
|
||||||
CONF_CUSTOM_EFFECT_SPEED_PCT,
|
CONF_CUSTOM_EFFECT_SPEED_PCT,
|
||||||
CONF_CUSTOM_EFFECT_TRANSITION,
|
CONF_CUSTOM_EFFECT_TRANSITION,
|
||||||
|
CONF_EFFECT,
|
||||||
CONF_SPEED_PCT,
|
CONF_SPEED_PCT,
|
||||||
CONF_TRANSITION,
|
CONF_TRANSITION,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
@ -1119,6 +1121,36 @@ async def test_rgb_light_custom_effect_via_service(
|
||||||
)
|
)
|
||||||
bulb.async_set_custom_pattern.reset_mock()
|
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:
|
async def test_addressable_light(hass: HomeAssistant) -> None:
|
||||||
"""Test an addressable light."""
|
"""Test an addressable light."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue