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 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. | ||||
|   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: | ||||
|   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}; | ||||
|   CRGB *leds_{nullptr}; | ||||
|   uint8_t *effect_data_{nullptr}; | ||||
|   | ||||
| @@ -4,6 +4,9 @@ | ||||
| namespace esphome { | ||||
| 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 { | ||||
|   // based on FastLED's hsv rainbow to rgb | ||||
|   const uint8_t hue = this->hue; | ||||
| @@ -76,9 +79,18 @@ void ESPRangeView::set(const ESPColor &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_]; } | ||||
|  | ||||
| int32_t HOT interpret_index(int32_t index, int32_t size) { | ||||
|   if (index < 0) | ||||
|     return size + index; | ||||
|   return index; | ||||
| } | ||||
|  | ||||
| }  // namespace light | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -147,6 +147,9 @@ struct ESPColor { | ||||
|   ESPColor fade_to_black(uint8_t amnt) { return *this * amnt; } | ||||
|   ESPColor lighten(uint8_t delta) { return *this + delta; } | ||||
|   ESPColor darken(uint8_t delta) { return *this - delta; } | ||||
|  | ||||
|   static const ESPColor BLACK; | ||||
|   static const ESPColor WHITE; | ||||
| }; | ||||
|  | ||||
| struct ESPHSVColor { | ||||
| @@ -353,6 +356,8 @@ class ESPColorView : public ESPColorSettable { | ||||
|  | ||||
| class AddressableLight; | ||||
|  | ||||
| int32_t interpret_index(int32_t index, int32_t size); | ||||
|  | ||||
| class ESPRangeView : public ESPColorSettable { | ||||
|  public: | ||||
|   class Iterator { | ||||
| @@ -385,6 +390,10 @@ class ESPRangeView : public ESPColorSettable { | ||||
|     this->set(rhs); | ||||
|     return *this; | ||||
|   } | ||||
|   ESPRangeView &operator=(const ESPColorView &rhs) { | ||||
|     this->set(rhs.get()); | ||||
|     return *this; | ||||
|   } | ||||
|   ESPRangeView &operator=(const ESPHSVColor &rhs) { | ||||
|     this->set_hsv(rhs); | ||||
|     return *this; | ||||
| @@ -463,9 +472,14 @@ class ESPRangeView : public ESPColorSettable { | ||||
| class AddressableLight : public LightOutput { | ||||
|  public: | ||||
|   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; | ||||
|   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::Iterator begin() { return this->all().begin(); } | ||||
|   ESPRangeView::Iterator end() { return this->all().end(); } | ||||
| @@ -476,7 +490,7 @@ class AddressableLight : public LightOutput { | ||||
|     } | ||||
|     if (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) { | ||||
|     if (amnt < 0) { | ||||
| @@ -485,7 +499,7 @@ class AddressableLight : public LightOutput { | ||||
|     } | ||||
|     if (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_; } | ||||
|   void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; } | ||||
| @@ -516,6 +530,7 @@ class AddressableLight : public LightOutput { | ||||
|  protected: | ||||
|   bool should_show_() const { return this->effect_active_ || this->next_show_; } | ||||
|   void mark_shown_() { this->next_show_ = false; } | ||||
|   virtual ESPColorView get_view_internal(int32_t index) const = 0; | ||||
|  | ||||
|   bool effect_active_{false}; | ||||
|   bool next_show_{true}; | ||||
|   | ||||
| @@ -113,11 +113,10 @@ class AddressableColorWipeEffect : public AddressableLightEffect { | ||||
|       it.shift_right(1); | ||||
|     const AddressableColorWipeEffectColor color = this->colors_[this->at_color_]; | ||||
|     const ESPColor esp_color = ESPColor(color.r, color.g, color.b, color.w); | ||||
|     if (!this->reverse_) { | ||||
|       it[it.size() - 1] = esp_color; | ||||
|     } else { | ||||
|     if (!this->reverse_) | ||||
|       it[-1] = esp_color; | ||||
|     else | ||||
|       it[0] = esp_color; | ||||
|     } | ||||
|     if (++this->leds_added_ >= color.num_leds) { | ||||
|       this->leds_added_ = 0; | ||||
|       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) {} | ||||
|   void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; } | ||||
|   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; | ||||
|     const uint32_t now = millis(); | ||||
|     if (now - this->last_move_ > this->move_interval_) { | ||||
| @@ -190,7 +189,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect { | ||||
|         else | ||||
|           view.set_effect_data(new_pos); | ||||
|       } else { | ||||
|         view = ESPColor(0, 0, 0, 0); | ||||
|         view = ESPColor::BLACK; | ||||
|       } | ||||
|     } | ||||
|     while (random_float() < this->twinkle_probability_) { | ||||
| @@ -220,8 +219,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect { | ||||
|       this->last_progress_ = now; | ||||
|     } | ||||
|     uint8_t subsine = ((8 * (now - this->last_progress_)) / this->progress_interval_) & 0b111; | ||||
|     for (auto &&i : it) { | ||||
|       ESPColorView view = i; | ||||
|     for (auto view : it) { | ||||
|       if (view.get_effect_data() != 0) { | ||||
|         const uint8_t x = (view.get_effect_data() >> 3) & 0b11111; | ||||
|         const uint8_t color = view.get_effect_data() & 0b111; | ||||
| @@ -261,9 +259,8 @@ class AddressableFireworksEffect : public AddressableLightEffect { | ||||
|  public: | ||||
|   explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {} | ||||
|   void start() override { | ||||
|     const auto &it = *this->get_addressable_(); | ||||
|     for (int i = 0; i < it.size(); i++) | ||||
|       it[i] = ESPColor(0, 0, 0, 0); | ||||
|     auto &it = *this->get_addressable_(); | ||||
|     it.all() = ESPColor::BLACK; | ||||
|   } | ||||
|   void apply(AddressableLight &it, const ESPColor ¤t_color) override { | ||||
|     const uint32_t now = millis(); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "esphome/core/automation.h" | ||||
| #include "light_state.h" | ||||
| #include "addressable_light.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace light { | ||||
| @@ -83,7 +84,7 @@ template<typename... Ts> class DimRelativeAction : public Action<Ts...> { | ||||
| template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> { | ||||
|  public: | ||||
|   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: | ||||
|   LightState *state_; | ||||
| @@ -91,11 +92,41 @@ template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> { | ||||
| template<typename... Ts> class LightIsOffCondition : public Condition<LightState, Ts...> { | ||||
|  public: | ||||
|   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: | ||||
|   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 esphome | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "light_effect.h" | ||||
| #include "esphome/core/automation.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace light { | ||||
| @@ -63,6 +64,21 @@ class LambdaLightEffect : public LightEffect { | ||||
|   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 { | ||||
|   LightColorValues color; | ||||
|   uint32_t duration; | ||||
|   | ||||
| @@ -1,14 +1,17 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| 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_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 .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \ | ||||
|     StrobeLightEffectColor, LightColorValues, AddressableLightRef, AddressableLambdaLightEffect, \ | ||||
|     FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \ | ||||
|     AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \ | ||||
|     AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect | ||||
|     AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect, \ | ||||
|     AutomationLightEffect | ||||
|  | ||||
| CONF_ADD_LED_INTERVAL = 'add_led_interval' | ||||
| CONF_REVERSE = 'reverse' | ||||
| @@ -28,8 +31,9 @@ CONF_ADDRESSABLE_TWINKLE = 'addressable_twinkle' | ||||
| CONF_ADDRESSABLE_RANDOM_TWINKLE = 'addressable_random_twinkle' | ||||
| CONF_ADDRESSABLE_FIREWORKS = 'addressable_fireworks' | ||||
| CONF_ADDRESSABLE_FLICKER = 'addressable_flicker' | ||||
| CONF_AUTOMATION = 'automation' | ||||
|  | ||||
| BINARY_EFFECTS = ['lambda', 'strobe'] | ||||
| BINARY_EFFECTS = ['lambda', 'automation', 'strobe'] | ||||
| MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + ['flicker'] | ||||
| RGB_EFFECTS = MONOCHROMATIC_EFFECTS + ['random'] | ||||
| 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]) | ||||
|  | ||||
|  | ||||
| @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", { | ||||
|     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, | ||||
|   | ||||
| @@ -20,6 +20,7 @@ DimRelativeAction = light_ns.class_('DimRelativeAction', automation.Action) | ||||
| LightEffect = light_ns.class_('LightEffect') | ||||
| RandomLightEffect = light_ns.class_('RandomLightEffect', LightEffect) | ||||
| LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect) | ||||
| AutomationLightEffect = light_ns.class_('AutomationLightEffect', LightEffect) | ||||
| StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect) | ||||
| StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', 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> | ||||
| class NeoPixelRGBLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COLOR_FEATURE> { | ||||
|  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 { | ||||
|     auto traits = light::LightTraits(); | ||||
|     traits.set_supports_brightness(true); | ||||
|     traits.set_supports_rgb(true); | ||||
|     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> | ||||
| class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COLOR_FEATURE> { | ||||
|  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 { | ||||
|     auto traits = light::LightTraits(); | ||||
|     traits.set_supports_brightness(true); | ||||
| @@ -174,6 +169,13 @@ class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_CO | ||||
|     traits.set_supports_rgb_white_value(true); | ||||
|     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 | ||||
|   | ||||
| @@ -8,6 +8,7 @@ script_ns = cg.esphome_ns.namespace('script') | ||||
| Script = script_ns.class_('Script', automation.Trigger.template()) | ||||
| ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', automation.Action) | ||||
| ScriptStopAction = script_ns.class_('ScriptStopAction', automation.Action) | ||||
| IsRunningCondition = script_ns.class_('IsRunningCondition', automation.Condition) | ||||
|  | ||||
| CONFIG_SCHEMA = automation.validate_automation({ | ||||
|     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): | ||||
|     paren = yield cg.get_variable(config[CONF_ID]) | ||||
|     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<> { | ||||
|  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...> { | ||||
| @@ -30,5 +39,15 @@ template<typename... Ts> class ScriptStopAction : public Action<Ts...> { | ||||
|   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 esphome | ||||
|   | ||||
| @@ -52,6 +52,11 @@ template<typename... Ts> class Trigger { | ||||
|       return; | ||||
|     this->automation_parent_->stop(); | ||||
|   } | ||||
|   bool is_running() { | ||||
|     if (this->automation_parent_ == nullptr) | ||||
|       return false; | ||||
|     return this->automation_parent_.is_running(); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   Automation<Ts...> *automation_parent_{nullptr}; | ||||
| @@ -81,6 +86,12 @@ template<typename... Ts> class Action { | ||||
|       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) { | ||||
|     this->play_next_tuple_(tuple, typename gens<sizeof...(Ts)>::type()); | ||||
| @@ -121,6 +132,11 @@ template<typename... Ts> class ActionList { | ||||
|       this->actions_begin_->stop_complex(); | ||||
|   } | ||||
|   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: | ||||
|   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...); } | ||||
|  | ||||
|   bool is_running() { return this->actions_.is_running(); } | ||||
|  | ||||
|  protected: | ||||
|   Trigger<Ts...> *trigger_; | ||||
|   ActionList<Ts...> actions_; | ||||
|   | ||||
| @@ -108,16 +108,29 @@ template<typename... Ts> class DelayAction : public Action<Ts...>, public Compon | ||||
|  | ||||
|   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_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); | ||||
|   } | ||||
|   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...> { | ||||
| @@ -168,6 +181,8 @@ template<typename... Ts> class IfAction : public Action<Ts...> { | ||||
|     this->else_.stop(); | ||||
|   } | ||||
|  | ||||
|   bool is_running() override { return this->then_.is_running() || this->else_.is_running() || this->is_running_next(); } | ||||
|  | ||||
|  protected: | ||||
|   Condition<Ts...> *condition_; | ||||
|   ActionList<Ts...> then_; | ||||
| @@ -210,6 +225,8 @@ template<typename... Ts> class WhileAction : public Action<Ts...> { | ||||
|  | ||||
|   void stop() override { this->then_.stop(); } | ||||
|  | ||||
|   bool is_running() override { return this->then_.is_running() || this->is_running_next(); } | ||||
|  | ||||
|  protected: | ||||
|   Condition<Ts...> *condition_; | ||||
|   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; } | ||||
|  | ||||
|   bool is_running() override { return this->triggered_ || this->is_running_next(); } | ||||
|  | ||||
|  protected: | ||||
|   Condition<Ts...> *condition_; | ||||
|   bool triggered_{false}; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user