Browse Source

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.

pull/9/head
Maurice Makaay 3 years ago
parent
commit
5fc4290eb6
2 changed files with 123 additions and 64 deletions
  1. +62
    -21
      light/__init__.py
  2. +61
    -43
      light/presets.h

+ 62
- 21
light/__init__.py View File

@ -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)


+ 61
- 43
light/presets.h View File

@ -1,30 +1,69 @@
#pragma once
#include "../common.h"
#include "esphome/core/optional.h"
#include <functional>
#include <vector>
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<uint32_t> transition_length_;
optional<float> brightness_;
optional<float> red_;
optional<float> green_;
optional<float> blue_;
optional<float> color_temperature_;
optional<std::string> 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


Loading…
Cancel
Save