mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Neopixelbus redo method definitions (#2616)
This commit is contained in:
parent
d8e33c5a69
commit
8aa72f4c1e
6 changed files with 585 additions and 112 deletions
418
esphome/components/neopixelbus/_methods.py
Normal file
418
esphome/components/neopixelbus/_methods.py
Normal file
|
@ -0,0 +1,418 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Any, List
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_CHANNEL,
|
||||
CONF_CLOCK_PIN,
|
||||
CONF_DATA_PIN,
|
||||
CONF_METHOD,
|
||||
CONF_PIN,
|
||||
CONF_SPEED,
|
||||
)
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32C3,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from .const import (
|
||||
CONF_ASYNC,
|
||||
CONF_BUS,
|
||||
CHIP_400KBPS,
|
||||
CHIP_800KBPS,
|
||||
CHIP_APA106,
|
||||
CHIP_DOTSTAR,
|
||||
CHIP_LC8812,
|
||||
CHIP_LPD6803,
|
||||
CHIP_LPD8806,
|
||||
CHIP_P9813,
|
||||
CHIP_SK6812,
|
||||
CHIP_TM1814,
|
||||
CHIP_TM1829,
|
||||
CHIP_TM1914,
|
||||
CHIP_WS2801,
|
||||
CHIP_WS2811,
|
||||
CHIP_WS2812,
|
||||
CHIP_WS2812X,
|
||||
CHIP_WS2813,
|
||||
ONE_WIRE_CHIPS,
|
||||
TWO_WIRE_CHIPS,
|
||||
)
|
||||
|
||||
METHOD_BIT_BANG = "bit_bang"
|
||||
METHOD_ESP8266_UART = "esp8266_uart"
|
||||
METHOD_ESP8266_DMA = "esp8266_dma"
|
||||
METHOD_ESP32_RMT = "esp32_rmt"
|
||||
METHOD_ESP32_I2S = "esp32_i2s"
|
||||
METHOD_SPI = "spi"
|
||||
|
||||
CHANNEL_DYNAMIC = "dynamic"
|
||||
BUS_DYNAMIC = "dynamic"
|
||||
SPI_BUS_VSPI = "vspi"
|
||||
SPI_BUS_HSPI = "hspi"
|
||||
SPI_SPEEDS = [40e6, 20e6, 10e6, 5e6, 2e6, 1e6, 500e3]
|
||||
|
||||
|
||||
def _esp32_rmt_default_channel():
|
||||
return {
|
||||
VARIANT_ESP32S2: 1,
|
||||
VARIANT_ESP32C3: 1,
|
||||
}.get(get_esp32_variant(), 6)
|
||||
|
||||
|
||||
def _validate_esp32_rmt_channel(value):
|
||||
if isinstance(value, str) and value.lower() == CHANNEL_DYNAMIC:
|
||||
value = CHANNEL_DYNAMIC
|
||||
else:
|
||||
value = cv.int_(value)
|
||||
variant_channels = {
|
||||
VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7, CHANNEL_DYNAMIC],
|
||||
VARIANT_ESP32S2: [0, 1, 2, 3, CHANNEL_DYNAMIC],
|
||||
VARIANT_ESP32C3: [0, 1, CHANNEL_DYNAMIC],
|
||||
}
|
||||
variant = get_esp32_variant()
|
||||
if variant not in variant_channels:
|
||||
raise cv.Invalid(f"{variant} does not support the rmt method")
|
||||
if value not in variant_channels[variant]:
|
||||
raise cv.Invalid(f"{variant} does not support rmt channel {value}")
|
||||
return value
|
||||
|
||||
|
||||
def _esp32_i2s_default_bus():
|
||||
return {
|
||||
VARIANT_ESP32: 1,
|
||||
VARIANT_ESP32S2: 0,
|
||||
}.get(get_esp32_variant(), 0)
|
||||
|
||||
|
||||
def _validate_esp32_i2s_bus(value):
|
||||
if isinstance(value, str) and value.lower() == CHANNEL_DYNAMIC:
|
||||
value = CHANNEL_DYNAMIC
|
||||
else:
|
||||
value = cv.int_(value)
|
||||
variant_buses = {
|
||||
VARIANT_ESP32: [0, 1, BUS_DYNAMIC],
|
||||
VARIANT_ESP32S2: [0, BUS_DYNAMIC],
|
||||
}
|
||||
variant = get_esp32_variant()
|
||||
if variant not in variant_buses:
|
||||
raise cv.Invalid(f"{variant} does not support the i2s method")
|
||||
if value not in variant_buses[variant]:
|
||||
raise cv.Invalid(f"{variant} does not support i2s bus {value}")
|
||||
return value
|
||||
|
||||
|
||||
neo_ns = cg.global_ns
|
||||
|
||||
|
||||
def _bit_bang_to_code(config, chip: str, inverted: bool):
|
||||
# https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/NeoEspBitBangMethod.h
|
||||
# Some chips are only aliases
|
||||
chip = {
|
||||
CHIP_WS2813: CHIP_WS2812X,
|
||||
CHIP_LC8812: CHIP_SK6812,
|
||||
CHIP_TM1914: CHIP_TM1814,
|
||||
CHIP_WS2812: CHIP_800KBPS,
|
||||
}.get(chip, chip)
|
||||
|
||||
lookup = {
|
||||
CHIP_WS2811: (neo_ns.NeoEspBitBangSpeedWs2811, False),
|
||||
CHIP_WS2812X: (neo_ns.NeoEspBitBangSpeedWs2812x, False),
|
||||
CHIP_SK6812: (neo_ns.NeoEspBitBangSpeedSk6812, False),
|
||||
CHIP_TM1814: (neo_ns.NeoEspBitBangSpeedTm1814, True),
|
||||
CHIP_TM1829: (neo_ns.NeoEspBitBangSpeedTm1829, True),
|
||||
CHIP_800KBPS: (neo_ns.NeoEspBitBangSpeed800Kbps, False),
|
||||
CHIP_400KBPS: (neo_ns.NeoEspBitBangSpeed400Kbps, False),
|
||||
CHIP_APA106: (neo_ns.NeoEspBitBangSpeedApa106, False),
|
||||
}
|
||||
# For tm variants opposite of inverted is needed
|
||||
speed, pinset_inverted = lookup[chip]
|
||||
pinset = {
|
||||
False: neo_ns.NeoEspPinset,
|
||||
True: neo_ns.NeoEspPinsetInverted,
|
||||
}[inverted != pinset_inverted]
|
||||
return neo_ns.NeoEspBitBangMethodBase.template(speed, pinset)
|
||||
|
||||
|
||||
def _bit_bang_extra_validate(config):
|
||||
pin = config[CONF_PIN]
|
||||
if CORE.is_esp8266 and not (0 <= pin <= 15):
|
||||
# Due to use of w1ts
|
||||
raise cv.Invalid("Bit bang only supports pins GPIO0-GPIO15 on ESP8266")
|
||||
if CORE.is_esp32 and not (0 <= pin <= 31):
|
||||
raise cv.Invalid("Bit bang only supports pins GPIO0-GPIO31 on ESP32")
|
||||
|
||||
|
||||
def _esp8266_uart_to_code(config, chip: str, inverted: bool):
|
||||
# https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/NeoEsp8266UartMethod.h
|
||||
uart_context, uart_base = {
|
||||
False: (neo_ns.NeoEsp8266UartContext, neo_ns.NeoEsp8266Uart),
|
||||
True: (neo_ns.NeoEsp8266UartInterruptContext, neo_ns.NeoEsp8266AsyncUart),
|
||||
}[config[CONF_ASYNC]]
|
||||
uart_feature = {
|
||||
0: neo_ns.UartFeature0,
|
||||
1: neo_ns.UartFeature1,
|
||||
}[config[CONF_BUS]]
|
||||
# Some chips are only aliases
|
||||
chip = {
|
||||
CHIP_WS2811: CHIP_WS2812X,
|
||||
CHIP_WS2813: CHIP_WS2812X,
|
||||
CHIP_LC8812: CHIP_SK6812,
|
||||
CHIP_TM1914: CHIP_TM1814,
|
||||
CHIP_WS2812: CHIP_800KBPS,
|
||||
}.get(chip, chip)
|
||||
|
||||
lookup = {
|
||||
CHIP_WS2812X: (neo_ns.NeoEsp8266UartSpeedWs2812x, False),
|
||||
CHIP_SK6812: (neo_ns.NeoEsp8266UartSpeedSk6812, False),
|
||||
CHIP_TM1814: (neo_ns.NeoEsp8266UartSpeedTm1814, True),
|
||||
CHIP_TM1829: (neo_ns.NeoEsp8266UartSpeedTm1829, True),
|
||||
CHIP_800KBPS: (neo_ns.NeoEsp8266UartSpeed800Kbps, False),
|
||||
CHIP_400KBPS: (neo_ns.NeoEsp8266UartSpeed400Kbps, False),
|
||||
CHIP_APA106: (neo_ns.NeoEsp8266UartSpeedApa106, False),
|
||||
}
|
||||
speed, uart_inverted = lookup[chip]
|
||||
# For tm variants opposite of inverted is needed
|
||||
inv = {
|
||||
False: neo_ns.NeoEsp8266UartNotInverted,
|
||||
True: neo_ns.NeoEsp8266UartInverted,
|
||||
}[inverted != uart_inverted]
|
||||
return neo_ns.NeoEsp8266UartMethodBase.template(
|
||||
speed, uart_base.template(uart_feature, uart_context), inv
|
||||
)
|
||||
|
||||
|
||||
def _esp8266_uart_extra_validate(config):
|
||||
pin = config[CONF_PIN]
|
||||
bus = config[CONF_METHOD][CONF_BUS]
|
||||
right_pin = {
|
||||
0: 1, # U0TXD
|
||||
1: 2, # U1TXD
|
||||
}[bus]
|
||||
if pin != right_pin:
|
||||
raise cv.Invalid(f"ESP8266 uart bus {bus} only supports pin GPIO{right_pin}")
|
||||
|
||||
|
||||
def _esp8266_dma_to_code(config, chip: str, inverted: bool):
|
||||
# https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/NeoEsp8266DmaMethod.h
|
||||
# Some chips are only aliases
|
||||
chip = {
|
||||
CHIP_WS2811: CHIP_WS2812X,
|
||||
CHIP_WS2813: CHIP_WS2812X,
|
||||
CHIP_LC8812: CHIP_SK6812,
|
||||
CHIP_TM1914: CHIP_TM1814,
|
||||
CHIP_WS2812: CHIP_800KBPS,
|
||||
}.get(chip, chip)
|
||||
|
||||
lookup = {
|
||||
(CHIP_WS2812X, False): neo_ns.NeoEsp8266DmaSpeedWs2812x,
|
||||
(CHIP_SK6812, False): neo_ns.NeoEsp8266DmaSpeedSk6812,
|
||||
(CHIP_TM1814, True): neo_ns.NeoEsp8266DmaInvertedSpeedTm1814,
|
||||
(CHIP_TM1829, True): neo_ns.NeoEsp8266DmaInvertedSpeedTm1829,
|
||||
(CHIP_800KBPS, False): neo_ns.NeoEsp8266DmaSpeed800Kbps,
|
||||
(CHIP_400KBPS, False): neo_ns.NeoEsp8266DmaSpeed400Kbps,
|
||||
(CHIP_APA106, False): neo_ns.NeoEsp8266DmaSpeedApa106,
|
||||
(CHIP_WS2812X, True): neo_ns.NeoEsp8266DmaInvertedSpeedWs2812x,
|
||||
(CHIP_SK6812, True): neo_ns.NeoEsp8266DmaInvertedSpeedSk6812,
|
||||
(CHIP_TM1814, False): neo_ns.NeoEsp8266DmaSpeedTm1814,
|
||||
(CHIP_TM1829, False): neo_ns.NeoEsp8266DmaSpeedTm1829,
|
||||
(CHIP_800KBPS, True): neo_ns.NeoEsp8266DmaInvertedSpeed800Kbps,
|
||||
(CHIP_400KBPS, True): neo_ns.NeoEsp8266DmaInvertedSpeed400Kbps,
|
||||
(CHIP_APA106, True): neo_ns.NeoEsp8266DmaInvertedSpeedApa106,
|
||||
}
|
||||
speed = lookup[(chip, inverted)]
|
||||
return neo_ns.NeoEsp8266DmaMethodBase.template(speed)
|
||||
|
||||
|
||||
def _esp8266_dma_extra_validate(config):
|
||||
if config[CONF_PIN] != 3:
|
||||
raise cv.Invalid("ESP8266 dma method only supports pin GPIO3")
|
||||
|
||||
|
||||
def _esp32_rmt_to_code(config, chip: str, inverted: bool):
|
||||
# https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/NeoEsp32RmtMethod.h
|
||||
channel = {
|
||||
0: neo_ns.NeoEsp32RmtChannel0,
|
||||
1: neo_ns.NeoEsp32RmtChannel1,
|
||||
2: neo_ns.NeoEsp32RmtChannel2,
|
||||
3: neo_ns.NeoEsp32RmtChannel3,
|
||||
4: neo_ns.NeoEsp32RmtChannel4,
|
||||
5: neo_ns.NeoEsp32RmtChannel5,
|
||||
6: neo_ns.NeoEsp32RmtChannel6,
|
||||
7: neo_ns.NeoEsp32RmtChannel7,
|
||||
CHANNEL_DYNAMIC: neo_ns.NeoEsp32RmtChannelN,
|
||||
}[config[CONF_CHANNEL]]
|
||||
# Some chips are only aliases
|
||||
chip = {
|
||||
CHIP_WS2813: CHIP_WS2812X,
|
||||
CHIP_LC8812: CHIP_SK6812,
|
||||
CHIP_WS2812: CHIP_800KBPS,
|
||||
}.get(chip, chip)
|
||||
|
||||
lookup = {
|
||||
(CHIP_WS2811, False): neo_ns.NeoEsp32RmtSpeedWs2811,
|
||||
(CHIP_WS2812X, False): neo_ns.NeoEsp32RmtSpeedWs2812x,
|
||||
(CHIP_SK6812, False): neo_ns.NeoEsp32RmtSpeedSk6812,
|
||||
(CHIP_TM1814, False): neo_ns.NeoEsp32RmtSpeedTm1814,
|
||||
(CHIP_TM1829, False): neo_ns.NeoEsp32RmtSpeedTm1829,
|
||||
(CHIP_TM1914, False): neo_ns.NeoEsp32RmtSpeedTm1914,
|
||||
(CHIP_800KBPS, False): neo_ns.NeoEsp32RmtSpeed800Kbps,
|
||||
(CHIP_400KBPS, False): neo_ns.NeoEsp32RmtSpeed400Kbps,
|
||||
(CHIP_APA106, False): neo_ns.NeoEsp32RmtSpeedApa106,
|
||||
(CHIP_WS2811, True): neo_ns.NeoEsp32RmtInvertedSpeedWs2811,
|
||||
(CHIP_WS2812X, True): neo_ns.NeoEsp32RmtInvertedSpeedWs2812x,
|
||||
(CHIP_SK6812, True): neo_ns.NeoEsp32RmtInvertedSpeedSk6812,
|
||||
(CHIP_TM1814, True): neo_ns.NeoEsp32RmtInvertedSpeedTm1814,
|
||||
(CHIP_TM1829, True): neo_ns.NeoEsp32RmtInvertedSpeedTm1829,
|
||||
(CHIP_TM1914, True): neo_ns.NeoEsp32RmtInvertedSpeedTm1914,
|
||||
(CHIP_800KBPS, True): neo_ns.NeoEsp32RmtInvertedSpeed800Kbps,
|
||||
(CHIP_400KBPS, True): neo_ns.NeoEsp32RmtInvertedSpeed400Kbps,
|
||||
(CHIP_APA106, True): neo_ns.NeoEsp32RmtInvertedSpeedApa106,
|
||||
}
|
||||
speed = lookup[(chip, inverted)]
|
||||
return neo_ns.NeoEsp32RmtMethodBase.template(speed, channel)
|
||||
|
||||
|
||||
def _esp32_i2s_to_code(config, chip: str, inverted: bool):
|
||||
# https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/NeoEsp32I2sMethod.h
|
||||
bus = {
|
||||
0: neo_ns.NeoEsp32I2sBusZero,
|
||||
1: neo_ns.NeoEsp32I2sBusOne,
|
||||
BUS_DYNAMIC: neo_ns.NeoEsp32I2sBusN,
|
||||
}[config[CONF_BUS]]
|
||||
# Some chips are only aliases
|
||||
chip = {
|
||||
CHIP_WS2811: CHIP_WS2812X,
|
||||
CHIP_WS2813: CHIP_WS2812X,
|
||||
CHIP_LC8812: CHIP_SK6812,
|
||||
CHIP_WS2812: CHIP_800KBPS,
|
||||
}.get(chip, chip)
|
||||
|
||||
lookup = {
|
||||
CHIP_WS2812X: (neo_ns.NeoEsp32I2sSpeedWs2812x, False),
|
||||
CHIP_SK6812: (neo_ns.NeoEsp32I2sSpeedSk6812, False),
|
||||
CHIP_TM1814: (neo_ns.NeoEsp32I2sSpeedTm1814, True),
|
||||
CHIP_TM1914: (neo_ns.NeoEsp32I2sSpeedTm1914, True),
|
||||
CHIP_TM1829: (neo_ns.NeoEsp32I2sSpeedTm1829, True),
|
||||
CHIP_800KBPS: (neo_ns.NeoEsp32I2sSpeed800Kbps, False),
|
||||
CHIP_400KBPS: (neo_ns.NeoEsp32I2sSpeed400Kbps, False),
|
||||
CHIP_APA106: (neo_ns.NeoEsp32I2sSpeedApa106, False),
|
||||
}
|
||||
speed, inv_inverted = lookup[chip]
|
||||
# For tm variants opposite of inverted is needed
|
||||
inv = {
|
||||
False: neo_ns.NeoEsp32I2sNotInverted,
|
||||
True: neo_ns.NeoEsp32I2sInverted,
|
||||
}[inverted != inv_inverted]
|
||||
return neo_ns.NeoEsp32I2sMethodBase.template(speed, bus, inv)
|
||||
|
||||
|
||||
def _spi_to_code(config, chip: str, inverted: bool):
|
||||
# https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/TwoWireSpiImple.h
|
||||
spi_imple = {
|
||||
None: neo_ns.TwoWireSpiImple,
|
||||
SPI_BUS_VSPI: neo_ns.TwoWireSpiImple,
|
||||
SPI_BUS_HSPI: neo_ns.TwoWireHspiImple,
|
||||
}[config.get(CONF_BUS)]
|
||||
spi_speed = {
|
||||
40e6: neo_ns.SpiSpeed40Mhz,
|
||||
20e6: neo_ns.SpiSpeed20Mhz,
|
||||
10e6: neo_ns.SpiSpeed10Mhz,
|
||||
5e6: neo_ns.SpiSpeed5Mhz,
|
||||
2e6: neo_ns.SpiSpeed2Mhz,
|
||||
1e6: neo_ns.SpiSpeed1Mhz,
|
||||
500e3: neo_ns.SpiSpeed500Khz,
|
||||
}[config[CONF_SPEED]]
|
||||
chip_method_base = {
|
||||
CHIP_DOTSTAR: neo_ns.DotStarMethodBase,
|
||||
CHIP_LPD6803: neo_ns.Lpd6803MethodBase,
|
||||
CHIP_LPD8806: neo_ns.Lpd8806MethodBase,
|
||||
CHIP_WS2801: neo_ns.Ws2801MethodBase,
|
||||
CHIP_P9813: neo_ns.P9813MethodBase,
|
||||
}[chip]
|
||||
return chip_method_base.template(spi_imple.template(spi_speed))
|
||||
|
||||
|
||||
def _spi_extra_validate(config):
|
||||
if CORE.is_esp32:
|
||||
return
|
||||
|
||||
if config[CONF_DATA_PIN] != 13 and config[CONF_CLOCK_PIN] != 14:
|
||||
raise cv.Invalid(
|
||||
"SPI only supports pins GPIO13 for data and GPIO14 for clock on ESP8266"
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MethodDescriptor:
|
||||
method_schema: Any
|
||||
to_code: Any
|
||||
supported_chips: List[str]
|
||||
extra_validate: Any = None
|
||||
|
||||
|
||||
METHODS = {
|
||||
METHOD_BIT_BANG: MethodDescriptor(
|
||||
method_schema={},
|
||||
to_code=_bit_bang_to_code,
|
||||
extra_validate=_bit_bang_extra_validate,
|
||||
supported_chips=ONE_WIRE_CHIPS,
|
||||
),
|
||||
METHOD_ESP8266_UART: MethodDescriptor(
|
||||
method_schema=cv.All(
|
||||
cv.only_on_esp8266,
|
||||
{
|
||||
cv.Optional(CONF_ASYNC, default=False): cv.boolean,
|
||||
cv.Optional(CONF_BUS, default=1): cv.int_range(min=0, max=1),
|
||||
},
|
||||
),
|
||||
extra_validate=_esp8266_uart_extra_validate,
|
||||
to_code=_esp8266_uart_to_code,
|
||||
supported_chips=ONE_WIRE_CHIPS,
|
||||
),
|
||||
METHOD_ESP8266_DMA: MethodDescriptor(
|
||||
method_schema=cv.All(cv.only_on_esp8266, {}),
|
||||
extra_validate=_esp8266_dma_extra_validate,
|
||||
to_code=_esp8266_dma_to_code,
|
||||
supported_chips=ONE_WIRE_CHIPS,
|
||||
),
|
||||
METHOD_ESP32_RMT: MethodDescriptor(
|
||||
method_schema=cv.All(
|
||||
cv.only_on_esp32,
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_CHANNEL, default=_esp32_rmt_default_channel
|
||||
): _validate_esp32_rmt_channel,
|
||||
},
|
||||
),
|
||||
to_code=_esp32_rmt_to_code,
|
||||
supported_chips=ONE_WIRE_CHIPS,
|
||||
),
|
||||
METHOD_ESP32_I2S: MethodDescriptor(
|
||||
method_schema=cv.All(
|
||||
cv.only_on_esp32,
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_BUS, default=_esp32_i2s_default_bus
|
||||
): _validate_esp32_i2s_bus,
|
||||
},
|
||||
),
|
||||
to_code=_esp32_i2s_to_code,
|
||||
supported_chips=ONE_WIRE_CHIPS,
|
||||
),
|
||||
METHOD_SPI: MethodDescriptor(
|
||||
method_schema={
|
||||
cv.Optional(CONF_BUS): cv.All(
|
||||
cv.only_on_esp32, cv.one_of(SPI_BUS_VSPI, SPI_BUS_HSPI, lower=True)
|
||||
),
|
||||
cv.Optional(CONF_SPEED, default="10MHz"): cv.All(
|
||||
cv.frequency, cv.one_of(*SPI_SPEEDS)
|
||||
),
|
||||
},
|
||||
to_code=_spi_to_code,
|
||||
extra_validate=_spi_extra_validate,
|
||||
supported_chips=TWO_WIRE_CHIPS,
|
||||
),
|
||||
}
|
42
esphome/components/neopixelbus/const.py
Normal file
42
esphome/components/neopixelbus/const.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
CHIP_DOTSTAR = "dotstar"
|
||||
CHIP_WS2801 = "ws2801"
|
||||
CHIP_WS2811 = "ws2811"
|
||||
CHIP_WS2812 = "ws2812"
|
||||
CHIP_WS2812X = "ws2812x"
|
||||
CHIP_WS2813 = "ws2813"
|
||||
CHIP_SK6812 = "sk6812"
|
||||
CHIP_TM1814 = "tm1814"
|
||||
CHIP_TM1829 = "tm1829"
|
||||
CHIP_TM1914 = "tm1914"
|
||||
CHIP_800KBPS = "800kbps"
|
||||
CHIP_400KBPS = "400kbps"
|
||||
CHIP_APA106 = "apa106"
|
||||
CHIP_LC8812 = "lc8812"
|
||||
CHIP_LPD8806 = "lpd8806"
|
||||
CHIP_LPD6803 = "lpd6803"
|
||||
CHIP_P9813 = "p9813"
|
||||
|
||||
ONE_WIRE_CHIPS = [
|
||||
CHIP_WS2811,
|
||||
CHIP_WS2812,
|
||||
CHIP_WS2812X,
|
||||
CHIP_WS2813,
|
||||
CHIP_SK6812,
|
||||
CHIP_TM1814,
|
||||
CHIP_TM1829,
|
||||
CHIP_TM1914,
|
||||
CHIP_800KBPS,
|
||||
CHIP_400KBPS,
|
||||
CHIP_APA106,
|
||||
CHIP_LC8812,
|
||||
]
|
||||
TWO_WIRE_CHIPS = [
|
||||
CHIP_DOTSTAR,
|
||||
CHIP_WS2801,
|
||||
CHIP_LPD6803,
|
||||
CHIP_LPD8806,
|
||||
CHIP_P9813,
|
||||
]
|
||||
CHIP_TYPES = [*ONE_WIRE_CHIPS, *TWO_WIRE_CHIPS]
|
||||
CONF_ASYNC = "async"
|
||||
CONF_BUS = "bus"
|
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
|||
from esphome import pins
|
||||
from esphome.components import light
|
||||
from esphome.const import (
|
||||
CONF_CHANNEL,
|
||||
CONF_CLOCK_PIN,
|
||||
CONF_DATA_PIN,
|
||||
CONF_METHOD,
|
||||
|
@ -13,7 +14,26 @@ from esphome.const import (
|
|||
CONF_OUTPUT_ID,
|
||||
CONF_INVERT,
|
||||
)
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32C3,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from ._methods import (
|
||||
METHODS,
|
||||
METHOD_SPI,
|
||||
METHOD_ESP8266_UART,
|
||||
METHOD_BIT_BANG,
|
||||
METHOD_ESP32_I2S,
|
||||
METHOD_ESP32_RMT,
|
||||
METHOD_ESP8266_DMA,
|
||||
)
|
||||
from .const import (
|
||||
CHIP_TYPES,
|
||||
CONF_ASYNC,
|
||||
CONF_BUS,
|
||||
ONE_WIRE_CHIPS,
|
||||
)
|
||||
|
||||
neopixelbus_ns = cg.esphome_ns.namespace("neopixelbus")
|
||||
NeoPixelBusLightOutputBase = neopixelbus_ns.class_(
|
||||
|
@ -46,127 +66,115 @@ def validate_type(value):
|
|||
return value
|
||||
|
||||
|
||||
def validate_variant(value):
|
||||
value = cv.string(value).upper()
|
||||
if value == "WS2813":
|
||||
value = "WS2812X"
|
||||
if value == "WS2812":
|
||||
value = "800KBPS"
|
||||
if value == "LC8812":
|
||||
value = "SK6812"
|
||||
return cv.one_of(*VARIANTS)(value)
|
||||
def _choose_default_method(config):
|
||||
if CONF_METHOD in config:
|
||||
return config
|
||||
config = config.copy()
|
||||
if CONF_PIN not in config:
|
||||
config[CONF_METHOD] = _validate_method(METHOD_SPI)
|
||||
return config
|
||||
|
||||
|
||||
def validate_method(value):
|
||||
if value is None:
|
||||
if CORE.is_esp32:
|
||||
return "ESP32_I2S_1"
|
||||
if CORE.is_esp8266:
|
||||
return "ESP8266_DMA"
|
||||
raise NotImplementedError
|
||||
|
||||
if CORE.is_esp32:
|
||||
return cv.one_of(*ESP32_METHODS, upper=True, space="_")(value)
|
||||
pin = config[CONF_PIN]
|
||||
if CORE.is_esp8266:
|
||||
return cv.one_of(*ESP8266_METHODS, upper=True, space="_")(value)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def validate_method_pin(value):
|
||||
method = value[CONF_METHOD]
|
||||
method_pins = {
|
||||
"ESP8266_DMA": [3],
|
||||
"ESP8266_UART0": [1],
|
||||
"ESP8266_ASYNC_UART0": [1],
|
||||
"ESP8266_UART1": [2],
|
||||
"ESP8266_ASYNC_UART1": [2],
|
||||
"ESP32_I2S_0": list(range(0, 32)),
|
||||
"ESP32_I2S_1": list(range(0, 32)),
|
||||
}
|
||||
if CORE.is_esp8266:
|
||||
method_pins["BIT_BANG"] = list(range(0, 16))
|
||||
elif CORE.is_esp32:
|
||||
method_pins["BIT_BANG"] = list(range(0, 32))
|
||||
pins_ = method_pins.get(method)
|
||||
if pins_ is None:
|
||||
# all pins allowed for this method
|
||||
return value
|
||||
|
||||
for opt in (CONF_PIN, CONF_CLOCK_PIN, CONF_DATA_PIN):
|
||||
if opt in value and value[opt] not in pins_:
|
||||
raise cv.Invalid(
|
||||
f"Method {method} only supports pin(s) {', '.join(f'GPIO{x}' for x in pins_)}",
|
||||
path=[CONF_METHOD],
|
||||
if pin == 3:
|
||||
config[CONF_METHOD] = _validate_method(METHOD_ESP8266_DMA)
|
||||
elif pin == 1:
|
||||
config[CONF_METHOD] = _validate_method(
|
||||
{
|
||||
CONF_TYPE: METHOD_ESP8266_UART,
|
||||
CONF_BUS: 0,
|
||||
}
|
||||
)
|
||||
elif pin == 2:
|
||||
config[CONF_METHOD] = _validate_method(
|
||||
{
|
||||
CONF_TYPE: METHOD_ESP8266_UART,
|
||||
CONF_BUS: 1,
|
||||
}
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
VARIANTS = {
|
||||
"WS2812X": "Ws2812x",
|
||||
"SK6812": "Sk6812",
|
||||
"800KBPS": "800Kbps",
|
||||
"400KBPS": "400Kbps",
|
||||
}
|
||||
|
||||
ESP8266_METHODS = {
|
||||
"ESP8266_DMA": "NeoEsp8266Dma{}Method",
|
||||
"ESP8266_UART0": "NeoEsp8266Uart0{}Method",
|
||||
"ESP8266_UART1": "NeoEsp8266Uart1{}Method",
|
||||
"ESP8266_ASYNC_UART0": "NeoEsp8266AsyncUart0{}Method",
|
||||
"ESP8266_ASYNC_UART1": "NeoEsp8266AsyncUart1{}Method",
|
||||
"BIT_BANG": "NeoEsp8266BitBang{}Method",
|
||||
}
|
||||
ESP32_METHODS = {
|
||||
"ESP32_I2S_0": "NeoEsp32I2s0{}Method",
|
||||
"ESP32_I2S_1": "NeoEsp32I2s1{}Method",
|
||||
"ESP32_RMT_0": "NeoEsp32Rmt0{}Method",
|
||||
"ESP32_RMT_1": "NeoEsp32Rmt1{}Method",
|
||||
"ESP32_RMT_2": "NeoEsp32Rmt2{}Method",
|
||||
"ESP32_RMT_3": "NeoEsp32Rmt3{}Method",
|
||||
"ESP32_RMT_4": "NeoEsp32Rmt4{}Method",
|
||||
"ESP32_RMT_5": "NeoEsp32Rmt5{}Method",
|
||||
"ESP32_RMT_6": "NeoEsp32Rmt6{}Method",
|
||||
"ESP32_RMT_7": "NeoEsp32Rmt7{}Method",
|
||||
"BIT_BANG": "NeoEsp32BitBang{}Method",
|
||||
}
|
||||
|
||||
|
||||
def format_method(config):
|
||||
variant = VARIANTS[config[CONF_VARIANT]]
|
||||
method = config[CONF_METHOD]
|
||||
|
||||
if config[CONF_INVERT]:
|
||||
if method == "ESP8266_DMA":
|
||||
variant = f"Inverted{variant}"
|
||||
else:
|
||||
variant += "Inverted"
|
||||
config[CONF_METHOD] = _validate_method(METHOD_BIT_BANG)
|
||||
|
||||
if CORE.is_esp8266:
|
||||
return ESP8266_METHODS[method].format(variant)
|
||||
if CORE.is_esp32:
|
||||
return ESP32_METHODS[method].format(variant)
|
||||
raise NotImplementedError
|
||||
if get_esp32_variant() == VARIANT_ESP32C3:
|
||||
config[CONF_METHOD] = _validate_method(METHOD_ESP32_RMT)
|
||||
else:
|
||||
config[CONF_METHOD] = _validate_method(METHOD_ESP32_I2S)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def _validate(config):
|
||||
if CONF_PIN in config:
|
||||
variant = config[CONF_VARIANT]
|
||||
if variant in ONE_WIRE_CHIPS:
|
||||
if CONF_PIN not in config:
|
||||
raise cv.Invalid(
|
||||
f"Chip {variant} is a 1-wire chip and needs the [pin] option."
|
||||
)
|
||||
if CONF_CLOCK_PIN in config or CONF_DATA_PIN in config:
|
||||
raise cv.Invalid("Cannot specify both 'pin' and 'clock_pin'+'data_pin'")
|
||||
return config
|
||||
if CONF_CLOCK_PIN in config:
|
||||
if CONF_DATA_PIN not in config:
|
||||
raise cv.Invalid("If you give clock_pin, you must also specify data_pin")
|
||||
return config
|
||||
raise cv.Invalid("Must specify at least one of 'pin' or 'clock_pin'+'data_pin'")
|
||||
raise cv.Invalid(
|
||||
f"Chip {variant} is a 1-wire chip, you need to set [pin] instead of ."
|
||||
)
|
||||
else:
|
||||
if CONF_PIN in config:
|
||||
raise cv.Invalid(
|
||||
f"Chip {variant} is a 2-wire chip and needs the [data_pin]+[clock_pin] option instead of [pin]."
|
||||
)
|
||||
if CONF_CLOCK_PIN not in config or CONF_DATA_PIN not in config:
|
||||
raise cv.Invalid(
|
||||
f"Chip {variant} is a 2-wire chip, you need to set [data_pin]+[clock_pin]."
|
||||
)
|
||||
|
||||
method_type = config[CONF_METHOD][CONF_TYPE]
|
||||
method_desc = METHODS[method_type]
|
||||
if variant not in method_desc.supported_chips:
|
||||
raise cv.Invalid(f"Method {method_type} does not support {variant}")
|
||||
if method_desc.extra_validate is not None:
|
||||
method_desc.extra_validate(config)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def _validate_method(value):
|
||||
if value is None:
|
||||
# default method is determined afterwards because it depends on the chip type chosen
|
||||
return None
|
||||
|
||||
compat_methods = {}
|
||||
for bus in [0, 1]:
|
||||
for is_async in [False, True]:
|
||||
compat_methods[f"ESP8266{'_ASYNC' if is_async else ''}_UART{bus}"] = {
|
||||
CONF_TYPE: METHOD_ESP8266_UART,
|
||||
CONF_BUS: bus,
|
||||
CONF_ASYNC: is_async,
|
||||
}
|
||||
compat_methods[f"ESP32_I2S_{bus}"] = {
|
||||
CONF_TYPE: METHOD_ESP32_I2S,
|
||||
CONF_BUS: bus,
|
||||
}
|
||||
for channel in range(8):
|
||||
compat_methods[f"ESP32_RMT_{channel}"] = {
|
||||
CONF_TYPE: METHOD_ESP32_RMT,
|
||||
CONF_CHANNEL: channel,
|
||||
}
|
||||
|
||||
if isinstance(value, str):
|
||||
if value.upper() in compat_methods:
|
||||
return _validate_method(compat_methods[value.upper()])
|
||||
return _validate_method({CONF_TYPE: value})
|
||||
return cv.typed_schema(
|
||||
{k: v.method_schema for k, v in METHODS.items()}, lower=True
|
||||
)(value)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.only_with_arduino,
|
||||
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(NeoPixelBusLightOutputBase),
|
||||
cv.Optional(CONF_TYPE, default="GRB"): validate_type,
|
||||
cv.Optional(CONF_VARIANT, default="800KBPS"): validate_variant,
|
||||
cv.Optional(CONF_METHOD, default=None): validate_method,
|
||||
cv.Required(CONF_VARIANT): cv.one_of(*CHIP_TYPES, lower=True),
|
||||
cv.Optional(CONF_METHOD): _validate_method,
|
||||
cv.Optional(CONF_INVERT, default="no"): cv.boolean,
|
||||
cv.Optional(CONF_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_CLOCK_PIN): pins.internal_gpio_output_pin_number,
|
||||
|
@ -174,19 +182,23 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
_choose_default_method,
|
||||
_validate,
|
||||
validate_method_pin,
|
||||
cv.only_with_arduino,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
has_white = "W" in config[CONF_TYPE]
|
||||
template = cg.TemplateArguments(getattr(cg.global_ns, format_method(config)))
|
||||
method = config[CONF_METHOD]
|
||||
|
||||
method_template = METHODS[method[CONF_TYPE]].to_code(
|
||||
method, config[CONF_VARIANT], config[CONF_INVERT]
|
||||
)
|
||||
|
||||
if has_white:
|
||||
out_type = NeoPixelRGBWLightOutput.template(template)
|
||||
out_type = NeoPixelRGBWLightOutput.template(method_template)
|
||||
else:
|
||||
out_type = NeoPixelRGBLightOutput.template(template)
|
||||
out_type = NeoPixelRGBLightOutput.template(method_template)
|
||||
rhs = out_type.new()
|
||||
var = cg.Pvariable(config[CONF_OUTPUT_ID], rhs, out_type)
|
||||
await light.register_light(var, config)
|
||||
|
@ -204,4 +216,4 @@ async def to_code(config):
|
|||
cg.add(var.set_pixel_order(getattr(ESPNeoPixelOrder, config[CONF_TYPE])))
|
||||
|
||||
# https://github.com/Makuna/NeoPixelBus/blob/master/library.json
|
||||
cg.add_library("makuna/NeoPixelBus", "2.6.7")
|
||||
cg.add_library("makuna/NeoPixelBus", "2.6.9")
|
||||
|
|
|
@ -1399,7 +1399,7 @@ def typed_schema(schemas, **kwargs):
|
|||
if schema_option is None:
|
||||
raise Invalid(f"{key} not specified!")
|
||||
key_v = key_validator(schema_option)
|
||||
value = schemas[key_v](value)
|
||||
value = Schema(schemas[key_v])(value)
|
||||
value[key] = key_v
|
||||
return value
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ build_flags =
|
|||
[common]
|
||||
lib_deps =
|
||||
esphome/noise-c@0.1.4 ; api
|
||||
makuna/NeoPixelBus@2.6.7 ; neopixelbus
|
||||
makuna/NeoPixelBus@2.6.9 ; neopixelbus
|
||||
build_flags =
|
||||
-DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
src_filter =
|
||||
|
|
|
@ -32,6 +32,7 @@ def clang_options(idedata):
|
|||
'-D_PGMSPACE_H_',
|
||||
'-Dpgm_read_byte(s)=(*(const uint8_t *)(s))',
|
||||
'-Dpgm_read_byte_near(s)=(*(const uint8_t *)(s))',
|
||||
'-Dpgm_read_word(s)=(*(const uint16_t *)(s))',
|
||||
'-Dpgm_read_dword(s)=(*(const uint32_t *)(s))',
|
||||
'-DPROGMEM=',
|
||||
'-DPGM_P=const char *',
|
||||
|
|
Loading…
Reference in a new issue