mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Addressable updates
This commit is contained in:
		| @@ -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}; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user