mirror of
https://github.com/esphome/esphome.git
synced 2026-10-05 10:31:29 +00:00
[light] Add color option to strobe and other color state (#19874)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
Claude Sonnet 5
J. Nick Koston
parent
0ed997da92
commit
bfb7e76b2e
@@ -0,0 +1,22 @@
|
||||
esphome:
|
||||
name: test-color-wipe-color
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
light:
|
||||
- platform: fastled_clockless
|
||||
id: wipe_light
|
||||
chipset: WS2811
|
||||
pin: GPIO13
|
||||
num_leds: 10
|
||||
effects:
|
||||
- addressable_color_wipe:
|
||||
name: Wipe
|
||||
colors:
|
||||
- color: darkred
|
||||
num_leds: 1
|
||||
- red: 50%
|
||||
num_leds: 1
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Regression test: addressable_color_wipe's `color:` must scale by color_brightness.
|
||||
|
||||
The per-item schema tests in test_effect_color.py only check the validated config
|
||||
dict; they would still pass if addressable_color_wipe_effect_to_code dropped
|
||||
color_brightness on the floor (as it originally did), since a dark color's red/green/
|
||||
blue are already peak-normalized to 1.0 by the time codegen sees them. Generate the
|
||||
actual C++ and check the AddressableColorWipeEffectColor byte values instead, so a
|
||||
regression in the scaling multiplication itself is caught.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def main_cpp(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> str:
|
||||
return generate_main(
|
||||
component_config_path("addressable_color_wipe_color_test.yaml")
|
||||
)
|
||||
|
||||
|
||||
def test_dark_color_name_is_scaled_by_color_brightness(main_cpp: str) -> None:
|
||||
# color: darkred (0x8B0000) normalizes to r=1.0 but color_brightness=0x8B/0xFF,
|
||||
# so the byte value must come back down to 0x8B (139), not 255.
|
||||
assert ".r = 139," in main_cpp
|
||||
assert ".g = 0," in main_cpp
|
||||
assert ".b = 0," in main_cpp
|
||||
|
||||
|
||||
def test_explicit_channel_is_unaffected(main_cpp: str) -> None:
|
||||
# red: 50% with no color: or color_brightness: set must still scale by the
|
||||
# default color_brightness of 1.0, i.e. come out as plain 50% of 255.
|
||||
assert ".r = 128," in main_cpp
|
||||
@@ -0,0 +1,69 @@
|
||||
"""Tests for the `color` option in per-item effect color lists."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import config_validation as cv
|
||||
from esphome.components.light.effects import (
|
||||
ADDRESSABLE_COLOR_WIPE_COLOR_SCHEMA,
|
||||
STROBE_COLOR_SCHEMA,
|
||||
)
|
||||
from esphome.const import CONF_BLUE, CONF_COLOR_BRIGHTNESS, CONF_GREEN, CONF_RED
|
||||
|
||||
|
||||
def test_strobe_plain_defaults_full_level() -> None:
|
||||
result = STROBE_COLOR_SCHEMA({"duration": "1s"})
|
||||
assert (result[CONF_RED], result[CONF_GREEN], result[CONF_BLUE]) == (1.0, 1.0, 1.0)
|
||||
assert result[CONF_COLOR_BRIGHTNESS] == 1.0
|
||||
|
||||
|
||||
def test_strobe_color_name() -> None:
|
||||
result = STROBE_COLOR_SCHEMA({"duration": "1s", "color": "darkred"})
|
||||
assert "color" not in result
|
||||
assert (result[CONF_RED], result[CONF_GREEN], result[CONF_BLUE]) == (1.0, 0.0, 0.0)
|
||||
assert result[CONF_COLOR_BRIGHTNESS] == pytest.approx(0x8B / 0xFF)
|
||||
|
||||
|
||||
def test_strobe_hex_color() -> None:
|
||||
result = STROBE_COLOR_SCHEMA({"duration": "1s", "color": "0x223344"})
|
||||
assert result[CONF_BLUE] == 1.0
|
||||
assert result[CONF_COLOR_BRIGHTNESS] == pytest.approx(0x44 / 0xFF)
|
||||
|
||||
|
||||
def test_strobe_explicit_channels_unaffected() -> None:
|
||||
result = STROBE_COLOR_SCHEMA({"duration": "1s", "red": "50%"})
|
||||
assert result[CONF_RED] == 0.5
|
||||
assert (result[CONF_GREEN], result[CONF_BLUE]) == (1.0, 1.0)
|
||||
assert result[CONF_COLOR_BRIGHTNESS] == 1.0
|
||||
|
||||
|
||||
def test_strobe_color_conflicts_with_rgb() -> None:
|
||||
with pytest.raises(cv.Invalid, match="cannot be used with"):
|
||||
STROBE_COLOR_SCHEMA({"duration": "1s", "color": "red", "red": "10%"})
|
||||
|
||||
|
||||
def test_addressable_color_wipe_plain_defaults_full_level() -> None:
|
||||
result = ADDRESSABLE_COLOR_WIPE_COLOR_SCHEMA({"num_leds": 1})
|
||||
assert (result[CONF_RED], result[CONF_GREEN], result[CONF_BLUE]) == (1.0, 1.0, 1.0)
|
||||
assert result[CONF_COLOR_BRIGHTNESS] == 1.0
|
||||
|
||||
|
||||
def test_addressable_color_wipe_color_name() -> None:
|
||||
result = ADDRESSABLE_COLOR_WIPE_COLOR_SCHEMA({"num_leds": 1, "color": "darkred"})
|
||||
assert "color" not in result
|
||||
assert (result[CONF_RED], result[CONF_GREEN], result[CONF_BLUE]) == (1.0, 0.0, 0.0)
|
||||
assert result[CONF_COLOR_BRIGHTNESS] == pytest.approx(0x8B / 0xFF)
|
||||
|
||||
|
||||
def test_addressable_color_wipe_explicit_channels_unaffected() -> None:
|
||||
result = ADDRESSABLE_COLOR_WIPE_COLOR_SCHEMA({"num_leds": 1, "red": "50%"})
|
||||
assert result[CONF_RED] == 0.5
|
||||
assert result[CONF_COLOR_BRIGHTNESS] == 1.0
|
||||
|
||||
|
||||
def test_addressable_color_wipe_color_conflicts_with_rgb() -> None:
|
||||
with pytest.raises(cv.Invalid, match="cannot be used with"):
|
||||
ADDRESSABLE_COLOR_WIPE_COLOR_SCHEMA(
|
||||
{"num_leds": 1, "color": "blue", "blue": "10%"}
|
||||
)
|
||||
@@ -21,6 +21,8 @@ light:
|
||||
green: 0%
|
||||
blue: 0%
|
||||
num_leds: 1
|
||||
- color: darkred
|
||||
num_leds: 1
|
||||
add_led_interval: 100ms
|
||||
reverse: false
|
||||
- addressable_scan:
|
||||
|
||||
@@ -429,6 +429,10 @@ light:
|
||||
blue: 0%
|
||||
duration: 1s
|
||||
transition_length: 500ms
|
||||
- state: true
|
||||
color: darkred
|
||||
duration: 1s
|
||||
transition_length: 500ms
|
||||
- platform: partition
|
||||
id: test_addressable_transition
|
||||
name: Addressable Transition Test
|
||||
|
||||
Reference in New Issue
Block a user