mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[light] Replace rgb_order/is_rgbw/is_wrgb with channel_colors (#18474)
This commit is contained in:
@@ -3,7 +3,7 @@ light:
|
||||
id: led_matrix_32x8
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
num_leds: 256
|
||||
pin: ${pin}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ light:
|
||||
id: led_matrix_32x8
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
num_leds: 256
|
||||
pin: ${pin}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
light:
|
||||
- platform: beken_spi_led_strip
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
pin: P16
|
||||
num_leds: 30
|
||||
chipset: ws2812
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# The deprecated rgb_order / is_rgbw / is_wrgb keys, kept working until 2027.3.0.
|
||||
# Config-only, and only one strip because P16 is the sole supported pin.
|
||||
light:
|
||||
- platform: beken_spi_led_strip
|
||||
name: Legacy RGBW
|
||||
pin: P16
|
||||
num_leds: 30
|
||||
chipset: sk6812
|
||||
rgb_order: GRB
|
||||
is_rgbw: true # -> GRBW
|
||||
@@ -5,7 +5,7 @@ light:
|
||||
id: led_matrix_32x8
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
num_leds: 256
|
||||
pin: ${pin}
|
||||
effects:
|
||||
|
||||
@@ -5,7 +5,7 @@ light:
|
||||
id: led_matrix_32x8
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
num_leds: 256
|
||||
pin: ${pin}
|
||||
effects:
|
||||
|
||||
@@ -6,7 +6,7 @@ light:
|
||||
pin: 2
|
||||
pio: 0
|
||||
num_leds: 256
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
chipset: WS2812
|
||||
effects:
|
||||
- e131:
|
||||
|
||||
@@ -3,13 +3,13 @@ light:
|
||||
id: led_strip1
|
||||
pin: ${pin1}
|
||||
num_leds: 60
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
chipset: ws2812
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: led_strip2
|
||||
pin: ${pin2}
|
||||
num_leds: 60
|
||||
rgbw_order: RWGB
|
||||
channel_colors: RWGB
|
||||
bit0_high: 100us
|
||||
bit0_low: 100us
|
||||
bit1_high: 100us
|
||||
|
||||
@@ -8,14 +8,14 @@ light:
|
||||
id: led_strip1
|
||||
pin: ${pin1}
|
||||
num_leds: 60
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
chipset: ws2812
|
||||
use_dma: "true"
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: led_strip2
|
||||
pin: ${pin2}
|
||||
num_leds: 60
|
||||
rgb_order: RGB
|
||||
channel_colors: RGB
|
||||
bit0_high: 100us
|
||||
bit0_low: 100us
|
||||
bit1_high: 100us
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# The deprecated rgb_order / is_rgbw / is_wrgb keys, kept working until 2027.3.0.
|
||||
# Config-only: each strip below must migrate to the channel_colors shown in the comment.
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: legacy_rgb
|
||||
pin: GPIO13
|
||||
num_leds: 60
|
||||
chipset: ws2812
|
||||
rgb_order: GRB # -> GRB
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: legacy_rgbw
|
||||
pin: GPIO14
|
||||
num_leds: 60
|
||||
chipset: sk6812
|
||||
rgb_order: GRB
|
||||
is_rgbw: true # -> GRBW
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: legacy_wrgb
|
||||
pin: GPIO15
|
||||
num_leds: 60
|
||||
chipset: sk6812
|
||||
rgb_order: GRB
|
||||
is_wrgb: true # -> WGRB
|
||||
@@ -4,7 +4,7 @@ light:
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
num_leds: 256
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
pin: ${pin}
|
||||
- platform: partition
|
||||
name: Partition Light
|
||||
|
||||
@@ -4,7 +4,7 @@ light:
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
num_leds: 256
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
pin: ${pin}
|
||||
- platform: partition
|
||||
name: Partition Light
|
||||
|
||||
@@ -4,14 +4,14 @@ light:
|
||||
pin: 4
|
||||
num_leds: 60
|
||||
pio: 0
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
chipset: WS2812
|
||||
- platform: rp2040_pio_led_strip
|
||||
id: led_strip_custom_timings
|
||||
pin: 5
|
||||
num_leds: 60
|
||||
pio: 1
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
bit0_high: .1us
|
||||
bit0_low: 1.2us
|
||||
bit1_high: .69us
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# The deprecated rgb_order / is_rgbw keys, kept working until 2027.3.0.
|
||||
# Config-only: each strip below must migrate to the channel_colors shown in the comment.
|
||||
light:
|
||||
- platform: rp2040_pio_led_strip
|
||||
id: legacy_rgb
|
||||
pin: 4
|
||||
num_leds: 60
|
||||
pio: 0
|
||||
chipset: WS2812
|
||||
rgb_order: GRB # -> GRB
|
||||
- platform: rp2040_pio_led_strip
|
||||
id: legacy_rgbw
|
||||
pin: 5
|
||||
num_leds: 60
|
||||
pio: 1
|
||||
chipset: SK6812
|
||||
rgb_order: GRB
|
||||
is_rgbw: true # -> GRBW
|
||||
@@ -9,7 +9,7 @@ light:
|
||||
id: led_matrix_32x8
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
rgb_order: GRB
|
||||
channel_colors: GRB
|
||||
num_leds: 256
|
||||
pin: 2
|
||||
effects:
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
"""Tests for the shared addressable-strip channel order helpers."""
|
||||
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.const import CONF_CHANNEL_COLORS, CONF_IS_WRGB
|
||||
from esphome.components.light import (
|
||||
channel_colors_struct,
|
||||
migrate_channel_colors,
|
||||
validate_channel_colors,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_IS_RGBW, CONF_RGB_ORDER
|
||||
from esphome.types import ConfigType
|
||||
|
||||
NO_WHITE = "light::ChannelColors::NO_WHITE"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
("RGB", "RGB"),
|
||||
("grb", "GRB"),
|
||||
("BRG", "BRG"),
|
||||
("rgbw", "RGBW"),
|
||||
("WRGB", "WRGB"),
|
||||
("GWRB", "GWRB"),
|
||||
],
|
||||
)
|
||||
def test_validate_channel_colors(value: str, expected: str) -> None:
|
||||
assert validate_channel_colors(value) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
"RG", # missing a channel
|
||||
"RGBB", # duplicate channel
|
||||
"RRGB", # duplicate channel, correct length
|
||||
"RGBWW", # two white channels
|
||||
"RGBX", # unknown channel
|
||||
"RGBWX", # unknown channel, correct length
|
||||
"",
|
||||
],
|
||||
)
|
||||
def test_validate_channel_colors_rejects_invalid(value: str) -> None:
|
||||
with pytest.raises(cv.Invalid, match="is not a valid channel order"):
|
||||
validate_channel_colors(value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
("RGB", (0, 1, 2, NO_WHITE)),
|
||||
("GRB", (1, 0, 2, NO_WHITE)),
|
||||
("BRG", (1, 2, 0, NO_WHITE)),
|
||||
("RGBW", (0, 1, 2, 3)),
|
||||
("GRBW", (1, 0, 2, 3)),
|
||||
("WRGB", (1, 2, 3, 0)),
|
||||
("GWRB", (2, 0, 3, 1)),
|
||||
],
|
||||
)
|
||||
def test_channel_colors_struct(value: str, expected: tuple[int, int, int, int]) -> None:
|
||||
struct = channel_colors_struct(value)
|
||||
assert str(struct.base) == "light::ChannelColors"
|
||||
assert tuple(str(arg) for arg in struct.args.values()) == tuple(
|
||||
str(field) for field in expected
|
||||
)
|
||||
|
||||
|
||||
def _migrate(config: ConfigType) -> ConfigType:
|
||||
return migrate_channel_colors(removed_in="2027.3.0", component="test_strip")(config)
|
||||
|
||||
|
||||
def test_migrate_passes_through_channel_colors() -> None:
|
||||
config = {CONF_CHANNEL_COLORS: "GRBW"}
|
||||
assert _migrate(config) == {CONF_CHANNEL_COLORS: "GRBW"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("deprecated", "expected", "named"),
|
||||
[
|
||||
({}, "GRB", "'rgb_order' is"),
|
||||
(
|
||||
{CONF_IS_RGBW: False, CONF_IS_WRGB: False},
|
||||
"GRB",
|
||||
"'rgb_order', 'is_rgbw' and 'is_wrgb' are",
|
||||
),
|
||||
({CONF_IS_RGBW: True}, "GRBW", "'rgb_order' and 'is_rgbw' are"),
|
||||
({CONF_IS_WRGB: True}, "WGRB", "'rgb_order' and 'is_wrgb' are"),
|
||||
],
|
||||
)
|
||||
def test_migrate_folds_deprecated_keys(
|
||||
deprecated: ConfigType,
|
||||
expected: str,
|
||||
named: str,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
config = {CONF_RGB_ORDER: "GRB", "num_leds": 1, **deprecated}
|
||||
with caplog.at_level(logging.WARNING):
|
||||
result = _migrate(config)
|
||||
|
||||
assert result == {CONF_CHANNEL_COLORS: expected, "num_leds": 1}
|
||||
assert f"[test_strip] {named} deprecated" in caplog.text
|
||||
assert f"'{CONF_CHANNEL_COLORS}: {expected}'" in caplog.text
|
||||
assert "2027.3.0" in caplog.text
|
||||
|
||||
|
||||
def test_migrate_does_not_mutate_input() -> None:
|
||||
config = {CONF_RGB_ORDER: "GRB", CONF_IS_RGBW: True}
|
||||
_migrate(config)
|
||||
assert config == {CONF_RGB_ORDER: "GRB", CONF_IS_RGBW: True}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("deprecated", [CONF_RGB_ORDER, CONF_IS_RGBW, CONF_IS_WRGB])
|
||||
def test_migrate_rejects_mixing_old_and_new(deprecated: str) -> None:
|
||||
config = {CONF_CHANNEL_COLORS: "GRBW", deprecated: "GRB"}
|
||||
with pytest.raises(cv.Invalid, match=f"cannot be combined with '{deprecated}'"):
|
||||
_migrate(config)
|
||||
|
||||
|
||||
def test_migrate_reports_every_conflicting_key() -> None:
|
||||
config = {
|
||||
CONF_CHANNEL_COLORS: "GRBW",
|
||||
CONF_RGB_ORDER: "GRB",
|
||||
CONF_IS_RGBW: True,
|
||||
CONF_IS_WRGB: False,
|
||||
}
|
||||
with pytest.raises(
|
||||
cv.Invalid, match="cannot be combined with 'rgb_order', 'is_rgbw' and 'is_wrgb'"
|
||||
):
|
||||
_migrate(config)
|
||||
|
||||
|
||||
def test_migrate_requires_channel_colors() -> None:
|
||||
with pytest.raises(cv.Invalid, match=f"'{CONF_CHANNEL_COLORS}' is required"):
|
||||
_migrate({"num_leds": 1})
|
||||
|
||||
|
||||
def test_migrate_rejects_is_rgbw_with_is_wrgb() -> None:
|
||||
config = {CONF_RGB_ORDER: "GRB", CONF_IS_RGBW: True, CONF_IS_WRGB: True}
|
||||
with pytest.raises(cv.Invalid, match="cannot both be enabled"):
|
||||
_migrate(config)
|
||||
@@ -1,57 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from esphome.components.esp32_rmt_led_strip.light import (
|
||||
CONF_IS_WRGB,
|
||||
CONF_RGBW_ORDER,
|
||||
_split_rgbw_order,
|
||||
_validate_rgbw_order,
|
||||
_validate_rgbw_order_exclusivity,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_IS_RGBW
|
||||
|
||||
|
||||
def test_validate_rgbw_order() -> None:
|
||||
assert _validate_rgbw_order("rwgb") == "RWGB"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("rgbw_order", ["RGB", "RRGB", "RGBWW"])
|
||||
def test_validate_rgbw_order_rejects_invalid_order(rgbw_order: str) -> None:
|
||||
with pytest.raises(cv.Invalid, match="permutation of RGBW"):
|
||||
_validate_rgbw_order(rgbw_order)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("rgbw_order", "expected"),
|
||||
[
|
||||
("WRGB", ("RGB", 0)),
|
||||
("RWGB", ("RGB", 1)),
|
||||
("GWRB", ("GRB", 1)),
|
||||
("RGBW", ("RGB", 3)),
|
||||
],
|
||||
)
|
||||
def test_split_rgbw_order(rgbw_order: str, expected: tuple[str, int]) -> None:
|
||||
assert _split_rgbw_order(rgbw_order) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("conflict", [CONF_IS_RGBW, CONF_IS_WRGB])
|
||||
def test_rgbw_order_is_mutually_exclusive(conflict: str) -> None:
|
||||
with pytest.raises(cv.Invalid, match="cannot be used with"):
|
||||
_validate_rgbw_order_exclusivity(
|
||||
{
|
||||
CONF_RGBW_ORDER: "RGBW",
|
||||
CONF_IS_RGBW: conflict == CONF_IS_RGBW,
|
||||
CONF_IS_WRGB: conflict == CONF_IS_WRGB,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("legacy_option", [CONF_IS_RGBW, CONF_IS_WRGB])
|
||||
def test_rgbw_order_allows_disabled_legacy_options(legacy_option: str) -> None:
|
||||
config = {
|
||||
CONF_RGBW_ORDER: "RGBW",
|
||||
CONF_IS_RGBW: False,
|
||||
CONF_IS_WRGB: False,
|
||||
}
|
||||
config[legacy_option] = False
|
||||
assert _validate_rgbw_order_exclusivity(config) is config
|
||||
Reference in New Issue
Block a user