From 5fc4290eb6d34bb190d906d94f29d7e938c9eaa7 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Sun, 18 Apr 2021 01:01:45 +0200 Subject: [PATCH] Presets can now use brightness, transition_length and effect. I also switched from using prepared LightCall objects to my own property storage that creates a new LightCall when a preset must be activated. I found that a LightCall should only be called once. Its internal state gets modified when calling perform() on it. Too bad, since it forced me into doing some duplicate coding, but I'm happy with the end result. --- light/__init__.py | 83 ++++++++++++++++++++++++++---------- light/presets.h | 104 +++++++++++++++++++++++++++------------------- 2 files changed, 123 insertions(+), 64 deletions(-) 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