From f85ff71d4353e6eda36003594782479aaec9e059 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Sun, 18 Apr 2021 01:41:52 +0200 Subject: [PATCH] Add support for presets (#9) Implemented support for presets. --- common.h | 3 +- doc/FLASHING.md | 8 +- doc/example.yaml | 41 ++++++-- front_panel_hal.h | 4 +- light/__init__.py | 162 +++++++++++++++++++++++++++-- light/automation.h | 30 ++++++ light/color_night_light.h | 1 + light/color_off.h | 4 +- light/color_rgb_light.h | 1 + light/color_white_light.h | 1 + light/gpio_outputs.h | 9 +- light/light_modes.h | 15 +++ light/light_output.h | 20 ---- light/light_state.h | 31 ++++++ light/presets.h | 210 ++++++++++++++++++++++++++++++++++++++ sensor/slider_sensor.h | 2 +- 16 files changed, 487 insertions(+), 55 deletions(-) create mode 100644 light/light_modes.h create mode 100644 light/light_state.h create mode 100644 light/presets.h diff --git a/common.h b/common.h index c46b185..44ebf17 100644 --- a/common.h +++ b/common.h @@ -4,8 +4,9 @@ namespace esphome { namespace xiaomi { namespace bslamp2 { +// Used for logging purposes. static const char *TAG = "xiaomi_bslamp2"; - + } // namespace bslamp2 } // namespace xiaomi } // namespace esphome diff --git a/doc/FLASHING.md b/doc/FLASHING.md index 02d1015..3f7c9d9 100644 --- a/doc/FLASHING.md +++ b/doc/FLASHING.md @@ -138,9 +138,9 @@ reconnect the power to boot into the restored firmware. ## Flash new firmware -Setup an ESPHome Project, see [README.md](../README.md) -Compile the firmware for the device and download the `firmware.bin` file -to the device to which the serial adapter is connected. +Setup an ESPHome Project (see [README.md](../README.md)),compile the firmware +for the device and download the `firmware.bin` file to the device to which +the serial adapter is connected. You can flash the device using esphome or esptool. I normally use the [esphome-flasher](https://github.com/esphome/esphome-flasher) @@ -187,5 +187,3 @@ https://github.com/arendst/Tasmota/tree/firmware/firmware/tasmota32/ESP32_needed (remember that the [esphome-flasher](https://github.com/esphome/esphome-flasher) will give you a bit less of a hard-core experience during flashing) - - diff --git a/doc/example.yaml b/doc/example.yaml index 5fad226..e92a90b 100644 --- a/doc/example.yaml +++ b/doc/example.yaml @@ -94,6 +94,26 @@ light: name: "Fast Random" transition_length: 3s update_interval: 3s + # You can define one or more groups of presets. These presets can + # be activated using various "preset.activate" action options. + # The presets can for example be used to mimic the behavior of the + # original firmware (tapping the color button = go to next preset, + # holding the color button = switch between RGB and white light mode). + # These bindings have been setup below, using the binary_sensor for + # the color button. + presets: + rgb: + red: { red: 100%, green: 0%, blue: 0% } + green: { red: 0%, green: 100%, blue: 0% } + blue: { red: 0%, green: 0%, blue: 100% } + yellow: { red: 100%, green: 100%, blue: 0% } + purple: { red: 100%, green: 0%, blue: 100% } + randomize: { effect: Fast Random } + white: + cold: { color_temperature: 153 mireds } + chilly: { color_temperature: 275 mireds } + luke: { color_temperature: 400 mireds } + warm: { color_temperature: 588 mireds } # This text sensor propagates the currently active light mode. # The possible light modes are: "off", "rgb", "white" and "night". @@ -135,17 +155,22 @@ binary_sensor: blue: 1 brightness: 0.01 - # When touching the color button, set a random color. + # When touching the color button, acivate the next preset. + # When holding the color button, activate the next preset group. - platform: xiaomi_bslamp2 id: ${id_color_button} part: color button - on_press: - then: - - light.turn_on: - id: ${id_light} - red: !lambda return random_float(); - green: !lambda return random_float(); - blue: !lambda return random_float(); + on_multi_click: + - timing: + - ON for at most 0.6s + then: + - preset.activate: + next: preset + - timing: + - ON for at least 0.6s + then: + - preset.activate: + next: group # This sensor component publishes touch events for the front panel slider. # The published value represents the level at which the slider was touched. diff --git a/front_panel_hal.h b/front_panel_hal.h index 19e6580..a191763 100644 --- a/front_panel_hal.h +++ b/front_panel_hal.h @@ -185,8 +185,8 @@ public: } void dump_config() { - ESP_LOGCONFIG(TAG, "I2C interrupt"); - LOG_PIN(" Interrupt pin: ", trigger_pin_); + ESP_LOGCONFIG(TAG, "FrontPanelHAL:"); + LOG_PIN(" I2C interrupt pin: ", trigger_pin_); } void loop() { diff --git a/light/__init__.py b/light/__init__.py index e4525b4..025113d 100644 --- a/light/__init__.py +++ b/light/__init__.py @@ -2,9 +2,11 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light from esphome import automation +from esphome.core import coroutine from esphome.const import ( - CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, - CONF_OUTPUT_ID, CONF_TRIGGER_ID, CONF_ID + 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 @@ -13,14 +15,54 @@ 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" +CONF_GROUP = "group" +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: 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( @@ -28,16 +70,120 @@ CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BrightnessTrigger), } ), + cv.GenerateID(CONF_PRESETS_ID): cv.declare_id(PresetsContainer), + cv.Optional(CONF_PRESETS): cv.Schema({ + str.lower: cv.Schema({ + str.lower: PRESET_SCHEMA + }) + }), } ) -def to_code(config): - var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) - yield light.register_light(var, config) +def is_preset_group(value): + return value + +def is_preset(value): + return value +def maybe_simple_preset_action(schema): + def validator(value): + if isinstance(value, dict): + return schema(value) + value = value.lower() + conf = {} + if value == "next_group": + conf[CONF_NEXT] = CONF_GROUP + elif value == "next_preset": + conf[CONF_NEXT] = CONF_PRESET + elif "." not in value: + conf[CONF_GROUP] = value + else: + group, preset = value.split(".", 2) + conf[CONF_GROUP] = group + conf[CONF_PRESET] = preset + return schema(conf) + + return validator + +@automation.register_action( + "preset.activate", + ActivatePresetAction, + cv.Schema( + maybe_simple_preset_action(cv.Any( + cv.Schema({ + cv.GenerateID(CONF_PRESETS_ID): cv.use_id(PresetsContainer), + cv.Required(CONF_GROUP): is_preset_group, + cv.Optional(CONF_PRESET): is_preset + }), + cv.Schema({ + cv.GenerateID(CONF_PRESETS_ID): cv.use_id(PresetsContainer), + cv.Required(CONF_NEXT): cv.one_of(CONF_GROUP, CONF_PRESET, lower=True) + }) + )) + ) +) +def preset_activate_to_code(config, action_id, template_arg, args): + presets_var = yield cg.get_variable(config[CONF_PRESETS_ID]) + action_var = cg.new_Pvariable(action_id, template_arg, presets_var) + if CONF_NEXT in config: + cg.add(action_var.set_operation(f"next_{config[CONF_NEXT]}")) + elif CONF_PRESET in config: + cg.add(action_var.set_operation("activate_preset")) + cg.add(action_var.set_group(config[CONF_GROUP])) + cg.add(action_var.set_preset(config[CONF_PRESET])) + else: + cg.add(action_var.set_operation("activate_group")) + cg.add(action_var.set_group(config[CONF_GROUP])) + yield action_var + +@coroutine +def light_output_to_code(config): + light_output_var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) + yield light.register_light(light_output_var, config) light_hal_var = yield cg.get_variable(config[CONF_LIGHT_HAL_ID]) - cg.add(var.set_parent(light_hal_var)) + cg.add(light_output_var.set_parent(light_hal_var)) +@coroutine +def on_brightness_to_code(config): + light_output_var = yield cg.get_variable(config[CONF_OUTPUT_ID]) for conf in config.get(CONF_ON_BRIGHTNESS, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + 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): + 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 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) + yield on_brightness_to_code(config) + yield presets_to_code(config) diff --git a/light/automation.h b/light/automation.h index d463dda..2460798 100644 --- a/light/automation.h +++ b/light/automation.h @@ -4,6 +4,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "light_output.h" +#include "presets.h" namespace esphome { namespace xiaomi { @@ -28,6 +29,35 @@ protected: float last_brightness_ = -1.0f; }; +template class ActivatePresetAction : public Action { +public: + explicit ActivatePresetAction(PresetsContainer *presets) : presets_(presets) {} + + TEMPLATABLE_VALUE(std::string, operation); + TEMPLATABLE_VALUE(std::string, group); + TEMPLATABLE_VALUE(std::string, preset); + + void play(Ts... x) override { + auto operation = this->operation_.value(x...); + + if (operation == "next_group") { + presets_->activate_next_group(); + } else if (operation == "next_preset") { + presets_->activate_next_preset(); + } else if (operation == "activate_group") { + auto group = this->group_.value(x...); + presets_->activate_group(group); + } else if (operation == "activate_preset") { + auto group = this->group_.value(x...); + auto preset = this->preset_.value(x...); + presets_->activate_preset(group, preset); + } + } + +protected: + PresetsContainer *presets_; +}; + } // namespace bslamp2 } // namespace xiaomi } // namespace esphome diff --git a/light/color_night_light.h b/light/color_night_light.h index 310f4c9..1c48d7a 100644 --- a/light/color_night_light.h +++ b/light/color_night_light.h @@ -1,6 +1,7 @@ #pragma once #include "../common.h" +#include "light_modes.h" #include "gpio_outputs.h" namespace esphome { diff --git a/light/color_off.h b/light/color_off.h index 6e684dd..8ffa236 100644 --- a/light/color_off.h +++ b/light/color_off.h @@ -1,9 +1,7 @@ #pragma once -#include -#include - #include "../common.h" +#include "light_modes.h" #include "gpio_outputs.h" namespace esphome { diff --git a/light/color_rgb_light.h b/light/color_rgb_light.h index 9eb2a68..7a3a6a9 100644 --- a/light/color_rgb_light.h +++ b/light/color_rgb_light.h @@ -4,6 +4,7 @@ #include #include "../common.h" +#include "light_modes.h" #include "gpio_outputs.h" namespace esphome { diff --git a/light/color_white_light.h b/light/color_white_light.h index b41e4ce..5042be7 100644 --- a/light/color_white_light.h +++ b/light/color_white_light.h @@ -4,6 +4,7 @@ #include #include "../common.h" +#include "light_modes.h" #include "gpio_outputs.h" namespace esphome { diff --git a/light/gpio_outputs.h b/light/gpio_outputs.h index bb7c995..f8c7877 100644 --- a/light/gpio_outputs.h +++ b/light/gpio_outputs.h @@ -1,16 +1,11 @@ #pragma once +#include "light_modes.h" + namespace esphome { namespace xiaomi { namespace bslamp2 { -// Light modes that can be reported by implementations of GPIOOutputs. -static const std::string LIGHT_MODE_UNKNOWN { "unknown" }; -static const std::string LIGHT_MODE_OFF { "off" }; -static const std::string LIGHT_MODE_RGB { "rgb" }; -static const std::string LIGHT_MODE_WHITE { "white" }; -static const std::string LIGHT_MODE_NIGHT { "night" }; - /** * This abstract class is used for implementing classes that translate * LightColorValues into the required GPIO PWM duty cycle levels to represent diff --git a/light/light_modes.h b/light/light_modes.h new file mode 100644 index 0000000..6df31a1 --- /dev/null +++ b/light/light_modes.h @@ -0,0 +1,15 @@ +#pragma once + +namespace esphome { +namespace xiaomi { +namespace bslamp2 { + +static const std::string LIGHT_MODE_UNKNOWN { "unknown" }; +static const std::string LIGHT_MODE_OFF { "off" }; +static const std::string LIGHT_MODE_RGB { "rgb" }; +static const std::string LIGHT_MODE_WHITE { "white" }; +static const std::string LIGHT_MODE_NIGHT { "night" }; + +} // namespace bslamp2 +} // namespace xiaomi +} // namespace esphome diff --git a/light/light_output.h b/light/light_output.h index c58c1b6..0c4afd0 100644 --- a/light/light_output.h +++ b/light/light_output.h @@ -108,26 +108,6 @@ protected: transition_handler_ = new ColorTransitionHandler(exposer); } }; - -/** - * This custom LightState class is used to provide access to the protected - * LightTranformer information in the LightState class. - * - * This class is used by the ColorTransitionHandler class to inspect if - * an ongoing light color transition is active in a LightState object. - */ -class XiaomiBslamp2LightState : public light::LightState, public LightStateTransformerInspector -{ -public: - XiaomiBslamp2LightState(const std::string &name, XiaomiBslamp2LightOutput *output) : light::LightState(name, output) { - output->set_transformer_inspector(this); - } - - bool is_active() { return transformer_ != nullptr; } - bool is_transition() { return transformer_->is_transition(); } - light::LightColorValues get_end_values() { return transformer_->get_end_values(); } - float get_progress() { return transformer_->get_progress(); } -}; } // namespace bslamp2 } // namespace xiaomi diff --git a/light/light_state.h b/light/light_state.h new file mode 100644 index 0000000..06d72ca --- /dev/null +++ b/light/light_state.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../common.h" + +namespace esphome { +namespace xiaomi { +namespace bslamp2 { + +/** + * This custom LightState class is used to provide access to the protected + * LightTranformer information in the LightState class. + * + * This class is used by the ColorTransitionHandler class to inspect if + * an ongoing light color transition is active in the LightState object. + */ +class XiaomiBslamp2LightState : public light::LightState, public LightStateTransformerInspector +{ +public: + XiaomiBslamp2LightState(const std::string &name, XiaomiBslamp2LightOutput *output) : light::LightState(name, output) { + output->set_transformer_inspector(this); + } + + bool is_active() { return transformer_ != nullptr; } + bool is_transition() { return transformer_->is_transition(); } + light::LightColorValues get_end_values() { return transformer_->get_end_values(); } + float get_progress() { return transformer_->get_progress(); } +}; + +} // namespace bslamp2 +} // namespace xiaomi +} // namespace esphome diff --git a/light/presets.h b/light/presets.h new file mode 100644 index 0000000..bd6c95c --- /dev/null +++ b/light/presets.h @@ -0,0 +1,210 @@ +#pragma once + +#include "../common.h" +#include "esphome/core/optional.h" + +namespace esphome { +namespace xiaomi { +namespace bslamp2 { + +class Preset : public Component { +public: + std::string group_name; + std::string name; + Preset *next_preset = nullptr; + + explicit Preset(light::LightState *light, std::string group_name, std::string name): + group_name(group_name), name(name), light_state_(light) {} + + 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()); + 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 { +public: + std::string name; + PresetGroup *next_group = nullptr; + Preset *first_preset = nullptr; + Preset *last_preset = nullptr; + Preset *active_preset = nullptr; + + explicit PresetGroup(std::string g_name) : name(g_name) {} + + 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; + } + } + + Preset *get_preset(std::string p_name) { + for (auto p = first_preset; p != nullptr; p = p->next_preset) { + if (p->name == p_name) { + return p; + } + } + return nullptr; + } +}; + +class PresetsContainer : public Component { +public: + PresetGroup *first_group = nullptr; + PresetGroup *last_group = nullptr; + PresetGroup *active_group = nullptr; + + void dump_config() { + if (first_group == nullptr) { + return; + } + ESP_LOGCONFIG(TAG, "Light Presets:"); + for (auto g = first_group; g != nullptr; g = g->next_group) { + ESP_LOGCONFIG(TAG, " Preset group: %s", g->name.c_str()); + for (auto p = g->first_preset; p != nullptr; p = p->next_preset) { + ESP_LOGCONFIG(TAG, " Preset: %s", p->name.c_str()); + } + } + } + + void add_preset(Preset *preset) { + auto g = make_preset_group_(preset->group_name); + g->add_preset(preset); + } + + PresetGroup *get_group(std::string g_name) { + for (auto g = first_group; g != nullptr; g = g->next_group) { + if (g->name == g_name) { + return g; + } + } + return nullptr; + } + + void activate_next_group() { + if (active_group == nullptr) { + ESP_LOGW(TAG, "activate_next_group(): no preset groups defined"); + return; + } + active_group = active_group->next_group == nullptr + ? first_group : active_group->next_group; + if (active_group->active_preset == nullptr) { + ESP_LOGW(TAG, "activate_next_group(): no presets defined for group %s", + active_group->name.c_str()); + return; + } + active_group->active_preset->apply(); + } + + void activate_next_preset() { + if (active_group == nullptr) { + ESP_LOGW(TAG, "activate_next_preset(): no preset groups defined"); + return; + } + auto p = active_group->active_preset; + if (p == nullptr) { + ESP_LOGW(TAG, "activate_next_preset(): no presets defined for group %s", + active_group->name.c_str()); + return; + } + active_group->active_preset = p->next_preset == nullptr + ? active_group->first_preset : p->next_preset; + active_group->active_preset->apply(); + } + + void activate_group(std::string g_name) { + auto g = get_group(g_name); + if (g == nullptr) { + ESP_LOGE(TAG, "activate_group(%s): preset group does not exist", + g_name.c_str()); + return; + } + auto p = g->active_preset; + if (p == nullptr) { + ESP_LOGW(TAG, "activate_group(%s): no presets defined for group", + g_name.c_str()); + return; + } + p->apply(); + } + + 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", + 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()); + return; + } + p->apply(); + } + +protected: + PresetGroup *make_preset_group_(std::string g_name) { + auto g = get_group(g_name); + if (g == nullptr) { + g = new PresetGroup(g_name); + if (first_group == nullptr) { + first_group = last_group = active_group = g; + } else { + last_group->next_group = g; + last_group = g; + } + } + return g; + } +}; + +} // namespace bslamp2 +} // namespace xiaomi +} // namespace esphome diff --git a/sensor/slider_sensor.h b/sensor/slider_sensor.h index ebb94eb..f329c21 100644 --- a/sensor/slider_sensor.h +++ b/sensor/slider_sensor.h @@ -48,7 +48,7 @@ public: } void dump_config() { - ESP_LOGCONFIG(TAG, "Front Panel slider sensor:"); + ESP_LOGCONFIG(TAG, "Front panel slider sensor:"); ESP_LOGCONFIG(TAG, " Range from: %f", range_from_); ESP_LOGCONFIG(TAG, " Range to: %f", range_to_); }