1 #include "automation.h" 12 static const char *
const TAG =
"sprinkler";
72 this->
state_ = latch_state;
79 : turn_on_trigger_(new
Trigger<>()), turn_off_trigger_(new
Trigger<>()) {}
82 if (!this->
f_.has_value())
84 auto s = (*this->
f_)();
121 if (!restored.has_value())
124 ESP_LOGD(TAG,
" Restored state %s", ONOFF(*restored));
133 LOG_SWITCH(
"",
"Sprinkler Switch",
this);
134 ESP_LOGCONFIG(TAG,
" Restore State: %s", YESNO(this->
restore_state_));
135 ESP_LOGCONFIG(TAG,
" Optimistic: %s", YESNO(this->
optimistic_));
144 : controller_(controller), valve_(valve) {}
176 if (controller !=
nullptr) {
182 if (valve !=
nullptr) {
294 if (this->
valve_ ==
nullptr) {
303 if (this->
valve_ ==
nullptr) {
326 : valve_number_(valve_number),
run_duration_(run_duration), valve_op_(valve_op) {}
341 if (valve_op !=
nullptr) {
371 for (
auto &p : this->
pump_) {
374 for (
auto &v : this->
valve_) {
375 v.valve_switch.loop();
384 this->
valve_.resize(new_valve_number + 1);
405 if (enable_sw !=
nullptr) {
417 for (
size_t valve_number = 0; valve_number < this->
number_of_valves(); valve_number++) {
418 if (this->
valve_[valve_number].controller_switch->
state) {
454 this->
valve_[valve_number].valve_switch.set_on_switch(valve_switch);
455 this->
valve_[valve_number].run_duration = run_duration;
461 uint32_t run_duration) {
463 this->
valve_[valve_number].valve_switch.set_off_switch(valve_switch_off);
464 this->
valve_[valve_number].valve_switch.set_on_switch(valve_switch_on);
465 this->
valve_[valve_number].valve_switch.set_pulse_duration(pulse_duration);
466 this->
valve_[valve_number].run_duration = run_duration;
472 for (
size_t i = 0; i < this->
pump_.size(); i++) {
473 if (this->
pump_[i].on_switch() == pump_switch) {
474 this->
valve_[valve_number].pump_switch_index = i;
479 this->
pump_.back().set_on_switch(pump_switch);
480 this->
valve_[valve_number].pump_switch_index = this->
pump_.size() - 1;
487 for (
size_t i = 0; i < this->
pump_.size(); i++) {
488 if ((this->
pump_[i].off_switch() == pump_switch_off) &&
489 (this->
pump_[i].on_switch() == pump_switch_on)) {
490 this->
valve_[valve_number].pump_switch_index = i;
495 this->
pump_.back().set_off_switch(pump_switch_off);
496 this->
pump_.back().set_on_switch(pump_switch_on);
497 this->
pump_.back().set_pulse_duration(pulse_duration);
498 this->
valve_[valve_number].pump_switch_index = this->
pump_.size() - 1;
504 if (multiplier.
value() > 0) {
535 if (valve_open_delay > 0) {
544 if (valve_overlap > 0) {
554 if (manual_selection_delay > 0) {
591 return this->
valve_[valve_number].run_duration;
597 uint32_t run_duration = 0;
600 run_duration = this->
valve_[valve_number].run_duration;
602 run_duration =
static_cast<uint32_t
>(roundf(run_duration * this->
multiplier_));
702 ESP_LOGD(TAG,
"Valve %u placed into queue with run duration of %u seconds", valve_number.
value_or(0),
710 ESP_LOGD(TAG,
"Queue cleared");
774 ESP_LOGD(TAG,
"No valve to resume!");
793 return this->
valve_[valve_number].controller_switch->get_name().c_str();
817 if (pump_switch ==
nullptr) {
827 if ((vo.state() !=
BYPASS) && (vo.pump_switch() !=
nullptr)) {
829 if ((vo.pump_switch()->off_switch() == pump_switch->
off_switch()) &&
830 (vo.pump_switch()->on_switch() == pump_switch->
on_switch())) {
834 if ((vo.state() ==
ACTIVE) ||
853 if (pump_switch ==
nullptr) {
857 bool hold_pump_on =
false;
860 if (controller !=
this) {
861 if (controller->pump_in_use(pump_switch)) {
872 ESP_LOGD(TAG,
"Leaving pump on because another controller instance is using it");
877 }
else if (!hold_pump_on && !this->
pump_in_use(pump_switch)) {
879 }
else if (hold_pump_on) {
891 if (vo.state() !=
IDLE) {
892 return vo.time_remaining();
900 return this->
valve_[valve_number].controller_switch;
907 return this->
valve_[valve_number].enable_switch;
914 return &this->
valve_[valve_number].valve_switch;
921 return &this->
pump_[this->
valve_[valve_number].pump_switch_index.value()];
927 if (pump_index < this->
pump_.size()) {
928 return &this->
pump_[pump_index];
938 return this->
valve_[valve_number].enable_switch->state;
948 ESP_LOGD(TAG,
"Marking valve %u complete", valve_number);
949 this->
valve_[valve_number].valve_cycle_complete =
true;
955 return this->
valve_[valve_number].valve_cycle_complete;
962 return first_valve + 1;
969 return first_valve - 1;
1005 ESP_LOGD(TAG,
"Repeating - starting cycle %u of %u", this->
repeat_count_ + 1,
1020 while (new_valve_number != first_valve.
value_or(this->number_of_valves() - 1)) {
1022 return new_valve_number;
1033 while (new_valve_number != first_valve.
value_or(0)) {
1035 return new_valve_number;
1044 for (
size_t valve_number = 0; valve_number < this->
number_of_valves(); valve_number++) {
1059 if (vo.state() ==
IDLE) {
1061 ESP_LOGD(TAG,
"Starting valve %u for %u seconds, cycle %u of %u", req->
valve(), run_duration,
1064 vo.set_controller(
this);
1066 vo.set_run_duration(run_duration);
1076 for (
size_t valve_index = 0; valve_index < this->
number_of_valves(); valve_index++) {
1078 this->
valve_[valve_index].valve_switch.turn_off();
1084 ESP_LOGD(TAG,
"All valves stopped%s", include_pump ?
", including pumps" :
"");
1094 for (
auto &valve : this->
valve_) {
1095 if (valve.enable_switch !=
nullptr) {
1096 valve.enable_switch->publish_state(
true);
1104 for (
auto &valve : this->
valve_) {
1105 valve.valve_cycle_complete =
false;
1124 ESP_LOGVV(TAG,
"fsm_transition_ called; state is %s", this->
state_as_str_(this->
state_).c_str());
1167 ESP_LOGVV(TAG,
"fsm_transition_ complete; new state is %s", this->
state_as_str_(this->
state_).c_str());
1191 ESP_LOGD(TAG,
"Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1260 this->
timer_[timer_index].active =
true;
1262 ESP_LOGVV(TAG,
"Timer %u started for %u sec", static_cast<size_t>(timer_index),
1267 this->
timer_[timer_index].active =
false;
1274 this->
timer_[timer_index].time = 1000 * time;
1280 return this->
timer_[timer_index].func;
1285 ESP_LOGVV(TAG,
"Valve selection timer expired");
1294 ESP_LOGVV(TAG,
"State machine timer expired");
1299 ESP_LOGCONFIG(TAG,
"Sprinkler Controller -- %s", this->
name_.c_str());
1308 ESP_LOGCONFIG(TAG,
" Pump Start Valve Delay: %u seconds", this->
start_delay_);
1310 ESP_LOGCONFIG(TAG,
" Pump Start Pump Delay: %u seconds", this->
start_delay_);
1315 ESP_LOGCONFIG(TAG,
" Pump Stop Valve Delay: %u seconds", this->
stop_delay_);
1317 ESP_LOGCONFIG(TAG,
" Pump Stop Pump Delay: %u seconds", this->
stop_delay_);
1325 ESP_LOGCONFIG(TAG,
" Pump Switch Off During Valve Open Delay: %s",
1329 for (
size_t valve_number = 0; valve_number < this->
number_of_valves(); valve_number++) {
1330 ESP_LOGCONFIG(TAG,
" Valve %u:", valve_number);
1331 ESP_LOGCONFIG(TAG,
" Name: %s", this->
valve_name(valve_number));
1332 ESP_LOGCONFIG(TAG,
" Run Duration: %u seconds", this->
valve_[valve_number].run_duration);
1334 ESP_LOGCONFIG(TAG,
" Pulse Duration: %u milliseconds",
1338 if (!this->
pump_.empty()) {
1339 ESP_LOGCONFIG(TAG,
" Total number of pumps: %u", this->
pump_.size());
1341 if (!this->
valve_.empty()) {
1342 ESP_LOGCONFIG(TAG,
" Total number of valves: %u", this->
valve_.size());
Base class for all switches.
value_type const & value() const
void set_repeat(optional< uint32_t > repeat)
set the number of times to repeat a full cycle
void set_assumed_state(bool assumed_state)
void start_timer_(SprinklerTimerIndex timer_index)
Start/cancel/get status of valve timers.
bool pump_switch_off_during_valve_open_delay_
Pump should be off during valve_open_delay interval.
optional< size_t > previous_enabled_incomplete_valve_number_(optional< size_t > first_valve)
bool timer_active_(SprinklerTimerIndex timer_index)
returns true if the specified timer is active/running
SprinklerControllerSwitch * controller_switch
float get_setup_priority() const override
void previous_valve()
advances to the previous valve (numerically)
SprinklerValveRunRequest next_req_
The next run request for the controller to consume after active_req_ is complete. ...
std::vector< SprinklerValveOperator > valve_op_
Sprinkler valve operator objects.
bool cancel_timeout(const std::string &name)
Cancel a timeout function.
uint32_t repeat_count_
Number of times the full cycle has been repeated.
optional< std::function< optional< bool >)> > f_
uint32_t valve_run_duration(size_t valve_number)
returns valve_number's run duration in seconds
void next_valve()
advances to the next valve (numerically)
void set_run_duration(uint32_t run_duration)
optional< uint32_t > resume_duration_
Set from time_remaining() when paused.
void valve_selection_callback_()
callback functions for timers
void queue_valve(optional< size_t > valve_number, optional< uint32_t > run_duration)
adds a valve into the queue.
bool assumed_state() override
optional< uint32_t > time_remaining()
returns the amount of time remaining in seconds for the active valve, if any. check with 'has_value()...
optional< size_t > paused_valve()
returns the number of the valve that is paused, if any. check with 'has_value()'
std::vector< SprinklerValve > valve_
Sprinkler valve objects.
bool auto_advance()
returns true if auto_advance is enabled
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
void set_valve(size_t valve_number)
void fsm_transition_to_shutdown_()
starts up the system from IDLE state
optional< size_t > queued_valve()
returns the number of the next valve in the queue, if any. check with 'has_value()' ...
optional< uint32_t > switching_delay_
Valve switching delay.
void set_multiplier(optional< float > multiplier)
value multiplied by configured run times – used to extend or shorten the cycle
SprinklerSwitch * valve_switch(size_t valve_number)
returns a pointer to a valve's switch object
SprinklerValveOperator * valve_operator()
void fsm_request_(size_t requested_valve, uint32_t requested_run_duration=0)
make a request of the state machine
void add_controller(Sprinkler *other_controller)
add another controller to the controller so it can check if pumps/main valves are in use ...
void reset_resume()
resets resume state
bool is_a_valid_valve(size_t valve_number)
returns true if valve number is valid
void set_state_lambda(std::function< optional< bool >()> &&f)
switch_::Switch * on_switch_
void shutdown(bool clear_queue=false)
turns off all valves, effectively shutting down the system.
std::unique_ptr< ResumeOrStartAction<> > sprinkler_resumeorstart_action_
void set_pump_start_delay(uint32_t start_delay)
set how long the pump should start after the valve (when the pump is starting)
optional< size_t > pump_switch_index
void set_controller_queue_enable_switch(SprinklerControllerSwitch *queue_enable_switch)
SprinklerSwitch * valve_pump_switch_by_pump_index(size_t pump_index)
returns a pointer to a valve's pump switch object
void set_valve_run_duration(optional< size_t > valve_number, optional< uint32_t > run_duration)
set how long the valve should remain on/open. run_duration is time in seconds
std::unique_ptr< Automation<> > valve_turn_off_automation
std::vector< Sprinkler * > other_controllers_
Other Sprinkler instances we should be aware of (used to check if pumps are in use) ...
void set_optimistic(bool optimistic)
optional< size_t > valve_as_opt()
uint32_t hash_base() override
void resume_or_start_full_cycle()
if a cycle was suspended using pause(), resumes it. otherwise calls start_full_cycle() ...
uint32_t IRAM_ATTR HOT millis()
SprinklerSwitch * valve_pump_switch(size_t valve_number)
returns a pointer to a valve's pump switch object
void start_single_valve(optional< size_t > valve_number)
activates a single valve and disables auto_advance.
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
SprinklerControllerSwitch * auto_adv_sw_
Switches we'll present to the front end.
void set_valve_overlap(uint32_t valve_overlap)
set how long the controller should wait after opening a valve before closing the previous valve ...
size_t next_valve_number_(size_t first_valve)
returns the number of the next/previous valve in the vector
bool valve_overlap_
Sprinkler valve cycle should overlap.
uint32_t valve_run_duration_adjusted(size_t valve_number)
returns valve_number's run duration (in seconds) adjusted by multiplier_
void set_controller_reverse_switch(SprinklerControllerSwitch *reverse_switch)
uint32_t pulse_duration()
switch_::Switch * on_switch()
std::unique_ptr< Automation<> > sprinkler_turn_on_automation_
std::vector< SprinklerTimer > timer_
Valve control timers.
const nullopt_t nullopt((nullopt_t::init()))
Trigger * get_turn_off_trigger() const
void fsm_kick_()
kicks the state machine to advance, starting it if it is not already active
void set_pump_state(SprinklerSwitch *pump_switch, bool state)
switches on/off a pump "safely" by checking that the new state will not conflict with another control...
optional< bool > get_initial_state()
Returns the initial state of the switch, as persisted previously, or empty if never persisted...
Trigger * get_turn_on_trigger() const
void sm_timer_callback_()
uint32_t timer_duration_(SprinklerTimerIndex timer_index)
returns time in milliseconds (ms)
void resume()
resumes a cycle that was suspended using pause()
bool has_valve_operator()
void pause()
same as shutdown(), but also stores active_valve() and time_remaining() allowing resume() to continue...
void start_full_cycle()
starts a full cycle of all enabled valves and enables auto_advance.
SprinklerControllerSwitch * controller_sw_
void write_state(bool state) override
void turn_on()
Turn this switch on.
bool reverse()
returns true if reverse is enabled
optional< size_t > next_valve_number_in_cycle_(optional< size_t > first_valve=nullopt)
returns the number of the next valve that should be activated in a full cycle.
void start_valve_(SprinklerValveRunRequest *req)
loads an available SprinklerValveOperator (valve_op_) based on req and starts it (switches it on)...
void set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay)
void set_timer_duration_(SprinklerTimerIndex timer_index, uint32_t time)
time is converted to milliseconds (ms) for set_timeout()
void set_auto_advance(bool auto_advance)
if auto_advance is true, controller will iterate through all enabled valves
void set_restore_state(bool restore_state)
SprinklerControllerSwitch * control_switch(size_t valve_number)
returns a pointer to a valve's control switch object
SprinklerValveOperator * valve_op_
void set_valve_open_delay(uint32_t valve_open_delay)
set how long the controller should wait to open/switch on the valve after it becomes active ...
void set_controller_auto_adv_switch(SprinklerControllerSwitch *auto_adv_switch)
optional< size_t > manual_valve_
The number of the manually selected valve currently selected.
SprinklerControllerSwitch * enable_switch(size_t valve_number)
returns a pointer to a valve's enable switch object
void set_controller(Sprinkler *controller)
SprinklerControllerSwitch * queue_enable_sw_
optional< uint32_t > target_repeats_
Set the number of times to repeat a full cycle.
void set_manual_selection_delay(uint32_t manual_selection_delay)
set how long the controller should wait to activate a valve after next_valve() or previous_valve() is...
void set_queue_enable(bool queue_enable)
if queue_enable is true, controller will iterate through valves in the queue
void set_reverse(bool reverse)
if reverse is true, controller will iterate through all enabled valves in reverse (descending) order ...
switch_::Switch * off_switch_
void fsm_transition_()
advance controller state, advancing to target_valve if provided
bool queue_enabled()
returns true if the queue is enabled to run
void set_valve_operator(SprinklerValveOperator *valve_op)
bool valve_is_enabled_(size_t valve_number)
returns true if valve number is enabled
void set_pump_switch_off_during_valve_open_delay(bool pump_switch_off_during_valve_open_delay)
if pump_switch_off_during_valve_open_delay is true, the controller will switch off the pump during th...
void set_controller_main_switch(SprinklerControllerSwitch *controller_switch)
configure important controller switches
void set_valve(SprinklerValve *valve)
uint32_t time_remaining()
void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw=nullptr)
add a valve to the controller
const uint8_t max_queue_size_
Maximum allowed queue size.
std::vector< SprinklerSwitch > pump_
Sprinkler valve pump objects.
void reset_cycle_states_()
resets the cycle state for all valves
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
void start_from_queue()
starts the controller from the first valve in the queue and disables auto_advance.
void prep_full_cycle_()
prepares for a full cycle by verifying auto-advance is on as well as one or more valve enable switche...
SprinklerSwitch valve_switch
uint32_t start_delay_
Pump start/stop delay intervals.
void configure_valve_pump_switch(size_t valve_number, switch_::Switch *pump_switch)
configure a valve's associated pump switch object
SprinklerState state_
Sprinkler controller state.
bool stop_delay_is_valve_delay_
SprinklerValveRunRequest()
optional< size_t > paused_valve_
The number of the valve to resume from (if paused)
optional< uint32_t > repeat_count()
if a cycle is active, returns the number of times the controller has repeated the cycle...
void clear_queued_valves()
clears/removes all valves from the queue
std::unique_ptr< StartSingleValveAction<> > valve_resumeorstart_action
void sync_valve_state(bool latch_state)
bool valve_cycle_complete_(size_t valve_number)
returns true if valve's cycle is flagged as complete
std::unique_ptr< ShutdownAction<> > sprinkler_shutdown_action_
bool any_valve_is_enabled_()
returns true if any valve is enabled
void configure_valve_switch(size_t valve_number, switch_::Switch *valve_switch, uint32_t run_duration)
configure a valve's switch object and run duration. run_duration is time in seconds.
bool cancel_timer_(SprinklerTimerIndex timer_index)
std::unique_ptr< Automation<> > sprinkler_turn_off_automation_
Trigger * turn_on_trigger_
size_t previous_valve_number_(size_t first_valve)
void fsm_transition_from_shutdown_()
starts up the system from IDLE state
std::string state_as_str_(SprinklerState state)
return the current FSM state as a string
std::function< void()> timer_cbf_(SprinklerTimerIndex timer_index)
bool pump_in_use(SprinklerSwitch *pump_switch)
returns true if the pump the pointer points to is in use
void set_valve_start_delay(uint32_t start_delay)
set how long the valve should start after the pump (when the pump is stopping)
bool start_delay_is_valve_delay_
Pump start/stop delay interval types.
const char * valve_name(size_t valve_number)
returns a pointer to a valve's name string object; returns nullptr if valve_number is invalid ...
void dump_config() override
void configure_valve_pump_switch_pulsed(size_t valve_number, switch_::Switch *pump_switch_off, switch_::Switch *pump_switch_on, uint32_t pulse_duration)
optional< uint32_t > manual_selection_delay_
Manual switching delay.
void set_valve_stop_delay(uint32_t stop_delay)
set how long the valve should stop after the pump (when the pump is stopping)
SprinklerControllerSwitch * reverse_sw_
size_t number_of_valves()
returns the number of valves the controller is configured with
std::unique_ptr< Automation<> > valve_turn_on_automation
SprinklerControllerSwitch()
void dump_config() override
void configure_valve_switch_pulsed(size_t valve_number, switch_::Switch *valve_switch_off, switch_::Switch *valve_switch_on, uint32_t pulse_duration, uint32_t run_duration)
std::vector< SprinklerQueueItem > queued_valves_
Queue of valves to activate next, regardless of auto-advance.
void load_next_valve_run_request_(optional< size_t > first_valve=nullopt)
loads next_req_ with the next valve that should be activated, including its run duration.
void mark_valve_cycle_complete_(size_t valve_number)
marks a valve's cycle as complete
void set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay)
void publish_state(bool state)
Publish a state to the front-end from the back-end.
void set_run_duration(uint32_t run_duration)
SprinklerControllerSwitch * enable_switch
float multiplier_
Sprinkler valve run time multiplier value.
bool state
The current reported state of the binary sensor.
value_type value_or(U const &v) const
optional< size_t > active_valve()
returns the number of the valve that is currently active, if any. check with 'has_value()' ...
void stop_action()
Stop any action connected to this trigger.
optional< size_t > next_enabled_incomplete_valve_number_(optional< size_t > first_valve)
returns the number of the next/previous valve that should be activated.
void fsm_transition_from_valve_run_()
transitions from ACTIVE state to ACTIVE (as in, next valve) or to a SHUTDOWN or IDLE state ...
bool start_delay_is_valve_delay_
float multiplier()
returns the current value of the multiplier
Trigger * turn_off_trigger_
switch_::Switch * off_switch()
bool stop_delay_is_valve_delay_
SprinklerValveRunRequest active_req_
The valve run request that is currently active.
optional< uint32_t > repeat()
returns the number of times the controller is set to repeat cycles, if at all. check with 'has_value(...
SprinklerSwitch * pump_switch()
void set_pump_stop_delay(uint32_t stop_delay)
set how long the pump should stop after the valve (when the pump is starting)
void turn_off()
Turn this switch off.
std::unique_ptr< ShutdownAction<> > valve_shutdown_action
optional< size_t > manual_valve()
returns the number of the valve that is manually selected, if any.
void all_valves_off_(bool include_pump=false)
turns off/closes all valves, including pump if include_pump is true