import esphome.codegen as cg from esphome.components import light, spi import esphome.config_validation as cv from esphome.const import ( CONF_CHIPSET, CONF_NUM_LEDS, CONF_OUTPUT_ID, CONF_RGB_ORDER, ) from esphome.types import ConfigType spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip") SpiLedStrip = spi_led_strip_ns.class_( "SpiLedStrip", light.AddressableLight, spi.SPIDevice ) SpiLedChipset = spi_led_strip_ns.enum("SpiLedChipset", is_class=True) RGBOrder = spi_led_strip_ns.enum("RGBOrder", is_class=True) CHIPSET_APA102 = "APA102" CHIPSET_WS2801 = "WS2801" CHIPSETS = { CHIPSET_APA102: SpiLedChipset.SPI_LED_CHIPSET_APA102, CHIPSET_WS2801: SpiLedChipset.SPI_LED_CHIPSET_WS2801, } 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, } BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( { cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpiLedStrip), cv.Optional(CONF_NUM_LEDS, default=1): cv.positive_not_null_int, } ) def rgb_order_schema(default: str) -> cv.Schema: return cv.Schema( { cv.Optional(CONF_RGB_ORDER, default=default): cv.enum( RGB_ORDERS, upper=True ), } ) CONFIG_SCHEMA = cv.typed_schema( { CHIPSET_APA102: BASE_SCHEMA.extend(rgb_order_schema("BGR")).extend( spi.spi_device_schema(False, "1MHz") ), # WS2801 latches the data when the clock stays low, so the clock must idle low CHIPSET_WS2801: BASE_SCHEMA.extend(rgb_order_schema("RGB")).extend( spi.spi_device_schema(False, "1MHz", "MODE0") ), }, key=CONF_CHIPSET, default_type=CHIPSET_APA102, enum=CHIPSETS, upper=True, ) async def to_code(config: ConfigType) -> None: chipset = config[CONF_CHIPSET] if chipset == CHIPSET_WS2801: cg.add_define("USE_SPI_LED_STRIP_WS2801") var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_NUM_LEDS], chipset) cg.add(var.set_rgb_order(config[CONF_RGB_ORDER])) await light.register_light(var, config) await spi.register_spi_device(var, config) await cg.register_component(var, config)