diff --git a/CODEOWNERS b/CODEOWNERS index a661eba9b3..65ea3b1683 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -52,6 +52,7 @@ esphome/components/bedjet/* @jhansche esphome/components/bedjet/climate/* @jhansche esphome/components/bedjet/fan/* @jhansche esphome/components/bedjet/sensor/* @javawizard @jhansche +esphome/components/beken_spi_led_strip/* @Mat931 esphome/components/bh1750/* @OttoWinter esphome/components/binary_sensor/* @esphome/core esphome/components/bk72xx/* @kuba2k2 diff --git a/esphome/components/beken_spi_led_strip/__init__.py b/esphome/components/beken_spi_led_strip/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/beken_spi_led_strip/led_strip.cpp b/esphome/components/beken_spi_led_strip/led_strip.cpp new file mode 100644 index 0000000000..04c8649b90 --- /dev/null +++ b/esphome/components/beken_spi_led_strip/led_strip.cpp @@ -0,0 +1,384 @@ +#include "led_strip.h" + +#ifdef USE_BK72XX + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +extern "C" { +#include "rtos_pub.h" +#include "spi.h" +#include "arm_arch.h" +#include "general_dma_pub.h" +#include "gpio_pub.h" +#include "icu_pub.h" +#undef SPI_DAT +#undef SPI_BASE +}; + +static const uint32_t SPI_TX_DMA_CHANNEL = GDMA_CHANNEL_3; + +// TODO: Check if SPI_PERI_CLK_DCO depends on the chip variant +static const uint32_t SPI_PERI_CLK_26M = 26000000; +static const uint32_t SPI_PERI_CLK_DCO = 120000000; + +static const uint32_t SPI_BASE = 0x00802700; +static const uint32_t SPI_DAT = SPI_BASE + 3 * 4; +static const uint32_t SPI_CONFIG = SPI_BASE + 1 * 4; + +static const uint32_t SPI_TX_EN = 1 << 0; +static const uint32_t CTRL_NSSMD_3 = 1 << 17; +static const uint32_t SPI_TX_FINISH_EN = 1 << 2; +static const uint32_t SPI_RX_FINISH_EN = 1 << 3; + +namespace esphome { +namespace beken_spi_led_strip { + +static const char *const TAG = "beken_spi_led_strip"; + +struct spi_data_t { + SemaphoreHandle_t dma_tx_semaphore; + volatile bool tx_in_progress; + bool first_run; +}; + +static spi_data_t *spi_data = nullptr; + +static void set_spi_ctrl_register(unsigned long bit, bool val) { + uint32_t value = REG_READ(SPI_CTRL); + if (val == 0) { + value &= ~bit; + } else if (val == 1) { + value |= bit; + } + REG_WRITE(SPI_CTRL, value); +} + +static void set_spi_config_register(unsigned long bit, bool val) { + uint32_t value = REG_READ(SPI_CONFIG); + if (val == 0) { + value &= ~bit; + } else if (val == 1) { + value |= bit; + } + REG_WRITE(SPI_CONFIG, value); +} + +void spi_dma_tx_enable(bool enable) { + GDMA_CFG_ST en_cfg; + set_spi_config_register(SPI_TX_EN, enable ? 1 : 0); + en_cfg.channel = SPI_TX_DMA_CHANNEL; + en_cfg.param = enable ? 1 : 0; + sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg); +} + +static void spi_set_clock(uint32_t max_hz) { + int source_clk = 0; + int spi_clk = 0; + int div = 0; + uint32_t param; + if (max_hz > 4333000) { + if (max_hz > 30000000) { + spi_clk = 30000000; + } else { + spi_clk = max_hz; + } + sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_DOWN, ¶m); + source_clk = SPI_PERI_CLK_DCO; + param = PCLK_POSI_SPI; + sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_DCO, ¶m); + param = PWD_SPI_CLK_BIT; + sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, ¶m); + } else { + spi_clk = max_hz; +#if CFG_XTAL_FREQUENCE + source_clk = CFG_XTAL_FREQUENCE; +#else + source_clk = SPI_PERI_CLK_26M; +#endif + param = PCLK_POSI_SPI; + sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, ¶m); + } + div = ((source_clk >> 1) / spi_clk); + if (div < 2) { + div = 2; + } else if (div >= 255) { + div = 255; + } + param = REG_READ(SPI_CTRL); + param &= ~(SPI_CKR_MASK << SPI_CKR_POSI); + param |= (div << SPI_CKR_POSI); + REG_WRITE(SPI_CTRL, param); + ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div); +} + +void spi_dma_tx_finish_callback(unsigned int param) { + spi_data->tx_in_progress = false; + xSemaphoreGive(spi_data->dma_tx_semaphore); + spi_dma_tx_enable(0); +} + +void BekenSPILEDStripLightOutput::setup() { + ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip..."); + + size_t buffer_size = this->get_buffer_size_(); + size_t dma_buffer_size = (buffer_size * 8) + (2 * 64); + + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->buf_ = allocator.allocate(buffer_size); + if (this->buf_ == nullptr) { + ESP_LOGE(TAG, "Cannot allocate LED buffer!"); + this->mark_failed(); + return; + } + + this->effect_data_ = allocator.allocate(this->num_leds_); + if (this->effect_data_ == nullptr) { + ESP_LOGE(TAG, "Cannot allocate effect data!"); + this->mark_failed(); + return; + } + + this->dma_buf_ = allocator.allocate(dma_buffer_size); + if (this->dma_buf_ == nullptr) { + ESP_LOGE(TAG, "Cannot allocate DMA buffer!"); + this->mark_failed(); + return; + } + + memset(this->buf_, 0, buffer_size); + memset(this->effect_data_, 0, this->num_leds_); + memset(this->dma_buf_, 0, dma_buffer_size); + + uint32_t value = PCLK_POSI_SPI; + sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &value); + + value = PWD_SPI_CLK_BIT; + sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &value); + + if (spi_data != nullptr) { + ESP_LOGE(TAG, "SPI device already initialized!"); + this->mark_failed(); + return; + } + + spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t)); + if (spi_data == nullptr) { + ESP_LOGE(TAG, "Cannot allocate spi_data!"); + this->mark_failed(); + return; + } + + spi_data->dma_tx_semaphore = xSemaphoreCreateBinary(); + if (spi_data->dma_tx_semaphore == nullptr) { + ESP_LOGE(TAG, "TX Semaphore init faild!"); + this->mark_failed(); + return; + } + + spi_data->first_run = true; + + set_spi_ctrl_register(MSTEN, 0); + set_spi_ctrl_register(BIT_WDTH, 0); + spi_set_clock(this->spi_frequency_); + set_spi_ctrl_register(CKPOL, 0); + set_spi_ctrl_register(CKPHA, 0); + set_spi_ctrl_register(MSTEN, 1); + set_spi_ctrl_register(SPIEN, 1); + + set_spi_ctrl_register(TXINT_EN, 0); + set_spi_ctrl_register(RXINT_EN, 0); + set_spi_config_register(SPI_TX_FINISH_EN, 1); + set_spi_config_register(SPI_RX_FINISH_EN, 1); + set_spi_ctrl_register(RXOVR_EN, 0); + set_spi_ctrl_register(TXOVR_EN, 0); + + value = REG_READ(SPI_CTRL); + value &= ~CTRL_NSSMD_3; + value |= (1 << 17); + REG_WRITE(SPI_CTRL, value); + + value = GFUNC_MODE_SPI_DMA; + sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value); + set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0); + + GDMA_CFG_ST en_cfg; + GDMACFG_TPYES_ST init_cfg; + memset(&init_cfg, 0, sizeof(GDMACFG_TPYES_ST)); + + init_cfg.dstdat_width = 8; + init_cfg.srcdat_width = 32; + init_cfg.dstptr_incr = 0; + init_cfg.srcptr_incr = 1; + init_cfg.src_start_addr = this->dma_buf_; + init_cfg.dst_start_addr = (void *) SPI_DAT; // SPI_DMA_REG4_TXFIFO + init_cfg.channel = SPI_TX_DMA_CHANNEL; + init_cfg.prio = 0; // 10 + init_cfg.u.type4.src_loop_start_addr = this->dma_buf_; + init_cfg.u.type4.src_loop_end_addr = this->dma_buf_ + dma_buffer_size; + init_cfg.half_fin_handler = nullptr; + init_cfg.fin_handler = spi_dma_tx_finish_callback; + init_cfg.src_module = GDMA_X_SRC_DTCM_RD_REQ; + init_cfg.dst_module = GDMA_X_DST_GSPI_TX_REQ; // GDMA_X_DST_HSSPI_TX_REQ + sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_TYPE4, (void *) &init_cfg); + en_cfg.channel = SPI_TX_DMA_CHANNEL; + en_cfg.param = dma_buffer_size; + sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_TRANS_LENGTH, (void *) &en_cfg); + en_cfg.channel = SPI_TX_DMA_CHANNEL; + en_cfg.param = 0; + sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_WORK_MODE, (void *) &en_cfg); + en_cfg.channel = SPI_TX_DMA_CHANNEL; + en_cfg.param = 0; + sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg); + + spi_dma_tx_enable(0); + + value = REG_READ(SPI_CONFIG); + value &= ~(0xFFF << 8); + value |= ((dma_buffer_size & 0xFFF) << 8); + REG_WRITE(SPI_CONFIG, value); +} + +void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency) { + this->bit0_ = bit0; + this->bit1_ = bit1; + this->spi_frequency_ = spi_frequency; +} + +void BekenSPILEDStripLightOutput::write_state(light::LightState *state) { + // protect from refreshing too often + uint32_t now = micros(); + if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) { + // try again next loop iteration, so that this change won't get lost + this->schedule_show(); + return; + } + this->last_refresh_ = now; + this->mark_shown_(); + + ESP_LOGVV(TAG, "Writing RGB values to bus..."); + + if (spi_data == nullptr) { + ESP_LOGE(TAG, "SPI not initialized"); + this->status_set_warning(); + return; + } + + if (!spi_data->first_run && !xSemaphoreTake(spi_data->dma_tx_semaphore, 10 / portTICK_PERIOD_MS)) { + ESP_LOGE(TAG, "Timed out waiting for semaphore"); + return; + } + + if (spi_data->tx_in_progress) { + ESP_LOGE(TAG, "tx_in_progress is set"); + this->status_set_warning(); + return; + } + + spi_data->tx_in_progress = true; + + size_t buffer_size = this->get_buffer_size_(); + size_t size = 0; + uint8_t *psrc = this->buf_; + uint8_t *pdest = this->dma_buf_ + 64; + // The 64 byte padding is a workaround for a SPI DMA bug where the + // output doesn't exactly start at the beginning of dma_buf_ + + while (size < buffer_size) { + uint8_t b = *psrc; + for (int i = 0; i < 8; i++) { + *pdest++ = b & (1 << (7 - i)) ? this->bit1_ : this->bit0_; + } + size++; + psrc++; + } + + spi_data->first_run = false; + spi_dma_tx_enable(1); + + this->status_clear_warning(); +} + +light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index) const { + int32_t r = 0, g = 0, b = 0; + switch (this->rgb_order_) { + case ORDER_RGB: + r = 0; + g = 1; + b = 2; + break; + case ORDER_RBG: + r = 0; + g = 2; + b = 1; + break; + case ORDER_GRB: + r = 1; + g = 0; + b = 2; + break; + case ORDER_GBR: + r = 2; + g = 0; + b = 1; + break; + case ORDER_BGR: + r = 2; + g = 1; + b = 0; + break; + case ORDER_BRG: + r = 1; + g = 2; + b = 0; + break; + } + uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3; + uint8_t white = this->is_wrgb_ ? 0 : 3; + + 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_, + this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr, + &this->effect_data_[index], + &this->correction_}; +} + +void BekenSPILEDStripLightOutput::dump_config() { + ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:"); + ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); + const char *rgb_order; + switch (this->rgb_order_) { + case ORDER_RGB: + rgb_order = "RGB"; + break; + case ORDER_RBG: + rgb_order = "RBG"; + break; + case ORDER_GRB: + rgb_order = "GRB"; + break; + case ORDER_GBR: + rgb_order = "GBR"; + break; + case ORDER_BGR: + rgb_order = "BGR"; + break; + case ORDER_BRG: + rgb_order = "BRG"; + break; + default: + rgb_order = "UNKNOWN"; + break; + } + ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order); + ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_); + ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_); +} + +float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } + +} // namespace beken_spi_led_strip +} // namespace esphome + +#endif // USE_BK72XX diff --git a/esphome/components/beken_spi_led_strip/led_strip.h b/esphome/components/beken_spi_led_strip/led_strip.h new file mode 100644 index 0000000000..705f9102a9 --- /dev/null +++ b/esphome/components/beken_spi_led_strip/led_strip.h @@ -0,0 +1,85 @@ +#pragma once + +#ifdef USE_BK72XX + +#include "esphome/components/light/addressable_light.h" +#include "esphome/components/light/light_output.h" +#include "esphome/core/color.h" +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace beken_spi_led_strip { + +enum RGBOrder : uint8_t { + ORDER_RGB, + ORDER_RBG, + ORDER_GRB, + ORDER_GBR, + ORDER_BGR, + ORDER_BRG, +}; + +class BekenSPILEDStripLightOutput : public light::AddressableLight { + public: + void setup() override; + void write_state(light::LightState *state) override; + float get_setup_priority() const override; + + int32_t size() const override { return this->num_leds_; } + light::LightTraits get_traits() override { + auto traits = light::LightTraits(); + if (this->is_rgbw_ || this->is_wrgb_) { + traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE}); + } else { + traits.set_supported_color_modes({light::ColorMode::RGB}); + } + return traits; + } + + void set_pin(uint8_t pin) { this->pin_ = pin; } + 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; } + + /// Set a maximum refresh rate in µs as some lights do not like being updated too often. + void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; } + + void set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency); + + void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; } + + void clear_effect_data() override { + for (int i = 0; i < this->size(); i++) + this->effect_data_[i] = 0; + } + + void dump_config() override; + + protected: + light::ESPColorView get_view_internal(int32_t index) const override; + + size_t get_buffer_size_() const { return this->num_leds_ * (this->is_rgbw_ || this->is_wrgb_ ? 4 : 3); } + + uint8_t *buf_{nullptr}; + uint8_t *effect_data_{nullptr}; + uint8_t *dma_buf_{nullptr}; + + uint8_t pin_; + uint16_t num_leds_; + bool is_rgbw_; + bool is_wrgb_; + + uint32_t spi_frequency_{6666666}; + uint8_t bit0_{0xE0}; + uint8_t bit1_{0xFC}; + RGBOrder rgb_order_; + + uint32_t last_refresh_{0}; + optional max_refresh_rate_{}; +}; + +} // namespace beken_spi_led_strip +} // namespace esphome + +#endif // USE_BK72XX diff --git a/esphome/components/beken_spi_led_strip/light.py b/esphome/components/beken_spi_led_strip/light.py new file mode 100644 index 0000000000..2a1aa05c79 --- /dev/null +++ b/esphome/components/beken_spi_led_strip/light.py @@ -0,0 +1,134 @@ +from dataclasses import dataclass + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import libretiny, light +from esphome.const import ( + CONF_CHIPSET, + CONF_IS_RGBW, + CONF_MAX_REFRESH_RATE, + CONF_NUM_LEDS, + CONF_OUTPUT_ID, + CONF_PIN, + CONF_RGB_ORDER, +) + +CODEOWNERS = ["@Mat931"] +DEPENDENCIES = ["libretiny"] + +beken_spi_led_strip_ns = cg.esphome_ns.namespace("beken_spi_led_strip") +BekenSPILEDStripLightOutput = beken_spi_led_strip_ns.class_( + "BekenSPILEDStripLightOutput", light.AddressableLight +) + +RGBOrder = beken_spi_led_strip_ns.enum("RGBOrder") + +RGB_ORDERS = { + "RGB": RGBOrder.ORDER_RGB, + "RBG": RGBOrder.ORDER_RBG, + "GRB": RGBOrder.ORDER_GRB, + "GBR": RGBOrder.ORDER_GBR, + "BGR": RGBOrder.ORDER_BGR, + "BRG": RGBOrder.ORDER_BRG, +} + + +@dataclass +class LEDStripTimings: + bit0: int + bit1: int + spi_frequency: int + + +CHIPSETS = { + "WS2812": LEDStripTimings( + 0b11100000, 0b11111100, 6666666 + ), # Clock divider: 9, Bit time: 1350ns + "SK6812": LEDStripTimings( + 0b11000000, 0b11111000, 7500000 + ), # Clock divider: 8, Bit time: 1200ns + "APA106": LEDStripTimings( + 0b11000000, 0b11111110, 5454545 + ), # Clock divider: 11, Bit time: 1650ns + "SM16703": LEDStripTimings( + 0b11000000, 0b11111110, 7500000 + ), # Clock divider: 8, Bit time: 1200ns +} + + +CONF_IS_WRGB = "is_wrgb" + +SUPPORTED_PINS = { + libretiny.const.FAMILY_BK7231N: [16], + libretiny.const.FAMILY_BK7231T: [16], + libretiny.const.FAMILY_BK7251: [16], +} + + +def _validate_pin(value): + family = libretiny.get_libretiny_family() + if family not in SUPPORTED_PINS: + raise cv.Invalid(f"Chip family {family} is not supported.") + if value not in SUPPORTED_PINS[family]: + supported_pin_info = ", ".join(f"{x}" for x in SUPPORTED_PINS[family]) + raise cv.Invalid( + f"Pin {value} is not supported on the {family}. Supported pins: {supported_pin_info}" + ) + return value + + +def _validate_num_leds(value): + max_num_leds = 165 # 170 + if value[CONF_IS_RGBW] or value[CONF_IS_WRGB]: + max_num_leds = 123 # 127 + if value[CONF_NUM_LEDS] > max_num_leds: + raise cv.Invalid( + f"The maximum number of LEDs for this configuration is {max_num_leds}.", + path=CONF_NUM_LEDS, + ) + return value + + +CONFIG_SCHEMA = cv.All( + light.ADDRESSABLE_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BekenSPILEDStripLightOutput), + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_output_pin_number, _validate_pin + ), + cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, + cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True), + cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, + cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), + cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, + cv.Optional(CONF_IS_WRGB, default=False): cv.boolean, + } + ), + _validate_num_leds, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) + await light.register_light(var, config) + await cg.register_component(var, config) + + cg.add(var.set_num_leds(config[CONF_NUM_LEDS])) + cg.add(var.set_pin(config[CONF_PIN])) + + if CONF_MAX_REFRESH_RATE in config: + cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE])) + + chipset = CHIPSETS[config[CONF_CHIPSET]] + cg.add( + var.set_led_params( + chipset.bit0, + chipset.bit1, + chipset.spi_frequency, + ) + ) + + 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])) diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 1c15a468d9..4c8472b8d2 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -6,6 +6,7 @@ from esphome import pins from esphome.components import esp32_rmt, light from esphome.const import ( CONF_CHIPSET, + CONF_IS_RGBW, CONF_MAX_REFRESH_RATE, CONF_NUM_LEDS, CONF_OUTPUT_ID, @@ -52,7 +53,6 @@ CHIPSETS = { } -CONF_IS_RGBW = "is_rgbw" CONF_IS_WRGB = "is_wrgb" CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_LOW = "bit0_low" diff --git a/esphome/components/rp2040_pio_led_strip/light.py b/esphome/components/rp2040_pio_led_strip/light.py index 6c51b57e97..202263c914 100644 --- a/esphome/components/rp2040_pio_led_strip/light.py +++ b/esphome/components/rp2040_pio_led_strip/light.py @@ -5,6 +5,7 @@ from esphome.components import light, rp2040 from esphome.const import ( CONF_CHIPSET, CONF_ID, + CONF_IS_RGBW, CONF_NUM_LEDS, CONF_OUTPUT_ID, CONF_PIN, @@ -165,7 +166,6 @@ CHIPSETS = { "SM16703": LEDStripTimings(17, 52, 52, 17), } -CONF_IS_RGBW = "is_rgbw" CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_LOW = "bit0_low" CONF_BIT1_HIGH = "bit1_high" diff --git a/esphome/const.py b/esphome/const.py index 542183d3b2..eb48080040 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -399,6 +399,7 @@ CONF_INVERT_COLORS = "invert_colors" CONF_INVERTED = "inverted" CONF_IP_ADDRESS = "ip_address" CONF_IRQ_PIN = "irq_pin" +CONF_IS_RGBW = "is_rgbw" CONF_JS_INCLUDE = "js_include" CONF_JS_URL = "js_url" CONF_JVC = "jvc" diff --git a/tests/components/beken_spi_led_strip/test.bk72xx.yaml b/tests/components/beken_spi_led_strip/test.bk72xx.yaml new file mode 100644 index 0000000000..15409caeaf --- /dev/null +++ b/tests/components/beken_spi_led_strip/test.bk72xx.yaml @@ -0,0 +1,7 @@ +light: + - platform: beken_spi_led_strip + rgb_order: GRB + pin: P16 + num_leds: 30 + chipset: ws2812 + name: "My Light" diff --git a/tests/test_build_components/build_components_base.bk72xx.yaml b/tests/test_build_components/build_components_base.bk72xx.yaml index 9fd9431826..9a4e15d5cf 100644 --- a/tests/test_build_components/build_components_base.bk72xx.yaml +++ b/tests/test_build_components/build_components_base.bk72xx.yaml @@ -3,7 +3,7 @@ esphome: friendly_name: $component_name bk72xx: - board: cb3s + board: generic-bk7231n-qfn32-tuya logger: level: VERY_VERBOSE