1 #include "automation.h" 12 static const char *
const TAG =
"sprinkler";
72 this->
state_ = latch_state;
80 if (!this->restore_value_) {
81 value = this->initial_value_;
84 if (!this->pref_.load(&value)) {
85 if (!std::isnan(this->initial_value_)) {
86 value = this->initial_value_;
88 value = this->traits.get_min_value();
92 this->publish_state(value);
96 this->set_trigger_->trigger(value);
98 this->publish_state(value);
100 if (this->restore_value_)
101 this->pref_.save(&value);
107 : turn_on_trigger_(new
Trigger<>()), turn_off_trigger_(new
Trigger<>()) {}
110 if (!this->
f_.has_value())
112 auto s = (*this->
f_)();
147 : controller_(controller), valve_(valve) {}
179 if (controller !=
nullptr) {
185 if (valve !=
nullptr) {
266 if (completed_millis >
millis()) {
267 return (completed_millis -
millis()) / 1000;
313 if (this->
valve_ ==
nullptr) {
322 if (this->
valve_ ==
nullptr) {
345 : valve_number_(valve_number),
run_duration_(run_duration), valve_op_(valve_op) {}
362 if (valve_op !=
nullptr) {
392 this->all_valves_off_(
true);
396 for (
auto &p : this->pump_) {
399 for (
auto &v : this->valve_) {
400 v.valve_switch.loop();
405 if (this->prev_req_.has_request() && this->prev_req_.valve_operator()->state() ==
IDLE) {
406 this->prev_req_.reset();
411 auto new_valve_number = this->number_of_valves();
412 this->valve_.resize(new_valve_number + 1);
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();
420 return this->valve_switch(new_valve_number)->state();
433 if (enable_sw !=
nullptr) {
441 this->controller_sw_ = controller_switch;
443 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
444 if (this->valve_[valve_number].controller_switch->
state) {
448 return this->active_req_.has_request();
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()});
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()});
461 this->auto_adv_sw_ = auto_adv_switch;
465 this->queue_enable_sw_ = queue_enable_switch;
469 this->reverse_sw_ = reverse_switch;
473 this->standby_sw_ = standby_switch;
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()});
481 this->multiplier_number_ = multiplier_number;
485 this->repeat_number_ = repeat_number;
489 if (this->is_a_valid_valve(valve_number)) {
490 this->valve_[valve_number].valve_switch.set_on_switch(valve_switch);
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);
507 if (this->is_a_valid_valve(valve_number)) {
508 for (
size_t i = 0; i < this->pump_.size(); i++) {
509 if (this->pump_[i].on_switch() == pump_switch) {
510 this->valve_[valve_number].pump_switch_index = i;
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;
522 if (this->is_a_valid_valve(valve_number)) {
523 for (
size_t i = 0; i < this->pump_.size(); i++) {
524 if ((this->pump_[i].off_switch() == pump_switch_off) &&
525 (this->pump_[i].on_switch() == pump_switch_on)) {
526 this->valve_[valve_number].pump_switch_index = i;
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;
540 if (this->is_a_valid_valve(valve_number)) {
541 this->valve_[valve_number].run_duration_number = run_duration_number;
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);
562 this->multiplier_ = multiplier.
value();
563 if (this->multiplier_number_ ==
nullptr) {
566 if (this->multiplier_number_->state == multiplier.
value()) {
569 auto call = this->multiplier_number_->make_call();
570 call.set_value(multiplier.
value());
575 this->next_prev_ignore_disabled_ = ignore_disabled;
579 this->start_delay_is_valve_delay_ =
false;
580 this->start_delay_ = start_delay;
584 this->stop_delay_is_valve_delay_ =
false;
585 this->stop_delay_ = stop_delay;
589 this->start_delay_is_valve_delay_ =
true;
590 this->start_delay_ = start_delay;
594 this->stop_delay_is_valve_delay_ =
true;
595 this->stop_delay_ = stop_delay;
599 this->pump_switch_off_during_valve_open_delay_ = pump_switch_off_during_valve_open_delay;
603 if (valve_open_delay > 0) {
604 this->valve_overlap_ =
false;
605 this->switching_delay_ = valve_open_delay;
607 this->switching_delay_.reset();
612 if (valve_overlap > 0) {
613 this->valve_overlap_ =
true;
614 this->switching_delay_ = valve_overlap;
616 this->switching_delay_.reset();
618 this->pump_switch_off_during_valve_open_delay_ =
false;
622 if (manual_selection_delay > 0) {
623 this->manual_selection_delay_ = manual_selection_delay;
625 this->manual_selection_delay_.reset();
633 if (!this->is_a_valid_valve(valve_number.
value())) {
636 this->valve_[valve_number.
value()].run_duration = run_duration.
value();
637 if (this->valve_[valve_number.
value()].run_duration_number ==
nullptr) {
640 if (this->valve_[valve_number.
value()].run_duration_number->state == run_duration.
value()) {
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);
647 call.set_value(run_duration.
value());
653 if (this->auto_adv_sw_ ==
nullptr) {
656 if (this->auto_adv_sw_->state == auto_advance) {
660 this->auto_adv_sw_->turn_on();
662 this->auto_adv_sw_->turn_off();
667 this->target_repeats_ = repeat;
668 if (this->repeat_number_ ==
nullptr) {
671 if (this->repeat_number_->state == repeat.
value()) {
674 auto call = this->repeat_number_->make_call();
680 if (this->queue_enable_sw_ ==
nullptr) {
683 if (this->queue_enable_sw_->state == queue_enable) {
687 this->queue_enable_sw_->turn_on();
689 this->queue_enable_sw_->turn_off();
694 if (this->reverse_sw_ ==
nullptr) {
697 if (this->reverse_sw_->state == reverse) {
701 this->reverse_sw_->turn_on();
703 this->reverse_sw_->turn_off();
708 if (this->standby_sw_ ==
nullptr) {
711 if (this->standby_sw_->state == standby) {
715 this->standby_sw_->turn_on();
717 this->standby_sw_->turn_off();
722 if (!this->is_a_valid_valve(valve_number)) {
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));
729 return static_cast<uint32_t
>(roundf(this->valve_[valve_number].run_duration_number->state));
732 return this->valve_[valve_number].run_duration;
738 if (this->is_a_valid_valve(valve_number)) {
739 run_duration = this->valve_run_duration(valve_number);
741 run_duration =
static_cast<uint32_t
>(roundf(run_duration * this->multiplier()));
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_));
751 if (this->auto_adv_sw_ !=
nullptr) {
752 return this->auto_adv_sw_->state;
758 if (this->multiplier_number_ !=
nullptr) {
759 return this->multiplier_number_->state;
761 return this->multiplier_;
765 if (this->repeat_number_ !=
nullptr) {
766 return static_cast<uint32_t
>(roundf(this->repeat_number_->state));
768 return this->target_repeats_;
773 if (this->active_req_.has_request() && this->auto_advance()) {
774 return this->repeat_count_;
780 if (this->queue_enable_sw_ !=
nullptr) {
781 return this->queue_enable_sw_->state;
787 if (this->reverse_sw_ !=
nullptr) {
788 return this->reverse_sw_->state;
794 if (this->standby_sw_ !=
nullptr) {
795 return this->standby_sw_->state;
801 if (this->standby()) {
802 ESP_LOGD(TAG,
"start_from_queue called but standby is enabled; no action taken");
805 if (this->multiplier() == 0) {
806 ESP_LOGD(TAG,
"start_from_queue called but multiplier is set to zero; no action taken");
809 if (this->queued_valves_.empty()) {
812 if (this->queue_enabled() && this->active_valve().has_value()) {
816 this->set_auto_advance(
false);
817 this->set_queue_enable(
true);
819 this->reset_cycle_states_();
820 this->repeat_count_ = 0;
825 if (this->standby()) {
826 ESP_LOGD(TAG,
"start_full_cycle called but standby is enabled; no action taken");
829 if (this->multiplier() == 0) {
830 ESP_LOGD(TAG,
"start_full_cycle called but multiplier is set to zero; no action taken");
833 if (this->auto_advance() && this->active_valve().has_value()) {
837 this->set_queue_enable(
false);
839 this->prep_full_cycle_();
840 this->repeat_count_ = 0;
842 if (!this->active_req_.has_request()) {
848 if (this->standby()) {
849 ESP_LOGD(TAG,
"start_single_valve called but standby is enabled; no action taken");
852 if (this->multiplier() == 0) {
853 ESP_LOGD(TAG,
"start_single_valve called but multiplier is set to zero; no action taken");
856 if (!valve_number.
has_value() || (valve_number == this->active_valve())) {
860 this->set_auto_advance(
false);
861 this->set_queue_enable(
false);
863 this->reset_cycle_states_();
864 this->repeat_count_ = 0;
865 this->fsm_request_(valve_number.
value(), run_duration.
value_or(0));
870 if (this->is_a_valid_valve(valve_number.
value()) && (this->queued_valves_.size() < this->max_queue_size_)) {
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),
880 this->queued_valves_.clear();
881 ESP_LOGD(TAG,
"Queue cleared");
885 if (this->state_ ==
IDLE) {
886 this->reset_cycle_states_();
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);
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?");
899 if (this->manual_selection_delay_.has_value()) {
903 this->fsm_request_(this->manual_valve_.value());
908 if (this->state_ ==
IDLE) {
909 this->reset_cycle_states_();
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);
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?");
922 if (this->manual_selection_delay_.has_value()) {
926 this->fsm_request_(this->manual_valve_.value());
932 this->active_req_.reset();
933 this->manual_valve_.reset();
934 this->next_req_.reset();
938 this->fsm_transition_to_shutdown_();
940 this->clear_queued_valves();
941 this->repeat_count_ = 0;
946 if (this->paused_valve_.has_value() || !this->active_req_.has_request()) {
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));
957 if (this->standby()) {
958 ESP_LOGD(TAG,
"resume called but standby is enabled; no action taken");
962 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
964 if (!this->valve_cycle_complete_(this->paused_valve_.value())) {
965 ESP_LOGD(TAG,
"Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
966 this->resume_duration_.value_or(0));
967 this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
969 this->reset_resume();
971 ESP_LOGD(TAG,
"No valve to resume!");
976 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
979 this->start_full_cycle();
984 this->paused_valve_.reset();
985 this->resume_duration_.reset();
989 if (this->is_a_valid_valve(valve_number)) {
990 return this->valve_[valve_number].controller_switch->get_name().c_str();
996 if (this->active_req_.has_request()) {
997 return this->active_req_.request_is_from();
1003 if (!this->valve_overlap_ && this->prev_req_.has_request() &&
1004 (this->prev_req_.valve_operator()->state() ==
STARTING || this->prev_req_.valve_operator()->state() ==
ACTIVE)) {
1005 return this->prev_req_.valve_as_opt();
1007 return this->active_req_.valve_as_opt();
1013 if (!this->queued_valves_.empty()) {
1014 return this->queued_valves_.back().valve_number;
1024 return ((valve_number >= 0) && (valve_number < this->number_of_valves()));
1028 if (pump_switch ==
nullptr) {
1038 if ((vo.state() !=
BYPASS) && (vo.pump_switch() !=
nullptr)) {
1040 if ((vo.pump_switch()->off_switch() == pump_switch->
off_switch()) &&
1041 (vo.pump_switch()->on_switch() == pump_switch->
on_switch())) {
1044 if ((vo.state() ==
ACTIVE) ||
1045 ((vo.state() ==
STARTING) && this->start_delay_ && this->start_delay_is_valve_delay_) ||
1046 ((vo.state() ==
STOPPING) && this->stop_delay_ && this->stop_delay_is_valve_delay_)) {
1052 if (!this->valve_overlap_ && !this->pump_switch_off_during_valve_open_delay_ && this->switching_delay_.has_value() &&
1053 this->active_req_.has_request() && (this->state_ !=
STOPPING)) {
1056 return (pump_switch->
off_switch() == this->valve_pump_switch(this->active_req_.valve())->off_switch()) &&
1057 (pump_switch->
on_switch() == this->valve_pump_switch(this->active_req_.valve())->on_switch());
1063 if (pump_switch ==
nullptr) {
1067 bool hold_pump_on =
false;
1069 for (
auto &controller : this->other_controllers_) {
1070 if (controller !=
this) {
1071 if (controller->pump_in_use(pump_switch)) {
1072 hold_pump_on =
true;
1082 ESP_LOGD(TAG,
"Leaving pump on because another controller instance is using it");
1087 }
else if (!hold_pump_on && !this->pump_in_use(pump_switch)) {
1089 }
else if (hold_pump_on) {
1095 uint32_t total_time_remaining = 0;
1098 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1101 if (this->valve_overlap_) {
1102 total_time_remaining -= this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1104 total_time_remaining += this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1107 return total_time_remaining;
1111 uint32_t total_time_remaining = 0;
1112 uint32_t valve_count = 0;
1115 if (this->valve_is_enabled_(
valve)) {
1116 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1122 if (this->valve_overlap_) {
1123 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1125 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1129 return total_time_remaining;
1133 uint32_t total_time_remaining = 0;
1134 uint32_t enabled_valve_count = 0;
1135 uint32_t incomplete_valve_count = 0;
1138 if (this->valve_is_enabled_(
valve)) {
1139 enabled_valve_count++;
1140 if (!this->valve_cycle_complete_(
valve)) {
1141 if (!this->active_valve().has_value() || (
valve != this->active_valve().value())) {
1142 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1143 incomplete_valve_count++;
1146 if (this->active_req_.valve_operator() ==
nullptr) {
1147 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1148 incomplete_valve_count++;
1155 if (incomplete_valve_count >= enabled_valve_count) {
1156 incomplete_valve_count--;
1158 if (incomplete_valve_count) {
1159 if (this->valve_overlap_) {
1160 total_time_remaining -= this->switching_delay_.value_or(0) * incomplete_valve_count;
1162 total_time_remaining += this->switching_delay_.value_or(0) * incomplete_valve_count;
1166 return total_time_remaining;
1170 uint32_t total_time_remaining = 0;
1171 uint32_t valve_count = 0;
1173 for (
auto &
valve : this->queued_valves_) {
1174 if (
valve.run_duration) {
1175 total_time_remaining +=
valve.run_duration;
1177 total_time_remaining += this->valve_run_duration_adjusted(
valve.valve_number);
1183 if (this->valve_overlap_) {
1184 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1186 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1190 return total_time_remaining;
1194 if (this->active_req_.has_request()) {
1195 if (this->active_req_.valve_operator() !=
nullptr) {
1196 return this->active_req_.valve_operator()->time_remaining();
1199 if (this->prev_req_.has_request()) {
1200 if (this->prev_req_.valve_operator() !=
nullptr) {
1201 return this->prev_req_.valve_operator()->time_remaining();
1208 if (!this->time_remaining_active_valve().has_value() && this->state_ ==
IDLE) {
1212 auto total_time_remaining = this->time_remaining_active_valve().value_or(0);
1213 if (this->auto_advance()) {
1214 total_time_remaining += this->total_cycle_time_enabled_incomplete_valves();
1215 if (this->repeat().value_or(0) > 0) {
1216 total_time_remaining +=
1217 (this->total_cycle_time_enabled_valves() * (this->repeat().value_or(0) - this->repeat_count().value_or(0)));
1221 if (this->queue_enabled()) {
1222 total_time_remaining += this->total_queue_time();
1224 return total_time_remaining;
1228 if (this->state_ !=
IDLE) {
1232 for (
auto &controller : this->other_controllers_) {
1233 if (controller !=
this) {
1234 if (controller->controller_state() !=
IDLE) {
1243 if (this->is_a_valid_valve(valve_number)) {
1244 return this->valve_[valve_number].controller_switch;
1250 if (this->is_a_valid_valve(valve_number)) {
1251 return this->valve_[valve_number].enable_switch;
1257 if (this->is_a_valid_valve(valve_number)) {
1258 return &this->valve_[valve_number].valve_switch;
1264 if (this->is_a_valid_valve(valve_number) && this->valve_[valve_number].pump_switch_index.has_value()) {
1265 return &this->pump_[this->valve_[valve_number].pump_switch_index.value()];
1271 if (pump_index < this->pump_.size()) {
1272 return &this->pump_[pump_index];
1278 if (this->is_a_valid_valve(valve_number)) {
1279 if (this->valve_[valve_number].enable_switch !=
nullptr) {
1280 return this->valve_[valve_number].enable_switch->state;
1289 if (this->is_a_valid_valve(valve_number)) {
1290 ESP_LOGD(TAG,
"Marking valve %u complete", valve_number);
1291 this->valve_[valve_number].valve_cycle_complete =
true;
1296 if (this->is_a_valid_valve(valve_number)) {
1297 return this->valve_[valve_number].valve_cycle_complete;
1303 const bool include_complete) {
1305 size_t start = first_valve.
has_value() ? 1 : 0;
1307 if (!this->is_a_valid_valve(
valve)) {
1311 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1312 auto valve_of_interest =
valve + offset;
1313 if (!this->is_a_valid_valve(valve_of_interest)) {
1314 valve_of_interest -= this->number_of_valves();
1317 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1318 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1319 return valve_of_interest;
1326 const bool include_complete) {
1327 auto valve = first_valve.
value_or(this->number_of_valves() - 1);
1328 size_t start = first_valve.
has_value() ? 1 : 0;
1330 if (!this->is_a_valid_valve(
valve)) {
1331 valve = this->number_of_valves() - 1;
1334 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1335 auto valve_of_interest =
valve - offset;
1336 if (!this->is_a_valid_valve(valve_of_interest)) {
1337 valve_of_interest += this->number_of_valves();
1340 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1341 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1342 return valve_of_interest;
1349 if (this->reverse()) {
1350 return this->previous_valve_number_(first_valve,
false,
false);
1352 return this->next_valve_number_(first_valve,
false,
false);
1356 if (this->active_req_.has_request()) {
1357 this->prev_req_ = this->active_req_;
1359 this->prev_req_.reset();
1362 if (this->next_req_.has_request()) {
1363 if (!this->next_req_.run_duration()) {
1364 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->next_req_.valve()));
1367 }
else if (this->queue_enabled() && !this->queued_valves_.empty()) {
1368 this->next_req_.set_valve(this->queued_valves_.back().valve_number);
1369 this->next_req_.set_request_from(
QUEUE);
1370 if (this->queued_valves_.back().run_duration) {
1371 this->next_req_.set_run_duration(this->queued_valves_.back().run_duration);
1372 this->queued_valves_.pop_back();
1373 }
else if (this->multiplier()) {
1374 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->queued_valves_.back().valve_number));
1375 this->queued_valves_.pop_back();
1377 this->next_req_.reset();
1379 }
else if (this->auto_advance() && this->multiplier()) {
1380 if (this->next_valve_number_in_cycle_(first_valve).has_value()) {
1382 this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1383 this->next_req_.set_request_from(
CYCLE);
1384 this->next_req_.set_run_duration(
1385 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1386 }
else if ((this->repeat_count_++ < this->repeat().value_or(0))) {
1387 ESP_LOGD(TAG,
"Repeating - starting cycle %u of %u", this->repeat_count_ + 1, this->repeat().value_or(0) + 1);
1389 this->prep_full_cycle_();
1390 if (this->next_valve_number_in_cycle_().has_value()) {
1391 this->next_req_.set_valve(this->next_valve_number_in_cycle_().value_or(0));
1392 this->next_req_.set_request_from(
CYCLE);
1393 this->next_req_.set_run_duration(
1394 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_().value_or(0)));
1401 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1402 if (this->valve_is_enabled_(valve_number))
1412 if (!this->is_a_valid_valve(req->
valve())) {
1416 if (vo.state() ==
IDLE) {
1418 ESP_LOGD(TAG,
"%s is starting valve %u for %u seconds, cycle %u of %u",
1420 this->repeat().value_or(0) + 1);
1422 vo.set_controller(
this);
1423 vo.set_valve(&this->valve_[req->
valve()]);
1425 vo.set_start_delay(this->start_delay_, this->start_delay_is_valve_delay_);
1426 vo.set_stop_delay(this->stop_delay_, this->stop_delay_is_valve_delay_);
1434 for (
size_t valve_index = 0; valve_index < this->number_of_valves(); valve_index++) {
1435 if (this->valve_[valve_index].valve_switch.state()) {
1436 this->valve_[valve_index].valve_switch.turn_off();
1439 this->set_pump_state(this->valve_pump_switch(valve_index),
false);
1442 ESP_LOGD(TAG,
"All valves stopped%s", include_pump ?
", including pumps" :
"");
1446 this->set_auto_advance(
true);
1448 if (!this->any_valve_is_enabled_()) {
1449 for (
auto &
valve : this->valve_) {
1450 if (
valve.enable_switch !=
nullptr) {
1451 if (!
valve.enable_switch->state) {
1452 valve.enable_switch->turn_on();
1457 this->reset_cycle_states_();
1461 for (
auto &
valve : this->valve_) {
1462 valve.valve_cycle_complete =
false;
1467 this->next_req_.set_valve(requested_valve);
1468 this->next_req_.set_run_duration(requested_run_duration);
1475 if ((this->state_ ==
IDLE) || (this->state_ ==
ACTIVE)) {
1476 this->fsm_transition_();
1481 ESP_LOGVV(TAG,
"fsm_transition_ called; state is %s", this->state_as_str_(this->state_).c_str());
1482 switch (this->state_) {
1485 this->fsm_transition_from_shutdown_();
1490 this->fsm_transition_from_valve_run_();
1496 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1498 this->start_valve_(&this->active_req_);
1500 if (this->next_req_.has_request()) {
1502 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1510 this->active_req_.reset();
1511 this->manual_valve_.reset();
1512 this->all_valves_off_(
true);
1513 this->state_ =
IDLE;
1519 if (this->next_req_.has_request() && (this->state_ ==
IDLE)) {
1521 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1524 ESP_LOGVV(TAG,
"fsm_transition_ complete; new state is %s", this->state_as_str_(this->state_).c_str());
1528 this->load_next_valve_run_request_();
1530 if (this->next_req_.has_request()) {
1531 this->active_req_.set_valve(this->next_req_.valve());
1532 this->active_req_.set_request_from(this->next_req_.request_is_from());
1533 this->active_req_.set_run_duration(this->next_req_.run_duration());
1534 this->next_req_.reset();
1537 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1539 this->start_valve_(&this->active_req_);
1545 if (!this->active_req_.has_request()) {
1546 this->fsm_transition_to_shutdown_();
1551 if ((this->active_req_.request_is_from() ==
CYCLE) || (this->active_req_.request_is_from() ==
USER)) {
1552 this->mark_valve_cycle_complete_(this->active_req_.valve());
1555 ESP_LOGD(TAG,
"Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1561 this->load_next_valve_run_request_(this->active_req_.valve());
1563 if (this->next_req_.has_request()) {
1565 this->valve_pump_switch(this->active_req_.valve()) == this->valve_pump_switch(this->next_req_.valve());
1567 this->active_req_.set_valve(this->next_req_.valve());
1568 this->active_req_.set_request_from(this->next_req_.request_is_from());
1569 this->active_req_.set_run_duration(this->next_req_.run_duration());
1570 this->next_req_.reset();
1573 if (this->valve_overlap_ || !this->switching_delay_.has_value()) {
1575 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1577 this->start_valve_(&this->active_req_);
1579 this->set_timer_duration_(
1581 this->switching_delay_.value() * 2 +
1582 (this->pump_switch_off_during_valve_open_delay_ && same_pump ? this->stop_delay_ : 0));
1587 this->fsm_transition_to_shutdown_();
1594 this->start_delay_ + this->stop_delay_ + this->switching_delay_.value_or(0) + 1);
1637 if (this->timer_duration_(timer_index) > 0) {
1638 this->set_timeout(this->timer_[timer_index].
name, this->timer_duration_(timer_index),
1639 this->timer_cbf_(timer_index));
1640 this->timer_[timer_index].start_time =
millis();
1641 this->timer_[timer_index].active =
true;
1643 ESP_LOGVV(TAG,
"Timer %u started for %u sec", static_cast<size_t>(timer_index),
1644 this->timer_duration_(timer_index) / 1000);
1648 this->timer_[timer_index].active =
false;
1649 return this->cancel_timeout(this->timer_[timer_index].
name);
1655 this->timer_[timer_index].time = 1000 * time;
1661 return this->timer_[timer_index].func;
1666 ESP_LOGVV(TAG,
"Valve selection timer expired");
1667 if (this->manual_valve_.has_value()) {
1668 this->fsm_request_(this->manual_valve_.value());
1669 this->manual_valve_.reset();
1675 ESP_LOGVV(TAG,
"State machine timer expired");
1676 this->fsm_transition_();
1680 ESP_LOGCONFIG(TAG,
"Sprinkler Controller -- %s", this->name_.c_str());
1681 if (this->manual_selection_delay_.has_value()) {
1682 ESP_LOGCONFIG(TAG,
" Manual Selection Delay: %u seconds", this->manual_selection_delay_.value_or(0));
1684 if (this->repeat().has_value()) {
1685 ESP_LOGCONFIG(TAG,
" Repeat Cycles: %u times", this->repeat().value_or(0));
1687 if (this->start_delay_) {
1688 if (this->start_delay_is_valve_delay_) {
1689 ESP_LOGCONFIG(TAG,
" Pump Start Valve Delay: %u seconds", this->start_delay_);
1691 ESP_LOGCONFIG(TAG,
" Pump Start Pump Delay: %u seconds", this->start_delay_);
1694 if (this->stop_delay_) {
1695 if (this->stop_delay_is_valve_delay_) {
1696 ESP_LOGCONFIG(TAG,
" Pump Stop Valve Delay: %u seconds", this->stop_delay_);
1698 ESP_LOGCONFIG(TAG,
" Pump Stop Pump Delay: %u seconds", this->stop_delay_);
1701 if (this->switching_delay_.has_value()) {
1702 if (this->valve_overlap_) {
1703 ESP_LOGCONFIG(TAG,
" Valve Overlap: %u seconds", this->switching_delay_.value_or(0));
1705 ESP_LOGCONFIG(TAG,
" Valve Open Delay: %u seconds", this->switching_delay_.value_or(0));
1706 ESP_LOGCONFIG(TAG,
" Pump Switch Off During Valve Open Delay: %s",
1707 YESNO(this->pump_switch_off_during_valve_open_delay_));
1710 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1711 ESP_LOGCONFIG(TAG,
" Valve %u:", valve_number);
1712 ESP_LOGCONFIG(TAG,
" Name: %s", this->valve_name(valve_number));
1713 ESP_LOGCONFIG(TAG,
" Run Duration: %u seconds", this->valve_run_duration(valve_number));
1714 if (this->valve_[valve_number].valve_switch.pulse_duration()) {
1715 ESP_LOGCONFIG(TAG,
" Pulse Duration: %u milliseconds",
1716 this->valve_[valve_number].valve_switch.pulse_duration());
1719 if (!this->pump_.empty()) {
1720 ESP_LOGCONFIG(TAG,
" Total number of pumps: %u", this->pump_.size());
1722 if (!this->valve_.empty()) {
1723 ESP_LOGCONFIG(TAG,
" Total number of valves: %u", this->valve_.size());
Base class for all switches.
value_type const & value() const
void control(float value) override
void set_repeat(optional< uint32_t > repeat)
set the number of times to repeat a full cycle
uint32_t total_cycle_time_all_valves()
returns the amount of time in seconds required for all valves
void start_timer_(SprinklerTimerIndex timer_index)
Start/cancel/get status of valve timers.
bool timer_active_(SprinklerTimerIndex timer_index)
returns true if the specified timer is active/running
SprinklerControllerSwitch * controller_switch
float get_setup_priority() const override
void previous_valve()
advances to the previous valve (numerically)
void set_standby(bool standby)
if standby is true, controller will refuse to activate any valves
optional< std::function< optional< bool >)> > f_
uint32_t valve_run_duration(size_t valve_number)
returns valve_number's run duration in seconds
void next_valve()
advances to the next valve (numerically)
void set_run_duration(uint32_t run_duration)
void valve_selection_callback_()
callback functions for timers
void queue_valve(optional< size_t > valve_number, optional< uint32_t > run_duration)
adds a valve into the queue.
void set_divider(optional< uint32_t > divider)
sets the multiplier value to '1 / divider' and sets repeat value to divider
optional< size_t > paused_valve()
returns the number of the valve that is paused, if any. check with 'has_value()'
bool auto_advance()
returns true if auto_advance is enabled
SprinklerValveRunRequestOrigin request_is_from()
void set_valve(size_t valve_number)
void fsm_transition_to_shutdown_()
starts up the system from IDLE state
optional< size_t > queued_valve()
returns the number of the next valve in the queue, if any. check with 'has_value()' ...
void set_multiplier(optional< float > multiplier)
value multiplied by configured run times – used to extend or shorten the cycle
SprinklerSwitch * valve_switch(size_t valve_number)
returns a pointer to a valve's switch object
SprinklerValveOperator * valve_operator()
void fsm_request_(size_t requested_valve, uint32_t requested_run_duration=0)
make a request of the state machine
void add_controller(Sprinkler *other_controller)
add another controller to the controller so it can check if pumps/main valves are in use ...
void reset_resume()
resets resume state
bool is_a_valid_valve(size_t valve_number)
returns true if valve number is valid
void set_next_prev_ignore_disabled_valves(bool ignore_disabled)
enable/disable skipping of disabled valves by the next and previous actions
void set_state_lambda(std::function< optional< bool >()> &&f)
switch_::Switch * on_switch_
void shutdown(bool clear_queue=false)
turns off all valves, effectively shutting down the system.
void set_pump_start_delay(uint32_t start_delay)
set how long the pump should start after the valve (when the pump is starting)
optional< size_t > pump_switch_index
void set_controller_queue_enable_switch(SprinklerControllerSwitch *queue_enable_switch)
SprinklerSwitch * valve_pump_switch_by_pump_index(size_t pump_index)
returns a pointer to a valve's pump switch object
void set_valve_run_duration(optional< size_t > valve_number, optional< uint32_t > run_duration)
set how long the valve should remain on/open. run_duration is time in seconds
std::unique_ptr< Automation<> > valve_turn_off_automation
optional< size_t > valve_as_opt()
void configure_valve_run_duration_number(size_t valve_number, SprinklerControllerNumber *run_duration_number)
configure a valve's run duration number component
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 ...
void resume_or_start_full_cycle()
if a cycle was suspended using pause(), resumes it. otherwise calls start_full_cycle() ...
uint32_t IRAM_ATTR HOT millis()
SprinklerSwitch * valve_pump_switch(size_t valve_number)
returns a pointer to a valve's pump switch object
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
void set_controller_standby_switch(SprinklerControllerSwitch *standby_switch)
void set_valve_overlap(uint32_t valve_overlap)
set how long the controller should wait after opening a valve before closing the previous valve ...
SprinklerValveRunRequestOrigin
uint32_t total_cycle_time_enabled_valves()
returns the amount of time in seconds required for all enabled valves
void set_request_from(SprinklerValveRunRequestOrigin origin)
uint32_t valve_run_duration_adjusted(size_t valve_number)
returns valve_number's run duration (in seconds) adjusted by multiplier_
void set_controller_reverse_switch(SprinklerControllerSwitch *reverse_switch)
void set_controller_multiplier_number(SprinklerControllerNumber *multiplier_number)
configure important controller number components
uint32_t pulse_duration()
switch_::Switch * on_switch()
const nullopt_t nullopt((nullopt_t::init()))
Trigger * get_turn_off_trigger() const
void fsm_kick_()
kicks the state machine to advance, starting it if it is not already active
void set_pump_state(SprinklerSwitch *pump_switch, bool state)
switches on/off a pump "safely" by checking that the new state will not conflict with another control...
optional< bool > get_initial_state_with_restore_mode()
Returns the initial state of the switch, after applying restore mode rules.
std::string req_as_str_(SprinklerValveRunRequestOrigin origin)
return the specified SprinklerValveRunRequestOrigin as a string
Trigger * get_turn_on_trigger() const
void sm_timer_callback_()
uint32_t timer_duration_(SprinklerTimerIndex timer_index)
returns time in milliseconds (ms)
void resume()
resumes a cycle that was suspended using pause()
void set_controller_repeat_number(SprinklerControllerNumber *repeat_number)
bool has_valve_operator()
ESPPreferences * global_preferences
void pause()
same as shutdown(), but also stores active_valve() and time_remaining() allowing resume() to continue...
void start_full_cycle()
starts a full cycle of all enabled valves and enables auto_advance.
void write_state(bool state) override
SprinklerValveRunRequestOrigin origin_
void turn_on()
Turn this switch on.
bool reverse()
returns true if reverse is enabled
optional< size_t > next_valve_number_in_cycle_(optional< size_t > first_valve=nullopt)
returns the number of the next valve that should be activated in a full cycle.
void start_valve_(SprinklerValveRunRequest *req)
loads an available SprinklerValveOperator (valve_op_) based on req and starts it (switches it on)...
void set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay)
void set_timer_duration_(SprinklerTimerIndex timer_index, uint32_t time)
time is converted to milliseconds (ms) for set_timeout()
void dump_config() override
void set_auto_advance(bool auto_advance)
if auto_advance is true, controller will iterate through all enabled valves
SprinklerControllerSwitch * control_switch(size_t valve_number)
returns a pointer to a valve's control switch object
SprinklerValveOperator * valve_op_
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
uint32_t total_queue_time()
returns the amount of time in seconds required for all valves in the queue
void set_valve_open_delay(uint32_t valve_open_delay)
set how long the controller should wait to open/switch on the valve after it becomes active ...
void set_controller_auto_adv_switch(SprinklerControllerSwitch *auto_adv_switch)
SprinklerControllerSwitch * enable_switch(size_t valve_number)
returns a pointer to a valve's enable switch object
void set_controller(Sprinkler *controller)
void set_manual_selection_delay(uint32_t manual_selection_delay)
set how long the controller should wait to activate a valve after next_valve() or previous_valve() is...
void set_queue_enable(bool queue_enable)
if queue_enable is true, controller will iterate through valves in the queue
void set_reverse(bool reverse)
if reverse is true, controller will iterate through all enabled valves in reverse (descending) order ...
switch_::Switch * off_switch_
bool any_controller_is_active()
returns true if this or any sprinkler controller this controller knows about is active ...
void fsm_transition_()
advance controller state, advancing to target_valve if provided
optional< uint32_t > time_remaining_active_valve()
returns the amount of time remaining in seconds for the active valve, if any
bool queue_enabled()
returns true if the queue is enabled to run
void set_valve_operator(SprinklerValveOperator *valve_op)
bool valve_is_enabled_(size_t valve_number)
returns true if valve number is enabled
void set_pump_switch_off_during_valve_open_delay(bool pump_switch_off_during_valve_open_delay)
if pump_switch_off_during_valve_open_delay is true, the controller will switch off the pump during th...
void set_controller_main_switch(SprinklerControllerSwitch *controller_switch)
configure important controller switches
void set_valve(SprinklerValve *valve)
uint32_t time_remaining()
void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw=nullptr)
add a valve to the controller
void reset_cycle_states_()
resets the cycle state for all valves
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
bool standby()
returns true if standby is enabled
void start_from_queue()
starts the controller from the first valve in the queue and disables auto_advance.
void prep_full_cycle_()
prepares for a full cycle by verifying auto-advance is on as well as one or more valve enable switche...
SprinklerSwitch valve_switch
void configure_valve_pump_switch(size_t valve_number, switch_::Switch *pump_switch)
configure a valve's associated pump switch object
optional< uint32_t > time_remaining_current_operation()
returns the amount of time remaining in seconds for all valves remaining, including the active valve...
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
SprinklerValveRunRequest()
optional< uint32_t > repeat_count()
if a cycle is active, returns the number of times the controller has repeated the cycle...
void clear_queued_valves()
clears/removes all valves from the queue
std::unique_ptr< StartSingleValveAction<> > valve_resumeorstart_action
void sync_valve_state(bool latch_state)
bool valve_cycle_complete_(size_t valve_number)
returns true if valve's cycle is flagged as complete
optional< SprinklerValveRunRequestOrigin > active_valve_request_is_from()
returns what invoked the valve that is currently active, if any. check with 'has_value()' ...
bool any_valve_is_enabled_()
returns true if any valve is enabled
void configure_valve_switch(size_t valve_number, switch_::Switch *valve_switch, uint32_t run_duration)
configure a valve's switch object and run duration. run_duration is time in seconds.
bool cancel_timer_(SprinklerTimerIndex timer_index)
Trigger * turn_on_trigger_
Implementation of SPI Controller mode.
void fsm_transition_from_shutdown_()
starts up the system from IDLE state
std::string state_as_str_(SprinklerState state)
return the specified SprinklerState state as a string
const std::string min_str
std::function< void()> timer_cbf_(SprinklerTimerIndex timer_index)
bool pump_in_use(SprinklerSwitch *pump_switch)
returns true if the pump the pointer points to is in use
void set_valve_start_delay(uint32_t start_delay)
set how long the valve should start after the pump (when the pump is stopping)
const char * valve_name(size_t valve_number)
returns a pointer to a valve's name string object; returns nullptr if valve_number is invalid ...
void dump_config() override
void configure_valve_pump_switch_pulsed(size_t valve_number, switch_::Switch *pump_switch_off, switch_::Switch *pump_switch_on, uint32_t pulse_duration)
void set_valve_stop_delay(uint32_t stop_delay)
set how long the valve should stop after the pump (when the pump is stopping)
size_t number_of_valves()
returns the number of valves the controller is configured with
std::unique_ptr< Automation<> > valve_turn_on_automation
SprinklerControllerSwitch()
void dump_config() override
void configure_valve_switch_pulsed(size_t valve_number, switch_::Switch *valve_switch_off, switch_::Switch *valve_switch_on, uint32_t pulse_duration, uint32_t run_duration)
void load_next_valve_run_request_(optional< size_t > first_valve=nullopt)
loads next_req_ with the next valve that should be activated, including its run duration.
void mark_valve_cycle_complete_(size_t valve_number)
marks a valve's cycle as complete
void set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay)
void publish_state(bool state)
Publish a state to the front-end from the back-end.
void set_run_duration(uint32_t run_duration)
SprinklerControllerSwitch * enable_switch
void start_single_valve(optional< size_t > valve_number, optional< uint32_t > run_duration=nullopt)
activates a single valve and disables auto_advance.
bool state
The current reported state of the binary sensor.
value_type value_or(U const &v) const
optional< size_t > active_valve()
returns the number of the valve that is currently active, if any. check with 'has_value()' ...
void stop_action()
Stop any action connected to this trigger.
void fsm_transition_from_valve_run_()
transitions from ACTIVE state to ACTIVE (as in, next valve) or to a SHUTDOWN or IDLE state ...
bool start_delay_is_valve_delay_
float multiplier()
returns the current value of the multiplier
Trigger * turn_off_trigger_
switch_::Switch * off_switch()
bool stop_delay_is_valve_delay_
optional< uint32_t > repeat()
returns the number of times the controller is set to repeat cycles, if at all. check with 'has_value(...
SprinklerSwitch * pump_switch()
void set_pump_stop_delay(uint32_t stop_delay)
set how long the pump should stop after the valve (when the pump is starting)
void turn_off()
Turn this switch off.
std::unique_ptr< ShutdownAction<> > valve_shutdown_action
optional< size_t > manual_valve()
returns the number of the valve that is manually selected, if any.
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 ...
void all_valves_off_(bool include_pump=false)
turns off/closes all valves, including pump if include_pump is true