ESPHome  2023.9.3
sprinkler.cpp
Go to the documentation of this file.
1 #include "automation.h"
2 #include "sprinkler.h"
3 
5 #include "esphome/core/helpers.h"
6 #include "esphome/core/log.h"
7 #include <utility>
8 
9 namespace esphome {
10 namespace sprinkler {
11 
12 static const char *const TAG = "sprinkler";
13 
15 SprinklerSwitch::SprinklerSwitch(switch_::Switch *sprinkler_switch) : on_switch_(sprinkler_switch) {}
17  : pulse_duration_(pulse_duration), off_switch_(off_switch), on_switch_(on_switch) {}
18 
19 bool SprinklerSwitch::is_latching_valve() { return (this->off_switch_ != nullptr) && (this->on_switch_ != nullptr); }
20 
22  if ((this->pinned_millis_) && (millis() > this->pinned_millis_ + this->pulse_duration_)) {
23  this->pinned_millis_ = 0; // reset tracker
24  if (this->off_switch_->state) {
25  this->off_switch_->turn_off();
26  }
27  if (this->on_switch_->state) {
28  this->on_switch_->turn_off();
29  }
30  }
31 }
32 
34  if (!this->state()) { // do nothing if we're already in the requested state
35  return;
36  }
37  if (this->off_switch_ != nullptr) { // latching valve, start a pulse
38  if (!this->off_switch_->state) {
39  this->off_switch_->turn_on();
40  }
41  this->pinned_millis_ = millis();
42  } else if (this->on_switch_ != nullptr) { // non-latching valve
43  this->on_switch_->turn_off();
44  }
45  this->state_ = false;
46 }
47 
49  if (this->state()) { // do nothing if we're already in the requested state
50  return;
51  }
52  if (this->off_switch_ != nullptr) { // latching valve, start a pulse
53  if (!this->on_switch_->state) {
54  this->on_switch_->turn_on();
55  }
56  this->pinned_millis_ = millis();
57  } else if (this->on_switch_ != nullptr) { // non-latching valve
58  this->on_switch_->turn_on();
59  }
60  this->state_ = true;
61 }
62 
64  if ((this->off_switch_ == nullptr) && (this->on_switch_ != nullptr)) { // latching valve is not configured...
65  return this->on_switch_->state; // ...so just return the pump switch state
66  }
67  return this->state_;
68 }
69 
70 void SprinklerSwitch::sync_valve_state(bool latch_state) {
71  if (this->is_latching_valve()) {
72  this->state_ = latch_state;
73  } else if (this->on_switch_ != nullptr) {
74  this->state_ = this->on_switch_->state;
75  }
76 }
77 
79  float value;
80  if (!this->restore_value_) {
81  value = this->initial_value_;
82  } else {
83  this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
84  if (!this->pref_.load(&value)) {
85  if (!std::isnan(this->initial_value_)) {
86  value = this->initial_value_;
87  } else {
88  value = this->traits.get_min_value();
89  }
90  }
91  }
92  this->publish_state(value);
93 }
94 
96  this->set_trigger_->trigger(value);
97 
98  this->publish_state(value);
99 
100  if (this->restore_value_)
101  this->pref_.save(&value);
102 }
103 
104 void SprinklerControllerNumber::dump_config() { LOG_NUMBER("", "Sprinkler Controller Number", this); }
105 
107  : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {}
108 
110  if (!this->f_.has_value())
111  return;
112  auto s = (*this->f_)();
113  if (!s.has_value())
114  return;
115 
116  this->publish_state(*s);
117 }
118 
120  if (this->prev_trigger_ != nullptr) {
121  this->prev_trigger_->stop_action();
122  }
123 
124  if (state) {
125  this->prev_trigger_ = this->turn_on_trigger_;
126  this->turn_on_trigger_->trigger();
127  } else {
128  this->prev_trigger_ = this->turn_off_trigger_;
129  this->turn_off_trigger_->trigger();
130  }
131 
132  this->publish_state(state);
133 }
134 
135 void SprinklerControllerSwitch::set_state_lambda(std::function<optional<bool>()> &&f) { this->f_ = f; }
137 
140 
142 
143 void SprinklerControllerSwitch::dump_config() { LOG_SWITCH("", "Sprinkler Switch", this); }
144 
147  : controller_(controller), valve_(valve) {}
148 
150  if (millis() >= this->start_millis_) { // dummy check
151  switch (this->state_) {
152  case STARTING:
153  if (millis() > (this->start_millis_ + this->start_delay_)) {
154  this->run_(); // start_delay_ has been exceeded, so ensure both valves are on and update the state
155  }
156  break;
157 
158  case ACTIVE:
159  if (millis() > (this->start_millis_ + this->start_delay_ + this->run_duration_)) {
160  this->stop(); // start_delay_ + run_duration_ has been exceeded, start shutting down
161  }
162  break;
163 
164  case STOPPING:
165  if (millis() > (this->stop_millis_ + this->stop_delay_)) {
166  this->kill_(); // stop_delay_has been exceeded, ensure all valves are off
167  }
168  break;
169 
170  default:
171  break;
172  }
173  } else { // perhaps millis() rolled over...or something else is horribly wrong!
174  this->stop(); // bail out (TODO: handle this highly unlikely situation better...)
175  }
176 }
177 
179  if (controller != nullptr) {
180  this->controller_ = controller;
181  }
182 }
183 
185  if (valve != nullptr) {
186  this->state_ = IDLE; // reset state
187  this->run_duration_ = 0; // reset to ensure the valve isn't started without updating it
188  this->start_millis_ = 0; // reset because (new) valve has not been started yet
189  this->stop_millis_ = 0; // reset because (new) valve has not been started yet
190  this->kill_(); // ensure everything is off before we let go!
191  this->valve_ = valve; // finally, set the pointer to the new valve
192  }
193 }
194 
196  if (run_duration) {
197  this->run_duration_ = run_duration * 1000;
198  }
199 }
200 
201 void SprinklerValveOperator::set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay) {
202  this->start_delay_is_valve_delay_ = start_delay_is_valve_delay;
203  this->start_delay_ = start_delay * 1000; // because 1000 milliseconds is one second
204 }
205 
206 void SprinklerValveOperator::set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay) {
207  this->stop_delay_is_valve_delay_ = stop_delay_is_valve_delay;
208  this->stop_delay_ = stop_delay * 1000; // because 1000 milliseconds is one second
209 }
210 
212  if (!this->run_duration_) { // can't start if zero run duration
213  return;
214  }
215  if (this->start_delay_ && (this->pump_switch() != nullptr)) {
216  this->state_ = STARTING; // STARTING state requires both a pump and a start_delay_
217  if (this->start_delay_is_valve_delay_) {
218  this->pump_on_();
219  } else if (!this->pump_switch()->state()) { // if the pump is already on, wait to switch on the valve
220  this->valve_on_(); // to ensure consistent run time
221  }
222  } else {
223  this->run_(); // there is no start_delay_, so just start the pump and valve
224  }
225  this->stop_millis_ = 0;
226  this->start_millis_ = millis(); // save the time the start request was made
227 }
228 
230  if ((this->state_ == IDLE) || (this->state_ == STOPPING)) { // can't stop if already stopped or stopping
231  return;
232  }
233  if (this->stop_delay_ && (this->pump_switch() != nullptr)) {
234  this->state_ = STOPPING; // STOPPING state requires both a pump and a stop_delay_
235  if (this->stop_delay_is_valve_delay_) {
236  this->pump_off_();
237  } else {
238  this->valve_off_();
239  }
240  if (this->pump_switch()->state()) { // if the pump is still on at this point, it may be in use...
241  this->valve_off_(); // ...so just switch the valve off now to ensure consistent run time
242  }
243  } else {
244  this->kill_(); // there is no stop_delay_, so just stop the pump and valve
245  }
246  this->stop_millis_ = millis(); // save the time the stop request was made
247 }
248 
249 uint32_t SprinklerValveOperator::run_duration() { return this->run_duration_ / 1000; }
250 
252  if (this->start_millis_ == 0) {
253  return this->run_duration(); // hasn't been started yet
254  }
255 
256  if (this->stop_millis_) {
257  if (this->stop_millis_ - this->start_millis_ >= this->start_delay_ + this->run_duration_) {
258  return 0; // valve was active for more than its configured duration, so we are done
259  } else {
260  // we're stopped; return time remaining
261  return (this->run_duration_ - (this->stop_millis_ - this->start_millis_)) / 1000;
262  }
263  }
264 
265  auto completed_millis = this->start_millis_ + this->start_delay_ + this->run_duration_;
266  if (completed_millis > millis()) {
267  return (completed_millis - millis()) / 1000; // running now
268  }
269  return 0; // run completed
270 }
271 
273 
275  if ((this->controller_ == nullptr) || (this->valve_ == nullptr)) {
276  return nullptr;
277  }
278  if (this->valve_->pump_switch_index.has_value()) {
280  }
281  return nullptr;
282 }
283 
285  if ((this->valve_ == nullptr) || (this->pump_switch() == nullptr)) { // safety first!
286  return;
287  }
288  if (this->controller_ == nullptr) { // safety first!
289  this->pump_switch()->turn_off(); // if no controller was set, just switch off the pump
290  } else { // ...otherwise, do it "safely"
291  auto state = this->state_; // this is silly, but...
292  this->state_ = BYPASS; // ...exclude me from the pump-in-use check that set_pump_state() does
293  this->controller_->set_pump_state(this->pump_switch(), false);
294  this->state_ = state;
295  }
296 }
297 
299  if ((this->valve_ == nullptr) || (this->pump_switch() == nullptr)) { // safety first!
300  return;
301  }
302  if (this->controller_ == nullptr) { // safety first!
303  this->pump_switch()->turn_on(); // if no controller was set, just switch on the pump
304  } else { // ...otherwise, do it "safely"
305  auto state = this->state_; // this is silly, but...
306  this->state_ = BYPASS; // ...exclude me from the pump-in-use check that set_pump_state() does
307  this->controller_->set_pump_state(this->pump_switch(), true);
308  this->state_ = state;
309  }
310 }
311 
313  if (this->valve_ == nullptr) { // safety first!
314  return;
315  }
316  if (this->valve_->valve_switch.state()) {
317  this->valve_->valve_switch.turn_off();
318  }
319 }
320 
322  if (this->valve_ == nullptr) { // safety first!
323  return;
324  }
325  if (!this->valve_->valve_switch.state()) {
326  this->valve_->valve_switch.turn_on();
327  }
328 }
329 
331  this->state_ = IDLE;
332  this->valve_off_();
333  this->pump_off_();
334 }
335 
337  this->state_ = ACTIVE;
338  this->valve_on_();
339  this->pump_on_();
340 }
341 
344  SprinklerValveOperator *valve_op)
345  : valve_number_(valve_number), run_duration_(run_duration), valve_op_(valve_op) {}
346 
348 bool SprinklerValveRunRequest::has_valve_operator() { return !(this->valve_op_ == nullptr); }
349 
351 
353 
354 void SprinklerValveRunRequest::set_valve(size_t valve_number) {
355  this->valve_number_ = valve_number;
356  this->run_duration_ = 0;
357  this->valve_op_ = nullptr;
358  this->has_valve_ = true;
359 }
360 
362  if (valve_op != nullptr) {
363  this->valve_op_ = valve_op;
364  }
365 }
366 
368  this->has_valve_ = false;
369  this->origin_ = USER;
370  this->run_duration_ = 0;
371  this->valve_op_ = nullptr;
372 }
373 
375 
377 
379  if (this->has_valve_) {
380  return this->valve_number_;
381  }
382  return nullopt;
383 }
384 
386 
388 
390  this->timer_.push_back({this->name_ + "sm", false, 0, 0, std::bind(&Sprinkler::sm_timer_callback_, this)});
391  this->timer_.push_back({this->name_ + "vs", false, 0, 0, std::bind(&Sprinkler::valve_selection_callback_, this)});
392  this->all_valves_off_(true);
393 }
394 
396  for (auto &p : this->pump_) {
397  p.loop();
398  }
399  for (auto &v : this->valve_) {
400  v.valve_switch.loop();
401  }
402  for (auto &vo : this->valve_op_) {
403  vo.loop();
404  }
405  if (this->prev_req_.has_request() && this->prev_req_.valve_operator()->state() == IDLE) {
406  this->prev_req_.reset();
407  }
408 }
409 
411  auto new_valve_number = this->number_of_valves();
412  this->valve_.resize(new_valve_number + 1);
413  SprinklerValve *new_valve = &this->valve_[new_valve_number];
414 
415  new_valve->controller_switch = valve_sw;
416  new_valve->controller_switch->set_state_lambda([=]() -> optional<bool> {
417  if (this->valve_pump_switch(new_valve_number) != nullptr) {
418  return this->valve_switch(new_valve_number)->state() && this->valve_pump_switch(new_valve_number)->state();
419  }
420  return this->valve_switch(new_valve_number)->state();
421  });
422 
423  new_valve->valve_turn_off_automation =
424  make_unique<Automation<>>(new_valve->controller_switch->get_turn_off_trigger());
425  new_valve->valve_shutdown_action = make_unique<sprinkler::ShutdownAction<>>(this);
426  new_valve->valve_turn_off_automation->add_actions({new_valve->valve_shutdown_action.get()});
427 
428  new_valve->valve_turn_on_automation = make_unique<Automation<>>(new_valve->controller_switch->get_turn_on_trigger());
429  new_valve->valve_resumeorstart_action = make_unique<sprinkler::StartSingleValveAction<>>(this);
430  new_valve->valve_resumeorstart_action->set_valve_to_start(new_valve_number);
431  new_valve->valve_turn_on_automation->add_actions({new_valve->valve_resumeorstart_action.get()});
432 
433  if (enable_sw != nullptr) {
434  new_valve->enable_switch = enable_sw;
435  }
436 }
437 
438 void Sprinkler::add_controller(Sprinkler *other_controller) { this->other_controllers_.push_back(other_controller); }
439 
441  this->controller_sw_ = controller_switch;
442  controller_switch->set_state_lambda([=]() -> optional<bool> {
443  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
444  if (this->valve_[valve_number].controller_switch->state) {
445  return true;
446  }
447  }
448  return this->active_req_.has_request();
449  });
450 
451  this->sprinkler_turn_off_automation_ = make_unique<Automation<>>(controller_switch->get_turn_off_trigger());
452  this->sprinkler_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(this);
453  this->sprinkler_turn_off_automation_->add_actions({sprinkler_shutdown_action_.get()});
454 
455  this->sprinkler_turn_on_automation_ = make_unique<Automation<>>(controller_switch->get_turn_on_trigger());
456  this->sprinkler_resumeorstart_action_ = make_unique<sprinkler::ResumeOrStartAction<>>(this);
457  this->sprinkler_turn_on_automation_->add_actions({sprinkler_resumeorstart_action_.get()});
458 }
459 
461  this->auto_adv_sw_ = auto_adv_switch;
462 }
463 
465  this->queue_enable_sw_ = queue_enable_switch;
466 }
467 
469  this->reverse_sw_ = reverse_switch;
470 }
471 
473  this->standby_sw_ = standby_switch;
474 
475  this->sprinkler_standby_turn_on_automation_ = make_unique<Automation<>>(standby_switch->get_turn_on_trigger());
476  this->sprinkler_standby_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(this);
477  this->sprinkler_standby_turn_on_automation_->add_actions({sprinkler_standby_shutdown_action_.get()});
478 }
479 
481  this->multiplier_number_ = multiplier_number;
482 }
483 
485  this->repeat_number_ = repeat_number;
486 }
487 
488 void Sprinkler::configure_valve_switch(size_t valve_number, switch_::Switch *valve_switch, uint32_t run_duration) {
489  if (this->is_a_valid_valve(valve_number)) {
490  this->valve_[valve_number].valve_switch.set_on_switch(valve_switch);
491  this->valve_[valve_number].run_duration = run_duration;
492  }
493 }
494 
495 void Sprinkler::configure_valve_switch_pulsed(size_t valve_number, switch_::Switch *valve_switch_off,
496  switch_::Switch *valve_switch_on, uint32_t pulse_duration,
497  uint32_t run_duration) {
498  if (this->is_a_valid_valve(valve_number)) {
499  this->valve_[valve_number].valve_switch.set_off_switch(valve_switch_off);
500  this->valve_[valve_number].valve_switch.set_on_switch(valve_switch_on);
501  this->valve_[valve_number].valve_switch.set_pulse_duration(pulse_duration);
502  this->valve_[valve_number].run_duration = run_duration;
503  }
504 }
505 
506 void Sprinkler::configure_valve_pump_switch(size_t valve_number, switch_::Switch *pump_switch) {
507  if (this->is_a_valid_valve(valve_number)) {
508  for (size_t i = 0; i < this->pump_.size(); i++) { // check each existing registered pump
509  if (this->pump_[i].on_switch() == pump_switch) { // if the "new" pump matches one we already have...
510  this->valve_[valve_number].pump_switch_index = i; // ...save its index in the SprinklerSwitch vector pump_...
511  return; // ...and we are done
512  }
513  } // if we end up here, no pumps matched, so add a new one and set the valve's SprinklerSwitch at it
514  this->pump_.resize(this->pump_.size() + 1);
515  this->pump_.back().set_on_switch(pump_switch);
516  this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1; // save the index to the new pump
517  }
518 }
519 
520 void Sprinkler::configure_valve_pump_switch_pulsed(size_t valve_number, switch_::Switch *pump_switch_off,
521  switch_::Switch *pump_switch_on, uint32_t pulse_duration) {
522  if (this->is_a_valid_valve(valve_number)) {
523  for (size_t i = 0; i < this->pump_.size(); i++) { // check each existing registered pump
524  if ((this->pump_[i].off_switch() == pump_switch_off) &&
525  (this->pump_[i].on_switch() == pump_switch_on)) { // if the "new" pump matches one we already have...
526  this->valve_[valve_number].pump_switch_index = i; // ...save its index in the SprinklerSwitch vector pump_...
527  return; // ...and we are done
528  }
529  } // if we end up here, no pumps matched, so add a new one and set the valve's SprinklerSwitch at it
530  this->pump_.resize(this->pump_.size() + 1);
531  this->pump_.back().set_off_switch(pump_switch_off);
532  this->pump_.back().set_on_switch(pump_switch_on);
533  this->pump_.back().set_pulse_duration(pulse_duration);
534  this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1; // save the index to the new pump
535  }
536 }
537 
539  SprinklerControllerNumber *run_duration_number) {
540  if (this->is_a_valid_valve(valve_number)) {
541  this->valve_[valve_number].run_duration_number = run_duration_number;
542  }
543 }
544 
546  if (!divider.has_value()) {
547  return;
548  }
549  if (divider.value() > 0) {
550  this->set_multiplier(1.0 / divider.value());
551  this->set_repeat(divider.value() - 1);
552  } else if (divider.value() == 0) {
553  this->set_multiplier(1.0);
554  this->set_repeat(0);
555  }
556 }
557 
559  if ((!multiplier.has_value()) || (multiplier.value() < 0)) {
560  return;
561  }
562  this->multiplier_ = multiplier.value();
563  if (this->multiplier_number_ == nullptr) {
564  return;
565  }
566  if (this->multiplier_number_->state == multiplier.value()) {
567  return;
568  }
569  auto call = this->multiplier_number_->make_call();
570  call.set_value(multiplier.value());
571  call.perform();
572 }
573 
575  this->next_prev_ignore_disabled_ = ignore_disabled;
576 }
577 
578 void Sprinkler::set_pump_start_delay(uint32_t start_delay) {
579  this->start_delay_is_valve_delay_ = false;
580  this->start_delay_ = start_delay;
581 }
582 
583 void Sprinkler::set_pump_stop_delay(uint32_t stop_delay) {
584  this->stop_delay_is_valve_delay_ = false;
585  this->stop_delay_ = stop_delay;
586 }
587 
588 void Sprinkler::set_valve_start_delay(uint32_t start_delay) {
589  this->start_delay_is_valve_delay_ = true;
590  this->start_delay_ = start_delay;
591 }
592 
593 void Sprinkler::set_valve_stop_delay(uint32_t stop_delay) {
594  this->stop_delay_is_valve_delay_ = true;
595  this->stop_delay_ = stop_delay;
596 }
597 
598 void Sprinkler::set_pump_switch_off_during_valve_open_delay(bool pump_switch_off_during_valve_open_delay) {
599  this->pump_switch_off_during_valve_open_delay_ = pump_switch_off_during_valve_open_delay;
600 }
601 
602 void Sprinkler::set_valve_open_delay(const uint32_t valve_open_delay) {
603  if (valve_open_delay > 0) {
604  this->valve_overlap_ = false;
605  this->switching_delay_ = valve_open_delay;
606  } else {
607  this->switching_delay_.reset();
608  }
609 }
610 
611 void Sprinkler::set_valve_overlap(uint32_t valve_overlap) {
612  if (valve_overlap > 0) {
613  this->valve_overlap_ = true;
614  this->switching_delay_ = valve_overlap;
615  } else {
616  this->switching_delay_.reset();
617  }
618  this->pump_switch_off_during_valve_open_delay_ = false; // incompatible option
619 }
620 
621 void Sprinkler::set_manual_selection_delay(uint32_t manual_selection_delay) {
622  if (manual_selection_delay > 0) {
623  this->manual_selection_delay_ = manual_selection_delay;
624  } else {
625  this->manual_selection_delay_.reset();
626  }
627 }
628 
630  if (!valve_number.has_value() || !run_duration.has_value()) {
631  return;
632  }
633  if (!this->is_a_valid_valve(valve_number.value())) {
634  return;
635  }
636  this->valve_[valve_number.value()].run_duration = run_duration.value();
637  if (this->valve_[valve_number.value()].run_duration_number == nullptr) {
638  return;
639  }
640  if (this->valve_[valve_number.value()].run_duration_number->state == run_duration.value()) {
641  return;
642  }
643  auto call = this->valve_[valve_number.value()].run_duration_number->make_call();
644  if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement() == min_str) {
645  call.set_value(run_duration.value() / 60.0);
646  } else {
647  call.set_value(run_duration.value());
648  }
649  call.perform();
650 }
651 
652 void Sprinkler::set_auto_advance(const bool auto_advance) {
653  if (this->auto_adv_sw_ == nullptr) {
654  return;
655  }
656  if (this->auto_adv_sw_->state == auto_advance) {
657  return;
658  }
659  if (auto_advance) {
660  this->auto_adv_sw_->turn_on();
661  } else {
662  this->auto_adv_sw_->turn_off();
663  }
664 }
665 
667  this->target_repeats_ = repeat;
668  if (this->repeat_number_ == nullptr) {
669  return;
670  }
671  if (this->repeat_number_->state == repeat.value()) {
672  return;
673  }
674  auto call = this->repeat_number_->make_call();
675  call.set_value(repeat.value_or(0));
676  call.perform();
677 }
678 
679 void Sprinkler::set_queue_enable(bool queue_enable) {
680  if (this->queue_enable_sw_ == nullptr) {
681  return;
682  }
683  if (this->queue_enable_sw_->state == queue_enable) {
684  return;
685  }
686  if (queue_enable) {
687  this->queue_enable_sw_->turn_on();
688  } else {
689  this->queue_enable_sw_->turn_off();
690  }
691 }
692 
693 void Sprinkler::set_reverse(const bool reverse) {
694  if (this->reverse_sw_ == nullptr) {
695  return;
696  }
697  if (this->reverse_sw_->state == reverse) {
698  return;
699  }
700  if (reverse) {
701  this->reverse_sw_->turn_on();
702  } else {
703  this->reverse_sw_->turn_off();
704  }
705 }
706 
707 void Sprinkler::set_standby(const bool standby) {
708  if (this->standby_sw_ == nullptr) {
709  return;
710  }
711  if (this->standby_sw_->state == standby) {
712  return;
713  }
714  if (standby) {
715  this->standby_sw_->turn_on();
716  } else {
717  this->standby_sw_->turn_off();
718  }
719 }
720 
721 uint32_t Sprinkler::valve_run_duration(const size_t valve_number) {
722  if (!this->is_a_valid_valve(valve_number)) {
723  return 0;
724  }
725  if (this->valve_[valve_number].run_duration_number != nullptr) {
726  if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() == min_str) {
727  return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
728  } else {
729  return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state));
730  }
731  }
732  return this->valve_[valve_number].run_duration;
733 }
734 
735 uint32_t Sprinkler::valve_run_duration_adjusted(const size_t valve_number) {
736  uint32_t run_duration = 0;
737 
738  if (this->is_a_valid_valve(valve_number)) {
739  run_duration = this->valve_run_duration(valve_number);
740  }
741  run_duration = static_cast<uint32_t>(roundf(run_duration * this->multiplier()));
742  // run_duration must not be less than any of these
743  if ((run_duration < this->start_delay_) || (run_duration < this->stop_delay_) ||
744  (run_duration < this->switching_delay_.value_or(0) * 2)) {
745  return std::max(this->switching_delay_.value_or(0) * 2, std::max(this->start_delay_, this->stop_delay_));
746  }
747  return run_duration;
748 }
749 
751  if (this->auto_adv_sw_ != nullptr) {
752  return this->auto_adv_sw_->state;
753  }
754  return true;
755 }
756 
758  if (this->multiplier_number_ != nullptr) {
759  return this->multiplier_number_->state;
760  }
761  return this->multiplier_;
762 }
763 
765  if (this->repeat_number_ != nullptr) {
766  return static_cast<uint32_t>(roundf(this->repeat_number_->state));
767  }
768  return this->target_repeats_;
769 }
770 
772  // if there is an active valve and auto-advance is enabled, we may be repeating, so return the count
773  if (this->active_req_.has_request() && this->auto_advance()) {
774  return this->repeat_count_;
775  }
776  return nullopt;
777 }
778 
780  if (this->queue_enable_sw_ != nullptr) {
781  return this->queue_enable_sw_->state;
782  }
783  return true;
784 }
785 
787  if (this->reverse_sw_ != nullptr) {
788  return this->reverse_sw_->state;
789  }
790  return false;
791 }
792 
794  if (this->standby_sw_ != nullptr) {
795  return this->standby_sw_->state;
796  }
797  return false;
798 }
799 
801  if (this->standby()) {
802  ESP_LOGD(TAG, "start_from_queue called but standby is enabled; no action taken");
803  return;
804  }
805  if (this->multiplier() == 0) {
806  ESP_LOGD(TAG, "start_from_queue called but multiplier is set to zero; no action taken");
807  return;
808  }
809  if (this->queued_valves_.empty()) {
810  return; // if there is nothing in the queue, don't do anything
811  }
812  if (this->queue_enabled() && this->active_valve().has_value()) {
813  return; // if there is already a valve running from the queue, do nothing
814  }
815 
816  this->set_auto_advance(false);
817  this->set_queue_enable(true);
818 
819  this->reset_cycle_states_(); // just in case auto-advance is switched on later
820  this->repeat_count_ = 0;
821  this->fsm_kick_(); // will automagically pick up from the queue (it has priority)
822 }
823 
825  if (this->standby()) {
826  ESP_LOGD(TAG, "start_full_cycle called but standby is enabled; no action taken");
827  return;
828  }
829  if (this->multiplier() == 0) {
830  ESP_LOGD(TAG, "start_full_cycle called but multiplier is set to zero; no action taken");
831  return;
832  }
833  if (this->auto_advance() && this->active_valve().has_value()) {
834  return; // if auto-advance is already enabled and there is already a valve running, do nothing
835  }
836 
837  this->set_queue_enable(false);
838 
839  this->prep_full_cycle_();
840  this->repeat_count_ = 0;
841  // if there is no active valve already, start the first valve in the cycle
842  if (!this->active_req_.has_request()) {
843  this->fsm_kick_();
844  }
845 }
846 
848  if (this->standby()) {
849  ESP_LOGD(TAG, "start_single_valve called but standby is enabled; no action taken");
850  return;
851  }
852  if (this->multiplier() == 0) {
853  ESP_LOGD(TAG, "start_single_valve called but multiplier is set to zero; no action taken");
854  return;
855  }
856  if (!valve_number.has_value() || (valve_number == this->active_valve())) {
857  return;
858  }
859 
860  this->set_auto_advance(false);
861  this->set_queue_enable(false);
862 
863  this->reset_cycle_states_(); // just in case auto-advance is switched on later
864  this->repeat_count_ = 0;
865  this->fsm_request_(valve_number.value(), run_duration.value_or(0));
866 }
867 
869  if (valve_number.has_value()) {
870  if (this->is_a_valid_valve(valve_number.value()) && (this->queued_valves_.size() < this->max_queue_size_)) {
871  SprinklerQueueItem item{valve_number.value(), run_duration.value()};
872  this->queued_valves_.insert(this->queued_valves_.begin(), item);
873  ESP_LOGD(TAG, "Valve %u placed into queue with run duration of %u seconds", valve_number.value_or(0),
874  run_duration.value_or(0));
875  }
876  }
877 }
878 
880  this->queued_valves_.clear();
881  ESP_LOGD(TAG, "Queue cleared");
882 }
883 
885  if (this->state_ == IDLE) {
886  this->reset_cycle_states_(); // just in case auto-advance is switched on later
887  }
888 
889  this->manual_valve_ = this->next_valve_number_(
890  this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(this->number_of_valves() - 1)),
891  !this->next_prev_ignore_disabled_, true);
892 
893  if (!this->manual_valve_.has_value()) {
894  ESP_LOGD(TAG, "next_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows only "
895  "enabled valves and no valves are enabled?");
896  return;
897  }
898 
899  if (this->manual_selection_delay_.has_value()) {
900  this->set_timer_duration_(sprinkler::TIMER_VALVE_SELECTION, this->manual_selection_delay_.value());
901  this->start_timer_(sprinkler::TIMER_VALVE_SELECTION);
902  } else {
903  this->fsm_request_(this->manual_valve_.value());
904  }
905 }
906 
908  if (this->state_ == IDLE) {
909  this->reset_cycle_states_(); // just in case auto-advance is switched on later
910  }
911 
912  this->manual_valve_ =
913  this->previous_valve_number_(this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(0)),
914  !this->next_prev_ignore_disabled_, true);
915 
916  if (!this->manual_valve_.has_value()) {
917  ESP_LOGD(TAG, "previous_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows "
918  "only enabled valves and no valves are enabled?");
919  return;
920  }
921 
922  if (this->manual_selection_delay_.has_value()) {
923  this->set_timer_duration_(sprinkler::TIMER_VALVE_SELECTION, this->manual_selection_delay_.value());
924  this->start_timer_(sprinkler::TIMER_VALVE_SELECTION);
925  } else {
926  this->fsm_request_(this->manual_valve_.value());
927  }
928 }
929 
930 void Sprinkler::shutdown(bool clear_queue) {
931  this->cancel_timer_(sprinkler::TIMER_VALVE_SELECTION);
932  this->active_req_.reset();
933  this->manual_valve_.reset();
934  this->next_req_.reset();
935  for (auto &vo : this->valve_op_) {
936  vo.stop();
937  }
938  this->fsm_transition_to_shutdown_();
939  if (clear_queue) {
940  this->clear_queued_valves();
941  this->repeat_count_ = 0;
942  }
943 }
944 
946  if (this->paused_valve_.has_value() || !this->active_req_.has_request()) {
947  return; // we can't pause if we're already paused or if there is no active valve
948  }
949  this->paused_valve_ = this->active_valve();
950  this->resume_duration_ = this->time_remaining_active_valve();
951  this->shutdown(false);
952  ESP_LOGD(TAG, "Paused valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
953  this->resume_duration_.value_or(0));
954 }
955 
957  if (this->standby()) {
958  ESP_LOGD(TAG, "resume called but standby is enabled; no action taken");
959  return;
960  }
961 
962  if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
963  // Resume only if valve has not been completed yet
964  if (!this->valve_cycle_complete_(this->paused_valve_.value())) {
965  ESP_LOGD(TAG, "Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
966  this->resume_duration_.value_or(0));
967  this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
968  }
969  this->reset_resume();
970  } else {
971  ESP_LOGD(TAG, "No valve to resume!");
972  }
973 }
974 
976  if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
977  this->resume();
978  } else {
979  this->start_full_cycle();
980  }
981 }
982 
984  this->paused_valve_.reset();
985  this->resume_duration_.reset();
986 }
987 
988 const char *Sprinkler::valve_name(const size_t valve_number) {
989  if (this->is_a_valid_valve(valve_number)) {
990  return this->valve_[valve_number].controller_switch->get_name().c_str();
991  }
992  return nullptr;
993 }
994 
996  if (this->active_req_.has_request()) {
997  return this->active_req_.request_is_from();
998  }
999  return nullopt;
1000 }
1001 
1003  if (!this->valve_overlap_ && this->prev_req_.has_request() &&
1004  (this->prev_req_.valve_operator()->state() == STARTING || this->prev_req_.valve_operator()->state() == ACTIVE)) {
1005  return this->prev_req_.valve_as_opt();
1006  }
1007  return this->active_req_.valve_as_opt();
1008 }
1009 
1010 optional<size_t> Sprinkler::paused_valve() { return this->paused_valve_; }
1011 
1013  if (!this->queued_valves_.empty()) {
1014  return this->queued_valves_.back().valve_number;
1015  }
1016  return nullopt;
1017 }
1018 
1019 optional<size_t> Sprinkler::manual_valve() { return this->manual_valve_; }
1020 
1021 size_t Sprinkler::number_of_valves() { return this->valve_.size(); }
1022 
1023 bool Sprinkler::is_a_valid_valve(const size_t valve_number) {
1024  return ((valve_number >= 0) && (valve_number < this->number_of_valves()));
1025 }
1026 
1028  if (pump_switch == nullptr) {
1029  return false; // we can't do anything if there's nothing to check
1030  }
1031  // a pump must be considered "in use" if a (distribution) valve it supplies is active. this means:
1032  // - at least one SprinklerValveOperator:
1033  // - has a valve loaded that depends on this pump
1034  // - is in a state that depends on the pump: (ACTIVE and _possibly_ STARTING/STOPPING)
1035  // - if NO SprinklerValveOperator is active but there is a run request pending (active_req_.has_request()) and the
1036  // controller state is STARTING, valve open delay is configured but NOT pump_switch_off_during_valve_open_delay_
1037  for (auto &vo : this->valve_op_) { // first, check if any SprinklerValveOperator has a valve dependent on this pump
1038  if ((vo.state() != BYPASS) && (vo.pump_switch() != nullptr)) {
1039  // the SprinklerValveOperator is configured with a pump; now check if it is the pump of interest
1040  if ((vo.pump_switch()->off_switch() == pump_switch->off_switch()) &&
1041  (vo.pump_switch()->on_switch() == pump_switch->on_switch())) {
1042  // now if the SprinklerValveOperator has a pump and it is either ACTIVE, is STARTING with a valve delay or
1043  // is STOPPING with a valve delay, its pump can be considered "in use", so just return indicating this now
1044  if ((vo.state() == ACTIVE) ||
1045  ((vo.state() == STARTING) && this->start_delay_ && this->start_delay_is_valve_delay_) ||
1046  ((vo.state() == STOPPING) && this->stop_delay_ && this->stop_delay_is_valve_delay_)) {
1047  return true;
1048  }
1049  }
1050  }
1051  } // if we end up here, no SprinklerValveOperator was in a "give-away" state indicating that the pump is in use...
1052  if (!this->valve_overlap_ && !this->pump_switch_off_during_valve_open_delay_ && this->switching_delay_.has_value() &&
1053  this->active_req_.has_request() && (this->state_ != STOPPING)) {
1054  // ...the controller is configured to keep the pump on during a valve open delay, so just return
1055  // whether or not the next valve shares the same pump
1056  return (pump_switch->off_switch() == this->valve_pump_switch(this->active_req_.valve())->off_switch()) &&
1057  (pump_switch->on_switch() == this->valve_pump_switch(this->active_req_.valve())->on_switch());
1058  }
1059  return false;
1060 }
1061 
1063  if (pump_switch == nullptr) {
1064  return; // we can't do anything if there's nothing to check
1065  }
1066 
1067  bool hold_pump_on = false;
1068 
1069  for (auto &controller : this->other_controllers_) { // check if the pump is in use by another controller
1070  if (controller != this) { // dummy check
1071  if (controller->pump_in_use(pump_switch)) {
1072  hold_pump_on = true; // if another controller says it's using this pump, keep it on
1073  // at this point we know if there exists another SprinklerSwitch that is "on" with its
1074  // off_switch_ and on_switch_ pointers pointing to the same pair of switch objects
1075  }
1076  }
1077  }
1078  if (hold_pump_on) {
1079  // at this point we know if there exists another SprinklerSwitch that is "on" with its
1080  // off_switch_ and on_switch_ pointers pointing to the same pair of switch objects...
1081  pump_switch->sync_valve_state(true); // ...so ensure our state is consistent
1082  ESP_LOGD(TAG, "Leaving pump on because another controller instance is using it");
1083  }
1084 
1085  if (state) { // ...and now we can set the new state of the switch
1086  pump_switch->turn_on();
1087  } else if (!hold_pump_on && !this->pump_in_use(pump_switch)) {
1088  pump_switch->turn_off();
1089  } else if (hold_pump_on) { // we must assume the other controller will switch off the pump when done...
1090  pump_switch->sync_valve_state(false); // ...this only impacts latching valves
1091  }
1092 }
1093 
1095  uint32_t total_time_remaining = 0;
1096 
1097  for (size_t valve = 0; valve < this->number_of_valves(); valve++) {
1098  total_time_remaining += this->valve_run_duration_adjusted(valve);
1099  }
1100 
1101  if (this->valve_overlap_) {
1102  total_time_remaining -= this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1103  } else {
1104  total_time_remaining += this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1105  }
1106 
1107  return total_time_remaining;
1108 }
1109 
1111  uint32_t total_time_remaining = 0;
1112  uint32_t valve_count = 0;
1113 
1114  for (size_t valve = 0; valve < this->number_of_valves(); valve++) {
1115  if (this->valve_is_enabled_(valve)) {
1116  total_time_remaining += this->valve_run_duration_adjusted(valve);
1117  valve_count++;
1118  }
1119  }
1120 
1121  if (valve_count) {
1122  if (this->valve_overlap_) {
1123  total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1124  } else {
1125  total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1126  }
1127  }
1128 
1129  return total_time_remaining;
1130 }
1131 
1133  uint32_t total_time_remaining = 0;
1134  uint32_t enabled_valve_count = 0;
1135  uint32_t incomplete_valve_count = 0;
1136 
1137  for (size_t valve = 0; valve < this->number_of_valves(); valve++) {
1138  if (this->valve_is_enabled_(valve)) {
1139  enabled_valve_count++;
1140  if (!this->valve_cycle_complete_(valve)) {
1141  if (!this->active_valve().has_value() || (valve != this->active_valve().value())) {
1142  total_time_remaining += this->valve_run_duration_adjusted(valve);
1143  incomplete_valve_count++;
1144  } else {
1145  // to get here, there must be an active valve and this valve must be equal to 'valve'
1146  if (this->active_req_.valve_operator() == nullptr) { // no SVO has been assigned yet so it hasn't started
1147  total_time_remaining += this->valve_run_duration_adjusted(valve);
1148  incomplete_valve_count++;
1149  }
1150  }
1151  }
1152  }
1153  }
1154 
1155  if (incomplete_valve_count >= enabled_valve_count) {
1156  incomplete_valve_count--;
1157  }
1158  if (incomplete_valve_count) {
1159  if (this->valve_overlap_) {
1160  total_time_remaining -= this->switching_delay_.value_or(0) * incomplete_valve_count;
1161  } else {
1162  total_time_remaining += this->switching_delay_.value_or(0) * incomplete_valve_count;
1163  }
1164  }
1165 
1166  return total_time_remaining;
1167 }
1168 
1170  uint32_t total_time_remaining = 0;
1171  uint32_t valve_count = 0;
1172 
1173  for (auto &valve : this->queued_valves_) {
1174  if (valve.run_duration) {
1175  total_time_remaining += valve.run_duration;
1176  } else {
1177  total_time_remaining += this->valve_run_duration_adjusted(valve.valve_number);
1178  }
1179  valve_count++;
1180  }
1181 
1182  if (valve_count) {
1183  if (this->valve_overlap_) {
1184  total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1185  } else {
1186  total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1187  }
1188  }
1189 
1190  return total_time_remaining;
1191 }
1192 
1194  if (this->active_req_.has_request()) { // first try to return the value based on active_req_...
1195  if (this->active_req_.valve_operator() != nullptr) {
1196  return this->active_req_.valve_operator()->time_remaining();
1197  }
1198  }
1199  if (this->prev_req_.has_request()) { // try to return the value based on prev_req_...
1200  if (this->prev_req_.valve_operator() != nullptr) {
1201  return this->prev_req_.valve_operator()->time_remaining();
1202  }
1203  }
1204  return nullopt;
1205 }
1206 
1208  if (!this->time_remaining_active_valve().has_value() && this->state_ == IDLE) {
1209  return nullopt;
1210  }
1211 
1212  auto total_time_remaining = this->time_remaining_active_valve().value_or(0);
1213  if (this->auto_advance()) {
1214  total_time_remaining += this->total_cycle_time_enabled_incomplete_valves();
1215  if (this->repeat().value_or(0) > 0) {
1216  total_time_remaining +=
1217  (this->total_cycle_time_enabled_valves() * (this->repeat().value_or(0) - this->repeat_count().value_or(0)));
1218  }
1219  }
1220 
1221  if (this->queue_enabled()) {
1222  total_time_remaining += this->total_queue_time();
1223  }
1224  return total_time_remaining;
1225 }
1226 
1228  if (this->state_ != IDLE) {
1229  return true;
1230  }
1231 
1232  for (auto &controller : this->other_controllers_) {
1233  if (controller != this) { // dummy check
1234  if (controller->controller_state() != IDLE) {
1235  return true;
1236  }
1237  }
1238  }
1239  return false;
1240 }
1241 
1243  if (this->is_a_valid_valve(valve_number)) {
1244  return this->valve_[valve_number].controller_switch;
1245  }
1246  return nullptr;
1247 }
1248 
1250  if (this->is_a_valid_valve(valve_number)) {
1251  return this->valve_[valve_number].enable_switch;
1252  }
1253  return nullptr;
1254 }
1255 
1256 SprinklerSwitch *Sprinkler::valve_switch(const size_t valve_number) {
1257  if (this->is_a_valid_valve(valve_number)) {
1258  return &this->valve_[valve_number].valve_switch;
1259  }
1260  return nullptr;
1261 }
1262 
1263 SprinklerSwitch *Sprinkler::valve_pump_switch(const size_t valve_number) {
1264  if (this->is_a_valid_valve(valve_number) && this->valve_[valve_number].pump_switch_index.has_value()) {
1265  return &this->pump_[this->valve_[valve_number].pump_switch_index.value()];
1266  }
1267  return nullptr;
1268 }
1269 
1271  if (pump_index < this->pump_.size()) {
1272  return &this->pump_[pump_index];
1273  }
1274  return nullptr;
1275 }
1276 
1277 bool Sprinkler::valve_is_enabled_(const size_t valve_number) {
1278  if (this->is_a_valid_valve(valve_number)) {
1279  if (this->valve_[valve_number].enable_switch != nullptr) {
1280  return this->valve_[valve_number].enable_switch->state;
1281  } else {
1282  return true;
1283  }
1284  }
1285  return false;
1286 }
1287 
1288 void Sprinkler::mark_valve_cycle_complete_(const size_t valve_number) {
1289  if (this->is_a_valid_valve(valve_number)) {
1290  ESP_LOGD(TAG, "Marking valve %u complete", valve_number);
1291  this->valve_[valve_number].valve_cycle_complete = true;
1292  }
1293 }
1294 
1295 bool Sprinkler::valve_cycle_complete_(const size_t valve_number) {
1296  if (this->is_a_valid_valve(valve_number)) {
1297  return this->valve_[valve_number].valve_cycle_complete;
1298  }
1299  return false;
1300 }
1301 
1302 optional<size_t> Sprinkler::next_valve_number_(const optional<size_t> first_valve, const bool include_disabled,
1303  const bool include_complete) {
1304  auto valve = first_valve.value_or(0);
1305  size_t start = first_valve.has_value() ? 1 : 0;
1306 
1307  if (!this->is_a_valid_valve(valve)) {
1308  valve = 0;
1309  }
1310 
1311  for (size_t offset = start; offset < this->number_of_valves(); offset++) {
1312  auto valve_of_interest = valve + offset;
1313  if (!this->is_a_valid_valve(valve_of_interest)) {
1314  valve_of_interest -= this->number_of_valves();
1315  }
1316 
1317  if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1318  (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1319  return valve_of_interest;
1320  }
1321  }
1322  return nullopt;
1323 }
1324 
1325 optional<size_t> Sprinkler::previous_valve_number_(const optional<size_t> first_valve, const bool include_disabled,
1326  const bool include_complete) {
1327  auto valve = first_valve.value_or(this->number_of_valves() - 1);
1328  size_t start = first_valve.has_value() ? 1 : 0;
1329 
1330  if (!this->is_a_valid_valve(valve)) {
1331  valve = this->number_of_valves() - 1;
1332  }
1333 
1334  for (size_t offset = start; offset < this->number_of_valves(); offset++) {
1335  auto valve_of_interest = valve - offset;
1336  if (!this->is_a_valid_valve(valve_of_interest)) {
1337  valve_of_interest += this->number_of_valves();
1338  }
1339 
1340  if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1341  (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1342  return valve_of_interest;
1343  }
1344  }
1345  return nullopt;
1346 }
1347 
1349  if (this->reverse()) {
1350  return this->previous_valve_number_(first_valve, false, false);
1351  }
1352  return this->next_valve_number_(first_valve, false, false);
1353 }
1354 
1356  if (this->active_req_.has_request()) {
1357  this->prev_req_ = this->active_req_;
1358  } else {
1359  this->prev_req_.reset();
1360  }
1361 
1362  if (this->next_req_.has_request()) {
1363  if (!this->next_req_.run_duration()) { // ensure the run duration is set correctly for consumption later on
1364  this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->next_req_.valve()));
1365  }
1366  return; // there is already a request pending
1367  } else if (this->queue_enabled() && !this->queued_valves_.empty()) {
1368  this->next_req_.set_valve(this->queued_valves_.back().valve_number);
1369  this->next_req_.set_request_from(QUEUE);
1370  if (this->queued_valves_.back().run_duration) {
1371  this->next_req_.set_run_duration(this->queued_valves_.back().run_duration);
1372  this->queued_valves_.pop_back();
1373  } else if (this->multiplier()) {
1374  this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->queued_valves_.back().valve_number));
1375  this->queued_valves_.pop_back();
1376  } else {
1377  this->next_req_.reset();
1378  }
1379  } else if (this->auto_advance() && this->multiplier()) {
1380  if (this->next_valve_number_in_cycle_(first_valve).has_value()) {
1381  // if there is another valve to run as a part of a cycle, load that
1382  this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1383  this->next_req_.set_request_from(CYCLE);
1384  this->next_req_.set_run_duration(
1385  this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1386  } else if ((this->repeat_count_++ < this->repeat().value_or(0))) {
1387  ESP_LOGD(TAG, "Repeating - starting cycle %u of %u", this->repeat_count_ + 1, this->repeat().value_or(0) + 1);
1388  // if there are repeats remaining and no more valves were left in the cycle, start a new cycle
1389  this->prep_full_cycle_();
1390  if (this->next_valve_number_in_cycle_().has_value()) { // this should always succeed here, but just in case...
1391  this->next_req_.set_valve(this->next_valve_number_in_cycle_().value_or(0));
1392  this->next_req_.set_request_from(CYCLE);
1393  this->next_req_.set_run_duration(
1394  this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_().value_or(0)));
1395  }
1396  }
1397  }
1398 }
1399 
1401  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1402  if (this->valve_is_enabled_(valve_number))
1403  return true;
1404  }
1405  return false;
1406 }
1407 
1409  if (!req->has_request()) {
1410  return; // we can't do anything if the request contains nothing
1411  }
1412  if (!this->is_a_valid_valve(req->valve())) {
1413  return; // we can't do anything if the valve number isn't valid
1414  }
1415  for (auto &vo : this->valve_op_) { // find the first available SprinklerValveOperator, load it and start it up
1416  if (vo.state() == IDLE) {
1417  auto run_duration = req->run_duration() ? req->run_duration() : this->valve_run_duration_adjusted(req->valve());
1418  ESP_LOGD(TAG, "%s is starting valve %u for %u seconds, cycle %u of %u",
1419  this->req_as_str_(req->request_is_from()).c_str(), req->valve(), run_duration, this->repeat_count_ + 1,
1420  this->repeat().value_or(0) + 1);
1421  req->set_valve_operator(&vo);
1422  vo.set_controller(this);
1423  vo.set_valve(&this->valve_[req->valve()]);
1424  vo.set_run_duration(run_duration);
1425  vo.set_start_delay(this->start_delay_, this->start_delay_is_valve_delay_);
1426  vo.set_stop_delay(this->stop_delay_, this->stop_delay_is_valve_delay_);
1427  vo.start();
1428  return;
1429  }
1430  }
1431 }
1432 
1433 void Sprinkler::all_valves_off_(const bool include_pump) {
1434  for (size_t valve_index = 0; valve_index < this->number_of_valves(); valve_index++) {
1435  if (this->valve_[valve_index].valve_switch.state()) {
1436  this->valve_[valve_index].valve_switch.turn_off();
1437  }
1438  if (include_pump) {
1439  this->set_pump_state(this->valve_pump_switch(valve_index), false);
1440  }
1441  }
1442  ESP_LOGD(TAG, "All valves stopped%s", include_pump ? ", including pumps" : "");
1443 }
1444 
1446  this->set_auto_advance(true);
1447 
1448  if (!this->any_valve_is_enabled_()) {
1449  for (auto &valve : this->valve_) {
1450  if (valve.enable_switch != nullptr) {
1451  if (!valve.enable_switch->state) {
1452  valve.enable_switch->turn_on();
1453  }
1454  }
1455  }
1456  }
1457  this->reset_cycle_states_();
1458 }
1459 
1461  for (auto &valve : this->valve_) {
1462  valve.valve_cycle_complete = false;
1463  }
1464 }
1465 
1466 void Sprinkler::fsm_request_(size_t requested_valve, uint32_t requested_run_duration) {
1467  this->next_req_.set_valve(requested_valve);
1468  this->next_req_.set_run_duration(requested_run_duration);
1469  // if state is IDLE or ACTIVE, call fsm_transition_() to start it immediately;
1470  // otherwise, fsm_transition() will pick up next_req_ at the next appropriate transition
1471  this->fsm_kick_();
1472 }
1473 
1475  if ((this->state_ == IDLE) || (this->state_ == ACTIVE)) {
1476  this->fsm_transition_();
1477  }
1478 }
1479 
1481  ESP_LOGVV(TAG, "fsm_transition_ called; state is %s", this->state_as_str_(this->state_).c_str());
1482  switch (this->state_) {
1483  case IDLE: // the system was off -> start it up
1484  // advances to ACTIVE
1485  this->fsm_transition_from_shutdown_();
1486  break;
1487 
1488  case ACTIVE:
1489  // advances to STOPPING or ACTIVE (again)
1490  this->fsm_transition_from_valve_run_();
1491  break;
1492 
1493  case STARTING: {
1494  // follows valve open delay interval
1495  this->set_timer_duration_(sprinkler::TIMER_SM,
1496  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1497  this->start_timer_(sprinkler::TIMER_SM);
1498  this->start_valve_(&this->active_req_);
1499  this->state_ = ACTIVE;
1500  if (this->next_req_.has_request()) {
1501  // another valve has been requested, so restart the timer so we pick it up quickly
1502  this->set_timer_duration_(sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1503  this->start_timer_(sprinkler::TIMER_SM);
1504  }
1505  break;
1506  }
1507 
1508  case STOPPING:
1509  // stop_delay_ has elapsed so just shut everything off
1510  this->active_req_.reset();
1511  this->manual_valve_.reset();
1512  this->all_valves_off_(true);
1513  this->state_ = IDLE;
1514  break;
1515 
1516  default:
1517  break;
1518  }
1519  if (this->next_req_.has_request() && (this->state_ == IDLE)) {
1520  // another valve has been requested, so restart the timer so we pick it up quickly
1521  this->set_timer_duration_(sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1522  this->start_timer_(sprinkler::TIMER_SM);
1523  }
1524  ESP_LOGVV(TAG, "fsm_transition_ complete; new state is %s", this->state_as_str_(this->state_).c_str());
1525 }
1526 
1528  this->load_next_valve_run_request_();
1529 
1530  if (this->next_req_.has_request()) { // there is a valve to run...
1531  this->active_req_.set_valve(this->next_req_.valve());
1532  this->active_req_.set_request_from(this->next_req_.request_is_from());
1533  this->active_req_.set_run_duration(this->next_req_.run_duration());
1534  this->next_req_.reset();
1535 
1536  this->set_timer_duration_(sprinkler::TIMER_SM,
1537  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1538  this->start_timer_(sprinkler::TIMER_SM);
1539  this->start_valve_(&this->active_req_);
1540  this->state_ = ACTIVE;
1541  }
1542 }
1543 
1545  if (!this->active_req_.has_request()) { // dummy check...
1546  this->fsm_transition_to_shutdown_();
1547  return;
1548  }
1549 
1550  if (!this->timer_active_(sprinkler::TIMER_SM)) { // only flag the valve as "complete" if the timer finished
1551  if ((this->active_req_.request_is_from() == CYCLE) || (this->active_req_.request_is_from() == USER)) {
1552  this->mark_valve_cycle_complete_(this->active_req_.valve());
1553  }
1554  } else {
1555  ESP_LOGD(TAG, "Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1556  for (auto &vo : this->valve_op_) {
1557  vo.stop();
1558  }
1559  }
1560 
1561  this->load_next_valve_run_request_(this->active_req_.valve());
1562 
1563  if (this->next_req_.has_request()) { // there is another valve to run...
1564  bool same_pump =
1565  this->valve_pump_switch(this->active_req_.valve()) == this->valve_pump_switch(this->next_req_.valve());
1566 
1567  this->active_req_.set_valve(this->next_req_.valve());
1568  this->active_req_.set_request_from(this->next_req_.request_is_from());
1569  this->active_req_.set_run_duration(this->next_req_.run_duration());
1570  this->next_req_.reset();
1571 
1572  // this->state_ = ACTIVE; // state isn't changing
1573  if (this->valve_overlap_ || !this->switching_delay_.has_value()) {
1574  this->set_timer_duration_(sprinkler::TIMER_SM,
1575  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1576  this->start_timer_(sprinkler::TIMER_SM);
1577  this->start_valve_(&this->active_req_);
1578  } else {
1579  this->set_timer_duration_(
1581  this->switching_delay_.value() * 2 +
1582  (this->pump_switch_off_during_valve_open_delay_ && same_pump ? this->stop_delay_ : 0));
1583  this->start_timer_(sprinkler::TIMER_SM);
1584  this->state_ = STARTING;
1585  }
1586  } else { // there is NOT another valve to run...
1587  this->fsm_transition_to_shutdown_();
1588  }
1589 }
1590 
1592  this->state_ = STOPPING;
1593  this->set_timer_duration_(sprinkler::TIMER_SM,
1594  this->start_delay_ + this->stop_delay_ + this->switching_delay_.value_or(0) + 1);
1595  this->start_timer_(sprinkler::TIMER_SM);
1596 }
1597 
1599  switch (origin) {
1600  case USER:
1601  return "USER";
1602 
1603  case CYCLE:
1604  return "CYCLE";
1605 
1606  case QUEUE:
1607  return "QUEUE";
1608 
1609  default:
1610  return "UNKNOWN";
1611  }
1612 }
1613 
1615  switch (state) {
1616  case IDLE:
1617  return "IDLE";
1618 
1619  case STARTING:
1620  return "STARTING";
1621 
1622  case ACTIVE:
1623  return "ACTIVE";
1624 
1625  case STOPPING:
1626  return "STOPPING";
1627 
1628  case BYPASS:
1629  return "BYPASS";
1630 
1631  default:
1632  return "UNKNOWN";
1633  }
1634 }
1635 
1637  if (this->timer_duration_(timer_index) > 0) {
1638  this->set_timeout(this->timer_[timer_index].name, this->timer_duration_(timer_index),
1639  this->timer_cbf_(timer_index));
1640  this->timer_[timer_index].start_time = millis();
1641  this->timer_[timer_index].active = true;
1642  }
1643  ESP_LOGVV(TAG, "Timer %u started for %u sec", static_cast<size_t>(timer_index),
1644  this->timer_duration_(timer_index) / 1000);
1645 }
1646 
1648  this->timer_[timer_index].active = false;
1649  return this->cancel_timeout(this->timer_[timer_index].name);
1650 }
1651 
1652 bool Sprinkler::timer_active_(const SprinklerTimerIndex timer_index) { return this->timer_[timer_index].active; }
1653 
1654 void Sprinkler::set_timer_duration_(const SprinklerTimerIndex timer_index, const uint32_t time) {
1655  this->timer_[timer_index].time = 1000 * time;
1656 }
1657 
1658 uint32_t Sprinkler::timer_duration_(const SprinklerTimerIndex timer_index) { return this->timer_[timer_index].time; }
1659 
1660 std::function<void()> Sprinkler::timer_cbf_(const SprinklerTimerIndex timer_index) {
1661  return this->timer_[timer_index].func;
1662 }
1663 
1665  this->timer_[sprinkler::TIMER_VALVE_SELECTION].active = false;
1666  ESP_LOGVV(TAG, "Valve selection timer expired");
1667  if (this->manual_valve_.has_value()) {
1668  this->fsm_request_(this->manual_valve_.value());
1669  this->manual_valve_.reset();
1670  }
1671 }
1672 
1674  this->timer_[sprinkler::TIMER_SM].active = false;
1675  ESP_LOGVV(TAG, "State machine timer expired");
1676  this->fsm_transition_();
1677 }
1678 
1680  ESP_LOGCONFIG(TAG, "Sprinkler Controller -- %s", this->name_.c_str());
1681  if (this->manual_selection_delay_.has_value()) {
1682  ESP_LOGCONFIG(TAG, " Manual Selection Delay: %u seconds", this->manual_selection_delay_.value_or(0));
1683  }
1684  if (this->repeat().has_value()) {
1685  ESP_LOGCONFIG(TAG, " Repeat Cycles: %u times", this->repeat().value_or(0));
1686  }
1687  if (this->start_delay_) {
1688  if (this->start_delay_is_valve_delay_) {
1689  ESP_LOGCONFIG(TAG, " Pump Start Valve Delay: %u seconds", this->start_delay_);
1690  } else {
1691  ESP_LOGCONFIG(TAG, " Pump Start Pump Delay: %u seconds", this->start_delay_);
1692  }
1693  }
1694  if (this->stop_delay_) {
1695  if (this->stop_delay_is_valve_delay_) {
1696  ESP_LOGCONFIG(TAG, " Pump Stop Valve Delay: %u seconds", this->stop_delay_);
1697  } else {
1698  ESP_LOGCONFIG(TAG, " Pump Stop Pump Delay: %u seconds", this->stop_delay_);
1699  }
1700  }
1701  if (this->switching_delay_.has_value()) {
1702  if (this->valve_overlap_) {
1703  ESP_LOGCONFIG(TAG, " Valve Overlap: %u seconds", this->switching_delay_.value_or(0));
1704  } else {
1705  ESP_LOGCONFIG(TAG, " Valve Open Delay: %u seconds", this->switching_delay_.value_or(0));
1706  ESP_LOGCONFIG(TAG, " Pump Switch Off During Valve Open Delay: %s",
1707  YESNO(this->pump_switch_off_during_valve_open_delay_));
1708  }
1709  }
1710  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1711  ESP_LOGCONFIG(TAG, " Valve %u:", valve_number);
1712  ESP_LOGCONFIG(TAG, " Name: %s", this->valve_name(valve_number));
1713  ESP_LOGCONFIG(TAG, " Run Duration: %u seconds", this->valve_run_duration(valve_number));
1714  if (this->valve_[valve_number].valve_switch.pulse_duration()) {
1715  ESP_LOGCONFIG(TAG, " Pulse Duration: %u milliseconds",
1716  this->valve_[valve_number].valve_switch.pulse_duration());
1717  }
1718  }
1719  if (!this->pump_.empty()) {
1720  ESP_LOGCONFIG(TAG, " Total number of pumps: %u", this->pump_.size());
1721  }
1722  if (!this->valve_.empty()) {
1723  ESP_LOGCONFIG(TAG, " Total number of valves: %u", this->valve_.size());
1724  }
1725 }
1726 
1727 } // namespace sprinkler
1728 } // namespace esphome
Base class for all switches.
Definition: switch.h:39
value_type const & value() const
Definition: optional.h:89
void set_repeat(optional< uint32_t > repeat)
set the number of times to repeat a full cycle
Definition: sprinkler.cpp:666
uint32_t total_cycle_time_all_valves()
returns the amount of time in seconds required for all valves
Definition: sprinkler.cpp:1094
const char * name
Definition: stm32flash.h:78
void start_timer_(SprinklerTimerIndex timer_index)
Start/cancel/get status of valve timers.
Definition: sprinkler.cpp:1636
bool timer_active_(SprinklerTimerIndex timer_index)
returns true if the specified timer is active/running
Definition: sprinkler.cpp:1652
SprinklerControllerSwitch * controller_switch
Definition: sprinkler.h:90
void previous_valve()
advances to the previous valve (numerically)
Definition: sprinkler.cpp:907
void set_standby(bool standby)
if standby is true, controller will refuse to activate any valves
Definition: sprinkler.cpp:707
optional< std::function< optional< bool >)> > f_
Definition: sprinkler.h:138
uint32_t valve_run_duration(size_t valve_number)
returns valve_number&#39;s run duration in seconds
Definition: sprinkler.cpp:721
void next_valve()
advances to the next valve (numerically)
Definition: sprinkler.cpp:884
void set_run_duration(uint32_t run_duration)
Definition: sprinkler.cpp:195
void valve_selection_callback_()
callback functions for timers
Definition: sprinkler.cpp:1664
void queue_valve(optional< size_t > valve_number, optional< uint32_t > run_duration)
adds a valve into the queue.
Definition: sprinkler.cpp:868
void set_divider(optional< uint32_t > divider)
sets the multiplier value to &#39;1 / divider&#39; and sets repeat value to divider
Definition: sprinkler.cpp:545
optional< size_t > paused_valve()
returns the number of the valve that is paused, if any. check with &#39;has_value()&#39;
Definition: sprinkler.cpp:1010
bool auto_advance()
returns true if auto_advance is enabled
Definition: sprinkler.cpp:750
SprinklerValveRunRequestOrigin request_is_from()
Definition: sprinkler.cpp:387
void fsm_transition_to_shutdown_()
starts up the system from IDLE state
Definition: sprinkler.cpp:1591
optional< size_t > queued_valve()
returns the number of the next valve in the queue, if any. check with &#39;has_value()&#39; ...
Definition: sprinkler.cpp:1012
void set_multiplier(optional< float > multiplier)
value multiplied by configured run times – used to extend or shorten the cycle
Definition: sprinkler.cpp:558
SprinklerSwitch * valve_switch(size_t valve_number)
returns a pointer to a valve&#39;s switch object
Definition: sprinkler.cpp:1256
SprinklerValveOperator * valve_operator()
Definition: sprinkler.cpp:385
void fsm_request_(size_t requested_valve, uint32_t requested_run_duration=0)
make a request of the state machine
Definition: sprinkler.cpp:1466
void add_controller(Sprinkler *other_controller)
add another controller to the controller so it can check if pumps/main valves are in use ...
Definition: sprinkler.cpp:438
void reset_resume()
resets resume state
Definition: sprinkler.cpp:983
bool is_a_valid_valve(size_t valve_number)
returns true if valve number is valid
Definition: sprinkler.cpp:1023
void set_next_prev_ignore_disabled_valves(bool ignore_disabled)
enable/disable skipping of disabled valves by the next and previous actions
Definition: sprinkler.cpp:574
void set_state_lambda(std::function< optional< bool >()> &&f)
Definition: sprinkler.cpp:135
void shutdown(bool clear_queue=false)
turns off all valves, effectively shutting down the system.
Definition: sprinkler.cpp:930
void set_pump_start_delay(uint32_t start_delay)
set how long the pump should start after the valve (when the pump is starting)
Definition: sprinkler.cpp:578
optional< size_t > pump_switch_index
Definition: sprinkler.h:94
void set_controller_queue_enable_switch(SprinklerControllerSwitch *queue_enable_switch)
Definition: sprinkler.cpp:464
SprinklerSwitch * valve_pump_switch_by_pump_index(size_t pump_index)
returns a pointer to a valve&#39;s pump switch object
Definition: sprinkler.cpp:1270
bool has_value() const
Definition: optional.h:87
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
Definition: sprinkler.cpp:629
std::unique_ptr< Automation<> > valve_turn_off_automation
Definition: sprinkler.h:98
void configure_valve_run_duration_number(size_t valve_number, SprinklerControllerNumber *run_duration_number)
configure a valve&#39;s run duration number component
Definition: sprinkler.cpp:538
optional< size_t > next_valve_number_(optional< size_t > first_valve=nullopt, bool include_disabled=true, bool include_complete=true)
returns the number of the next valve in the vector or nullopt if no valves match criteria ...
Definition: sprinkler.cpp:1302
void resume_or_start_full_cycle()
if a cycle was suspended using pause(), resumes it. otherwise calls start_full_cycle() ...
Definition: sprinkler.cpp:975
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
SprinklerSwitch * valve_pump_switch(size_t valve_number)
returns a pointer to a valve&#39;s pump switch object
Definition: sprinkler.cpp:1263
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition: automation.h:95
void set_controller_standby_switch(SprinklerControllerSwitch *standby_switch)
Definition: sprinkler.cpp:472
void set_valve_overlap(uint32_t valve_overlap)
set how long the controller should wait after opening a valve before closing the previous valve ...
Definition: sprinkler.cpp:611
uint32_t total_cycle_time_enabled_valves()
returns the amount of time in seconds required for all enabled valves
Definition: sprinkler.cpp:1110
void set_request_from(SprinklerValveRunRequestOrigin origin)
Definition: sprinkler.cpp:350
uint32_t valve_run_duration_adjusted(size_t valve_number)
returns valve_number&#39;s run duration (in seconds) adjusted by multiplier_
Definition: sprinkler.cpp:735
void set_controller_reverse_switch(SprinklerControllerSwitch *reverse_switch)
Definition: sprinkler.cpp:468
void set_controller_multiplier_number(SprinklerControllerNumber *multiplier_number)
configure important controller number components
Definition: sprinkler.cpp:480
switch_::Switch * on_switch()
Definition: sprinkler.h:65
const char *const TAG
Definition: spi.cpp:8
const nullopt_t nullopt((nullopt_t::init()))
void fsm_kick_()
kicks the state machine to advance, starting it if it is not already active
Definition: sprinkler.cpp:1474
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...
Definition: sprinkler.cpp:1062
optional< bool > get_initial_state_with_restore_mode()
Returns the initial state of the switch, after applying restore mode rules.
Definition: switch.cpp:33
std::string req_as_str_(SprinklerValveRunRequestOrigin origin)
return the specified SprinklerValveRunRequestOrigin as a string
Definition: sprinkler.cpp:1598
uint32_t timer_duration_(SprinklerTimerIndex timer_index)
returns time in milliseconds (ms)
Definition: sprinkler.cpp:1658
void resume()
resumes a cycle that was suspended using pause()
Definition: sprinkler.cpp:956
void set_controller_repeat_number(SprinklerControllerNumber *repeat_number)
Definition: sprinkler.cpp:484
ESPPreferences * global_preferences
void pause()
same as shutdown(), but also stores active_valve() and time_remaining() allowing resume() to continue...
Definition: sprinkler.cpp:945
void start_full_cycle()
starts a full cycle of all enabled valves and enables auto_advance.
Definition: sprinkler.cpp:824
SprinklerValveRunRequestOrigin origin_
Definition: sprinkler.h:202
void turn_on()
Turn this switch on.
Definition: switch.cpp:11
bool reverse()
returns true if reverse is enabled
Definition: sprinkler.cpp:786
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.
Definition: sprinkler.cpp:1348
void start_valve_(SprinklerValveRunRequest *req)
loads an available SprinklerValveOperator (valve_op_) based on req and starts it (switches it on)...
Definition: sprinkler.cpp:1408
void set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay)
Definition: sprinkler.cpp:201
void set_timer_duration_(SprinklerTimerIndex timer_index, uint32_t time)
time is converted to milliseconds (ms) for set_timeout()
Definition: sprinkler.cpp:1654
void set_auto_advance(bool auto_advance)
if auto_advance is true, controller will iterate through all enabled valves
Definition: sprinkler.cpp:652
SprinklerControllerSwitch * control_switch(size_t valve_number)
returns a pointer to a valve&#39;s control switch object
Definition: sprinkler.cpp:1242
uint32_t total_cycle_time_enabled_incomplete_valves()
returns the amount of time in seconds required for all enabled & incomplete valves, not including the active valve
Definition: sprinkler.cpp:1132
uint32_t total_queue_time()
returns the amount of time in seconds required for all valves in the queue
Definition: sprinkler.cpp:1169
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 ...
Definition: sprinkler.cpp:602
void set_controller_auto_adv_switch(SprinklerControllerSwitch *auto_adv_switch)
Definition: sprinkler.cpp:460
SprinklerControllerSwitch * enable_switch(size_t valve_number)
returns a pointer to a valve&#39;s enable switch object
Definition: sprinkler.cpp:1249
void set_controller(Sprinkler *controller)
Definition: sprinkler.cpp:178
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...
Definition: sprinkler.cpp:621
void set_queue_enable(bool queue_enable)
if queue_enable is true, controller will iterate through valves in the queue
Definition: sprinkler.cpp:679
void set_reverse(bool reverse)
if reverse is true, controller will iterate through all enabled valves in reverse (descending) order ...
Definition: sprinkler.cpp:693
bool any_controller_is_active()
returns true if this or any sprinkler controller this controller knows about is active ...
Definition: sprinkler.cpp:1227
void fsm_transition_()
advance controller state, advancing to target_valve if provided
Definition: sprinkler.cpp:1480
optional< uint32_t > time_remaining_active_valve()
returns the amount of time remaining in seconds for the active valve, if any
Definition: sprinkler.cpp:1193
bool queue_enabled()
returns true if the queue is enabled to run
Definition: sprinkler.cpp:779
void set_valve_operator(SprinklerValveOperator *valve_op)
Definition: sprinkler.cpp:361
bool valve_is_enabled_(size_t valve_number)
returns true if valve number is enabled
Definition: sprinkler.cpp:1277
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...
Definition: sprinkler.cpp:598
void set_controller_main_switch(SprinklerControllerSwitch *controller_switch)
configure important controller switches
Definition: sprinkler.cpp:440
void set_valve(SprinklerValve *valve)
Definition: sprinkler.cpp:184
void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw=nullptr)
add a valve to the controller
Definition: sprinkler.cpp:410
void reset_cycle_states_()
resets the cycle state for all valves
Definition: sprinkler.cpp:1460
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition: component.cpp:17
bool standby()
returns true if standby is enabled
Definition: sprinkler.cpp:793
void start_from_queue()
starts the controller from the first valve in the queue and disables auto_advance.
Definition: sprinkler.cpp:800
void prep_full_cycle_()
prepares for a full cycle by verifying auto-advance is on as well as one or more valve enable switche...
Definition: sprinkler.cpp:1445
void configure_valve_pump_switch(size_t valve_number, switch_::Switch *pump_switch)
configure a valve&#39;s associated pump switch object
Definition: sprinkler.cpp:506
optional< uint32_t > time_remaining_current_operation()
returns the amount of time remaining in seconds for all valves remaining, including the active valve...
Definition: sprinkler.cpp:1207
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
optional< uint32_t > repeat_count()
if a cycle is active, returns the number of times the controller has repeated the cycle...
Definition: sprinkler.cpp:771
void clear_queued_valves()
clears/removes all valves from the queue
Definition: sprinkler.cpp:879
std::unique_ptr< StartSingleValveAction<> > valve_resumeorstart_action
Definition: sprinkler.h:97
void sync_valve_state(bool latch_state)
Definition: sprinkler.cpp:70
bool valve_cycle_complete_(size_t valve_number)
returns true if valve&#39;s cycle is flagged as complete
Definition: sprinkler.cpp:1295
optional< SprinklerValveRunRequestOrigin > active_valve_request_is_from()
returns what invoked the valve that is currently active, if any. check with &#39;has_value()&#39; ...
Definition: sprinkler.cpp:995
bool any_valve_is_enabled_()
returns true if any valve is enabled
Definition: sprinkler.cpp:1400
void configure_valve_switch(size_t valve_number, switch_::Switch *valve_switch, uint32_t run_duration)
configure a valve&#39;s switch object and run duration. run_duration is time in seconds.
Definition: sprinkler.cpp:488
bool cancel_timer_(SprinklerTimerIndex timer_index)
Definition: sprinkler.cpp:1647
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void fsm_transition_from_shutdown_()
starts up the system from IDLE state
Definition: sprinkler.cpp:1527
std::string state_as_str_(SprinklerState state)
return the specified SprinklerState state as a string
Definition: sprinkler.cpp:1614
const std::string min_str
Definition: sprinkler.h:14
std::function< void()> timer_cbf_(SprinklerTimerIndex timer_index)
Definition: sprinkler.cpp:1660
bool pump_in_use(SprinklerSwitch *pump_switch)
returns true if the pump the pointer points to is in use
Definition: sprinkler.cpp:1027
void set_valve_start_delay(uint32_t start_delay)
set how long the valve should start after the pump (when the pump is stopping)
Definition: sprinkler.cpp:588
const char * valve_name(size_t valve_number)
returns a pointer to a valve&#39;s name string object; returns nullptr if valve_number is invalid ...
Definition: sprinkler.cpp:988
void configure_valve_pump_switch_pulsed(size_t valve_number, switch_::Switch *pump_switch_off, switch_::Switch *pump_switch_on, uint32_t pulse_duration)
Definition: sprinkler.cpp:520
void set_valve_stop_delay(uint32_t stop_delay)
set how long the valve should stop after the pump (when the pump is stopping)
Definition: sprinkler.cpp:593
size_t number_of_valves()
returns the number of valves the controller is configured with
Definition: sprinkler.cpp:1021
std::unique_ptr< Automation<> > valve_turn_on_automation
Definition: sprinkler.h:99
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)
Definition: sprinkler.cpp:495
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.
Definition: sprinkler.cpp:1355
void mark_valve_cycle_complete_(size_t valve_number)
marks a valve&#39;s cycle as complete
Definition: sprinkler.cpp:1288
void set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay)
Definition: sprinkler.cpp:206
void publish_state(bool state)
Publish a state to the front-end from the back-end.
Definition: switch.cpp:47
void set_run_duration(uint32_t run_duration)
Definition: sprinkler.cpp:352
SprinklerControllerSwitch * enable_switch
Definition: sprinkler.h:91
void start_single_valve(optional< size_t > valve_number, optional< uint32_t > run_duration=nullopt)
activates a single valve and disables auto_advance.
Definition: sprinkler.cpp:847
bool state
The current reported state of the binary sensor.
Definition: switch.h:53
value_type value_or(U const &v) const
Definition: optional.h:93
optional< size_t > active_valve()
returns the number of the valve that is currently active, if any. check with &#39;has_value()&#39; ...
Definition: sprinkler.cpp:1002
void stop_action()
Stop any action connected to this trigger.
Definition: automation.h:103
void fsm_transition_from_valve_run_()
transitions from ACTIVE state to ACTIVE (as in, next valve) or to a SHUTDOWN or IDLE state ...
Definition: sprinkler.cpp:1544
float multiplier()
returns the current value of the multiplier
Definition: sprinkler.cpp:757
switch_::Switch * off_switch()
Definition: sprinkler.h:64
optional< uint32_t > repeat()
returns the number of times the controller is set to repeat cycles, if at all. check with &#39;has_value(...
Definition: sprinkler.cpp:764
bool state
Definition: fan.h:34
void set_pump_stop_delay(uint32_t stop_delay)
set how long the pump should stop after the valve (when the pump is starting)
Definition: sprinkler.cpp:583
void turn_off()
Turn this switch off.
Definition: switch.cpp:15
std::unique_ptr< ShutdownAction<> > valve_shutdown_action
Definition: sprinkler.h:96
optional< size_t > manual_valve()
returns the number of the valve that is manually selected, if any.
Definition: sprinkler.cpp:1019
optional< size_t > previous_valve_number_(optional< size_t > first_valve=nullopt, bool include_disabled=true, bool include_complete=true)
returns the number of the previous valve in the vector or nullopt if no valves match criteria ...
Definition: sprinkler.cpp:1325
void all_valves_off_(bool include_pump=false)
turns off/closes all valves, including pump if include_pump is true
Definition: sprinkler.cpp:1433