ESPHome  2022.11.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  : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {}
80 
82  if (!this->f_.has_value())
83  return;
84  auto s = (*this->f_)();
85  if (!s.has_value())
86  return;
87 
88  this->publish_state(*s);
89 }
90 
92  if (this->prev_trigger_ != nullptr) {
93  this->prev_trigger_->stop_action();
94  }
95 
96  if (state) {
97  this->prev_trigger_ = this->turn_on_trigger_;
98  this->turn_on_trigger_->trigger();
99  } else {
100  this->prev_trigger_ = this->turn_off_trigger_;
101  this->turn_off_trigger_->trigger();
102  }
103 
104  if (this->optimistic_)
105  this->publish_state(state);
106 }
107 
108 void SprinklerControllerSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
110 void SprinklerControllerSwitch::set_state_lambda(std::function<optional<bool>()> &&f) { this->f_ = f; }
112 
115 
117  if (!this->restore_state_)
118  return;
119 
120  auto restored = this->get_initial_state();
121  if (!restored.has_value())
122  return;
123 
124  ESP_LOGD(TAG, " Restored state %s", ONOFF(*restored));
125  if (*restored) {
126  this->turn_on();
127  } else {
128  this->turn_off();
129  }
130 }
131 
133  LOG_SWITCH("", "Sprinkler Switch", this);
134  ESP_LOGCONFIG(TAG, " Restore State: %s", YESNO(this->restore_state_));
135  ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
136 }
137 
138 void SprinklerControllerSwitch::set_restore_state(bool restore_state) { this->restore_state_ = restore_state; }
139 
141 
144  : controller_(controller), valve_(valve) {}
145 
147  if (millis() >= this->pinned_millis_) { // dummy check
148  switch (this->state_) {
149  case STARTING:
150  if (millis() > (this->pinned_millis_ + this->start_delay_)) {
151  this->run_(); // start_delay_ has been exceeded, so ensure both valves are on and update the state
152  }
153  break;
154 
155  case ACTIVE:
156  if (millis() > (this->pinned_millis_ + this->start_delay_ + this->run_duration_)) {
157  this->stop(); // start_delay_ + run_duration_ has been exceeded, start shutting down
158  }
159  break;
160 
161  case STOPPING:
162  if (millis() > (this->pinned_millis_ + this->stop_delay_)) {
163  this->kill_(); // stop_delay_has been exceeded, ensure all valves are off
164  }
165  break;
166 
167  default:
168  break;
169  }
170  } else { // perhaps millis() rolled over...or something else is horribly wrong!
171  this->stop(); // bail out (TODO: handle this highly unlikely situation better...)
172  }
173 }
174 
176  if (controller != nullptr) {
177  this->controller_ = controller;
178  }
179 }
180 
182  if (valve != nullptr) {
183  this->state_ = IDLE; // reset state
184  this->run_duration_ = 0; // reset to ensure the valve isn't started without updating it
185  this->pinned_millis_ = 0; // reset because (new) valve has not been started yet
186  this->kill_(); // ensure everything is off before we let go!
187  this->valve_ = valve; // finally, set the pointer to the new valve
188  }
189 }
190 
192  if (run_duration) {
193  this->run_duration_ = run_duration * 1000;
194  }
195 }
196 
197 void SprinklerValveOperator::set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay) {
198  this->start_delay_is_valve_delay_ = start_delay_is_valve_delay;
199  this->start_delay_ = start_delay * 1000; // because 1000 milliseconds is one second
200 }
201 
202 void SprinklerValveOperator::set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay) {
203  this->stop_delay_is_valve_delay_ = stop_delay_is_valve_delay;
204  this->stop_delay_ = stop_delay * 1000; // because 1000 milliseconds is one second
205 }
206 
208  if (!this->run_duration_) { // can't start if zero run duration
209  return;
210  }
211  if (this->start_delay_ && (this->pump_switch() != nullptr)) {
212  this->state_ = STARTING; // STARTING state requires both a pump and a start_delay_
213  if (this->start_delay_is_valve_delay_) {
214  this->pump_on_();
215  } else if (!this->pump_switch()->state()) { // if the pump is already on, wait to switch on the valve
216  this->valve_on_(); // to ensure consistent run time
217  }
218  } else {
219  this->run_(); // there is no start_delay_, so just start the pump and valve
220  }
221  this->pinned_millis_ = millis(); // save the time the start request was made
222 }
223 
225  if ((this->state_ == IDLE) || (this->state_ == STOPPING)) { // can't stop if already stopped or stopping
226  return;
227  }
228  if (this->stop_delay_ && (this->pump_switch() != nullptr)) {
229  this->state_ = STOPPING; // STOPPING state requires both a pump and a stop_delay_
230  if (this->stop_delay_is_valve_delay_) {
231  this->pump_off_();
232  } else {
233  this->valve_off_();
234  }
235  if (this->pump_switch()->state()) { // if the pump is still on at this point, it may be in use...
236  this->valve_off_(); // ...so just switch the valve off now to ensure consistent run time
237  }
238  this->pinned_millis_ = millis(); // save the time the stop request was made
239  } else {
240  this->kill_(); // there is no stop_delay_, so just stop the pump and valve
241  }
242 }
243 
245 
247  if ((this->state_ == STARTING) || (this->state_ == ACTIVE)) {
248  return (this->pinned_millis_ + this->start_delay_ + this->run_duration_ - millis()) / 1000;
249  }
250  return 0;
251 }
252 
254 
256  if ((this->controller_ == nullptr) || (this->valve_ == nullptr)) {
257  return nullptr;
258  }
259  if (this->valve_->pump_switch_index.has_value()) {
261  }
262  return nullptr;
263 }
264 
266  if ((this->valve_ == nullptr) || (this->pump_switch() == nullptr)) { // safety first!
267  return;
268  }
269  if (this->controller_ == nullptr) { // safety first!
270  this->pump_switch()->turn_off(); // if no controller was set, just switch off the pump
271  } else { // ...otherwise, do it "safely"
272  auto state = this->state_; // this is silly, but...
273  this->state_ = BYPASS; // ...exclude me from the pump-in-use check that set_pump_state() does
274  this->controller_->set_pump_state(this->pump_switch(), false);
275  this->state_ = state;
276  }
277 }
278 
280  if ((this->valve_ == nullptr) || (this->pump_switch() == nullptr)) { // safety first!
281  return;
282  }
283  if (this->controller_ == nullptr) { // safety first!
284  this->pump_switch()->turn_on(); // if no controller was set, just switch on the pump
285  } else { // ...otherwise, do it "safely"
286  auto state = this->state_; // this is silly, but...
287  this->state_ = BYPASS; // ...exclude me from the pump-in-use check that set_pump_state() does
288  this->controller_->set_pump_state(this->pump_switch(), true);
289  this->state_ = state;
290  }
291 }
292 
294  if (this->valve_ == nullptr) { // safety first!
295  return;
296  }
297  if (this->valve_->valve_switch.state()) {
298  this->valve_->valve_switch.turn_off();
299  }
300 }
301 
303  if (this->valve_ == nullptr) { // safety first!
304  return;
305  }
306  if (!this->valve_->valve_switch.state()) {
307  this->valve_->valve_switch.turn_on();
308  }
309 }
310 
312  this->state_ = IDLE;
313  this->valve_off_();
314  this->pump_off_();
315 }
316 
318  this->state_ = ACTIVE;
319  this->valve_on_();
320  this->pump_on_();
321 }
322 
325  SprinklerValveOperator *valve_op)
326  : valve_number_(valve_number), run_duration_(run_duration), valve_op_(valve_op) {}
327 
329 bool SprinklerValveRunRequest::has_valve_operator() { return !(this->valve_op_ == nullptr); }
330 
332 
333 void SprinklerValveRunRequest::set_valve(size_t valve_number) {
334  this->valve_number_ = valve_number;
335  this->run_duration_ = 0;
336  this->valve_op_ = nullptr;
337  this->has_valve_ = true;
338 }
339 
341  if (valve_op != nullptr) {
342  this->valve_op_ = valve_op;
343  }
344 }
345 
347  this->has_valve_ = false;
348  this->run_duration_ = 0;
349  this->valve_op_ = nullptr;
350 }
351 
353 
355 
357  if (this->has_valve_) {
358  return this->valve_number_;
359  }
360  return nullopt;
361 }
362 
364 
366 Sprinkler::Sprinkler(const std::string &name) : EntityBase(name) {}
367 
368 void Sprinkler::setup() { this->all_valves_off_(true); }
369 
371  for (auto &p : this->pump_) {
372  p.loop();
373  }
374  for (auto &v : this->valve_) {
375  v.valve_switch.loop();
376  }
377  for (auto &vo : this->valve_op_) {
378  vo.loop();
379  }
380 }
381 
383  auto new_valve_number = this->number_of_valves();
384  this->valve_.resize(new_valve_number + 1);
385  SprinklerValve *new_valve = &this->valve_[new_valve_number];
386 
387  new_valve->controller_switch = valve_sw;
388  new_valve->controller_switch->set_state_lambda([=]() -> optional<bool> {
389  if (this->valve_pump_switch(new_valve_number) != nullptr) {
390  return this->valve_switch(new_valve_number)->state() && this->valve_pump_switch(new_valve_number)->state();
391  }
392  return this->valve_switch(new_valve_number)->state();
393  });
394 
395  new_valve->valve_turn_off_automation =
396  make_unique<Automation<>>(new_valve->controller_switch->get_turn_off_trigger());
397  new_valve->valve_shutdown_action = make_unique<sprinkler::ShutdownAction<>>(this);
398  new_valve->valve_turn_off_automation->add_actions({new_valve->valve_shutdown_action.get()});
399 
400  new_valve->valve_turn_on_automation = make_unique<Automation<>>(new_valve->controller_switch->get_turn_on_trigger());
401  new_valve->valve_resumeorstart_action = make_unique<sprinkler::StartSingleValveAction<>>(this);
402  new_valve->valve_resumeorstart_action->set_valve_to_start(new_valve_number);
403  new_valve->valve_turn_on_automation->add_actions({new_valve->valve_resumeorstart_action.get()});
404 
405  if (enable_sw != nullptr) {
406  new_valve->enable_switch = enable_sw;
407  new_valve->enable_switch->set_optimistic(true);
408  new_valve->enable_switch->set_restore_state(true);
409  }
410 }
411 
412 void Sprinkler::add_controller(Sprinkler *other_controller) { this->other_controllers_.push_back(other_controller); }
413 
415  this->controller_sw_ = controller_switch;
416  controller_switch->set_state_lambda([=]() -> optional<bool> {
417  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
418  if (this->valve_[valve_number].controller_switch->state) {
419  return true;
420  }
421  }
422  return this->active_req_.has_request();
423  });
424 
425  this->sprinkler_turn_off_automation_ = make_unique<Automation<>>(controller_switch->get_turn_off_trigger());
426  this->sprinkler_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(this);
427  this->sprinkler_turn_off_automation_->add_actions({sprinkler_shutdown_action_.get()});
428 
429  this->sprinkler_turn_on_automation_ = make_unique<Automation<>>(controller_switch->get_turn_on_trigger());
430  this->sprinkler_resumeorstart_action_ = make_unique<sprinkler::ResumeOrStartAction<>>(this);
431  this->sprinkler_turn_on_automation_->add_actions({sprinkler_resumeorstart_action_.get()});
432 }
433 
435  this->auto_adv_sw_ = auto_adv_switch;
436  auto_adv_switch->set_optimistic(true);
437  auto_adv_switch->set_restore_state(true);
438 }
439 
441  this->queue_enable_sw_ = queue_enable_switch;
442  queue_enable_switch->set_optimistic(true);
443  queue_enable_switch->set_restore_state(true);
444 }
445 
447  this->reverse_sw_ = reverse_switch;
448  reverse_switch->set_optimistic(true);
449  reverse_switch->set_restore_state(true);
450 }
451 
452 void Sprinkler::configure_valve_switch(size_t valve_number, switch_::Switch *valve_switch, uint32_t run_duration) {
453  if (this->is_a_valid_valve(valve_number)) {
454  this->valve_[valve_number].valve_switch.set_on_switch(valve_switch);
455  this->valve_[valve_number].run_duration = run_duration;
456  }
457 }
458 
459 void Sprinkler::configure_valve_switch_pulsed(size_t valve_number, switch_::Switch *valve_switch_off,
460  switch_::Switch *valve_switch_on, uint32_t pulse_duration,
461  uint32_t run_duration) {
462  if (this->is_a_valid_valve(valve_number)) {
463  this->valve_[valve_number].valve_switch.set_off_switch(valve_switch_off);
464  this->valve_[valve_number].valve_switch.set_on_switch(valve_switch_on);
465  this->valve_[valve_number].valve_switch.set_pulse_duration(pulse_duration);
466  this->valve_[valve_number].run_duration = run_duration;
467  }
468 }
469 
470 void Sprinkler::configure_valve_pump_switch(size_t valve_number, switch_::Switch *pump_switch) {
471  if (this->is_a_valid_valve(valve_number)) {
472  for (size_t i = 0; i < this->pump_.size(); i++) { // check each existing registered pump
473  if (this->pump_[i].on_switch() == pump_switch) { // if the "new" pump matches one we already have...
474  this->valve_[valve_number].pump_switch_index = i; // ...save its index in the SprinklerSwitch vector pump_...
475  return; // ...and we are done
476  }
477  } // if we end up here, no pumps matched, so add a new one and set the valve's SprinklerSwitch at it
478  this->pump_.resize(this->pump_.size() + 1);
479  this->pump_.back().set_on_switch(pump_switch);
480  this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1; // save the index to the new pump
481  }
482 }
483 
484 void Sprinkler::configure_valve_pump_switch_pulsed(size_t valve_number, switch_::Switch *pump_switch_off,
485  switch_::Switch *pump_switch_on, uint32_t pulse_duration) {
486  if (this->is_a_valid_valve(valve_number)) {
487  for (size_t i = 0; i < this->pump_.size(); i++) { // check each existing registered pump
488  if ((this->pump_[i].off_switch() == pump_switch_off) &&
489  (this->pump_[i].on_switch() == pump_switch_on)) { // if the "new" pump matches one we already have...
490  this->valve_[valve_number].pump_switch_index = i; // ...save its index in the SprinklerSwitch vector pump_...
491  return; // ...and we are done
492  }
493  } // if we end up here, no pumps matched, so add a new one and set the valve's SprinklerSwitch at it
494  this->pump_.resize(this->pump_.size() + 1);
495  this->pump_.back().set_off_switch(pump_switch_off);
496  this->pump_.back().set_on_switch(pump_switch_on);
497  this->pump_.back().set_pulse_duration(pulse_duration);
498  this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1; // save the index to the new pump
499  }
500 }
501 
503  if (multiplier.has_value()) {
504  if (multiplier.value() > 0) {
505  this->multiplier_ = multiplier.value();
506  }
507  }
508 }
509 
510 void Sprinkler::set_pump_start_delay(uint32_t start_delay) {
511  this->start_delay_is_valve_delay_ = false;
512  this->start_delay_ = start_delay;
513 }
514 
515 void Sprinkler::set_pump_stop_delay(uint32_t stop_delay) {
516  this->stop_delay_is_valve_delay_ = false;
517  this->stop_delay_ = stop_delay;
518 }
519 
520 void Sprinkler::set_valve_start_delay(uint32_t start_delay) {
521  this->start_delay_is_valve_delay_ = true;
522  this->start_delay_ = start_delay;
523 }
524 
525 void Sprinkler::set_valve_stop_delay(uint32_t stop_delay) {
526  this->stop_delay_is_valve_delay_ = true;
527  this->stop_delay_ = stop_delay;
528 }
529 
530 void Sprinkler::set_pump_switch_off_during_valve_open_delay(bool pump_switch_off_during_valve_open_delay) {
531  this->pump_switch_off_during_valve_open_delay_ = pump_switch_off_during_valve_open_delay;
532 }
533 
534 void Sprinkler::set_valve_open_delay(const uint32_t valve_open_delay) {
535  if (valve_open_delay > 0) {
536  this->valve_overlap_ = false;
537  this->switching_delay_ = valve_open_delay;
538  } else {
539  this->switching_delay_.reset();
540  }
541 }
542 
543 void Sprinkler::set_valve_overlap(uint32_t valve_overlap) {
544  if (valve_overlap > 0) {
545  this->valve_overlap_ = true;
546  this->switching_delay_ = valve_overlap;
547  } else {
548  this->switching_delay_.reset();
549  }
550  this->pump_switch_off_during_valve_open_delay_ = false; // incompatible option
551 }
552 
553 void Sprinkler::set_manual_selection_delay(uint32_t manual_selection_delay) {
554  if (manual_selection_delay > 0) {
555  this->manual_selection_delay_ = manual_selection_delay;
556  } else {
558  }
559 }
560 
561 void Sprinkler::set_valve_run_duration(const optional<size_t> valve_number, const optional<uint32_t> run_duration) {
562  if (valve_number.has_value() && run_duration.has_value()) {
563  if (this->is_a_valid_valve(valve_number.value())) {
564  this->valve_[valve_number.value()].run_duration = run_duration.value();
565  }
566  }
567 }
568 
570  if (this->auto_adv_sw_ != nullptr) {
571  this->auto_adv_sw_->publish_state(auto_advance);
572  }
573 }
574 
576 
577 void Sprinkler::set_queue_enable(bool queue_enable) {
578  if (this->queue_enable_sw_ != nullptr) {
579  this->queue_enable_sw_->publish_state(queue_enable);
580  }
581 }
582 
583 void Sprinkler::set_reverse(const bool reverse) {
584  if (this->reverse_sw_ != nullptr) {
585  this->reverse_sw_->publish_state(reverse);
586  }
587 }
588 
589 uint32_t Sprinkler::valve_run_duration(const size_t valve_number) {
590  if (this->is_a_valid_valve(valve_number)) {
591  return this->valve_[valve_number].run_duration;
592  }
593  return 0;
594 }
595 
596 uint32_t Sprinkler::valve_run_duration_adjusted(const size_t valve_number) {
597  uint32_t run_duration = 0;
598 
599  if (this->is_a_valid_valve(valve_number)) {
600  run_duration = this->valve_[valve_number].run_duration;
601  }
602  run_duration = static_cast<uint32_t>(roundf(run_duration * this->multiplier_));
603  // run_duration must not be less than any of these
604  if ((run_duration < this->start_delay_) || (run_duration < this->stop_delay_) ||
605  (run_duration < this->switching_delay_.value_or(0) * 2)) {
606  return std::max(this->switching_delay_.value_or(0) * 2, std::max(this->start_delay_, this->stop_delay_));
607  }
608  return run_duration;
609 }
610 
612  if (this->auto_adv_sw_ != nullptr) {
613  return this->auto_adv_sw_->state;
614  }
615  return false;
616 }
617 
618 float Sprinkler::multiplier() { return this->multiplier_; }
619 
621 
623  // if there is an active valve and auto-advance is enabled, we may be repeating, so return the count
624  if (this->auto_adv_sw_ != nullptr) {
625  if (this->active_req_.has_request() && this->auto_adv_sw_->state) {
626  return this->repeat_count_;
627  }
628  }
629  return nullopt;
630 }
631 
633  if (this->queue_enable_sw_ != nullptr) {
634  return this->queue_enable_sw_->state;
635  }
636  return true;
637 }
638 
640  if (this->reverse_sw_ != nullptr) {
641  return this->reverse_sw_->state;
642  }
643  return false;
644 }
645 
647  if (this->queued_valves_.empty()) {
648  return; // if there is nothing in the queue, don't do anything
649  }
650  if (this->queue_enabled() && this->active_valve().has_value()) {
651  return; // if there is already a valve running from the queue, do nothing
652  }
653 
654  if (this->auto_adv_sw_ != nullptr) {
655  this->auto_adv_sw_->publish_state(false);
656  }
657  if (this->queue_enable_sw_ != nullptr) {
658  this->queue_enable_sw_->publish_state(true);
659  }
660  this->reset_cycle_states_(); // just in case auto-advance is switched on later
661  this->repeat_count_ = 0;
662  this->fsm_kick_(); // will automagically pick up from the queue (it has priority)
663 }
664 
666  if (this->auto_advance() && this->active_valve().has_value()) {
667  return; // if auto-advance is already enabled and there is already a valve running, do nothing
668  }
669 
670  if (this->queue_enable_sw_ != nullptr) {
671  this->queue_enable_sw_->publish_state(false);
672  }
673  this->prep_full_cycle_();
674  this->repeat_count_ = 0;
675  // if there is no active valve already, start the first valve in the cycle
676  if (!this->active_req_.has_request()) {
677  this->fsm_kick_();
678  }
679 }
680 
682  if (!valve_number.has_value() || (valve_number == this->active_valve())) {
683  return;
684  }
685 
686  if (this->auto_adv_sw_ != nullptr) {
687  this->auto_adv_sw_->publish_state(false);
688  }
689  if (this->queue_enable_sw_ != nullptr) {
690  this->queue_enable_sw_->publish_state(false);
691  }
692  this->reset_cycle_states_(); // just in case auto-advance is switched on later
693  this->repeat_count_ = 0;
694  this->fsm_request_(valve_number.value());
695 }
696 
698  if (valve_number.has_value()) {
699  if (this->is_a_valid_valve(valve_number.value()) && (this->queued_valves_.size() < this->max_queue_size_)) {
700  SprinklerQueueItem item{valve_number.value(), run_duration.value()};
701  this->queued_valves_.insert(this->queued_valves_.begin(), item);
702  ESP_LOGD(TAG, "Valve %u placed into queue with run duration of %u seconds", valve_number.value_or(0),
703  run_duration.value_or(0));
704  }
705  }
706 }
707 
709  this->queued_valves_.clear();
710  ESP_LOGD(TAG, "Queue cleared");
711 }
712 
714  if (this->state_ == IDLE) {
715  this->reset_cycle_states_(); // just in case auto-advance is switched on later
716  }
717  this->manual_valve_ = this->next_valve_number_(
718  this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(this->number_of_valves() - 1)));
719  if (this->manual_selection_delay_.has_value()) {
722  } else {
723  this->fsm_request_(this->manual_valve_.value());
724  }
725 }
726 
728  if (this->state_ == IDLE) {
729  this->reset_cycle_states_(); // just in case auto-advance is switched on later
730  }
731  this->manual_valve_ =
732  this->previous_valve_number_(this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(0)));
733  if (this->manual_selection_delay_.has_value()) {
736  } else {
737  this->fsm_request_(this->manual_valve_.value());
738  }
739 }
740 
741 void Sprinkler::shutdown(bool clear_queue) {
743  this->active_req_.reset();
744  this->manual_valve_.reset();
745  this->next_req_.reset();
746  for (auto &vo : this->valve_op_) {
747  vo.stop();
748  }
750  if (clear_queue) {
751  this->clear_queued_valves();
752  this->repeat_count_ = 0;
753  }
754 }
755 
757  if (this->paused_valve_.has_value() || !this->active_req_.has_request()) {
758  return; // we can't pause if we're already paused or if there is no active valve
759  }
760  this->paused_valve_ = this->active_valve();
761  this->resume_duration_ = this->time_remaining();
762  this->shutdown(false);
763  ESP_LOGD(TAG, "Paused valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
764  this->resume_duration_.value_or(0));
765 }
766 
768  if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
769  ESP_LOGD(TAG, "Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
770  this->resume_duration_.value_or(0));
771  this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
772  this->reset_resume();
773  } else {
774  ESP_LOGD(TAG, "No valve to resume!");
775  }
776 }
777 
779  if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
780  this->resume();
781  } else {
782  this->start_full_cycle();
783  }
784 }
785 
787  this->paused_valve_.reset();
788  this->resume_duration_.reset();
789 }
790 
791 const char *Sprinkler::valve_name(const size_t valve_number) {
792  if (this->is_a_valid_valve(valve_number)) {
793  return this->valve_[valve_number].controller_switch->get_name().c_str();
794  }
795  return nullptr;
796 }
797 
800 
802  if (!this->queued_valves_.empty()) {
803  return this->queued_valves_.back().valve_number;
804  }
805  return nullopt;
806 }
807 
809 
810 size_t Sprinkler::number_of_valves() { return this->valve_.size(); }
811 
812 bool Sprinkler::is_a_valid_valve(const size_t valve_number) {
813  return ((valve_number >= 0) && (valve_number < this->number_of_valves()));
814 }
815 
817  if (pump_switch == nullptr) {
818  return false; // we can't do anything if there's nothing to check
819  }
820  // a pump must be considered "in use" if a (distribution) valve it supplies is active. this means:
821  // - at least one SprinklerValveOperator:
822  // - has a valve loaded that depends on this pump
823  // - is in a state that depends on the pump: (ACTIVE and _possibly_ STARTING/STOPPING)
824  // - if NO SprinklerValveOperator is active but there is a run request pending (active_req_.has_request()) and the
825  // controller state is STARTING, valve open delay is configured but NOT pump_switch_off_during_valve_open_delay_
826  for (auto &vo : this->valve_op_) { // first, check if any SprinklerValveOperator has a valve dependent on this pump
827  if ((vo.state() != BYPASS) && (vo.pump_switch() != nullptr)) {
828  // the SprinklerValveOperator is configured with a pump; now check if it is the pump of interest
829  if ((vo.pump_switch()->off_switch() == pump_switch->off_switch()) &&
830  (vo.pump_switch()->on_switch() == pump_switch->on_switch())) {
831  // now if the SprinklerValveOperator has a pump and it is either ACTIVE, is STARTING with a valve delay or
832  // is
833  // STOPPING with a valve delay, its pump can be considered "in use", so just return indicating this now
834  if ((vo.state() == ACTIVE) ||
835  ((vo.state() == STARTING) && this->start_delay_ && this->start_delay_is_valve_delay_) ||
836  ((vo.state() == STOPPING) && this->stop_delay_ && this->stop_delay_is_valve_delay_)) {
837  return true;
838  }
839  }
840  }
841  } // if we end up here, no SprinklerValveOperator was in a "give-away" state indicating that the pump is in use...
843  this->active_req_.has_request() && (this->state_ != STOPPING)) {
844  // ...the controller is configured to keep the pump on during a valve open delay, so just return
845  // whether or not the next valve shares the same pump
846  return (pump_switch->off_switch() == this->valve_pump_switch(this->active_req_.valve())->off_switch()) &&
847  (pump_switch->on_switch() == this->valve_pump_switch(this->active_req_.valve())->on_switch());
848  }
849  return false;
850 }
851 
853  if (pump_switch == nullptr) {
854  return; // we can't do anything if there's nothing to check
855  }
856 
857  bool hold_pump_on = false;
858 
859  for (auto &controller : this->other_controllers_) { // check if the pump is in use by another controller
860  if (controller != this) { // dummy check
861  if (controller->pump_in_use(pump_switch)) {
862  hold_pump_on = true; // if another controller says it's using this pump, keep it on
863  // at this point we know if there exists another SprinklerSwitch that is "on" with its
864  // off_switch_ and on_switch_ pointers pointing to the same pair of switch objects
865  }
866  }
867  }
868  if (hold_pump_on) {
869  // at this point we know if there exists another SprinklerSwitch that is "on" with its
870  // off_switch_ and on_switch_ pointers pointing to the same pair of switch objects...
871  pump_switch->sync_valve_state(true); // ...so ensure our state is consistent
872  ESP_LOGD(TAG, "Leaving pump on because another controller instance is using it");
873  }
874 
875  if (state) { // ...and now we can set the new state of the switch
876  pump_switch->turn_on();
877  } else if (!hold_pump_on && !this->pump_in_use(pump_switch)) {
878  pump_switch->turn_off();
879  } else if (hold_pump_on) { // we must assume the other controller will switch off the pump when done...
880  pump_switch->sync_valve_state(false); // ...this only impacts latching valves
881  }
882 }
883 
885  if (this->active_req_.has_request()) { // first try to return the value based on active_req_...
886  if (this->active_req_.valve_operator() != nullptr) {
887  return this->active_req_.valve_operator()->time_remaining();
888  }
889  }
890  for (auto &vo : this->valve_op_) { // ...else return the value from the first non-IDLE SprinklerValveOperator
891  if (vo.state() != IDLE) {
892  return vo.time_remaining();
893  }
894  }
895  return nullopt;
896 }
897 
899  if (this->is_a_valid_valve(valve_number)) {
900  return this->valve_[valve_number].controller_switch;
901  }
902  return nullptr;
903 }
904 
906  if (this->is_a_valid_valve(valve_number)) {
907  return this->valve_[valve_number].enable_switch;
908  }
909  return nullptr;
910 }
911 
912 SprinklerSwitch *Sprinkler::valve_switch(const size_t valve_number) {
913  if (this->is_a_valid_valve(valve_number)) {
914  return &this->valve_[valve_number].valve_switch;
915  }
916  return nullptr;
917 }
918 
919 SprinklerSwitch *Sprinkler::valve_pump_switch(const size_t valve_number) {
920  if (this->is_a_valid_valve(valve_number) && this->valve_[valve_number].pump_switch_index.has_value()) {
921  return &this->pump_[this->valve_[valve_number].pump_switch_index.value()];
922  }
923  return nullptr;
924 }
925 
927  if (pump_index < this->pump_.size()) {
928  return &this->pump_[pump_index];
929  }
930  return nullptr;
931 }
932 
933 uint32_t Sprinkler::hash_base() { return 3129891955UL; }
934 
935 bool Sprinkler::valve_is_enabled_(const size_t valve_number) {
936  if (this->is_a_valid_valve(valve_number)) {
937  if (this->valve_[valve_number].enable_switch != nullptr) {
938  return this->valve_[valve_number].enable_switch->state;
939  } else {
940  return true;
941  }
942  }
943  return false;
944 }
945 
946 void Sprinkler::mark_valve_cycle_complete_(const size_t valve_number) {
947  if (this->is_a_valid_valve(valve_number)) {
948  ESP_LOGD(TAG, "Marking valve %u complete", valve_number);
949  this->valve_[valve_number].valve_cycle_complete = true;
950  }
951 }
952 
953 bool Sprinkler::valve_cycle_complete_(const size_t valve_number) {
954  if (this->is_a_valid_valve(valve_number)) {
955  return this->valve_[valve_number].valve_cycle_complete;
956  }
957  return false;
958 }
959 
960 size_t Sprinkler::next_valve_number_(const size_t first_valve) {
961  if (this->is_a_valid_valve(first_valve) && (first_valve + 1 < this->number_of_valves()))
962  return first_valve + 1;
963 
964  return 0;
965 }
966 
967 size_t Sprinkler::previous_valve_number_(const size_t first_valve) {
968  if (this->is_a_valid_valve(first_valve) && (first_valve - 1 >= 0))
969  return first_valve - 1;
970 
971  return this->number_of_valves() - 1;
972 }
973 
975  if (this->reverse_sw_ != nullptr) {
976  if (this->reverse_sw_->state) {
977  return this->previous_enabled_incomplete_valve_number_(first_valve);
978  }
979  }
980  return this->next_enabled_incomplete_valve_number_(first_valve);
981 }
982 
984  if (this->next_req_.has_request()) {
985  if (!this->next_req_.run_duration()) { // ensure the run duration is set correctly for consumption later on
987  }
988  return; // there is already a request pending
989  } else if (this->queue_enabled() && !this->queued_valves_.empty()) {
990  this->next_req_.set_valve(this->queued_valves_.back().valve_number);
991  if (this->queued_valves_.back().run_duration) {
992  this->next_req_.set_run_duration(this->queued_valves_.back().run_duration);
993  } else {
994  this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->queued_valves_.back().valve_number));
995  }
996  this->queued_valves_.pop_back();
997  } else if (this->auto_adv_sw_ != nullptr) {
998  if (this->auto_adv_sw_->state) {
999  if (this->next_valve_number_in_cycle_(first_valve).has_value()) {
1000  // if there is another valve to run as a part of a cycle, load that
1001  this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1003  this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1004  } else if ((this->repeat_count_++ < this->target_repeats_.value_or(0))) {
1005  ESP_LOGD(TAG, "Repeating - starting cycle %u of %u", this->repeat_count_ + 1,
1006  this->target_repeats_.value_or(0) + 1);
1007  // if there are repeats remaining and no more valves were left in the cycle, start a new cycle
1008  this->prep_full_cycle_();
1009  this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1011  this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1012  }
1013  }
1014  }
1015 }
1016 
1018  auto new_valve_number = this->next_valve_number_(first_valve.value_or(this->number_of_valves() - 1));
1019 
1020  while (new_valve_number != first_valve.value_or(this->number_of_valves() - 1)) {
1021  if (this->valve_is_enabled_(new_valve_number) && (!this->valve_cycle_complete_(new_valve_number))) {
1022  return new_valve_number;
1023  } else {
1024  new_valve_number = this->next_valve_number_(new_valve_number);
1025  }
1026  }
1027  return nullopt;
1028 }
1029 
1031  auto new_valve_number = this->previous_valve_number_(first_valve.value_or(0));
1032 
1033  while (new_valve_number != first_valve.value_or(0)) {
1034  if (this->valve_is_enabled_(new_valve_number) && (!this->valve_cycle_complete_(new_valve_number))) {
1035  return new_valve_number;
1036  } else {
1037  new_valve_number = this->previous_valve_number_(new_valve_number);
1038  }
1039  }
1040  return nullopt;
1041 }
1042 
1044  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1045  if (this->valve_is_enabled_(valve_number))
1046  return true;
1047  }
1048  return false;
1049 }
1050 
1052  if (!req->has_request()) {
1053  return; // we can't do anything if the request contains nothing
1054  }
1055  if (!this->is_a_valid_valve(req->valve())) {
1056  return; // we can't do anything if the valve number isn't valid
1057  }
1058  for (auto &vo : this->valve_op_) { // find the first available SprinklerValveOperator, load it and start it up
1059  if (vo.state() == IDLE) {
1060  auto run_duration = req->run_duration() ? req->run_duration() : this->valve_run_duration_adjusted(req->valve());
1061  ESP_LOGD(TAG, "Starting valve %u for %u seconds, cycle %u of %u", req->valve(), run_duration,
1062  this->repeat_count_ + 1, this->target_repeats_.value_or(0) + 1);
1063  req->set_valve_operator(&vo);
1064  vo.set_controller(this);
1065  vo.set_valve(&this->valve_[req->valve()]);
1066  vo.set_run_duration(run_duration);
1067  vo.set_start_delay(this->start_delay_, this->start_delay_is_valve_delay_);
1068  vo.set_stop_delay(this->stop_delay_, this->stop_delay_is_valve_delay_);
1069  vo.start();
1070  return;
1071  }
1072  }
1073 }
1074 
1075 void Sprinkler::all_valves_off_(const bool include_pump) {
1076  for (size_t valve_index = 0; valve_index < this->number_of_valves(); valve_index++) {
1077  if (this->valve_[valve_index].valve_switch.state()) {
1078  this->valve_[valve_index].valve_switch.turn_off();
1079  }
1080  if (include_pump) {
1081  this->set_pump_state(this->valve_pump_switch(valve_index), false);
1082  }
1083  }
1084  ESP_LOGD(TAG, "All valves stopped%s", include_pump ? ", including pumps" : "");
1085 }
1086 
1088  if (this->auto_adv_sw_ != nullptr) {
1089  if (!this->auto_adv_sw_->state) {
1090  this->auto_adv_sw_->publish_state(true);
1091  }
1092  }
1093  if (!this->any_valve_is_enabled_()) {
1094  for (auto &valve : this->valve_) {
1095  if (valve.enable_switch != nullptr) {
1096  valve.enable_switch->publish_state(true);
1097  }
1098  }
1099  }
1100  this->reset_cycle_states_();
1101 }
1102 
1104  for (auto &valve : this->valve_) {
1105  valve.valve_cycle_complete = false;
1106  }
1107 }
1108 
1109 void Sprinkler::fsm_request_(size_t requested_valve, uint32_t requested_run_duration) {
1110  this->next_req_.set_valve(requested_valve);
1111  this->next_req_.set_run_duration(requested_run_duration);
1112  // if state is IDLE or ACTIVE, call fsm_transition_() to start it immediately;
1113  // otherwise, fsm_transition() will pick up next_req_ at the next appropriate transition
1114  this->fsm_kick_();
1115 }
1116 
1118  if ((this->state_ == IDLE) || (this->state_ == ACTIVE)) {
1119  this->fsm_transition_();
1120  }
1121 }
1122 
1124  ESP_LOGVV(TAG, "fsm_transition_ called; state is %s", this->state_as_str_(this->state_).c_str());
1125  switch (this->state_) {
1126  case IDLE: // the system was off -> start it up
1127  // advances to ACTIVE
1129  break;
1130 
1131  case ACTIVE:
1132  // advances to STOPPING or ACTIVE (again)
1134  break;
1135 
1136  case STARTING: {
1137  // follows valve open delay interval
1139  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1141  this->start_valve_(&this->active_req_);
1142  this->state_ = ACTIVE;
1143  if (this->next_req_.has_request()) {
1144  // another valve has been requested, so restart the timer so we pick it up quickly
1147  }
1148  break;
1149  }
1150 
1151  case STOPPING:
1152  // stop_delay_ has elapsed so just shut everything off
1153  this->active_req_.reset();
1154  this->manual_valve_.reset();
1155  this->all_valves_off_(true);
1156  this->state_ = IDLE;
1157  break;
1158 
1159  default:
1160  break;
1161  }
1162  if (this->next_req_.has_request() && (this->state_ == IDLE)) {
1163  // another valve has been requested, so restart the timer so we pick it up quickly
1166  }
1167  ESP_LOGVV(TAG, "fsm_transition_ complete; new state is %s", this->state_as_str_(this->state_).c_str());
1168 }
1169 
1172  this->active_req_.set_valve(this->next_req_.valve());
1174  this->next_req_.reset();
1175 
1178  this->start_valve_(&this->active_req_);
1179  this->state_ = ACTIVE;
1180 }
1181 
1183  if (!this->active_req_.has_request()) { // dummy check...
1185  return;
1186  }
1187 
1188  if (!this->timer_active_(sprinkler::TIMER_SM)) { // only flag the valve as "complete" if the timer finished
1190  } else {
1191  ESP_LOGD(TAG, "Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1192  for (auto &vo : this->valve_op_) {
1193  vo.stop();
1194  }
1195  }
1196 
1198 
1199  if (this->next_req_.has_request()) { // there is another valve to run...
1200  bool same_pump =
1201  this->valve_pump_switch(this->active_req_.valve()) == this->valve_pump_switch(this->next_req_.valve());
1202 
1203  this->active_req_.set_valve(this->next_req_.valve());
1205  this->next_req_.reset();
1206 
1207  // this->state_ = ACTIVE; // state isn't changing
1208  if (this->valve_overlap_ || !this->switching_delay_.has_value()) {
1210  this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1212  this->start_valve_(&this->active_req_);
1213  } else {
1214  this->set_timer_duration_(
1216  this->switching_delay_.value() * 2 +
1217  (this->pump_switch_off_during_valve_open_delay_ && same_pump ? this->stop_delay_ : 0));
1219  this->state_ = STARTING;
1220  }
1221  } else { // there is NOT another valve to run...
1223  }
1224 }
1225 
1227  this->state_ = STOPPING;
1229  this->start_delay_ + this->stop_delay_ + this->switching_delay_.value_or(0) + 1);
1231 }
1232 
1234  switch (state) {
1235  case IDLE:
1236  return "IDLE";
1237 
1238  case STARTING:
1239  return "STARTING";
1240 
1241  case ACTIVE:
1242  return "ACTIVE";
1243 
1244  case STOPPING:
1245  return "STOPPING";
1246 
1247  case BYPASS:
1248  return "BYPASS";
1249 
1250  default:
1251  return "UNKNOWN";
1252  }
1253 }
1254 
1256  if (this->timer_duration_(timer_index) > 0) {
1257  this->set_timeout(this->timer_[timer_index].name, this->timer_duration_(timer_index),
1258  this->timer_cbf_(timer_index));
1259  this->timer_[timer_index].start_time = millis();
1260  this->timer_[timer_index].active = true;
1261  }
1262  ESP_LOGVV(TAG, "Timer %u started for %u sec", static_cast<size_t>(timer_index),
1263  this->timer_duration_(timer_index) / 1000);
1264 }
1265 
1267  this->timer_[timer_index].active = false;
1268  return this->cancel_timeout(this->timer_[timer_index].name);
1269 }
1270 
1271 bool Sprinkler::timer_active_(const SprinklerTimerIndex timer_index) { return this->timer_[timer_index].active; }
1272 
1273 void Sprinkler::set_timer_duration_(const SprinklerTimerIndex timer_index, const uint32_t time) {
1274  this->timer_[timer_index].time = 1000 * time;
1275 }
1276 
1277 uint32_t Sprinkler::timer_duration_(const SprinklerTimerIndex timer_index) { return this->timer_[timer_index].time; }
1278 
1279 std::function<void()> Sprinkler::timer_cbf_(const SprinklerTimerIndex timer_index) {
1280  return this->timer_[timer_index].func;
1281 }
1282 
1284  this->timer_[sprinkler::TIMER_VALVE_SELECTION].active = false;
1285  ESP_LOGVV(TAG, "Valve selection timer expired");
1286  if (this->manual_valve_.has_value()) {
1287  this->fsm_request_(this->manual_valve_.value());
1288  this->manual_valve_.reset();
1289  }
1290 }
1291 
1293  this->timer_[sprinkler::TIMER_SM].active = false;
1294  ESP_LOGVV(TAG, "State machine timer expired");
1295  this->fsm_transition_();
1296 }
1297 
1299  ESP_LOGCONFIG(TAG, "Sprinkler Controller -- %s", this->name_.c_str());
1300  if (this->manual_selection_delay_.has_value()) {
1301  ESP_LOGCONFIG(TAG, " Manual Selection Delay: %u seconds", this->manual_selection_delay_.value_or(0));
1302  }
1303  if (this->target_repeats_.has_value()) {
1304  ESP_LOGCONFIG(TAG, " Repeat Cycles: %u times", this->target_repeats_.value_or(0));
1305  }
1306  if (this->start_delay_) {
1307  if (this->start_delay_is_valve_delay_) {
1308  ESP_LOGCONFIG(TAG, " Pump Start Valve Delay: %u seconds", this->start_delay_);
1309  } else {
1310  ESP_LOGCONFIG(TAG, " Pump Start Pump Delay: %u seconds", this->start_delay_);
1311  }
1312  }
1313  if (this->stop_delay_) {
1314  if (this->stop_delay_is_valve_delay_) {
1315  ESP_LOGCONFIG(TAG, " Pump Stop Valve Delay: %u seconds", this->stop_delay_);
1316  } else {
1317  ESP_LOGCONFIG(TAG, " Pump Stop Pump Delay: %u seconds", this->stop_delay_);
1318  }
1319  }
1320  if (this->switching_delay_.has_value()) {
1321  if (this->valve_overlap_) {
1322  ESP_LOGCONFIG(TAG, " Valve Overlap: %u seconds", this->switching_delay_.value_or(0));
1323  } else {
1324  ESP_LOGCONFIG(TAG, " Valve Open Delay: %u seconds", this->switching_delay_.value_or(0));
1325  ESP_LOGCONFIG(TAG, " Pump Switch Off During Valve Open Delay: %s",
1327  }
1328  }
1329  for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1330  ESP_LOGCONFIG(TAG, " Valve %u:", valve_number);
1331  ESP_LOGCONFIG(TAG, " Name: %s", this->valve_name(valve_number));
1332  ESP_LOGCONFIG(TAG, " Run Duration: %u seconds", this->valve_[valve_number].run_duration);
1333  if (this->valve_[valve_number].valve_switch.pulse_duration()) {
1334  ESP_LOGCONFIG(TAG, " Pulse Duration: %u milliseconds",
1335  this->valve_[valve_number].valve_switch.pulse_duration());
1336  }
1337  }
1338  if (!this->pump_.empty()) {
1339  ESP_LOGCONFIG(TAG, " Total number of pumps: %u", this->pump_.size());
1340  }
1341  if (!this->valve_.empty()) {
1342  ESP_LOGCONFIG(TAG, " Total number of valves: %u", this->valve_.size());
1343  }
1344 }
1345 
1346 } // namespace sprinkler
1347 } // namespace esphome
Base class for all switches.
Definition: switch.h:33
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:575
void set_assumed_state(bool assumed_state)
Definition: sprinkler.cpp:140
const char * name
Definition: stm32flash.h:78
void start_timer_(SprinklerTimerIndex timer_index)
Start/cancel/get status of valve timers.
Definition: sprinkler.cpp:1255
bool pump_switch_off_during_valve_open_delay_
Pump should be off during valve_open_delay interval.
Definition: sprinkler.h:448
optional< size_t > previous_enabled_incomplete_valve_number_(optional< size_t > first_valve)
Definition: sprinkler.cpp:1030
bool timer_active_(SprinklerTimerIndex timer_index)
returns true if the specified timer is active/running
Definition: sprinkler.cpp:1271
SprinklerControllerSwitch * controller_switch
Definition: sprinkler.h:77
void previous_valve()
advances to the previous valve (numerically)
Definition: sprinkler.cpp:727
SprinklerValveRunRequest next_req_
The next run request for the controller to consume after active_req_ is complete. ...
Definition: sprinkler.h:474
std::vector< SprinklerValveOperator > valve_op_
Sprinkler valve operator objects.
Definition: sprinkler.h:504
bool cancel_timeout(const std::string &name)
Cancel a timeout function.
Definition: component.cpp:72
uint32_t repeat_count_
Number of times the full cycle has been repeated.
Definition: sprinkler.h:489
optional< std::function< optional< bool >)> > f_
Definition: sprinkler.h:111
uint32_t valve_run_duration(size_t valve_number)
returns valve_number&#39;s run duration in seconds
Definition: sprinkler.cpp:589
void next_valve()
advances to the next valve (numerically)
Definition: sprinkler.cpp:713
void set_run_duration(uint32_t run_duration)
Definition: sprinkler.cpp:191
optional< uint32_t > resume_duration_
Set from time_remaining() when paused.
Definition: sprinkler.h:480
void valve_selection_callback_()
callback functions for timers
Definition: sprinkler.cpp:1283
void queue_valve(optional< size_t > valve_number, optional< uint32_t > run_duration)
adds a valve into the queue.
Definition: sprinkler.cpp:697
std::string name_
Definition: entity_base.h:54
optional< uint32_t > time_remaining()
returns the amount of time remaining in seconds for the active valve, if any. check with &#39;has_value()...
Definition: sprinkler.cpp:884
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:799
std::vector< SprinklerValve > valve_
Sprinkler valve objects.
Definition: sprinkler.h:501
bool auto_advance()
returns true if auto_advance is enabled
Definition: sprinkler.cpp:611
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:68
void fsm_transition_to_shutdown_()
starts up the system from IDLE state
Definition: sprinkler.cpp:1226
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:801
optional< uint32_t > switching_delay_
Valve switching delay.
Definition: sprinkler.h:486
void set_multiplier(optional< float > multiplier)
value multiplied by configured run times – used to extend or shorten the cycle
Definition: sprinkler.cpp:502
SprinklerSwitch * valve_switch(size_t valve_number)
returns a pointer to a valve&#39;s switch object
Definition: sprinkler.cpp:912
SprinklerValveOperator * valve_operator()
Definition: sprinkler.cpp:363
void fsm_request_(size_t requested_valve, uint32_t requested_run_duration=0)
make a request of the state machine
Definition: sprinkler.cpp:1109
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:412
void reset_resume()
resets resume state
Definition: sprinkler.cpp:786
bool is_a_valid_valve(size_t valve_number)
returns true if valve number is valid
Definition: sprinkler.cpp:812
void set_state_lambda(std::function< optional< bool >()> &&f)
Definition: sprinkler.cpp:110
void shutdown(bool clear_queue=false)
turns off all valves, effectively shutting down the system.
Definition: sprinkler.cpp:741
std::unique_ptr< ResumeOrStartAction<> > sprinkler_resumeorstart_action_
Definition: sprinkler.h:521
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:510
optional< size_t > pump_switch_index
Definition: sprinkler.h:81
void set_controller_queue_enable_switch(SprinklerControllerSwitch *queue_enable_switch)
Definition: sprinkler.cpp:440
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:926
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:561
std::unique_ptr< Automation<> > valve_turn_off_automation
Definition: sprinkler.h:85
std::vector< Sprinkler * > other_controllers_
Other Sprinkler instances we should be aware of (used to check if pumps are in use) ...
Definition: sprinkler.h:512
uint32_t hash_base() override
Definition: sprinkler.cpp:933
void resume_or_start_full_cycle()
if a cycle was suspended using pause(), resumes it. otherwise calls start_full_cycle() ...
Definition: sprinkler.cpp:778
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:26
SprinklerSwitch * valve_pump_switch(size_t valve_number)
returns a pointer to a valve&#39;s pump switch object
Definition: sprinkler.cpp:919
void start_single_valve(optional< size_t > valve_number)
activates a single valve and disables auto_advance.
Definition: sprinkler.cpp:681
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition: automation.h:95
SprinklerControllerSwitch * auto_adv_sw_
Switches we&#39;ll present to the front end.
Definition: sprinkler.h:515
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:543
size_t next_valve_number_(size_t first_valve)
returns the number of the next/previous valve in the vector
Definition: sprinkler.cpp:960
bool valve_overlap_
Sprinkler valve cycle should overlap.
Definition: sprinkler.h:451
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:596
void set_controller_reverse_switch(SprinklerControllerSwitch *reverse_switch)
Definition: sprinkler.cpp:446
switch_::Switch * on_switch()
Definition: sprinkler.h:53
std::unique_ptr< Automation<> > sprinkler_turn_on_automation_
Definition: sprinkler.h:524
std::vector< SprinklerTimer > timer_
Valve control timers.
Definition: sprinkler.h:507
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:1117
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:852
optional< bool > get_initial_state()
Definition: switch.cpp:24
uint32_t timer_duration_(SprinklerTimerIndex timer_index)
returns time in milliseconds (ms)
Definition: sprinkler.cpp:1277
void resume()
resumes a cycle that was suspended using pause()
Definition: sprinkler.cpp:767
void pause()
same as shutdown(), but also stores active_valve() and time_remaining() allowing resume() to continue...
Definition: sprinkler.cpp:756
void start_full_cycle()
starts a full cycle of all enabled valves and enables auto_advance.
Definition: sprinkler.cpp:665
SprinklerControllerSwitch * controller_sw_
Definition: sprinkler.h:516
void turn_on()
Turn this switch on.
Definition: switch.cpp:12
bool reverse()
returns true if reverse is enabled
Definition: sprinkler.cpp:639
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:974
void start_valve_(SprinklerValveRunRequest *req)
loads an available SprinklerValveOperator (valve_op_) based on req and starts it (switches it on)...
Definition: sprinkler.cpp:1051
void set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay)
Definition: sprinkler.cpp:197
void set_timer_duration_(SprinklerTimerIndex timer_index, uint32_t time)
time is converted to milliseconds (ms) for set_timeout()
Definition: sprinkler.cpp:1273
void set_auto_advance(bool auto_advance)
if auto_advance is true, controller will iterate through all enabled valves
Definition: sprinkler.cpp:569
void set_restore_state(bool restore_state)
Definition: sprinkler.cpp:138
SprinklerControllerSwitch * control_switch(size_t valve_number)
returns a pointer to a valve&#39;s control switch object
Definition: sprinkler.cpp:898
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:534
void set_controller_auto_adv_switch(SprinklerControllerSwitch *auto_adv_switch)
Definition: sprinkler.cpp:434
optional< size_t > manual_valve_
The number of the manually selected valve currently selected.
Definition: sprinkler.h:468
SprinklerControllerSwitch * enable_switch(size_t valve_number)
returns a pointer to a valve&#39;s enable switch object
Definition: sprinkler.cpp:905
void set_controller(Sprinkler *controller)
Definition: sprinkler.cpp:175
SprinklerControllerSwitch * queue_enable_sw_
Definition: sprinkler.h:517
optional< uint32_t > target_repeats_
Set the number of times to repeat a full cycle.
Definition: sprinkler.h:477
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:553
void set_queue_enable(bool queue_enable)
if queue_enable is true, controller will iterate through valves in the queue
Definition: sprinkler.cpp:577
void set_reverse(bool reverse)
if reverse is true, controller will iterate through all enabled valves in reverse (descending) order ...
Definition: sprinkler.cpp:583
void fsm_transition_()
advance controller state, advancing to target_valve if provided
Definition: sprinkler.cpp:1123
bool queue_enabled()
returns true if the queue is enabled to run
Definition: sprinkler.cpp:632
void set_valve_operator(SprinklerValveOperator *valve_op)
Definition: sprinkler.cpp:340
bool valve_is_enabled_(size_t valve_number)
returns true if valve number is enabled
Definition: sprinkler.cpp:935
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:530
void set_controller_main_switch(SprinklerControllerSwitch *controller_switch)
configure important controller switches
Definition: sprinkler.cpp:414
void set_valve(SprinklerValve *valve)
Definition: sprinkler.cpp:181
void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw=nullptr)
add a valve to the controller
Definition: sprinkler.cpp:382
const uint8_t max_queue_size_
Maximum allowed queue size.
Definition: sprinkler.h:445
std::vector< SprinklerSwitch > pump_
Sprinkler valve pump objects.
Definition: sprinkler.h:498
void reset_cycle_states_()
resets the cycle state for all valves
Definition: sprinkler.cpp:1103
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition: component.cpp:17
void start_from_queue()
starts the controller from the first valve in the queue and disables auto_advance.
Definition: sprinkler.cpp:646
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:1087
uint32_t start_delay_
Pump start/stop delay intervals.
Definition: sprinkler.h:458
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:470
SprinklerState state_
Sprinkler controller state.
Definition: sprinkler.h:462
optional< size_t > paused_valve_
The number of the valve to resume from (if paused)
Definition: sprinkler.h:471
optional< uint32_t > repeat_count()
if a cycle is active, returns the number of times the controller has repeated the cycle...
Definition: sprinkler.cpp:622
void clear_queued_valves()
clears/removes all valves from the queue
Definition: sprinkler.cpp:708
std::unique_ptr< StartSingleValveAction<> > valve_resumeorstart_action
Definition: sprinkler.h:84
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:953
std::unique_ptr< ShutdownAction<> > sprinkler_shutdown_action_
Definition: sprinkler.h:520
bool any_valve_is_enabled_()
returns true if any valve is enabled
Definition: sprinkler.cpp:1043
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:452
bool cancel_timer_(SprinklerTimerIndex timer_index)
Definition: sprinkler.cpp:1266
std::unique_ptr< Automation<> > sprinkler_turn_off_automation_
Definition: sprinkler.h:523
size_t previous_valve_number_(size_t first_valve)
Definition: sprinkler.cpp:967
Definition: a4988.cpp:4
void fsm_transition_from_shutdown_()
starts up the system from IDLE state
Definition: sprinkler.cpp:1170
std::string state_as_str_(SprinklerState state)
return the current FSM state as a string
Definition: sprinkler.cpp:1233
std::function< void()> timer_cbf_(SprinklerTimerIndex timer_index)
Definition: sprinkler.cpp:1279
bool pump_in_use(SprinklerSwitch *pump_switch)
returns true if the pump the pointer points to is in use
Definition: sprinkler.cpp:816
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:520
bool start_delay_is_valve_delay_
Pump start/stop delay interval types.
Definition: sprinkler.h:454
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:791
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:484
optional< uint32_t > manual_selection_delay_
Manual switching delay.
Definition: sprinkler.h:483
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:525
SprinklerControllerSwitch * reverse_sw_
Definition: sprinkler.h:518
size_t number_of_valves()
returns the number of valves the controller is configured with
Definition: sprinkler.cpp:810
std::unique_ptr< Automation<> > valve_turn_on_automation
Definition: sprinkler.h:86
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:459
std::vector< SprinklerQueueItem > queued_valves_
Queue of valves to activate next, regardless of auto-advance.
Definition: sprinkler.h: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:983
void mark_valve_cycle_complete_(size_t valve_number)
marks a valve&#39;s cycle as complete
Definition: sprinkler.cpp:946
void set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay)
Definition: sprinkler.cpp:202
void publish_state(bool state)
Publish a state to the front-end from the back-end.
Definition: switch.cpp:31
void set_run_duration(uint32_t run_duration)
Definition: sprinkler.cpp:331
SprinklerControllerSwitch * enable_switch
Definition: sprinkler.h:78
float multiplier_
Sprinkler valve run time multiplier value.
Definition: sprinkler.h:492
bool state
The current reported state of the binary sensor.
Definition: switch.h:48
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:798
void stop_action()
Stop any action connected to this trigger.
Definition: automation.h:103
optional< size_t > next_enabled_incomplete_valve_number_(optional< size_t > first_valve)
returns the number of the next/previous valve that should be activated.
Definition: sprinkler.cpp:1017
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:1182
float multiplier()
returns the current value of the multiplier
Definition: sprinkler.cpp:618
switch_::Switch * off_switch()
Definition: sprinkler.h:52
SprinklerValveRunRequest active_req_
The valve run request that is currently active.
Definition: sprinkler.h:465
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:620
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:515
void turn_off()
Turn this switch off.
Definition: switch.cpp:16
std::unique_ptr< ShutdownAction<> > valve_shutdown_action
Definition: sprinkler.h:83
optional< size_t > manual_valve()
returns the number of the valve that is manually selected, if any.
Definition: sprinkler.cpp:808
void all_valves_off_(bool include_pump=false)
turns off/closes all valves, including pump if include_pump is true
Definition: sprinkler.cpp:1075