mirror of
https://github.com/esphome/esphome.git
synced 2024-11-22 15:08:10 +01:00
Addressable updates
This commit is contained in:
parent
766f6c045d
commit
595dfe7e24
13 changed files with 193 additions and 41 deletions
|
@ -27,11 +27,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight {
|
||||||
|
|
||||||
inline int32_t size() const override { return this->num_leds_; }
|
inline int32_t size() const override { return this->num_leds_; }
|
||||||
|
|
||||||
inline light::ESPColorView operator[](int32_t index) const override {
|
|
||||||
return light::ESPColorView(&this->leds_[index].r, &this->leds_[index].g, &this->leds_[index].b, nullptr,
|
|
||||||
&this->effect_data_[index], &this->correction_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
|
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
|
||||||
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
|
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
|
||||||
|
|
||||||
|
@ -236,6 +231,11 @@ class FastLEDLightOutput : public Component, public light::AddressableLight {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
light::ESPColorView get_view_internal(int32_t index) const override {
|
||||||
|
return {&this->leds_[index].r, &this->leds_[index].g, &this->leds_[index].b, nullptr,
|
||||||
|
&this->effect_data_[index], &this->correction_};
|
||||||
|
}
|
||||||
|
|
||||||
CLEDController *controller_{nullptr};
|
CLEDController *controller_{nullptr};
|
||||||
CRGB *leds_{nullptr};
|
CRGB *leds_{nullptr};
|
||||||
uint8_t *effect_data_{nullptr};
|
uint8_t *effect_data_{nullptr};
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace light {
|
namespace light {
|
||||||
|
|
||||||
|
const ESPColor ESPColor::BLACK = ESPColor(0, 0, 0, 0);
|
||||||
|
const ESPColor ESPColor::WHITE = ESPColor(255, 255, 255, 255);
|
||||||
|
|
||||||
ESPColor ESPHSVColor::to_rgb() const {
|
ESPColor ESPHSVColor::to_rgb() const {
|
||||||
// based on FastLED's hsv rainbow to rgb
|
// based on FastLED's hsv rainbow to rgb
|
||||||
const uint8_t hue = this->hue;
|
const uint8_t hue = this->hue;
|
||||||
|
@ -76,9 +79,18 @@ void ESPRangeView::set(const ESPColor &color) {
|
||||||
(*this->parent_)[i] = color;
|
(*this->parent_)[i] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ESPColorView ESPRangeView::operator[](int32_t index) const { return (*this->parent_)[index]; }
|
ESPColorView ESPRangeView::operator[](int32_t index) const {
|
||||||
|
index = interpret_index(index, this->size());
|
||||||
|
return (*this->parent_)[index];
|
||||||
|
}
|
||||||
|
|
||||||
ESPColorView ESPRangeView::Iterator::operator*() const { return (*this->range_->parent_)[this->i_]; }
|
ESPColorView ESPRangeView::Iterator::operator*() const { return (*this->range_->parent_)[this->i_]; }
|
||||||
|
|
||||||
|
int32_t HOT interpret_index(int32_t index, int32_t size) {
|
||||||
|
if (index < 0)
|
||||||
|
return size + index;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace light
|
} // namespace light
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -147,6 +147,9 @@ struct ESPColor {
|
||||||
ESPColor fade_to_black(uint8_t amnt) { return *this * amnt; }
|
ESPColor fade_to_black(uint8_t amnt) { return *this * amnt; }
|
||||||
ESPColor lighten(uint8_t delta) { return *this + delta; }
|
ESPColor lighten(uint8_t delta) { return *this + delta; }
|
||||||
ESPColor darken(uint8_t delta) { return *this - delta; }
|
ESPColor darken(uint8_t delta) { return *this - delta; }
|
||||||
|
|
||||||
|
static const ESPColor BLACK;
|
||||||
|
static const ESPColor WHITE;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ESPHSVColor {
|
struct ESPHSVColor {
|
||||||
|
@ -353,6 +356,8 @@ class ESPColorView : public ESPColorSettable {
|
||||||
|
|
||||||
class AddressableLight;
|
class AddressableLight;
|
||||||
|
|
||||||
|
int32_t interpret_index(int32_t index, int32_t size);
|
||||||
|
|
||||||
class ESPRangeView : public ESPColorSettable {
|
class ESPRangeView : public ESPColorSettable {
|
||||||
public:
|
public:
|
||||||
class Iterator {
|
class Iterator {
|
||||||
|
@ -385,6 +390,10 @@ class ESPRangeView : public ESPColorSettable {
|
||||||
this->set(rhs);
|
this->set(rhs);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
ESPRangeView &operator=(const ESPColorView &rhs) {
|
||||||
|
this->set(rhs.get());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
ESPRangeView &operator=(const ESPHSVColor &rhs) {
|
ESPRangeView &operator=(const ESPHSVColor &rhs) {
|
||||||
this->set_hsv(rhs);
|
this->set_hsv(rhs);
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -463,9 +472,14 @@ class ESPRangeView : public ESPColorSettable {
|
||||||
class AddressableLight : public LightOutput {
|
class AddressableLight : public LightOutput {
|
||||||
public:
|
public:
|
||||||
virtual int32_t size() const = 0;
|
virtual int32_t size() const = 0;
|
||||||
virtual ESPColorView operator[](int32_t index) const = 0;
|
ESPColorView operator[](int32_t index) const { return this->get_view_internal(interpret_index(index, this->size())); }
|
||||||
|
ESPColorView get(int32_t index) { return this->get_view_internal(interpret_index(index, this->size())); }
|
||||||
virtual void clear_effect_data() = 0;
|
virtual void clear_effect_data() = 0;
|
||||||
ESPRangeView range(int32_t from, int32_t to) { return ESPRangeView(this, from, to); }
|
ESPRangeView range(int32_t from, int32_t to) {
|
||||||
|
from = interpret_index(from, this->size());
|
||||||
|
to = interpret_index(to, this->size());
|
||||||
|
return ESPRangeView(this, from, to);
|
||||||
|
}
|
||||||
ESPRangeView all() { return ESPRangeView(this, 0, this->size()); }
|
ESPRangeView all() { return ESPRangeView(this, 0, this->size()); }
|
||||||
ESPRangeView::Iterator begin() { return this->all().begin(); }
|
ESPRangeView::Iterator begin() { return this->all().begin(); }
|
||||||
ESPRangeView::Iterator end() { return this->all().end(); }
|
ESPRangeView::Iterator end() { return this->all().end(); }
|
||||||
|
@ -476,7 +490,7 @@ class AddressableLight : public LightOutput {
|
||||||
}
|
}
|
||||||
if (amnt > this->size())
|
if (amnt > this->size())
|
||||||
amnt = this->size();
|
amnt = this->size();
|
||||||
this->range(0, this->size() - amnt) = this->range(amnt, this->size());
|
this->range(0, -amnt) = this->range(amnt, this->size());
|
||||||
}
|
}
|
||||||
void shift_right(int32_t amnt) {
|
void shift_right(int32_t amnt) {
|
||||||
if (amnt < 0) {
|
if (amnt < 0) {
|
||||||
|
@ -485,7 +499,7 @@ class AddressableLight : public LightOutput {
|
||||||
}
|
}
|
||||||
if (amnt > this->size())
|
if (amnt > this->size())
|
||||||
amnt = this->size();
|
amnt = this->size();
|
||||||
this->range(amnt, this->size()) = this->range(0, this->size() - amnt);
|
this->range(amnt, this->size()) = this->range(0, -amnt);
|
||||||
}
|
}
|
||||||
bool is_effect_active() const { return this->effect_active_; }
|
bool is_effect_active() const { return this->effect_active_; }
|
||||||
void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; }
|
void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; }
|
||||||
|
@ -516,6 +530,7 @@ class AddressableLight : public LightOutput {
|
||||||
protected:
|
protected:
|
||||||
bool should_show_() const { return this->effect_active_ || this->next_show_; }
|
bool should_show_() const { return this->effect_active_ || this->next_show_; }
|
||||||
void mark_shown_() { this->next_show_ = false; }
|
void mark_shown_() { this->next_show_ = false; }
|
||||||
|
virtual ESPColorView get_view_internal(int32_t index) const = 0;
|
||||||
|
|
||||||
bool effect_active_{false};
|
bool effect_active_{false};
|
||||||
bool next_show_{true};
|
bool next_show_{true};
|
||||||
|
|
|
@ -113,11 +113,10 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
|
||||||
it.shift_right(1);
|
it.shift_right(1);
|
||||||
const AddressableColorWipeEffectColor color = this->colors_[this->at_color_];
|
const AddressableColorWipeEffectColor color = this->colors_[this->at_color_];
|
||||||
const ESPColor esp_color = ESPColor(color.r, color.g, color.b, color.w);
|
const ESPColor esp_color = ESPColor(color.r, color.g, color.b, color.w);
|
||||||
if (!this->reverse_) {
|
if (!this->reverse_)
|
||||||
it[it.size() - 1] = esp_color;
|
it[-1] = esp_color;
|
||||||
} else {
|
else
|
||||||
it[0] = esp_color;
|
it[0] = esp_color;
|
||||||
}
|
|
||||||
if (++this->leds_added_ >= color.num_leds) {
|
if (++this->leds_added_ >= color.num_leds) {
|
||||||
this->leds_added_ = 0;
|
this->leds_added_ = 0;
|
||||||
this->at_color_ = (this->at_color_ + 1) % this->colors_.size();
|
this->at_color_ = (this->at_color_ + 1) % this->colors_.size();
|
||||||
|
@ -145,7 +144,7 @@ class AddressableScanEffect : public AddressableLightEffect {
|
||||||
explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {}
|
explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {}
|
||||||
void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; }
|
void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; }
|
||||||
void apply(AddressableLight &it, const ESPColor ¤t_color) override {
|
void apply(AddressableLight &it, const ESPColor ¤t_color) override {
|
||||||
it.all() = ESPColor(0, 0, 0, 0);
|
it.all() = ESPColor::BLACK;
|
||||||
it[this->at_led_] = current_color;
|
it[this->at_led_] = current_color;
|
||||||
const uint32_t now = millis();
|
const uint32_t now = millis();
|
||||||
if (now - this->last_move_ > this->move_interval_) {
|
if (now - this->last_move_ > this->move_interval_) {
|
||||||
|
@ -190,7 +189,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect {
|
||||||
else
|
else
|
||||||
view.set_effect_data(new_pos);
|
view.set_effect_data(new_pos);
|
||||||
} else {
|
} else {
|
||||||
view = ESPColor(0, 0, 0, 0);
|
view = ESPColor::BLACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (random_float() < this->twinkle_probability_) {
|
while (random_float() < this->twinkle_probability_) {
|
||||||
|
@ -220,8 +219,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect {
|
||||||
this->last_progress_ = now;
|
this->last_progress_ = now;
|
||||||
}
|
}
|
||||||
uint8_t subsine = ((8 * (now - this->last_progress_)) / this->progress_interval_) & 0b111;
|
uint8_t subsine = ((8 * (now - this->last_progress_)) / this->progress_interval_) & 0b111;
|
||||||
for (auto &&i : it) {
|
for (auto view : it) {
|
||||||
ESPColorView view = i;
|
|
||||||
if (view.get_effect_data() != 0) {
|
if (view.get_effect_data() != 0) {
|
||||||
const uint8_t x = (view.get_effect_data() >> 3) & 0b11111;
|
const uint8_t x = (view.get_effect_data() >> 3) & 0b11111;
|
||||||
const uint8_t color = view.get_effect_data() & 0b111;
|
const uint8_t color = view.get_effect_data() & 0b111;
|
||||||
|
@ -261,9 +259,8 @@ class AddressableFireworksEffect : public AddressableLightEffect {
|
||||||
public:
|
public:
|
||||||
explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {}
|
explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {}
|
||||||
void start() override {
|
void start() override {
|
||||||
const auto &it = *this->get_addressable_();
|
auto &it = *this->get_addressable_();
|
||||||
for (int i = 0; i < it.size(); i++)
|
it.all() = ESPColor::BLACK;
|
||||||
it[i] = ESPColor(0, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
void apply(AddressableLight &it, const ESPColor ¤t_color) override {
|
void apply(AddressableLight &it, const ESPColor ¤t_color) override {
|
||||||
const uint32_t now = millis();
|
const uint32_t now = millis();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "light_state.h"
|
#include "light_state.h"
|
||||||
|
#include "addressable_light.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace light {
|
namespace light {
|
||||||
|
@ -83,7 +84,7 @@ template<typename... Ts> class DimRelativeAction : public Action<Ts...> {
|
||||||
template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> {
|
template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit LightIsOnCondition(LightState *state) : state_(state) {}
|
explicit LightIsOnCondition(LightState *state) : state_(state) {}
|
||||||
bool check(Ts... x) override { return this->state_->get_current_values().is_on(); }
|
bool check(Ts... x) override { return this->state_->current_values.is_on(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LightState *state_;
|
LightState *state_;
|
||||||
|
@ -91,11 +92,41 @@ template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> {
|
||||||
template<typename... Ts> class LightIsOffCondition : public Condition<LightState, Ts...> {
|
template<typename... Ts> class LightIsOffCondition : public Condition<LightState, Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit LightIsOffCondition(LightState *state) : state_(state) {}
|
explicit LightIsOffCondition(LightState *state) : state_(state) {}
|
||||||
bool check(Ts... x) override { return !this->state_->get_current_values().is_on(); }
|
bool check(Ts... x) override { return !this->state_->current_values.is_on(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LightState *state_;
|
LightState *state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AddressableSet : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit AddressableSet(LightState *parent) : parent_(parent) {}
|
||||||
|
|
||||||
|
TEMPLATABLE_VALUE(int32_t, range_from)
|
||||||
|
TEMPLATABLE_VALUE(int32_t, range_to)
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, red)
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, green)
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, blue)
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, white)
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
auto *out = (AddressableLight *) this->parent_->get_output();
|
||||||
|
int32_t range_from = this->range_from_.value_or(x..., 0);
|
||||||
|
int32_t range_to = this->range_to_.value_or(x..., out->size());
|
||||||
|
auto range = out->range(range_from, range_to);
|
||||||
|
if (this->red_.has_value())
|
||||||
|
range.set_red(this->red_.value(x...));
|
||||||
|
if (this->green_.has_value())
|
||||||
|
range.set_green(this->green_.value(x...));
|
||||||
|
if (this->blue_.has_value())
|
||||||
|
range.set_blue(this->blue_.value(x...));
|
||||||
|
if (this->white_.has_value())
|
||||||
|
range.set_white(this->white_.value(x...));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LightState *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace light
|
} // namespace light
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "light_effect.h"
|
#include "light_effect.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace light {
|
namespace light {
|
||||||
|
@ -63,6 +64,21 @@ class LambdaLightEffect : public LightEffect {
|
||||||
uint32_t last_run_{0};
|
uint32_t last_run_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AutomationLightEffect : public LightEffect {
|
||||||
|
public:
|
||||||
|
AutomationLightEffect(const std::string &name) : LightEffect(name) {}
|
||||||
|
void stop() override { this->trig_->stop(); }
|
||||||
|
void apply() override {
|
||||||
|
if (!this->trig_->is_running()) {
|
||||||
|
this->trig_->trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Trigger<> *get_trig() const { return trig_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Trigger<> *trig_{new Trigger<>};
|
||||||
|
};
|
||||||
|
|
||||||
struct StrobeLightEffectColor {
|
struct StrobeLightEffectColor {
|
||||||
LightColorValues color;
|
LightColorValues color;
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation
|
||||||
from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \
|
from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \
|
||||||
CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \
|
CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \
|
||||||
CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM
|
CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \
|
||||||
|
CONF_THEN
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \
|
from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \
|
||||||
StrobeLightEffectColor, LightColorValues, AddressableLightRef, AddressableLambdaLightEffect, \
|
StrobeLightEffectColor, LightColorValues, AddressableLightRef, AddressableLambdaLightEffect, \
|
||||||
FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \
|
FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \
|
||||||
AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \
|
AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \
|
||||||
AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect
|
AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect, \
|
||||||
|
AutomationLightEffect
|
||||||
|
|
||||||
CONF_ADD_LED_INTERVAL = 'add_led_interval'
|
CONF_ADD_LED_INTERVAL = 'add_led_interval'
|
||||||
CONF_REVERSE = 'reverse'
|
CONF_REVERSE = 'reverse'
|
||||||
|
@ -28,8 +31,9 @@ CONF_ADDRESSABLE_TWINKLE = 'addressable_twinkle'
|
||||||
CONF_ADDRESSABLE_RANDOM_TWINKLE = 'addressable_random_twinkle'
|
CONF_ADDRESSABLE_RANDOM_TWINKLE = 'addressable_random_twinkle'
|
||||||
CONF_ADDRESSABLE_FIREWORKS = 'addressable_fireworks'
|
CONF_ADDRESSABLE_FIREWORKS = 'addressable_fireworks'
|
||||||
CONF_ADDRESSABLE_FLICKER = 'addressable_flicker'
|
CONF_ADDRESSABLE_FLICKER = 'addressable_flicker'
|
||||||
|
CONF_AUTOMATION = 'automation'
|
||||||
|
|
||||||
BINARY_EFFECTS = ['lambda', 'strobe']
|
BINARY_EFFECTS = ['lambda', 'automation', 'strobe']
|
||||||
MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + ['flicker']
|
MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + ['flicker']
|
||||||
RGB_EFFECTS = MONOCHROMATIC_EFFECTS + ['random']
|
RGB_EFFECTS = MONOCHROMATIC_EFFECTS + ['random']
|
||||||
ADDRESSABLE_EFFECTS = RGB_EFFECTS + [CONF_ADDRESSABLE_LAMBDA, CONF_ADDRESSABLE_RAINBOW,
|
ADDRESSABLE_EFFECTS = RGB_EFFECTS + [CONF_ADDRESSABLE_LAMBDA, CONF_ADDRESSABLE_RAINBOW,
|
||||||
|
@ -58,6 +62,15 @@ def lambda_effect_to_code(config, effect_id):
|
||||||
config[CONF_UPDATE_INTERVAL])
|
config[CONF_UPDATE_INTERVAL])
|
||||||
|
|
||||||
|
|
||||||
|
@register_effect('automation', AutomationLightEffect, "Automation", {
|
||||||
|
cv.Required(CONF_THEN): automation.validate_automation(single=True),
|
||||||
|
})
|
||||||
|
def automation_effect_to_code(config, effect_id):
|
||||||
|
var = yield cg.new_Pvariable(effect_id, config[CONF_NAME])
|
||||||
|
yield automation.build_automation(var.get_trig(), [], config[CONF_THEN])
|
||||||
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@register_effect('random', RandomLightEffect, "Random", {
|
@register_effect('random', RandomLightEffect, "Random", {
|
||||||
cv.Optional(CONF_TRANSITION_LENGTH, default='7.5s'): cv.positive_time_period_milliseconds,
|
cv.Optional(CONF_TRANSITION_LENGTH, default='7.5s'): cv.positive_time_period_milliseconds,
|
||||||
cv.Optional(CONF_UPDATE_INTERVAL, default='10s'): cv.positive_time_period_milliseconds,
|
cv.Optional(CONF_UPDATE_INTERVAL, default='10s'): cv.positive_time_period_milliseconds,
|
||||||
|
|
|
@ -20,6 +20,7 @@ DimRelativeAction = light_ns.class_('DimRelativeAction', automation.Action)
|
||||||
LightEffect = light_ns.class_('LightEffect')
|
LightEffect = light_ns.class_('LightEffect')
|
||||||
RandomLightEffect = light_ns.class_('RandomLightEffect', LightEffect)
|
RandomLightEffect = light_ns.class_('RandomLightEffect', LightEffect)
|
||||||
LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect)
|
LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect)
|
||||||
|
AutomationLightEffect = light_ns.class_('AutomationLightEffect', LightEffect)
|
||||||
StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect)
|
StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect)
|
||||||
StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', LightEffect)
|
StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', LightEffect)
|
||||||
FlickerLightEffect = light_ns.class_('FlickerLightEffect', LightEffect)
|
FlickerLightEffect = light_ns.class_('FlickerLightEffect', LightEffect)
|
||||||
|
|
|
@ -144,29 +144,24 @@ class NeoPixelBusLightOutputBase : public Component, public light::AddressableLi
|
||||||
template<typename T_METHOD, typename T_COLOR_FEATURE = NeoRgbFeature>
|
template<typename T_METHOD, typename T_COLOR_FEATURE = NeoRgbFeature>
|
||||||
class NeoPixelRGBLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COLOR_FEATURE> {
|
class NeoPixelRGBLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COLOR_FEATURE> {
|
||||||
public:
|
public:
|
||||||
inline light::ESPColorView operator[](int32_t index) const override {
|
|
||||||
uint8_t *base = this->controller_->Pixels() + 3ULL * index;
|
|
||||||
return light::ESPColorView(base + this->rgb_offsets_[0], base + this->rgb_offsets_[1], base + this->rgb_offsets_[2],
|
|
||||||
nullptr, this->effect_data_ + index, &this->correction_);
|
|
||||||
}
|
|
||||||
|
|
||||||
light::LightTraits get_traits() override {
|
light::LightTraits get_traits() override {
|
||||||
auto traits = light::LightTraits();
|
auto traits = light::LightTraits();
|
||||||
traits.set_supports_brightness(true);
|
traits.set_supports_brightness(true);
|
||||||
traits.set_supports_rgb(true);
|
traits.set_supports_rgb(true);
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
light::ESPColorView get_view_internal(int32_t index) const override {
|
||||||
|
uint8_t *base = this->controller_->Pixels() + 3ULL * index;
|
||||||
|
return light::ESPColorView(base + this->rgb_offsets_[0], base + this->rgb_offsets_[1], base + this->rgb_offsets_[2],
|
||||||
|
nullptr, this->effect_data_ + index, &this->correction_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T_METHOD, typename T_COLOR_FEATURE = NeoRgbwFeature>
|
template<typename T_METHOD, typename T_COLOR_FEATURE = NeoRgbwFeature>
|
||||||
class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COLOR_FEATURE> {
|
class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COLOR_FEATURE> {
|
||||||
public:
|
public:
|
||||||
inline light::ESPColorView operator[](int32_t index) const override {
|
|
||||||
uint8_t *base = this->controller_->Pixels() + 4ULL * index;
|
|
||||||
return light::ESPColorView(base + this->rgb_offsets_[0], base + this->rgb_offsets_[1], base + this->rgb_offsets_[2],
|
|
||||||
base + this->rgb_offsets_[3], this->effect_data_ + index, &this->correction_);
|
|
||||||
}
|
|
||||||
|
|
||||||
light::LightTraits get_traits() override {
|
light::LightTraits get_traits() override {
|
||||||
auto traits = light::LightTraits();
|
auto traits = light::LightTraits();
|
||||||
traits.set_supports_brightness(true);
|
traits.set_supports_brightness(true);
|
||||||
|
@ -174,6 +169,13 @@ class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_CO
|
||||||
traits.set_supports_rgb_white_value(true);
|
traits.set_supports_rgb_white_value(true);
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
light::ESPColorView get_view_internal(int32_t index) const override {
|
||||||
|
uint8_t *base = this->controller_->Pixels() + 4ULL * index;
|
||||||
|
return light::ESPColorView(base + this->rgb_offsets_[0], base + this->rgb_offsets_[1], base + this->rgb_offsets_[2],
|
||||||
|
base + this->rgb_offsets_[3], this->effect_data_ + index, &this->correction_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace neopixelbus
|
} // namespace neopixelbus
|
||||||
|
|
|
@ -8,6 +8,7 @@ script_ns = cg.esphome_ns.namespace('script')
|
||||||
Script = script_ns.class_('Script', automation.Trigger.template())
|
Script = script_ns.class_('Script', automation.Trigger.template())
|
||||||
ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', automation.Action)
|
ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', automation.Action)
|
||||||
ScriptStopAction = script_ns.class_('ScriptStopAction', automation.Action)
|
ScriptStopAction = script_ns.class_('ScriptStopAction', automation.Action)
|
||||||
|
IsRunningCondition = script_ns.class_('IsRunningCondition', automation.Condition)
|
||||||
|
|
||||||
CONFIG_SCHEMA = automation.validate_automation({
|
CONFIG_SCHEMA = automation.validate_automation({
|
||||||
cv.Required(CONF_ID): cv.declare_id(Script),
|
cv.Required(CONF_ID): cv.declare_id(Script),
|
||||||
|
@ -34,3 +35,11 @@ def script_execute_action_to_code(config, action_id, template_arg, args):
|
||||||
def script_stop_action_to_code(config, action_id, template_arg, args):
|
def script_stop_action_to_code(config, action_id, template_arg, args):
|
||||||
paren = yield cg.get_variable(config[CONF_ID])
|
paren = yield cg.get_variable(config[CONF_ID])
|
||||||
yield cg.new_Pvariable(action_id, template_arg, paren)
|
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition('script.is_running', IsRunningCondition, automation.maybe_simple_id({
|
||||||
|
cv.Required(CONF_ID): cv.use_id(Script)
|
||||||
|
}))
|
||||||
|
def script_is_running_to_code(config, condition_id, template_arg, args):
|
||||||
|
paren = yield cg.get_variable(config[CONF_ID])
|
||||||
|
yield cg.new_Pvariable(condition_id, template_arg, paren)
|
||||||
|
|
|
@ -7,7 +7,16 @@ namespace script {
|
||||||
|
|
||||||
class Script : public Trigger<> {
|
class Script : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
void execute() { this->trigger(); }
|
void execute() {
|
||||||
|
bool prev = this->in_stack_;
|
||||||
|
this->in_stack_ = true;
|
||||||
|
this->trigger();
|
||||||
|
this->in_stack_ = prev;
|
||||||
|
}
|
||||||
|
bool script_is_running() { return this->in_stack_ || this->is_running(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool in_stack_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class ScriptExecuteAction : public Action<Ts...> {
|
template<typename... Ts> class ScriptExecuteAction : public Action<Ts...> {
|
||||||
|
@ -30,5 +39,15 @@ template<typename... Ts> class ScriptStopAction : public Action<Ts...> {
|
||||||
Script *script_;
|
Script *script_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class IsRunningCondition : public Condition<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit IsRunningCondition(Script *parent) : parent_(parent) {}
|
||||||
|
|
||||||
|
bool check(Ts... x) override { return this->parent_->script_is_running(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Script *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace script
|
} // namespace script
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -52,6 +52,11 @@ template<typename... Ts> class Trigger {
|
||||||
return;
|
return;
|
||||||
this->automation_parent_->stop();
|
this->automation_parent_->stop();
|
||||||
}
|
}
|
||||||
|
bool is_running() {
|
||||||
|
if (this->automation_parent_ == nullptr)
|
||||||
|
return false;
|
||||||
|
return this->automation_parent_.is_running();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Automation<Ts...> *automation_parent_{nullptr};
|
Automation<Ts...> *automation_parent_{nullptr};
|
||||||
|
@ -81,6 +86,12 @@ template<typename... Ts> class Action {
|
||||||
this->next_->stop_complex();
|
this->next_->stop_complex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual bool is_running() { return this->is_running_next(); }
|
||||||
|
bool is_running_next() {
|
||||||
|
if (this->next_ == nullptr)
|
||||||
|
return false;
|
||||||
|
return this->next_->is_running();
|
||||||
|
}
|
||||||
|
|
||||||
void play_next_tuple(const std::tuple<Ts...> &tuple) {
|
void play_next_tuple(const std::tuple<Ts...> &tuple) {
|
||||||
this->play_next_tuple_(tuple, typename gens<sizeof...(Ts)>::type());
|
this->play_next_tuple_(tuple, typename gens<sizeof...(Ts)>::type());
|
||||||
|
@ -121,6 +132,11 @@ template<typename... Ts> class ActionList {
|
||||||
this->actions_begin_->stop_complex();
|
this->actions_begin_->stop_complex();
|
||||||
}
|
}
|
||||||
bool empty() const { return this->actions_begin_ == nullptr; }
|
bool empty() const { return this->actions_begin_ == nullptr; }
|
||||||
|
bool is_running() {
|
||||||
|
if (this->actions_begin_ == nullptr)
|
||||||
|
return false;
|
||||||
|
return this->actions_begin_->is_running();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template<int... S> void play_tuple_(const std::tuple<Ts...> &tuple, seq<S...>) { this->play(std::get<S>(tuple)...); }
|
template<int... S> void play_tuple_(const std::tuple<Ts...> &tuple, seq<S...>) { this->play(std::get<S>(tuple)...); }
|
||||||
|
@ -140,6 +156,8 @@ template<typename... Ts> class Automation {
|
||||||
|
|
||||||
void trigger(Ts... x) { this->actions_.play(x...); }
|
void trigger(Ts... x) { this->actions_.play(x...); }
|
||||||
|
|
||||||
|
bool is_running() { return this->actions_.is_running(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Trigger<Ts...> *trigger_;
|
Trigger<Ts...> *trigger_;
|
||||||
ActionList<Ts...> actions_;
|
ActionList<Ts...> actions_;
|
||||||
|
|
|
@ -108,16 +108,29 @@ template<typename... Ts> class DelayAction : public Action<Ts...>, public Compon
|
||||||
|
|
||||||
TEMPLATABLE_VALUE(uint32_t, delay)
|
TEMPLATABLE_VALUE(uint32_t, delay)
|
||||||
|
|
||||||
void stop() override { this->cancel_timeout(""); }
|
void stop() override {
|
||||||
|
this->cancel_timeout("");
|
||||||
|
this->num_running_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void play(Ts... x) override { /* ignore - see play_complex */
|
void play(Ts... x) override { /* ignore - see play_complex */
|
||||||
}
|
}
|
||||||
|
|
||||||
void play_complex(Ts... x) override {
|
void play_complex(Ts... x) override {
|
||||||
auto f = std::bind(&DelayAction<Ts...>::play_next, this, x...);
|
auto f = std::bind(&DelayAction<Ts...>::delay_end_, this, x...);
|
||||||
|
this->num_running_++;
|
||||||
this->set_timeout(this->delay_.value(x...), f);
|
this->set_timeout(this->delay_.value(x...), f);
|
||||||
}
|
}
|
||||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
|
bool is_running() override { return this->num_running_ > 0 || this->is_running_next(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void delay_end_(Ts... x) {
|
||||||
|
this->num_running_--;
|
||||||
|
this->play_next(x...);
|
||||||
|
}
|
||||||
|
int num_running_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class LambdaAction : public Action<Ts...> {
|
template<typename... Ts> class LambdaAction : public Action<Ts...> {
|
||||||
|
@ -168,6 +181,8 @@ template<typename... Ts> class IfAction : public Action<Ts...> {
|
||||||
this->else_.stop();
|
this->else_.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_running() override { return this->then_.is_running() || this->else_.is_running() || this->is_running_next(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Condition<Ts...> *condition_;
|
Condition<Ts...> *condition_;
|
||||||
ActionList<Ts...> then_;
|
ActionList<Ts...> then_;
|
||||||
|
@ -210,6 +225,8 @@ template<typename... Ts> class WhileAction : public Action<Ts...> {
|
||||||
|
|
||||||
void stop() override { this->then_.stop(); }
|
void stop() override { this->then_.stop(); }
|
||||||
|
|
||||||
|
bool is_running() override { return this->then_.is_running() || this->is_running_next(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Condition<Ts...> *condition_;
|
Condition<Ts...> *condition_;
|
||||||
ActionList<Ts...> then_;
|
ActionList<Ts...> then_;
|
||||||
|
@ -251,6 +268,8 @@ template<typename... Ts> class WaitUntilAction : public Action<Ts...>, public Co
|
||||||
|
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
bool is_running() override { return this->triggered_ || this->is_running_next(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Condition<Ts...> *condition_;
|
Condition<Ts...> *condition_;
|
||||||
bool triggered_{false};
|
bool triggered_{false};
|
||||||
|
|
Loading…
Reference in a new issue