diff --git a/light/__init__.py b/light/__init__.py index 3d13342..025113d 100644 --- a/light/__init__.py +++ b/light/__init__.py @@ -6,6 +6,7 @@ from esphome.core import coroutine from esphome.const import ( CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, CONF_COLOR_TEMPERATURE, CONF_OUTPUT_ID, CONF_TRIGGER_ID, CONF_ID, + CONF_TRANSITION_LENGTH, CONF_BRIGHTNESS, CONF_EFFECT ) from .. import bslamp2_ns, CODEOWNERS, CONF_LIGHT_HAL_ID, LightHAL @@ -14,6 +15,7 @@ AUTO_LOAD = ["xiaomi_bslamp2"] CONF_MASTER1 = "master1" CONF_MASTER2 = "master2" CONF_ON_BRIGHTNESS = "on_brightness" +CONF_PRESET_ID = "preset_id" CONF_PRESETS_ID = "presets_id" CONF_PRESETS = "presets" CONF_NEXT = "next" @@ -23,27 +25,44 @@ CONF_PRESET = "preset" XiaomiBslamp2LightState = bslamp2_ns.class_("XiaomiBslamp2LightState", light.LightState) XiaomiBslamp2LightOutput = bslamp2_ns.class_("XiaomiBslamp2LightOutput", light.LightOutput) PresetsContainer = bslamp2_ns.class_("PresetsContainer", cg.Component) +Preset = bslamp2_ns.class_("Preset", cg.Component) BrightnessTrigger = bslamp2_ns.class_("BrightnessTrigger", automation.Trigger.template()) ActivatePresetAction = bslamp2_ns.class_("ActivatePresetAction", automation.Action) PRESETS_SCHEMA = cv.Schema({ str.lower: cv.Schema({ - str.lower: cv.Any( - cv.Schema({ - cv.Optional(CONF_RED, default=0): cv.percentage, - cv.Optional(CONF_GREEN, default=0): cv.percentage, - cv.Optional(CONF_BLUE, default=0): cv.percentage, - }), - cv.Schema({ - cv.Required(CONF_COLOR_TEMPERATURE): cv.int_range(min=153, max=588), - }), - ) + str.lower: light.automation.LIGHT_TURN_ON_ACTION_SCHEMA }) }) +PRESET_SCHEMA_BASE = cv.Schema( + { + cv.GenerateID(CONF_ID): cv.use_id(XiaomiBslamp2LightState), + cv.GenerateID(CONF_PRESET_ID): cv.declare_id(Preset), + } +) + +PRESET_SCHEMA = cv.Any( + PRESET_SCHEMA_BASE.extend({ + cv.Required(CONF_EFFECT): cv.string + }), + PRESET_SCHEMA_BASE.extend({ + cv.Required(CONF_COLOR_TEMPERATURE): cv.color_temperature, + cv.Optional(CONF_BRIGHTNESS): cv.percentage, + cv.Optional(CONF_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, + }), + PRESET_SCHEMA_BASE.extend({ + cv.Required(CONF_RED): cv.percentage, + cv.Required(CONF_GREEN): cv.percentage, + cv.Required(CONF_BLUE): cv.percentage, + cv.Optional(CONF_BRIGHTNESS): cv.percentage, + cv.Optional(CONF_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, + }), +) + CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( { - cv.GenerateID(): cv.declare_id(XiaomiBslamp2LightState), + cv.GenerateID(CONF_ID): cv.declare_id(XiaomiBslamp2LightState), cv.GenerateID(CONF_LIGHT_HAL_ID): cv.use_id(LightHAL), cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(XiaomiBslamp2LightOutput), cv.Optional(CONF_ON_BRIGHTNESS): automation.validate_automation( @@ -52,7 +71,11 @@ CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( } ), cv.GenerateID(CONF_PRESETS_ID): cv.declare_id(PresetsContainer), - cv.Optional(CONF_PRESETS): PRESETS_SCHEMA, + cv.Optional(CONF_PRESETS): cv.Schema({ + str.lower: cv.Schema({ + str.lower: PRESET_SCHEMA + }) + }), } ) @@ -127,20 +150,38 @@ def on_brightness_to_code(config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], light_output_var) yield automation.build_automation(trigger, [(float, "x")], conf) +@coroutine +def preset_to_code(config, preset_group, preset_name): + light_var = yield cg.get_variable(config[CONF_ID]) + preset_var = cg.new_Pvariable( + config[CONF_PRESET_ID], light_var, preset_group, preset_name) + if CONF_TRANSITION_LENGTH in config: + cg.add(preset_var.set_transition_length(config[CONF_TRANSITION_LENGTH])) + if CONF_BRIGHTNESS in config: + cg.add(preset_var.set_brightness(config[CONF_BRIGHTNESS])) + if CONF_RED in config: + cg.add(preset_var.set_red(config[CONF_RED])) + if CONF_GREEN in config: + cg.add(preset_var.set_green(config[CONF_GREEN])) + if CONF_BLUE in config: + cg.add(preset_var.set_blue(config[CONF_BLUE])) + if CONF_COLOR_TEMPERATURE in config: + cg.add(preset_var.set_color_temperature(config[CONF_COLOR_TEMPERATURE])) + if CONF_EFFECT in config: + cg.add(preset_var.set_effect(config[CONF_EFFECT])) + else: + cg.add(preset_var.set_effect("None")) + yield cg.register_component(preset_var, config) + @coroutine def presets_to_code(config): - light_state_var = yield cg.get_variable(config[CONF_ID]) - presets_var = cg.new_Pvariable(config[CONF_PRESETS_ID], light_state_var) + presets_var = cg.new_Pvariable(config[CONF_PRESETS_ID]) yield cg.register_component(presets_var, config) for preset_group, presets in config.get(CONF_PRESETS, {}).items(): - for name, preset in presets.items(): - if CONF_COLOR_TEMPERATURE in preset: - cg.add(presets_var.add( - preset_group, name, preset[CONF_COLOR_TEMPERATURE])) - else: - cg.add(presets_var.add( - preset_group, name, preset[CONF_RED], preset[CONF_GREEN], preset[CONF_BLUE])) + for preset_name, preset_config in presets.items(): + preset = yield preset_to_code(preset_config, preset_group, preset_name) + cg.add(presets_var.add_preset(preset)) def to_code(config): yield light_output_to_code(config) diff --git a/light/presets.h b/light/presets.h index 28b8243..6966900 100644 --- a/light/presets.h +++ b/light/presets.h @@ -1,30 +1,69 @@ #pragma once #include "../common.h" +#include "esphome/core/optional.h" +#include +#include namespace esphome { namespace xiaomi { namespace bslamp2 { -class Preset { +class Preset : public Component { public: + std::string group_name; std::string name; Preset *next_preset = nullptr; - std::string group_name; - light::LightCall *light_call; - explicit Preset(std::string name, std::string group_name) : - name(name), group_name(group_name) {} + explicit Preset(light::LightState *light, std::string group_name, std::string name): + group_name(group_name), name(name), light_state_(light) {} - void set_light_call(light::LightCall *light_call) { - this->light_call = light_call; - } + void set_transition_length(uint32_t t) { transition_length_ = t; } + void set_brightness(float t) { brightness_ = t; } + void set_red(float t) { red_ = t; } + void set_green(float t) { green_ = t; } + void set_blue(float t) { blue_ = t; } + void set_color_temperature(float t) { color_temperature_ = t; } + void set_effect(const std::string &effect) { effect_ = effect; } void apply() { ESP_LOGI(TAG, "Activating light preset: %s/%s", group_name.c_str(), name.c_str()); - light_call->perform(); + auto call = light_state_->make_call(); + call.set_state(true); + if (transition_length_.has_value()) { + call.set_transition_length(*transition_length_); + } + if (brightness_.has_value()) { + call.set_brightness(*brightness_); + } + if (red_.has_value()) { + call.set_red(*red_); + } + if (green_.has_value()) { + call.set_green(*green_); + } + if (blue_.has_value()) { + call.set_blue(*blue_); + } + if (color_temperature_.has_value()) { + call.set_color_temperature(*color_temperature_); + } + if (effect_.has_value()) { + call.set_effect(*effect_); + } + call.perform(); } + +protected: + light::LightState *light_state_; + optional transition_length_; + optional brightness_; + optional red_; + optional green_; + optional blue_; + optional color_temperature_; + optional effect_; }; class PresetGroup { @@ -37,22 +76,18 @@ public: explicit PresetGroup(std::string g_name) : name(g_name) {} - Preset *make_preset(std::string p_name) { - auto p = get_preset(p_name); - if (p == nullptr) { - p = new Preset(p_name, this->name); - if (first_preset == nullptr) { - first_preset = last_preset = active_preset = p; - } else { - last_preset->next_preset = p; - last_preset = p; - } + void add_preset(Preset *p) { + if (first_preset == nullptr) { + first_preset = last_preset = active_preset = p; + } else { + last_preset->next_preset = p; + last_preset = p; } - return p; } Preset *get_preset(std::string p_name) { for (auto p = first_preset; p != nullptr; p = p->next_preset) { + ESP_LOGE(TAG, "CHECK '%s' vs '%s'", p_name.c_str(), p->name.c_str()); if (p->name == p_name) { return p; } @@ -67,8 +102,6 @@ public: PresetGroup *last_group = nullptr; PresetGroup *active_group = nullptr; - explicit PresetsContainer(light::LightState *light) : _light(light) { } - void dump_config() { if (first_group == nullptr) { return; @@ -82,14 +115,9 @@ public: } } - void add(std::string g_name, std::string p_name, float r, float g, float b) { - auto call = make_preset_call_(g_name, p_name); - call->set_rgb(r, g, b); - } - - void add(std::string g_name, std::string p_name, float t) { - auto call = make_preset_call_(g_name, p_name); - call->set_color_temperature(t); + void add_preset(Preset *preset) { + auto g = make_preset_group_(preset->group_name); + g->add_preset(preset); } PresetGroup *get_group(std::string g_name) { @@ -151,22 +179,20 @@ public: void activate_preset(std::string g_name, std::string p_name) { auto g = get_group(g_name); if (g == nullptr) { - ESP_LOGE(TAG, "activate_preset(%s, %s): preset group %s does not exist", + ESP_LOGE(TAG, "activate_preset(%s, %s): preset group '%s' does not exist", g_name.c_str(), p_name.c_str(), g_name.c_str()); return; } auto p = g->get_preset(p_name); if (p == nullptr) { - ESP_LOGE(TAG, "activate_preset(%s, %s): preset %s does not exist in group %s", - g_name.c_str(), p_name.c_str(), p_name.c_str(), g_name.c_str()); + ESP_LOGE(TAG, "activate_preset(%s, %s): preset '%s' does not exist in group '%s'", + g_name.c_str(), p_name.c_str(), p_name.c_str(), g->name.c_str()); return; } p->apply(); } protected: - light::LightState *_light; - PresetGroup *make_preset_group_(std::string g_name) { auto g = get_group(g_name); if (g == nullptr) { @@ -180,14 +206,6 @@ protected: } return g; } - - light::LightCall *make_preset_call_(std::string g_name, std::string p_name) { - auto g = make_preset_group_(g_name); - auto p = g->make_preset(p_name); - auto c = new light::LightCall(_light); - p->set_light_call(c); - return c; - } }; } // namespace bslamp2