mirror of
https://github.com/esphome/esphome.git
synced 2024-11-22 23:18:10 +01:00
New script modes POC (#1168)
Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
This commit is contained in:
parent
275c12150e
commit
32efa5d83e
12 changed files with 226 additions and 27 deletions
|
@ -108,7 +108,7 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->prev_trigger_ != nullptr) {
|
if (this->prev_trigger_ != nullptr) {
|
||||||
this->prev_trigger_->stop();
|
this->prev_trigger_->stop_action();
|
||||||
this->prev_trigger_ = nullptr;
|
this->prev_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
Trigger<> *trig;
|
Trigger<> *trig;
|
||||||
|
|
|
@ -94,7 +94,7 @@ void EndstopCover::dump_config() {
|
||||||
float EndstopCover::get_setup_priority() const { return setup_priority::DATA; }
|
float EndstopCover::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
void EndstopCover::stop_prev_trigger_() {
|
void EndstopCover::stop_prev_trigger_() {
|
||||||
if (this->prev_command_trigger_ != nullptr) {
|
if (this->prev_command_trigger_ != nullptr) {
|
||||||
this->prev_command_trigger_->stop();
|
this->prev_command_trigger_->stop_action();
|
||||||
this->prev_command_trigger_ = nullptr;
|
this->prev_command_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,9 +67,9 @@ class LambdaLightEffect : public LightEffect {
|
||||||
class AutomationLightEffect : public LightEffect {
|
class AutomationLightEffect : public LightEffect {
|
||||||
public:
|
public:
|
||||||
AutomationLightEffect(const std::string &name) : LightEffect(name) {}
|
AutomationLightEffect(const std::string &name) : LightEffect(name) {}
|
||||||
void stop() override { this->trig_->stop(); }
|
void stop() override { this->trig_->stop_action(); }
|
||||||
void apply() override {
|
void apply() override {
|
||||||
if (!this->trig_->is_running()) {
|
if (!this->trig_->is_action_running()) {
|
||||||
this->trig_->trigger();
|
this->trig_->trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import maybe_simple_id
|
from esphome.automation import maybe_simple_id
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID, CONF_MODE
|
||||||
|
|
||||||
script_ns = cg.esphome_ns.namespace('script')
|
script_ns = cg.esphome_ns.namespace('script')
|
||||||
Script = script_ns.class_('Script', automation.Trigger.template())
|
Script = script_ns.class_('Script', automation.Trigger.template())
|
||||||
|
@ -10,10 +10,47 @@ ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', automation.Action)
|
||||||
ScriptStopAction = script_ns.class_('ScriptStopAction', automation.Action)
|
ScriptStopAction = script_ns.class_('ScriptStopAction', automation.Action)
|
||||||
ScriptWaitAction = script_ns.class_('ScriptWaitAction', automation.Action, cg.Component)
|
ScriptWaitAction = script_ns.class_('ScriptWaitAction', automation.Action, cg.Component)
|
||||||
IsRunningCondition = script_ns.class_('IsRunningCondition', automation.Condition)
|
IsRunningCondition = script_ns.class_('IsRunningCondition', automation.Condition)
|
||||||
|
SingleScript = script_ns.class_('SingleScript', Script)
|
||||||
|
RestartScript = script_ns.class_('RestartScript', Script)
|
||||||
|
QueueingScript = script_ns.class_('QueueingScript', Script, cg.Component)
|
||||||
|
ParallelScript = script_ns.class_('ParallelScript', Script)
|
||||||
|
|
||||||
|
CONF_SINGLE = 'single'
|
||||||
|
CONF_RESTART = 'restart'
|
||||||
|
CONF_QUEUE = 'queue'
|
||||||
|
CONF_PARALLEL = 'parallel'
|
||||||
|
CONF_MAX_RUNS = 'max_runs'
|
||||||
|
|
||||||
|
SCRIPT_MODES = {
|
||||||
|
CONF_SINGLE: SingleScript,
|
||||||
|
CONF_RESTART: RestartScript,
|
||||||
|
CONF_QUEUE: QueueingScript,
|
||||||
|
CONF_PARALLEL: ParallelScript,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def check_max_runs(value):
|
||||||
|
if CONF_MAX_RUNS not in value:
|
||||||
|
return value
|
||||||
|
if value[CONF_MODE] not in [CONF_QUEUE, CONF_PARALLEL]:
|
||||||
|
raise cv.Invalid("The option 'max_runs' is only valid in 'queue' and 'parallel' mode.",
|
||||||
|
path=[CONF_MAX_RUNS])
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def assign_declare_id(value):
|
||||||
|
value = value.copy()
|
||||||
|
value[CONF_ID] = cv.declare_id(SCRIPT_MODES[value[CONF_MODE]])(value[CONF_ID])
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = automation.validate_automation({
|
CONFIG_SCHEMA = automation.validate_automation({
|
||||||
cv.Required(CONF_ID): cv.declare_id(Script),
|
# Don't declare id as cv.declare_id yet, because the ID type
|
||||||
})
|
# dpeends on the mode. Will be checked later with assign_declare_id
|
||||||
|
cv.Required(CONF_ID): cv.string_strict,
|
||||||
|
cv.Optional(CONF_MODE, default=CONF_SINGLE): cv.one_of(*SCRIPT_MODES, lower=True),
|
||||||
|
cv.Optional(CONF_MAX_RUNS): cv.positive_int,
|
||||||
|
}, extra_validators=cv.All(check_max_runs, assign_declare_id))
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
|
@ -21,6 +58,15 @@ def to_code(config):
|
||||||
triggers = []
|
triggers = []
|
||||||
for conf in config:
|
for conf in config:
|
||||||
trigger = cg.new_Pvariable(conf[CONF_ID])
|
trigger = cg.new_Pvariable(conf[CONF_ID])
|
||||||
|
# Add a human-readable name to the script
|
||||||
|
cg.add(trigger.set_name(conf[CONF_ID].id))
|
||||||
|
|
||||||
|
if CONF_MAX_RUNS in conf:
|
||||||
|
cg.add(trigger.set_max_runs(conf[CONF_MAX_RUNS]))
|
||||||
|
|
||||||
|
if conf[CONF_MODE] == CONF_QUEUE:
|
||||||
|
yield cg.register_component(trigger, conf)
|
||||||
|
|
||||||
triggers.append((trigger, conf))
|
triggers.append((trigger, conf))
|
||||||
|
|
||||||
for trigger, conf in triggers:
|
for trigger, conf in triggers:
|
||||||
|
|
67
esphome/components/script/script.cpp
Normal file
67
esphome/components/script/script.cpp
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include "script.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace script {
|
||||||
|
|
||||||
|
static const char *TAG = "script";
|
||||||
|
|
||||||
|
void SingleScript::execute() {
|
||||||
|
if (this->is_action_running()) {
|
||||||
|
ESP_LOGW(TAG, "Script '%s' is already running! (mode: single)", this->name_.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartScript::execute() {
|
||||||
|
if (this->is_action_running()) {
|
||||||
|
ESP_LOGD(TAG, "Script '%s' restarting (mode: restart)", this->name_.c_str());
|
||||||
|
this->stop_action();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QueueingScript::execute() {
|
||||||
|
if (this->is_action_running()) {
|
||||||
|
// num_runs_ is the number of *queued* instances, so total number of instances is
|
||||||
|
// num_runs_ + 1
|
||||||
|
if (this->max_runs_ != 0 && this->num_runs_ + 1 >= this->max_runs_) {
|
||||||
|
ESP_LOGW(TAG, "Script '%s' maximum number of queued runs exceeded!", this->name_.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Script '%s' queueing new instance (mode: queue)", this->name_.c_str());
|
||||||
|
this->num_runs_++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->trigger();
|
||||||
|
// Check if the trigger was immediate and we can continue right away.
|
||||||
|
this->loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QueueingScript::stop() {
|
||||||
|
this->num_runs_ = 0;
|
||||||
|
Script::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QueueingScript::loop() {
|
||||||
|
if (this->num_runs_ != 0 && !this->is_action_running()) {
|
||||||
|
this->num_runs_--;
|
||||||
|
this->trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelScript::execute() {
|
||||||
|
if (this->max_runs_ != 0 && this->automation_parent_->num_running() >= this->max_runs_) {
|
||||||
|
ESP_LOGW(TAG, "Script '%s' maximum number of parallel runs exceeded!", this->name_.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace script
|
||||||
|
} // namespace esphome
|
|
@ -1,29 +1,86 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace script {
|
namespace script {
|
||||||
|
|
||||||
|
/// The abstract base class for all script types.
|
||||||
class Script : public Trigger<> {
|
class Script : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
void execute() {
|
/** Execute a new instance of this script.
|
||||||
bool prev = this->in_stack_;
|
*
|
||||||
this->in_stack_ = true;
|
* The behavior of this function when a script is already running is defined by the subtypes
|
||||||
this->trigger();
|
*/
|
||||||
this->in_stack_ = prev;
|
virtual void execute() = 0;
|
||||||
}
|
/// Check if any instance of this script is currently running.
|
||||||
bool script_is_running() { return this->in_stack_ || this->is_running(); }
|
virtual bool is_running() { return this->is_action_running(); }
|
||||||
|
/// Stop all instances of this script.
|
||||||
|
virtual void stop() { this->stop_action(); }
|
||||||
|
|
||||||
|
// Internal function to give scripts readable names.
|
||||||
|
void set_name(const std::string &name) { name_ = name; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool in_stack_{false};
|
std::string name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A script type for which only a single instance at a time is allowed.
|
||||||
|
*
|
||||||
|
* If a new instance is executed while the previous one hasn't finished yet,
|
||||||
|
* a warning is printed and the new instance is discarded.
|
||||||
|
*/
|
||||||
|
class SingleScript : public Script {
|
||||||
|
public:
|
||||||
|
void execute() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A script type that restarts scripts from the beginning when a new instance is started.
|
||||||
|
*
|
||||||
|
* If a new instance is started but another one is already running, the existing
|
||||||
|
* script is stopped and the new instance starts from the beginning.
|
||||||
|
*/
|
||||||
|
class RestartScript : public Script {
|
||||||
|
public:
|
||||||
|
void execute() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A script type that queues new instances that are created.
|
||||||
|
*
|
||||||
|
* Only one instance of the script can be active at a time.
|
||||||
|
*/
|
||||||
|
class QueueingScript : public Script, public Component {
|
||||||
|
public:
|
||||||
|
void execute() override;
|
||||||
|
void stop() override;
|
||||||
|
void loop() override;
|
||||||
|
void set_max_runs(int max_runs) { max_runs_ = max_runs; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int num_runs_ = 0;
|
||||||
|
int max_runs_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A script type that executes new instances in parallel.
|
||||||
|
*
|
||||||
|
* If a new instance is started while previous ones haven't finished yet,
|
||||||
|
* the new one is exeucted in parallel to the other instances.
|
||||||
|
*/
|
||||||
|
class ParallelScript : public Script {
|
||||||
|
public:
|
||||||
|
void execute() override;
|
||||||
|
void set_max_runs(int max_runs) { max_runs_ = max_runs; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int max_runs_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class ScriptExecuteAction : public Action<Ts...> {
|
template<typename... Ts> class ScriptExecuteAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
ScriptExecuteAction(Script *script) : script_(script) {}
|
ScriptExecuteAction(Script *script) : script_(script) {}
|
||||||
|
|
||||||
void play(Ts... x) override { this->script_->trigger(); }
|
void play(Ts... x) override { this->script_->execute(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Script *script_;
|
Script *script_;
|
||||||
|
@ -43,7 +100,7 @@ template<typename... Ts> class IsRunningCondition : public Condition<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit IsRunningCondition(Script *parent) : parent_(parent) {}
|
explicit IsRunningCondition(Script *parent) : parent_(parent) {}
|
||||||
|
|
||||||
bool check(Ts... x) override { return this->parent_->script_is_running(); }
|
bool check(Ts... x) override { return this->parent_->is_running(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Script *parent_;
|
Script *parent_;
|
||||||
|
|
|
@ -120,7 +120,7 @@ void TemplateCover::set_has_position(bool has_position) { this->has_position_ =
|
||||||
void TemplateCover::set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; }
|
void TemplateCover::set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; }
|
||||||
void TemplateCover::stop_prev_trigger_() {
|
void TemplateCover::stop_prev_trigger_() {
|
||||||
if (this->prev_command_trigger_ != nullptr) {
|
if (this->prev_command_trigger_ != nullptr) {
|
||||||
this->prev_command_trigger_->stop();
|
this->prev_command_trigger_->stop_action();
|
||||||
this->prev_command_trigger_ = nullptr;
|
this->prev_command_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ void TemplateSwitch::loop() {
|
||||||
}
|
}
|
||||||
void TemplateSwitch::write_state(bool state) {
|
void TemplateSwitch::write_state(bool state) {
|
||||||
if (this->prev_trigger_ != nullptr) {
|
if (this->prev_trigger_ != nullptr) {
|
||||||
this->prev_trigger_->stop();
|
this->prev_trigger_->stop_action();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
|
|
|
@ -223,7 +223,7 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->prev_action_trigger_ != nullptr) {
|
if (this->prev_action_trigger_ != nullptr) {
|
||||||
this->prev_action_trigger_->stop();
|
this->prev_action_trigger_->stop_action();
|
||||||
this->prev_action_trigger_ = nullptr;
|
this->prev_action_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
Trigger<> *trig = this->idle_action_trigger_;
|
Trigger<> *trig = this->idle_action_trigger_;
|
||||||
|
@ -262,7 +262,7 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->prev_fan_mode_trigger_ != nullptr) {
|
if (this->prev_fan_mode_trigger_ != nullptr) {
|
||||||
this->prev_fan_mode_trigger_->stop();
|
this->prev_fan_mode_trigger_->stop_action();
|
||||||
this->prev_fan_mode_trigger_ = nullptr;
|
this->prev_fan_mode_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
Trigger<> *trig = this->fan_mode_auto_trigger_;
|
Trigger<> *trig = this->fan_mode_auto_trigger_;
|
||||||
|
@ -313,7 +313,7 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->prev_mode_trigger_ != nullptr) {
|
if (this->prev_mode_trigger_ != nullptr) {
|
||||||
this->prev_mode_trigger_->stop();
|
this->prev_mode_trigger_->stop_action();
|
||||||
this->prev_mode_trigger_ = nullptr;
|
this->prev_mode_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
Trigger<> *trig = this->auto_mode_trigger_;
|
Trigger<> *trig = this->auto_mode_trigger_;
|
||||||
|
@ -355,7 +355,7 @@ void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mo
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->prev_swing_mode_trigger_ != nullptr) {
|
if (this->prev_swing_mode_trigger_ != nullptr) {
|
||||||
this->prev_swing_mode_trigger_->stop();
|
this->prev_swing_mode_trigger_->stop_action();
|
||||||
this->prev_swing_mode_trigger_ = nullptr;
|
this->prev_swing_mode_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
Trigger<> *trig = this->swing_mode_off_trigger_;
|
Trigger<> *trig = this->swing_mode_off_trigger_;
|
||||||
|
|
|
@ -78,7 +78,7 @@ void TimeBasedCover::control(const CoverCall &call) {
|
||||||
}
|
}
|
||||||
void TimeBasedCover::stop_prev_trigger_() {
|
void TimeBasedCover::stop_prev_trigger_() {
|
||||||
if (this->prev_command_trigger_ != nullptr) {
|
if (this->prev_command_trigger_ != nullptr) {
|
||||||
this->prev_command_trigger_->stop();
|
this->prev_command_trigger_->stop_action();
|
||||||
this->prev_command_trigger_ = nullptr;
|
this->prev_command_trigger_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -812,6 +812,7 @@ def mqtt_qos(value):
|
||||||
def requires_component(comp):
|
def requires_component(comp):
|
||||||
"""Validate that this option can only be specified when the component `comp` is loaded."""
|
"""Validate that this option can only be specified when the component `comp` is loaded."""
|
||||||
def validator(value):
|
def validator(value):
|
||||||
|
# pylint: disable=unsupported-membership-test
|
||||||
if comp not in CORE.raw_config:
|
if comp not in CORE.raw_config:
|
||||||
raise Invalid(f"This option requires component {comp}")
|
raise Invalid(f"This option requires component {comp}")
|
||||||
return value
|
return value
|
||||||
|
@ -1125,7 +1126,7 @@ def typed_schema(schemas, **kwargs):
|
||||||
def validator(value):
|
def validator(value):
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
raise Invalid("Value must be dict")
|
raise Invalid("Value must be dict")
|
||||||
if CONF_TYPE not in value:
|
if key not in value:
|
||||||
raise Invalid("type not specified!")
|
raise Invalid("type not specified!")
|
||||||
value = value.copy()
|
value = value.copy()
|
||||||
key_v = key_validator(value.pop(key))
|
key_v = key_validator(value.pop(key))
|
||||||
|
@ -1175,6 +1176,7 @@ class OnlyWith(Optional):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default(self):
|
def default(self):
|
||||||
|
# pylint: disable=unsupported-membership-test
|
||||||
if self._component not in CORE.raw_config:
|
if self._component not in CORE.raw_config:
|
||||||
return vol.UNDEFINED
|
return vol.UNDEFINED
|
||||||
return self._default
|
return self._default
|
||||||
|
|
|
@ -50,18 +50,22 @@ template<typename... Ts> class Automation;
|
||||||
|
|
||||||
template<typename... Ts> class Trigger {
|
template<typename... Ts> class Trigger {
|
||||||
public:
|
public:
|
||||||
|
/// Inform the parent automation that the event has triggered.
|
||||||
void trigger(Ts... x) {
|
void trigger(Ts... x) {
|
||||||
if (this->automation_parent_ == nullptr)
|
if (this->automation_parent_ == nullptr)
|
||||||
return;
|
return;
|
||||||
this->automation_parent_->trigger(x...);
|
this->automation_parent_->trigger(x...);
|
||||||
}
|
}
|
||||||
void set_automation_parent(Automation<Ts...> *automation_parent) { this->automation_parent_ = automation_parent; }
|
void set_automation_parent(Automation<Ts...> *automation_parent) { this->automation_parent_ = automation_parent; }
|
||||||
void stop() {
|
|
||||||
|
/// Stop any action connected to this trigger.
|
||||||
|
void stop_action() {
|
||||||
if (this->automation_parent_ == nullptr)
|
if (this->automation_parent_ == nullptr)
|
||||||
return;
|
return;
|
||||||
this->automation_parent_->stop();
|
this->automation_parent_->stop();
|
||||||
}
|
}
|
||||||
bool is_running() {
|
/// Returns true if any action connected to this trigger is running.
|
||||||
|
bool is_action_running() {
|
||||||
if (this->automation_parent_ == nullptr)
|
if (this->automation_parent_ == nullptr)
|
||||||
return false;
|
return false;
|
||||||
return this->automation_parent_->is_running();
|
return this->automation_parent_->is_running();
|
||||||
|
@ -87,8 +91,18 @@ template<typename... Ts> class Action {
|
||||||
}
|
}
|
||||||
this->stop_next_();
|
this->stop_next_();
|
||||||
}
|
}
|
||||||
|
/// Check if this or any of the following actions are currently running.
|
||||||
virtual bool is_running() { return this->num_running_ > 0 || this->is_running_next_(); }
|
virtual bool is_running() { return this->num_running_ > 0 || this->is_running_next_(); }
|
||||||
|
|
||||||
|
/// The total number of actions that are currently running in this plus any of
|
||||||
|
/// the following actions in the chain.
|
||||||
|
int num_running_total() {
|
||||||
|
int total = this->num_running_;
|
||||||
|
if (this->next_ != nullptr)
|
||||||
|
total += this->next_->num_running_total();
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend ActionList<Ts...>;
|
friend ActionList<Ts...>;
|
||||||
|
|
||||||
|
@ -123,6 +137,8 @@ template<typename... Ts> class Action {
|
||||||
|
|
||||||
Action<Ts...> *next_ = nullptr;
|
Action<Ts...> *next_ = nullptr;
|
||||||
|
|
||||||
|
/// The number of instances of this sequence in the list of actions
|
||||||
|
/// that is currently being executed.
|
||||||
int num_running_{0};
|
int num_running_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,11 +167,19 @@ 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; }
|
||||||
|
|
||||||
|
/// Check if any action in this action list is currently running.
|
||||||
bool is_running() {
|
bool is_running() {
|
||||||
if (this->actions_begin_ == nullptr)
|
if (this->actions_begin_ == nullptr)
|
||||||
return false;
|
return false;
|
||||||
return this->actions_begin_->is_running();
|
return this->actions_begin_->is_running();
|
||||||
}
|
}
|
||||||
|
/// Return the number of actions in this action list that are currently running.
|
||||||
|
int num_running() {
|
||||||
|
if (this->actions_begin_ == nullptr)
|
||||||
|
return false;
|
||||||
|
return this->actions_begin_->num_running_total();
|
||||||
|
}
|
||||||
|
|
||||||
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)...); }
|
||||||
|
@ -177,6 +201,9 @@ template<typename... Ts> class Automation {
|
||||||
|
|
||||||
bool is_running() { return this->actions_.is_running(); }
|
bool is_running() { return this->actions_.is_running(); }
|
||||||
|
|
||||||
|
/// Return the number of actions in the action part of this automation that are currently running.
|
||||||
|
int num_running() { return this->actions_.num_running(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Trigger<Ts...> *trigger_;
|
Trigger<Ts...> *trigger_;
|
||||||
ActionList<Ts...> actions_;
|
ActionList<Ts...> actions_;
|
||||||
|
|
Loading…
Reference in a new issue