import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components.ledc.output import LEDCOutput from esphome.components.gpio.output import GPIOBinaryOutput from esphome.components.i2c import I2CComponent, I2CDevice from esphome.core import coroutine from esphome.core import CORE from esphome.const import ( CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, CONF_TRIGGER_PIN, CONF_SDA, CONF_SCL, CONF_ADDRESS, CONF_PLATFORM ) CODEOWNERS = ["@mmakaay"] CONF_RED_ID = "red_id" CONF_GREEN_ID = "green_id" CONF_BLUE_ID = "blue_id" CONF_WHITE_ID = "white_id" CONF_MASTER1 = "master1" CONF_MASTER1_ID = "master1_id" CONF_MASTER2 = "master2" CONF_MASTER2_ID = "master2_id" CONF_FP_I2C_ID = "front_panel_i2c_id" CONF_LIGHT_HAL_ID = "light_hal_id" CONF_FRONT_PANEL_HAL_ID = "front_panel_hal_id" CONF_ON_BRIGHTNESS = "on_brightness" CONF_LEDS = "leds" AUTO_LOAD = ["ledc", "output", "i2c"] xiaomi_ns = cg.esphome_ns.namespace("xiaomi") bslamp2_ns = xiaomi_ns.namespace("bslamp2") LightHAL = bslamp2_ns.class_("LightHAL", cg.Component) FrontPanelHAL = bslamp2_ns.class_("FrontPanelHAL", cg.Component, I2CDevice) FrontPanelLEDs = bslamp2_ns.enum("FrontPanelLEDs") FRONT_PANEL_LED_OPTIONS = { "NONE": FrontPanelLEDs.LED_NONE, "POWER": FrontPanelLEDs.LED_POWER, "COLOR": FrontPanelLEDs.LED_COLOR, "1": FrontPanelLEDs.LED_1, "2": FrontPanelLEDs.LED_2, "3": FrontPanelLEDs.LED_3, "4": FrontPanelLEDs.LED_4, "5": FrontPanelLEDs.LED_5, "6": FrontPanelLEDs.LED_6, "7": FrontPanelLEDs.LED_7, "8": FrontPanelLEDs.LED_8, "9": FrontPanelLEDs.LED_9, "10": FrontPanelLEDs.LED_10, } CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({ # RGBWW Light cv.GenerateID(CONF_LIGHT_HAL_ID): cv.declare_id(LightHAL), cv.GenerateID(CONF_RED_ID): cv.declare_id(LEDCOutput), cv.Optional(CONF_RED, default="GPIO13"): pins.validate_gpio_pin, cv.GenerateID(CONF_GREEN_ID): cv.declare_id(LEDCOutput), cv.Optional(CONF_GREEN, default="GPIO14"): pins.validate_gpio_pin, cv.GenerateID(CONF_BLUE_ID): cv.declare_id(LEDCOutput), cv.Optional(CONF_BLUE, default="GPIO5"): pins.validate_gpio_pin, cv.GenerateID(CONF_WHITE_ID): cv.declare_id(LEDCOutput), cv.Optional(CONF_WHITE, default="GPIO12"): pins.validate_gpio_pin, cv.GenerateID(CONF_MASTER1_ID): cv.declare_id(GPIOBinaryOutput), cv.Optional(CONF_MASTER1, default="GPIO33"): pins.validate_gpio_pin, cv.GenerateID(CONF_MASTER2_ID): cv.declare_id(GPIOBinaryOutput), cv.Optional(CONF_MASTER2, default="GPIO4"): pins.validate_gpio_pin, # Front panel I2C cv.GenerateID(CONF_FRONT_PANEL_HAL_ID): cv.declare_id(FrontPanelHAL), cv.GenerateID(CONF_FP_I2C_ID): cv.use_id(I2CComponent), cv.Optional(CONF_SDA, default="GPIO21"): pins.validate_gpio_pin, cv.Optional(CONF_SCL, default="GPIO19"): pins.validate_gpio_pin, cv.Optional(CONF_ADDRESS, default="0x2C"): cv.i2c_address, cv.Optional(CONF_TRIGGER_PIN, default="GPIO16"): cv.All( pins.validate_gpio_pin, pins.validate_has_interrupt ), }) async def make_gpio(number, mode="OUTPUT"): return await cg.gpio_pin_expression({ "number": number, "mode": mode }); async def make_gpio_binary_output(id_, number): gpio_var = await make_gpio(number) output_var = cg.new_Pvariable(id_) cg.add(output_var.set_pin(gpio_var)) return await cg.register_component(output_var, {}) async def make_ledc_output(id_, number, frequency, channel): gpio_var = await make_gpio(number) ledc_var = cg.new_Pvariable(id_, gpio_var) cg.add(ledc_var.set_frequency(frequency)); cg.add(ledc_var.set_channel(channel)); return await cg.register_component(ledc_var, {}) async def make_light_hal(config): r_var = await make_ledc_output(config[CONF_RED_ID], config[CONF_RED], 3000, 0) g_var = await make_ledc_output(config[CONF_GREEN_ID], config[CONF_GREEN], 3000, 1) b_var = await make_ledc_output(config[CONF_BLUE_ID], config[CONF_BLUE], 3000, 2) w_var = await make_ledc_output(config[CONF_WHITE_ID], config[CONF_WHITE], 10000, 4) m1_var = await make_gpio_binary_output(config[CONF_MASTER1_ID], config[CONF_MASTER1]) m2_var = await make_gpio_binary_output(config[CONF_MASTER2_ID], config[CONF_MASTER2]) light_hal = cg.new_Pvariable(config[CONF_LIGHT_HAL_ID]) await cg.register_component(light_hal, config) cg.add(light_hal.set_red_pin(r_var)) cg.add(light_hal.set_green_pin(g_var)) cg.add(light_hal.set_blue_pin(b_var)) cg.add(light_hal.set_white_pin(w_var)) cg.add(light_hal.set_master1_pin(m1_var)) cg.add(light_hal.set_master2_pin(m2_var)) async def make_front_panel_hal(config): trigger_pin = await make_gpio(config[CONF_TRIGGER_PIN], "INPUT") fp_hal = cg.new_Pvariable(config[CONF_FRONT_PANEL_HAL_ID]) await cg.register_component(fp_hal, config) cg.add(fp_hal.set_trigger_pin(trigger_pin)) # The i2c component automatically sets up one I2C bus. # Take that bus and update is to make it work for the # front panel I2C communication. fp_i2c_var = await cg.get_variable(config[CONF_FP_I2C_ID]) cg.add(fp_i2c_var.set_sda_pin(config[CONF_SDA])) cg.add(fp_i2c_var.set_scl_pin(config[CONF_SCL])) cg.add(fp_i2c_var.set_scan(True)) cg.add(fp_hal.set_i2c_parent(fp_i2c_var)) cg.add(fp_hal.set_i2c_address(config[CONF_ADDRESS])) async def to_code(config): # Dirty little hack to make the ESPHome component loader include # the code for the "gpio" platform for the "output" domain. # Loading specific platform components is not possible using # the AUTO_LOAD feature unfortunately. CORE.config["output"].append({ CONF_PLATFORM: "gpio" }) await make_light_hal(config) await make_front_panel_hal(config)