mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[esp32_rmt_led_strip] Add RGBW channel ordering (#18028)
This commit is contained in:
@@ -255,11 +255,11 @@ light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index
|
||||
break;
|
||||
}
|
||||
uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
|
||||
uint8_t white = this->is_wrgb_ ? 0 : 3;
|
||||
uint8_t white = this->is_wrgb_ ? 0 : this->white_index_;
|
||||
|
||||
return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
|
||||
this->buf_ + (index * multiplier) + g + this->is_wrgb_,
|
||||
this->buf_ + (index * multiplier) + b + this->is_wrgb_,
|
||||
return {this->buf_ + (index * multiplier) + r + (white <= r),
|
||||
this->buf_ + (index * multiplier) + g + (white <= g),
|
||||
this->buf_ + (index * multiplier) + b + (white <= b),
|
||||
this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
|
||||
&this->effect_data_[index],
|
||||
&this->correction_};
|
||||
@@ -295,11 +295,22 @@ void ESP32RMTLEDStripLightOutput::dump_config() {
|
||||
rgb_order = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
if (this->is_rgbw_ || this->is_wrgb_) {
|
||||
char rgbw_order[5];
|
||||
uint8_t white = this->is_wrgb_ ? 0 : this->white_index_;
|
||||
uint8_t rgb_index = 0;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
rgbw_order[i] = i == white ? 'W' : rgb_order[rgb_index++];
|
||||
}
|
||||
rgbw_order[4] = '\0';
|
||||
ESP_LOGCONFIG(TAG, " RGBW Order: %s", rgbw_order);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" RGB Order: %s\n"
|
||||
" Max refresh rate: %" PRIu32 "\n"
|
||||
" Number of LEDs: %u",
|
||||
rgb_order, this->max_refresh_rate_.value_or(0), this->num_leds_);
|
||||
this->max_refresh_rate_.value_or(0), this->num_leds_);
|
||||
}
|
||||
|
||||
float ESP32RMTLEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
@@ -52,6 +52,11 @@ class ESP32RMTLEDStripLightOutput final : public light::AddressableLight {
|
||||
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
|
||||
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
|
||||
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
|
||||
void set_rgbw_order(uint8_t white_index) {
|
||||
this->is_rgbw_ = true;
|
||||
this->is_wrgb_ = false;
|
||||
this->white_index_ = white_index;
|
||||
}
|
||||
void set_use_dma(bool use_dma) { this->use_dma_ = use_dma; }
|
||||
void set_use_psram(bool use_psram) { this->use_psram_ = use_psram; }
|
||||
|
||||
@@ -91,6 +96,8 @@ class ESP32RMTLEDStripLightOutput final : public light::AddressableLight {
|
||||
uint16_t num_leds_;
|
||||
bool is_rgbw_{false};
|
||||
bool is_wrgb_{false};
|
||||
// An index after the RGB channels makes offset adjustment a no-op for three-channel strips.
|
||||
uint8_t white_index_{3};
|
||||
bool use_dma_{false};
|
||||
bool use_psram_{false};
|
||||
bool invert_out_{false};
|
||||
|
||||
@@ -20,6 +20,7 @@ from esphome.const import (
|
||||
CONF_RMT_SYMBOLS,
|
||||
CONF_USE_DMA,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -62,6 +63,7 @@ CHIPSETS = {
|
||||
}
|
||||
|
||||
CONF_IS_WRGB = "is_wrgb"
|
||||
CONF_RGBW_ORDER = "rgbw_order"
|
||||
CONF_BIT0_HIGH = "bit0_high"
|
||||
CONF_BIT0_LOW = "bit0_low"
|
||||
CONF_BIT1_HIGH = "bit1_high"
|
||||
@@ -70,6 +72,26 @@ CONF_RESET_HIGH = "reset_high"
|
||||
CONF_RESET_LOW = "reset_low"
|
||||
|
||||
|
||||
def _validate_rgbw_order(value: str) -> str:
|
||||
value = cv.string(value).upper()
|
||||
if len(value) != 4 or set(value) != set("RGBW"):
|
||||
raise cv.Invalid("RGBW order must be a permutation of RGBW")
|
||||
return value
|
||||
|
||||
|
||||
def _split_rgbw_order(rgbw_order: str) -> tuple[str, int]:
|
||||
return rgbw_order.replace("W", ""), rgbw_order.index("W")
|
||||
|
||||
|
||||
def _validate_rgbw_order_exclusivity(config: ConfigType) -> ConfigType:
|
||||
if CONF_RGBW_ORDER in config and (config[CONF_IS_RGBW] or config[CONF_IS_WRGB]):
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_RGBW_ORDER}' cannot be used with '{CONF_IS_RGBW}' or "
|
||||
f"'{CONF_IS_WRGB}'"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
esp32.only_on_variant(
|
||||
unsupported=list(esp32_rmt.VARIANTS_NO_RMT),
|
||||
@@ -80,7 +102,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ESP32RMTLEDStripLightOutput),
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||
cv.Optional(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||
cv.Optional(CONF_RGBW_ORDER): _validate_rgbw_order,
|
||||
cv.SplitDefault(
|
||||
CONF_RMT_SYMBOLS,
|
||||
esp32=192,
|
||||
@@ -130,6 +153,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
|
||||
cv.has_exactly_one_key(CONF_RGB_ORDER, CONF_RGBW_ORDER),
|
||||
_validate_rgbw_order_exclusivity,
|
||||
)
|
||||
|
||||
|
||||
@@ -173,6 +198,11 @@ async def to_code(config):
|
||||
)
|
||||
)
|
||||
|
||||
if (rgbw_order := config.get(CONF_RGBW_ORDER)) is not None:
|
||||
rgb_order, white_index = _split_rgbw_order(rgbw_order)
|
||||
cg.add(var.set_rgb_order(RGB_ORDERS[rgb_order]))
|
||||
cg.add(var.set_rgbw_order(white_index))
|
||||
else:
|
||||
cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
|
||||
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
|
||||
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
|
||||
|
||||
@@ -9,7 +9,7 @@ light:
|
||||
id: led_strip2
|
||||
pin: ${pin2}
|
||||
num_leds: 60
|
||||
rgb_order: RGB
|
||||
rgbw_order: RWGB
|
||||
bit0_high: 100us
|
||||
bit0_low: 100us
|
||||
bit1_high: 100us
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
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