import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components.ledc.output import LEDCOutput, validate_frequency from esphome.components.gpio.output import GPIOBinaryOutput from esphome.components.i2c import I2CComponent from esphome.core import coroutine from esphome.core import CORE from esphome.const import ( CONF_ID, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, CONF_TRIGGER_PIN, CONF_SDA, CONF_SCL, CONF_OUTPUT_ID, CONF_TRIGGER_ID, CONF_PIN, CONF_FREQUENCY, CONF_CHANNEL, CONF_PLATFORM, ) CONF_HUB_ID = "yeelight_bs2_hub_id" 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_ON_BRIGHTNESS = "on_brightness" CODEOWNERS = ["@mmakaay"] AUTO_LOAD = ["ledc", "output", "i2c"] PINS = { # Config key TYPE, ID GPIO, PARAMS CONF_RED : ( LEDCOutput, CONF_RED_ID, "GPIO13", 3000, 0 ), CONF_GREEN : ( LEDCOutput, CONF_GREEN_ID, "GPIO14", 3000, 1 ), CONF_BLUE : ( LEDCOutput, CONF_BLUE_ID, "GPIO5", 3000, 2 ), CONF_WHITE : ( LEDCOutput, CONF_WHITE_ID, "GPIO12", 10000, 4 ), CONF_MASTER1 : ( GPIOBinaryOutput, CONF_MASTER1_ID, "GPIO33" ), CONF_MASTER2 : ( GPIOBinaryOutput, CONF_MASTER2_ID, "GPIO4" ) } FRONT_PANEL = { CONF_SDA: "GPIO21", CONF_SCL: "GPIO19", CONF_TRIGGER_PIN: "GPIO16" } yeelight_ns = cg.esphome_ns.namespace("yeelight") bs2_ns = yeelight_ns.namespace("bs2") YeelightBS2Hub = bs2_ns.class_("YeelightBS2Hub", cg.Component) def make_config_schema(): schema = cv.COMPONENT_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(YeelightBS2Hub), cv.GenerateID(CONF_FP_I2C_ID): cv.use_id(I2CComponent), cv.Optional(CONF_SDA, default=FRONT_PANEL[CONF_SDA]): pins.validate_gpio_pin, cv.Optional(CONF_SCL, default=FRONT_PANEL[CONF_SCL]): pins.validate_gpio_pin, cv.Optional(CONF_TRIGGER_PIN, default=FRONT_PANEL[CONF_TRIGGER_PIN]): cv.All( pins.validate_gpio_pin, pins.validate_has_interrupt ), }) for key, pin_config in PINS.items(): type_, id_, pin, *_ = pin_config schema = schema.extend({ cv.GenerateID(id_): cv.declare_id(type_), cv.Optional(key, default=pin): pins.validate_gpio_pin }) return schema; CONFIG_SCHEMA = make_config_schema() @coroutine def make_gpio_pin(key, config): type_, id_, *_ = PINS[key] yield from cg.gpio_pin_expression({ "number": config[key], "mode": "OUTPUT" }); @coroutine def make_gpio_binary_output(key, config, gpio_var): type_, id_, *_ = PINS[key] output_var = cg.new_Pvariable(config[id_]) cg.add(output_var.set_pin(gpio_var)) yield from cg.register_component(output_var, {}) @coroutine def make_ledc_output(key, config, gpio_var): type_, id_, _, frequency, channel = PINS[key] ledc_var = cg.new_Pvariable(config[id_], gpio_var) cg.add(ledc_var.set_frequency(frequency)); cg.add(ledc_var.set_channel(channel)); yield from cg.register_component(ledc_var, {}) def to_code(config): # Dirty little hack to make the ESPHome component loader inlcude # 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" }) hub_var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(hub_var, config) for key in PINS: type_ = PINS[key][0] gpio_var = yield make_gpio_pin(key, config) if type_ == LEDCOutput: pin_var = yield make_ledc_output(key, config, gpio_var) if type_ == GPIOBinaryOutput: pin_var = yield make_gpio_binary_output(key, config, gpio_var) setter = getattr(hub_var, "set_%s_pin" % key) cg.add(setter(pin_var)) trigger_pin = yield cg.gpio_pin_expression({ "number": config[CONF_TRIGGER_PIN], "mode": "INPUT", "inverted": False }) cg.add(hub_var.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 = yield 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(hub_var.set_front_panel_i2c(fp_i2c_var)) for conf in config.get(CONF_ON_BRIGHTNESS, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) yield automation.build_automation(trigger, [(float, "x")], conf)