ESPHome  2023.5.4
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->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
958  ESP_LOGD(TAG, "Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
959  this->resume_duration_.value_or(0));
960  this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
961  this->reset_resume();
962  } else {
963  ESP_LOGD(TAG, "No valve to resume!");
964  }
965 }
966 
968  if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
969  this->resume();
970  } else {
971  this->start_full_cycle();
972  }
973 }
974 
976  this->paused_valve_.reset();
977  this->resume_duration_.reset();
978 }
979 
980 const char *Sprinkler::valve_name(const size_t valve_number) {
981  if (this->is_a_valid_valve(valve_number)) {
982  return this->valve_[valve_number].controller_switch->get_name().c_str();
983  }
984  return nullptr;
985 }
986 
988  if (this->active_req_.has_request()) {
989  return this->active_req_.request_is_from();
990  }
991  return nullopt;
992 }
993 
995  if (!this->valve_overlap_ && this->prev_req_.has_request() &&
996  (this->prev_req_.valve_operator()->state() == STARTING || this->prev_req_.valve_operator()->state() == ACTIVE)) {
997  return this->prev_req_.valve_as_opt();
998  }
999  return this->active_req_.valve_as_opt();
1000 }
1001 
1002 optional<size_t> Sprinkler::paused_valve() { return this->paused_valve_; }
1003 
1005  if (!this->queued_valves_.empty()) {
1006  return this->queued_valves_.back().valve_number;
1007  }
1008  return nullopt;
1009 }
1010 
1011 optional<size_t> Sprinkler::manual_valve() { return this->manual_valve_; }
1012 
1013 size_t Sprinkler::number_of_valves() { return this->valve_.size(); }
1014 
1015 bool Sprinkler::is_a_valid_valve(const size_t valve_number) {
1016  return ((valve_number >= 0) && (valve_number < this->number_of_valves()));
1017 }
1018 
1020  if (pump_switch == nullptr) {
1021  return false; // we can't do anything if there's nothing to check
1022  }
1023  // a pump must be considered "in use" if a (distribution) valve it supplies is active. this means:
1024  // - at least one SprinklerValveOperator:
1025  // - has a valve loaded that depends on this pump
1026  // - is in a state that depends on the pump: (ACTIVE and _possibly_ STARTING/STOPPING)
1027  // - if NO SprinklerValveOperator is active but there is a run request pending (active_req_.has_request()) and the
1028  // controller state is STARTING, valve open delay is configured but NOT pump_switch_off_during_valve_open_delay_
1029  for (auto &vo : this->valve_op_) { // first, check if any SprinklerValveOperator has a valve dependent on this pump
1030  if ((vo.state() != BYPASS) && (vo.pump_switch() != nullptr)) {
1031  // the SprinklerValveOperator is configured with a pump; now check if it is the pump of interest
1032  if ((vo.pump_switch()->off_switch() == pump_switch->off_switch()) &&
1033  (vo.pump_switch()->on_switch() == pump_switch->on_switch())) {
1034  // now if the SprinklerValveOperator has a pump and it is either ACTIVE, is STARTING with a valve delay or
1035  // is STOPPING with a valve delay, its pump can be considered "in use", so just return indicating this now
1036  if ((vo.state() == ACTIVE) ||
1037  ((vo.state() == STARTING) && this->start_delay_ && this->start_delay_is_valve_delay_) ||
1038  ((vo.state() == STOPPING) && this->stop_delay_ && this->stop_delay_is_valve_delay_)) {
1039  return true;
1040  }
1041  }
1042  }
1043  } // if we end up here, no SprinklerValveOperator was in a "give-away" state indicating that the pump is in use...
1044  if (!this->valve_overlap_ && !this->pump_switch_off_during_valve_open_delay_ && this->switching_delay_.has_value() &&
1045  this->active_req_.has_request() && (this->state_ != STOPPING)) {
1046  // ...the controller is configured to keep the pump on during a valve open delay, so just return
1047  // whether or not the next valve shares the same pump
1048  return (pump_switch->off_switch() == this->valve_pump_switch(this->active_req_.valve())->off_switch()) &&
1049  (pump_switch->on_switch() == this->valve_pump_switch(this->active_req_.valve())->on_switch());
1050  }
1051  return false;
1052 }
1053 
1055  if (pump_switch == nullptr) {
1056  return; // we can't do anything if there's nothing to check
1057  }
1058 
1059  bool hold_pump_on = false;
1060 
1061  for (auto &controller : this->other_controllers_) { // check if the pump is in use by another controller
1062  if (controller != this) { // dummy check
1063  if (controller->pump_in_use(pump_switch)) {
1064  hold_pump_on = true; // if another controller says it's using this pump, keep it on
1065  // at this point we know if there exists another SprinklerSwitch that is "on" with its
1066  // off_switch_ and on_switch_ pointers pointing to the same pair of switch objects
1067  }
1068  }
1069  }
1070  if (hold_pump_on) {
1071  // at this point we know if there exists another SprinklerSwitch that is "on" with its
1072  // off_switch_ and on_switch_ pointers pointing to the same pair of switch objects...
1073  pump_switch->sync_valve_state(true); // ...so ensure our state is consistent
1074  ESP_LOGD(TAG, "Leaving pump on because another controller instance is using it");
1075  }
1076 
1077  if (state) { // ...and now we can set the new state of the switch
1078  pump_switch->turn_on();
1079  } else if (!hold_pump_on && !this->pump_in_use(pump_switch)) {
1080  pump_switch->turn_off();
1081  } else if (hold_pump_on) { // we must assume the other controller will switch off the pump when done...
1082  pump_switch->sync_valve_state(false); // ...this only impacts latching valves
1083  }
1084 }
1085 
1087  uint32_t total_time_remaining = 0;
1088 
1089  for (size_t valve = 0; valve < this->number_of_valves(); valve++) {
1090  total_time_remaining += this->valve_run_duration_adjusted(valve);
1091  }
1092 
1093  if (this->valve_overlap_) {
1094  total_time_remaining -= this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1095  } else {
1096  total_time_remaining += this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1097  }
1098 
1099  return total_time_remaining;
1100 }
1101 
1103  uint32_t total_time_remaining = 0;
1104  uint32_t valve_count = 0;
1105 
1106  for (size_t valve = 0; valve < this->number_of_valves(); valve++) {
1107  if (this->valve_is_enabled_(valve)) {
1108  total_time_remaining += this->valve_run_duration_adjusted(valve);
1109  valve_count++;
1110  }
1111  }
1112 
1113  if (valve_count) {
1114  if (this->valve_overlap_) {
1115  total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1116  } else {
1117  total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1118  }
1119  }
1120 
1121  return total_time_remaining;
1122 }
1123 
1125  uint32_t total_time_remaining = 0;
1126  uint32_t enabled_valve_count = 0;
1127  uint32_t incomplete_valve_count = 0;
1128 
1129  for (size_t valve = 0; valve < this->number_of_valves(); valve++) {
1130  if (this->valve_is_enabled_(valve)) {
1131  enabled_valve_count++;
1132  if (!this->valve_cycle_complete_(valve)) {
1133  if (!this->active_valve().has_value() || (valve != this->active_valve().value())) {
1134  total_time_remaining += this->valve_run_duration_adjusted(valve);
1135  incomplete_valve_count++;
1136  } else {
1137  // to get here, there must be an active valve and this valve must be equal to 'valve'
1138  if (this->active_req_.valve_operator() == nullptr) { // no SVO has been assigned yet so it hasn't started
1139  total_time_remaining += this->valve_run_duration_adjusted(valve);
1140  incomplete_valve_count++;
1141  }
1142  }
1143  }
1144  }
1145  }
1146 
1147  if (incomplete_valve_count >= enabled_valve_count) {
1148  incomplete_valve_count--;
1149  }
1150  if (incomplete_valve_count) {
1151  if (this->valve_overlap_) {
1152  total_time_remaining -= this->switching_delay_.value_or(0) * incomplete_valve_count;
1153  } else {
1154  total_time_remaining += this->switching_delay_.value_or(0) * incomplete_valve_count;
1155  }
1156  }
1157 
1158  return total_time_remaining;
1159 }
1160 
1162  uint32_t total_time_remaining = 0;
1163  uint32_t valve_count = 0;
1164 
1165  for (auto &valve : this->queued_valves_) {
1166  if (valve.run_duration) {
1167  total_time_remaining += valve.run_duration;
1168  } else {
1169  total_time_remaining += this->valve_run_duration_adjusted(valve.valve_number);
1170  }
1171  valve_count++;
1172  }
1173 
1174  if (valve_count) {
1175  if (this->valve_overlap_) {
1176  total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1177  } else {
1178  total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1179  }
1180  }
1181 
1182  return total_time_remaining;
1183 }
1184 
1186  if (this->active_req_.has_request()) { // first try to return the value based on active_req_...
1187  if (this->active_req_.valve_operator() != nullptr) {
1188  return this->active_req_.valve_operator()->time_remaining();
1189  }
1190  }
1191  if (this->prev_req_.has_request()) { // try to return the value based on prev_req_...
1192  if (this->prev_req_.valve_operator() != nullptr) {
1193  return this->prev_req_.valve_operator()->time_remaining();
1194  }
1195  }
1196  return nullopt;
1197 }
1198 
1200  if (!this->time_remaining_active_valve().has_value() && this->state_ == IDLE) {
1201  return nullopt;
1202  }
1203 
1204  auto total_time_remaining = this->time_remaining_active_valve().value_or(0);
1205  if (this->auto_advance()) {
1206  total_time_remaining += this->total_cycle_time_enabled_incomplete_valves();
1207  if (this->repeat().value_or(0) > 0) {
1208  total_time_remaining +=
1209  (this->total_cycle_time_enabled_valves() * (this->repeat().value_or(0) - this->repeat_count().value_or(0)));
1210  }
1211  }
1212 
1213  if (this->queue_enabled()) {
1214  total_time_remaining += this->total_queue_time();
1215  }
1216  return total_time_remaining;
1217 }
1218 
1220  if (this->state_ != IDLE) {
1221  return true;
1222  }
1223 
1224  for (auto &controller : this->other_controllers_) {
1225  if (controller != this) { // dummy check
1226  if (controller->controller_state() != IDLE) {
1227  return true;
1228  }
1229  }
1230  }
1231  return false;
1232 }
1233 
1235  if (this->is_a_valid_valve(valve_number)) {
1236  return this->valve_[valve_number].controller_switch;
1237  }
1238  return nullptr;
1239 }
1240 
1242  if (this->is_a_valid_valve(valve_number)) {
1243  return this->valve_[valve_number].enable_switch;
1244  }
1245  return nullptr;
1246 }
1247 
1248 SprinklerSwitch *Sprinkler::valve_switch(const size_t valve_number) {
1249  if (this->is_a_valid_valve(valve_number)) {
1250  return &this->valve_[valve_number].valve_switch;
1251  }
1252  return nullptr;
1253 }
1254 
1255 SprinklerSwitch *Sprinkler::valve_pump_switch(const size_t valve_number) {
1256  if (this->is_a_valid_valve(valve_number) && this->valve_[valve_number].pump_switch_index.has_value()) {
1257  return &this->pump_[this->valve_[valve_number].pump_switch_index.value()];
1258  }
1259  return nullptr;
1260 }
1261 
1263  if (pump_index < this->pump_.size()) {
1264  return &this->pump_[pump_index];
1265  }
1266  return nullptr;
1267 }
1268 
1269 bool Sprinkler::valve_is_enabled_(const size_t valve_number) {
1270  if (this->is_a_valid_valve(valve_number)) {
1271  if (this->valve_[valve_number].enable_switch != nullptr) {
1272  return this->valve_[valve_number].enable_switch->state;
1273  } else {
1274  return true;
1275  }
1276  }
1277  return false;
1278 }
1279 
1280 void Sprinkler::mark_valve_cycle_complete_(const size_t valve_number) {
1281  if (this->is_a_valid_valve(valve_number)) {
1282  ESP_LOGD(TAG, "Marking valve %u complete", valve_number);
1283  this->valve_[valve_number].valve_cycle_complete = true;
1284  }
1285 }
1286 
1287 bool Sprinkler::valve_cycle_complete_(const size_t valve_number) {
1288  if (this->is_a_valid_valve(valve_number)) {
1289  return this->valve_[valve_number].valve_cycle_complete;
1290  }
1291  return false;
1292 }
1293 
1294 optional<size_t> Sprinkler::next_valve_number_(const optional<size_t> first_valve, const bool include_disabled,
1295  const bool include_complete) {
1296  auto valve = first_valve.value_or(0);
1297  size_t start = first_valve.has_value() ? 1 : 0;
1298 
1299  if (!this->is_a_valid_valve(valve)) {
1300  valve = 0;
1301  }
1302 
1303  for (size_t offset = start; offset < this->number_of_valves(); offset++) {
1304  auto valve_of_interest = valve + offset;
1305  if (!this->is_a_valid_valve(valve_of_interest)) {
1306  valve_of_interest -= this->number_of_valves();
1307  }
1308 
1309  if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1310  (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1311  return valve_of_interest;
1312  }
1313  }
1314  return nullopt;
1315 }
1316 
1317 optional<size_t> Sprinkler::previous_valve_number_(const optional<size_t> first_valve, const bool include_disabled,
1318  const bool include_complete) {
1319  auto valve = first_valve.value_or(this->number_of_valves() - 1);
1320  size_t start = first_valve.has_value() ? 1 : 0;
1321 
1322  if (!this->is_a_valid_valve(valve)) {
1323  valve = this->number_of_valves() - 1;
1324  }
1325 
1326  for (size_t offset = start; offset < this->number_of_valves(); offset++) {
1327  auto valve_of_interest = valve - offset;
1328  if (!this->is_a_valid_valve(valve_of_interest)) {
1329  valve_of_interest += this->number_of_valves();
1330  }
1331 
1332  if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1333  (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1334  return valve_of_interest;
1335  }
1336  }
1337  return nullopt;
1338 }
1339 
1341  if (this->reverse()) {
1342  return this->previous_valve_number_(first_valve, false, false);
1343  }
1344  return this->next_valve_number_(first_valve, false, false);
1345 }
1346 
1348  if (this->active_req_.has_request()) {
1349  this->prev_req_ = this->active_req_;
1350  } else {
1351  this->prev_req_.reset();
1352  }
1353 
1354  if (this->next_req_.has_request()) {
1355  if (!this->next_req_.run_duration()) { // ensure the run duration is set correctly for consumption later on
1356  this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->next_req_.valve()));
1357  }
1358  return; // there is already a request pending
1359  } else if (this->queue_enabled() && !this->queued_valves_.empty()) {
1360  this->next_req_.set_valve(this->queued_valves_.back().valve_number);
1361  this->next_req_.set_request_from(QUEUE);
1362  if (this->queued_valves_.back().run_duration) {
1363  this->next_req_.set_run_duration(this->queued_valves_.back().run_duration);
1364  this->queued_valves_.pop_back();
1365  } else if (this->multiplier()) {
1366  this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->queued_valves_.back().valve_number));
1367  this->queued_valves_.pop_back();
1368  } else {
1369  this->next_req_.reset();
1370  }
1371  } else if (this->auto_advance() && this->multiplier()) {
1372  if (this->next_valve_number_in_cycle_(first_valve).has_value()) {
1373  // if there is another valve to run as a part of a cycle, load that
1374  this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1375  this->next_req_.set_request_from(CYCLE);
1376  this->next_req_.set_run_duration(
1377  this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1378  } else if ((this->repeat_count_++ < this->repeat().value_or(0))) {
1379  ESP_LOGD(TAG, "Repeating - starting cycle %u of %u", this->repeat_count_ + 1, this->repeat().value_or(0) + 1);
1380  // if there are repeats remaining and no more valves were left in the cycle, start a new cycle
1381  this->prep_full_cycle_();
1382  if (this->next_valve_number_in_cycle_().has_value()) { // this should always succeed here, but just in case...
1383  this->next_req_.set_valve(this->next_valve_number_in_cycle_().value_or(0));
1384  this->next_req_.set_request_from(CYCLE);
1385  this->next_req_.set_run_duration(
1386  this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_().value_or(0)));
1387  }
1388  }
1389  }
1390 }
1391 
1393  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1394  if (this->valve_is_enabled_(valve_number))
1395  return true;
1396  }
1397  return false;
1398 }
1399 
1401  if (!req->has_request()) {
1402  return; // we can't do anything if the request contains nothing
1403  }
1404  if (!this->is_a_valid_valve(req->valve())) {
1405  return; // we can't do anything if the valve number isn't valid
1406  }
1407  for (auto &vo : this->valve_op_) { // find the first available SprinklerValveOperator, load it and start it up
1408  if (vo.state() == IDLE) {
1409  auto run_duration = req->run_duration() ? req->run_duration() : this->valve_run_duration_adjusted(req->valve());
1410  ESP_LOGD(TAG, "%s is starting valve %u for %u seconds, cycle %u of %u",
1411  this->req_as_str_(req->request_is_from()).c_str(), req->valve(), run_duration, this->repeat_count_ + 1,
1412  this->repeat().value_or(0) + 1);
1413  req->set_valve_operator(&vo);
1414  vo.set_controller(this);
1415  vo.set_valve(&this->valve_[req->valve()]);
1416  vo.set_run_duration(run_duration);
1417  vo.set_start_delay(this->start_delay_, this->start_delay_is_valve_delay_);
1418  vo.set_stop_delay(this->stop_delay_, this->stop_delay_is_valve_delay_);
1419  vo.start();
1420  return;
1421  }
1422  }
1423 }
1424 
1425 void Sprinkler::all_valves_off_(const bool include_pump) {
1426  for (size_t valve_index = 0; valve_index < this->number_of_valves(); valve_index++) {
1427  if (this->valve_[valve_index].valve_switch.state()) {
1428  this->valve_[valve_index].valve_switch.turn_off();
1429  }
1430  if (include_pump) {
1431  this->set_pump_state(this->valve_pump_switch(valve_index), false);
1432  }
1433  }
1434  ESP_LOGD(TAG, "All valves stopped%s", include_pump ? ", including pumps" : "");
1435 }
1436 
1438  this->set_auto_advance(true);
1439 
1440  if (!this->any_valve_is_enabled_()) {
1441  for (auto &valve : this->valve_) {
1442  if (valve.enable_switch != nullptr) {
1443  if (!valve.enable_switch->state) {
1444  valve.enable_switch->turn_on();
1445  }
1446  }
1447  }
1448  }
1449  this->reset_cycle_states_();
1450 }
1451 
1453  for (auto &valve : this->valve_) {
1454  valve.valve_cycle_complete = false;
1455  }
1456 }
1457 
1458 void Sprinkler::fsm_request_(size_t requested_valve, uint32_t requested_run_duration) {
1459  this->next_req_.set_valve(requested_valve);
1460  this->next_req_.set_run_duration(requested_run_duration);
1461  // if state is IDLE or ACTIVE, call fsm_transition_() to start it immediately;
1462  // otherwise, fsm_transition() will pick up next_req_ at the next appropriate transition
1463  this->fsm_kick_();
1464 }
1465 
1467  if ((this->state_ == IDLE) || (this->state_ == ACTIVE)) {
1468  this->fsm_transition_();
1469  }
1470 }
1471 
1473  ESP_LOGVV(TAG, "fsm_transition_ called; state is %s", this->state_as_str_(this->state_).c_str());
1474  switch (this->state_) {
1475  case IDLE: // the system was off -> start it up
1476  // advances to ACTIVE
1477  this->fsm_transition_from_shutdown_();
1478  break;
1479 
1480  case ACTIVE:
1481  // advances to STOPPING or ACTIVE (again)
1482  this->fsm_transition_from_valve_run_();
1483  break;
1484 
1485  case STARTING: {
1486  // follows valve open delay interval
1487  this->set_timer_duration_(sprinkler::TIMER_SM,
1488  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1489  this->start_timer_(sprinkler::TIMER_SM);
1490  this->start_valve_(&this->active_req_);
1491  this->state_ = ACTIVE;
1492  if (this->next_req_.has_request()) {
1493  // another valve has been requested, so restart the timer so we pick it up quickly
1494  this->set_timer_duration_(sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1495  this->start_timer_(sprinkler::TIMER_SM);
1496  }
1497  break;
1498  }
1499 
1500  case STOPPING:
1501  // stop_delay_ has elapsed so just shut everything off
1502  this->active_req_.reset();
1503  this->manual_valve_.reset();
1504  this->all_valves_off_(true);
1505  this->state_ = IDLE;
1506  break;
1507 
1508  default:
1509  break;
1510  }
1511  if (this->next_req_.has_request() && (this->state_ == IDLE)) {
1512  // another valve has been requested, so restart the timer so we pick it up quickly
1513  this->set_timer_duration_(sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1514  this->start_timer_(sprinkler::TIMER_SM);
1515  }
1516  ESP_LOGVV(TAG, "fsm_transition_ complete; new state is %s", this->state_as_str_(this->state_).c_str());
1517 }
1518 
1520  this->load_next_valve_run_request_();
1521 
1522  if (this->next_req_.has_request()) { // there is a valve to run...
1523  this->active_req_.set_valve(this->next_req_.valve());
1524  this->active_req_.set_request_from(this->next_req_.request_is_from());
1525  this->active_req_.set_run_duration(this->next_req_.run_duration());
1526  this->next_req_.reset();
1527 
1528  this->set_timer_duration_(sprinkler::TIMER_SM,
1529  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1530  this->start_timer_(sprinkler::TIMER_SM);
1531  this->start_valve_(&this->active_req_);
1532  this->state_ = ACTIVE;
1533  }
1534 }
1535 
1537  if (!this->active_req_.has_request()) { // dummy check...
1538  this->fsm_transition_to_shutdown_();
1539  return;
1540  }
1541 
1542  if (!this->timer_active_(sprinkler::TIMER_SM)) { // only flag the valve as "complete" if the timer finished
1543  if ((this->active_req_.request_is_from() == CYCLE) || (this->active_req_.request_is_from() == USER)) {
1544  this->mark_valve_cycle_complete_(this->active_req_.valve());
1545  }
1546  } else {
1547  ESP_LOGD(TAG, "Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1548  for (auto &vo : this->valve_op_) {
1549  vo.stop();
1550  }
1551  }
1552 
1553  this->load_next_valve_run_request_(this->active_req_.valve());
1554 
1555  if (this->next_req_.has_request()) { // there is another valve to run...
1556  bool same_pump =
1557  this->valve_pump_switch(this->active_req_.valve()) == this->valve_pump_switch(this->next_req_.valve());
1558 
1559  this->active_req_.set_valve(this->next_req_.valve());
1560  this->active_req_.set_request_from(this->next_req_.request_is_from());
1561  this->active_req_.set_run_duration(this->next_req_.run_duration());
1562  this->next_req_.reset();
1563 
1564  // this->state_ = ACTIVE; // state isn't changing
1565  if (this->valve_overlap_ || !this->switching_delay_.has_value()) {
1566  this->set_timer_duration_(sprinkler::TIMER_SM,
1567  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1568  this->start_timer_(sprinkler::TIMER_SM);
1569  this->start_valve_(&this->active_req_);
1570  } else {
1571  this->set_timer_duration_(
1573  this->switching_delay_.value() * 2 +
1574  (this->pump_switch_off_during_valve_open_delay_ && same_pump ? this->stop_delay_ : 0));
1575  this->start_timer_(sprinkler::TIMER_SM);
1576  this->state_ = STARTING;
1577  }
1578  } else { // there is NOT another valve to run...
1579  this->fsm_transition_to_shutdown_();
1580  }
1581 }
1582 
1584  this->state_ = STOPPING;
1585  this->set_timer_duration_(sprinkler::TIMER_SM,
1586  this->start_delay_ + this->stop_delay_ + this->switching_delay_.value_or(0) + 1);
1587  this->start_timer_(sprinkler::TIMER_SM);
1588 }
1589 
1591  switch (origin) {
1592  case USER:
1593  return "USER";
1594 
1595  case CYCLE:
1596  return "CYCLE";
1597 
1598  case QUEUE:
1599  return "QUEUE";
1600 
1601  default:
1602  return "UNKNOWN";
1603  }
1604 }
1605 
1607  switch (state) {
1608  case IDLE:
1609  return "IDLE";
1610 
1611  case STARTING:
1612  return "STARTING";
1613 
1614  case ACTIVE:
1615  return "ACTIVE";
1616 
1617  case STOPPING:
1618  return "STOPPING";
1619 
1620  case BYPASS:
1621  return "BYPASS";
1622 
1623  default:
1624  return "UNKNOWN";
1625  }
1626 }
1627 
1629  if (this->timer_duration_(timer_index) > 0) {
1630  this->set_timeout(this->timer_[timer_index].name, this->timer_duration_(timer_index),
1631  this->timer_cbf_(timer_index));
1632  this->timer_[timer_index].start_time = millis();
1633  this->timer_[timer_index].active = true;
1634  }
1635  ESP_LOGVV(TAG, "Timer %u started for %u sec", static_cast<size_t>(timer_index),
1636  this->timer_duration_(timer_index) / 1000);
1637 }
1638 
1640  this->timer_[timer_index].active = false;
1641  return this->cancel_timeout(this->timer_[timer_index].name);
1642 }
1643 
1644 bool Sprinkler::timer_active_(const SprinklerTimerIndex timer_index) { return this->timer_[timer_index].active; }
1645 
1646 void Sprinkler::set_timer_duration_(const SprinklerTimerIndex timer_index, const uint32_t time) {
1647  this->timer_[timer_index].time = 1000 * time;
1648 }
1649 
1650 uint32_t Sprinkler::timer_duration_(const SprinklerTimerIndex timer_index) { return this->timer_[timer_index].time; }
1651 
1652 std::function<void()> Sprinkler::timer_cbf_(const SprinklerTimerIndex timer_index) {
1653  return this->timer_[timer_index].func;
1654 }
1655 
1657  this->timer_[sprinkler::TIMER_VALVE_SELECTION].active = false;
1658  ESP_LOGVV(TAG, "Valve selection timer expired");
1659  if (this->manual_valve_.has_value()) {
1660  this->fsm_request_(this->manual_valve_.value());
1661  this->manual_valve_.reset();
1662  }
1663 }
1664 
1666  this->timer_[sprinkler::TIMER_SM].active = false;
1667  ESP_LOGVV(TAG, "State machine timer expired");
1668  this->fsm_transition_();
1669 }
1670 
1672  ESP_LOGCONFIG(TAG, "Sprinkler Controller -- %s", this->name_.c_str());
1673  if (this->manual_selection_delay_.has_value()) {
1674  ESP_LOGCONFIG(TAG, " Manual Selection Delay: %u seconds", this->manual_selection_delay_.value_or(0));
1675  }
1676  if (this->repeat().has_value()) {
1677  ESP_LOGCONFIG(TAG, " Repeat Cycles: %u times", this->repeat().value_or(0));
1678  }
1679  if (this->start_delay_) {
1680  if (this->start_delay_is_valve_delay_) {
1681  ESP_LOGCONFIG(TAG, " Pump Start Valve Delay: %u seconds", this->start_delay_);
1682  } else {
1683  ESP_LOGCONFIG(TAG, " Pump Start Pump Delay: %u seconds", this->start_delay_);
1684  }
1685  }
1686  if (this->stop_delay_) {
1687  if (this->stop_delay_is_valve_delay_) {
1688  ESP_LOGCONFIG(TAG, " Pump Stop Valve Delay: %u seconds", this->stop_delay_);
1689  } else {
1690  ESP_LOGCONFIG(TAG, " Pump Stop Pump Delay: %u seconds", this->stop_delay_);
1691  }
1692  }
1693  if (this->switching_delay_.has_value()) {
1694  if (this->valve_overlap_) {
1695  ESP_LOGCONFIG(TAG, " Valve Overlap: %u seconds", this->switching_delay_.value_or(0));
1696  } else {
1697  ESP_LOGCONFIG(TAG, " Valve Open Delay: %u seconds", this->switching_delay_.value_or(0));
1698  ESP_LOGCONFIG(TAG, " Pump Switch Off During Valve Open Delay: %s",
1699  YESNO(this->pump_switch_off_during_valve_open_delay_));
1700  }
1701  }
1702  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1703  ESP_LOGCONFIG(TAG, " Valve %u:", valve_number);
1704  ESP_LOGCONFIG(TAG, " Name: %s", this->valve_name(valve_number));
1705  ESP_LOGCONFIG(TAG, " Run Duration: %u seconds", this->valve_run_duration(valve_number));
1706  if (this->valve_[valve_number].valve_switch.pulse_duration()) {
1707  ESP_LOGCONFIG(TAG, " Pulse Duration: %u milliseconds",
1708  this->valve_[valve_number].valve_switch.pulse_duration());
1709  }
1710  }
1711  if (!this->pump_.empty()) {
1712  ESP_LOGCONFIG(TAG, " Total number of pumps: %u", this->pump_.size());
1713  }
1714  if (!this->valve_.empty()) {
1715  ESP_LOGCONFIG(TAG, " Total number of valves: %u", this->valve_.size());
1716  }
1717 }
1718 
1719 } // namespace sprinkler
1720 } // namespace esphome
Base class for all switches.
Definition: switch.h:32
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:1086
const char * name
Definition: stm32flash.h:78
void start_timer_(SprinklerTimerIndex timer_index)
Start/cancel/get status of valve timers.
Definition: sprinkler.cpp:1628
bool timer_active_(SprinklerTimerIndex timer_index)
returns true if the specified timer is active/running
Definition: sprinkler.cpp:1644
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:1656
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:1002
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:1583
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:1004
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:1248
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:1458
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:975
bool is_a_valid_valve(size_t valve_number)
returns true if valve number is valid
Definition: sprinkler.cpp:1015
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:1262
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:1294
void resume_or_start_full_cycle()
if a cycle was suspended using pause(), resumes it. otherwise calls start_full_cycle() ...
Definition: sprinkler.cpp:967
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:27
SprinklerSwitch * valve_pump_switch(size_t valve_number)
returns a pointer to a valve&#39;s pump switch object
Definition: sprinkler.cpp:1255
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:1102
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 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:1466
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:1054
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:1590
uint32_t timer_duration_(SprinklerTimerIndex timer_index)
returns time in milliseconds (ms)
Definition: sprinkler.cpp:1650
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:1340
void start_valve_(SprinklerValveRunRequest *req)
loads an available SprinklerValveOperator (valve_op_) based on req and starts it (switches it on)...
Definition: sprinkler.cpp:1400
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:1646
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:1234
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:1124
uint32_t total_queue_time()
returns the amount of time in seconds required for all valves in the queue
Definition: sprinkler.cpp:1161
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:1241
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:1219
void fsm_transition_()
advance controller state, advancing to target_valve if provided
Definition: sprinkler.cpp:1472
optional< uint32_t > time_remaining_active_valve()
returns the amount of time remaining in seconds for the active valve, if any
Definition: sprinkler.cpp:1185
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:1269
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:1452
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:1437
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:1199
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:1287
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:987
bool any_valve_is_enabled_()
returns true if any valve is enabled
Definition: sprinkler.cpp:1392
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:1639
Definition: a4988.cpp:4
void fsm_transition_from_shutdown_()
starts up the system from IDLE state
Definition: sprinkler.cpp:1519
std::string state_as_str_(SprinklerState state)
return the specified SprinklerState state as a string
Definition: sprinkler.cpp:1606
const std::string min_str
Definition: sprinkler.h:14
std::function< void()> timer_cbf_(SprinklerTimerIndex timer_index)
Definition: sprinkler.cpp:1652
bool pump_in_use(SprinklerSwitch *pump_switch)
returns true if the pump the pointer points to is in use
Definition: sprinkler.cpp:1019
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:980
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:1013
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:1347
void mark_valve_cycle_complete_(size_t valve_number)
marks a valve&#39;s cycle as complete
Definition: sprinkler.cpp:1280
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:46
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:994
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:1536
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:1011
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:1317
void all_valves_off_(bool include_pump=false)
turns off/closes all valves, including pump if include_pump is true
Definition: sprinkler.cpp:1425