1 #include "automation.h" 13 static const char *
const TAG =
"sprinkler";
73 this->
state_ = latch_state;
81 if (!this->restore_value_) {
82 value = this->initial_value_;
85 if (!this->pref_.load(&value)) {
86 if (!std::isnan(this->initial_value_)) {
87 value = this->initial_value_;
89 value = this->traits.get_min_value();
93 this->publish_state(value);
97 this->set_trigger_->trigger(value);
99 this->publish_state(value);
101 if (this->restore_value_)
102 this->pref_.save(&value);
108 : turn_on_trigger_(new
Trigger<>()), turn_off_trigger_(new
Trigger<>()) {}
111 if (!this->
f_.has_value())
113 auto s = (*this->
f_)();
148 : controller_(controller), valve_(valve) {}
180 if (controller !=
nullptr) {
186 if (valve !=
nullptr) {
269 if (completed_millis >
millis()) {
270 return (completed_millis -
millis()) / 1000;
316 if (this->
valve_ ==
nullptr) {
325 if (this->
valve_ ==
nullptr) {
348 : valve_number_(valve_number),
run_duration_(run_duration), valve_op_(valve_op) {}
365 if (valve_op !=
nullptr) {
404 for (
auto &p : this->pump_) {
407 for (
auto &v : this->valve_) {
408 v.valve_switch.loop();
413 if (this->prev_req_.has_request() && this->prev_req_.valve_operator()->state() ==
IDLE) {
414 this->prev_req_.reset();
419 auto new_valve_number = this->number_of_valves();
420 this->valve_.resize(new_valve_number + 1);
425 if (this->valve_pump_switch(new_valve_number) !=
nullptr) {
426 return this->valve_switch(new_valve_number)->state() && this->valve_pump_switch(new_valve_number)->state();
428 return this->valve_switch(new_valve_number)->state();
441 if (enable_sw !=
nullptr) {
449 this->controller_sw_ = controller_switch;
451 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
452 if (this->valve_[valve_number].controller_switch->
state) {
456 return this->active_req_.has_request();
459 this->sprinkler_turn_off_automation_ = make_unique<Automation<>>(controller_switch->
get_turn_off_trigger());
460 this->sprinkler_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(
this);
461 this->sprinkler_turn_off_automation_->add_actions({sprinkler_shutdown_action_.get()});
463 this->sprinkler_turn_on_automation_ = make_unique<Automation<>>(controller_switch->
get_turn_on_trigger());
464 this->sprinkler_resumeorstart_action_ = make_unique<sprinkler::ResumeOrStartAction<>>(
this);
465 this->sprinkler_turn_on_automation_->add_actions({sprinkler_resumeorstart_action_.get()});
469 this->auto_adv_sw_ = auto_adv_switch;
473 this->queue_enable_sw_ = queue_enable_switch;
477 this->reverse_sw_ = reverse_switch;
481 this->standby_sw_ = standby_switch;
483 this->sprinkler_standby_turn_on_automation_ = make_unique<Automation<>>(standby_switch->
get_turn_on_trigger());
484 this->sprinkler_standby_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(
this);
485 this->sprinkler_standby_turn_on_automation_->add_actions({sprinkler_standby_shutdown_action_.get()});
489 this->multiplier_number_ = multiplier_number;
493 this->repeat_number_ = repeat_number;
497 if (this->is_a_valid_valve(valve_number)) {
498 this->valve_[valve_number].valve_switch.set_on_switch(valve_switch);
506 if (this->is_a_valid_valve(valve_number)) {
507 this->valve_[valve_number].valve_switch.set_off_switch(valve_switch_off);
508 this->valve_[valve_number].valve_switch.set_on_switch(valve_switch_on);
509 this->valve_[valve_number].valve_switch.set_pulse_duration(pulse_duration);
515 if (this->is_a_valid_valve(valve_number)) {
516 for (
size_t i = 0; i < this->pump_.size(); i++) {
517 if (this->pump_[i].on_switch() == pump_switch) {
518 this->valve_[valve_number].pump_switch_index = i;
522 this->pump_.resize(this->pump_.size() + 1);
523 this->pump_.back().set_on_switch(pump_switch);
524 this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1;
530 if (this->is_a_valid_valve(valve_number)) {
531 for (
size_t i = 0; i < this->pump_.size(); i++) {
532 if ((this->pump_[i].off_switch() == pump_switch_off) &&
533 (this->pump_[i].on_switch() == pump_switch_on)) {
534 this->valve_[valve_number].pump_switch_index = i;
538 this->pump_.resize(this->pump_.size() + 1);
539 this->pump_.back().set_off_switch(pump_switch_off);
540 this->pump_.back().set_on_switch(pump_switch_on);
541 this->pump_.back().set_pulse_duration(pulse_duration);
542 this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1;
548 if (this->is_a_valid_valve(valve_number)) {
549 this->valve_[valve_number].run_duration_number = run_duration_number;
557 if (divider.
value() > 0) {
558 this->set_multiplier(1.0 / divider.
value());
559 this->set_repeat(divider.
value() - 1);
560 }
else if (divider.
value() == 0) {
561 this->set_multiplier(1.0);
570 this->multiplier_ = multiplier.
value();
571 if (this->multiplier_number_ ==
nullptr) {
574 if (this->multiplier_number_->state == multiplier.
value()) {
577 auto call = this->multiplier_number_->make_call();
578 call.set_value(multiplier.
value());
583 this->next_prev_ignore_disabled_ = ignore_disabled;
587 this->start_delay_is_valve_delay_ =
false;
588 this->start_delay_ = start_delay;
592 this->stop_delay_is_valve_delay_ =
false;
593 this->stop_delay_ = stop_delay;
597 this->start_delay_is_valve_delay_ =
true;
598 this->start_delay_ = start_delay;
602 this->stop_delay_is_valve_delay_ =
true;
603 this->stop_delay_ = stop_delay;
607 this->pump_switch_off_during_valve_open_delay_ = pump_switch_off_during_valve_open_delay;
611 if (valve_open_delay > 0) {
612 this->valve_overlap_ =
false;
613 this->switching_delay_ = valve_open_delay;
615 this->switching_delay_.reset();
620 if (valve_overlap > 0) {
621 this->valve_overlap_ =
true;
622 this->switching_delay_ = valve_overlap;
624 this->switching_delay_.reset();
626 this->pump_switch_off_during_valve_open_delay_ =
false;
630 if (manual_selection_delay > 0) {
631 this->manual_selection_delay_ = manual_selection_delay;
633 this->manual_selection_delay_.reset();
641 if (!this->is_a_valid_valve(valve_number.
value())) {
644 this->valve_[valve_number.
value()].run_duration = run_duration.
value();
645 if (this->valve_[valve_number.
value()].run_duration_number ==
nullptr) {
648 if (this->valve_[valve_number.
value()].run_duration_number->state == run_duration.
value()) {
651 auto call = this->valve_[valve_number.
value()].run_duration_number->make_call();
652 if (this->valve_[valve_number.
value()].run_duration_number->traits.get_unit_of_measurement() ==
MIN_STR) {
653 call.set_value(run_duration.
value() / 60.0);
655 call.set_value(run_duration.
value());
661 if (this->auto_adv_sw_ ==
nullptr) {
664 if (this->auto_adv_sw_->state == auto_advance) {
668 this->auto_adv_sw_->turn_on();
670 this->auto_adv_sw_->turn_off();
675 this->target_repeats_ = repeat;
676 if (this->repeat_number_ ==
nullptr) {
679 if (this->repeat_number_->state == repeat.
value()) {
682 auto call = this->repeat_number_->make_call();
688 if (this->queue_enable_sw_ ==
nullptr) {
691 if (this->queue_enable_sw_->state == queue_enable) {
695 this->queue_enable_sw_->turn_on();
697 this->queue_enable_sw_->turn_off();
702 if (this->reverse_sw_ ==
nullptr) {
705 if (this->reverse_sw_->state == reverse) {
709 this->reverse_sw_->turn_on();
711 this->reverse_sw_->turn_off();
716 if (this->standby_sw_ ==
nullptr) {
719 if (this->standby_sw_->state == standby) {
723 this->standby_sw_->turn_on();
725 this->standby_sw_->turn_off();
730 if (!this->is_a_valid_valve(valve_number)) {
733 if (this->valve_[valve_number].run_duration_number !=
nullptr) {
734 if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() ==
MIN_STR) {
735 return static_cast<uint32_t
>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
737 return static_cast<uint32_t
>(roundf(this->valve_[valve_number].run_duration_number->state));
740 return this->valve_[valve_number].run_duration;
746 if (this->is_a_valid_valve(valve_number)) {
747 run_duration = this->valve_run_duration(valve_number);
749 run_duration =
static_cast<uint32_t
>(roundf(run_duration * this->multiplier()));
751 if ((run_duration < this->start_delay_) || (run_duration < this->stop_delay_) ||
752 (run_duration < this->switching_delay_.value_or(0) * 2)) {
753 return std::max(this->switching_delay_.value_or(0) * 2, std::max(this->start_delay_, this->stop_delay_));
759 if (this->auto_adv_sw_ !=
nullptr) {
760 return this->auto_adv_sw_->state;
766 if (this->multiplier_number_ !=
nullptr) {
767 return this->multiplier_number_->state;
769 return this->multiplier_;
773 if (this->repeat_number_ !=
nullptr) {
774 return static_cast<uint32_t
>(roundf(this->repeat_number_->state));
776 return this->target_repeats_;
781 if (this->active_req_.has_request() && this->auto_advance()) {
782 return this->repeat_count_;
788 if (this->queue_enable_sw_ !=
nullptr) {
789 return this->queue_enable_sw_->state;
795 if (this->reverse_sw_ !=
nullptr) {
796 return this->reverse_sw_->state;
802 if (this->standby_sw_ !=
nullptr) {
803 return this->standby_sw_->state;
809 if (this->standby()) {
810 ESP_LOGD(TAG,
"start_from_queue called but standby is enabled; no action taken");
813 if (this->multiplier() == 0) {
814 ESP_LOGD(TAG,
"start_from_queue called but multiplier is set to zero; no action taken");
817 if (this->queued_valves_.empty()) {
820 if (this->queue_enabled() && this->active_valve().has_value()) {
824 this->set_auto_advance(
false);
825 this->set_queue_enable(
true);
827 this->reset_cycle_states_();
828 this->repeat_count_ = 0;
833 if (this->standby()) {
834 ESP_LOGD(TAG,
"start_full_cycle called but standby is enabled; no action taken");
837 if (this->multiplier() == 0) {
838 ESP_LOGD(TAG,
"start_full_cycle called but multiplier is set to zero; no action taken");
841 if (this->auto_advance() && this->active_valve().has_value()) {
845 this->set_queue_enable(
false);
847 this->prep_full_cycle_();
848 this->repeat_count_ = 0;
850 if (!this->active_req_.has_request()) {
856 if (this->standby()) {
857 ESP_LOGD(TAG,
"start_single_valve called but standby is enabled; no action taken");
860 if (this->multiplier() == 0) {
861 ESP_LOGD(TAG,
"start_single_valve called but multiplier is set to zero; no action taken");
864 if (!valve_number.
has_value() || (valve_number == this->active_valve())) {
868 this->set_auto_advance(
false);
869 this->set_queue_enable(
false);
871 this->reset_cycle_states_();
872 this->repeat_count_ = 0;
873 this->fsm_request_(valve_number.
value(), run_duration.
value_or(0));
878 if (this->is_a_valid_valve(valve_number.
value()) && (this->queued_valves_.size() < this->max_queue_size_)) {
880 this->queued_valves_.insert(this->queued_valves_.begin(), item);
881 ESP_LOGD(TAG,
"Valve %zu placed into queue with run duration of %" PRIu32
" seconds", valve_number.
value_or(0),
888 this->queued_valves_.clear();
889 ESP_LOGD(TAG,
"Queue cleared");
893 if (this->state_ ==
IDLE) {
894 this->reset_cycle_states_();
897 this->manual_valve_ = this->next_valve_number_(
898 this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(this->number_of_valves() - 1)),
899 !this->next_prev_ignore_disabled_,
true);
901 if (!this->manual_valve_.has_value()) {
902 ESP_LOGD(TAG,
"next_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows only " 903 "enabled valves and no valves are enabled?");
907 if (this->manual_selection_delay_.has_value()) {
911 this->fsm_request_(this->manual_valve_.value());
916 if (this->state_ ==
IDLE) {
917 this->reset_cycle_states_();
920 this->manual_valve_ =
921 this->previous_valve_number_(this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(0)),
922 !this->next_prev_ignore_disabled_,
true);
924 if (!this->manual_valve_.has_value()) {
925 ESP_LOGD(TAG,
"previous_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows " 926 "only enabled valves and no valves are enabled?");
930 if (this->manual_selection_delay_.has_value()) {
934 this->fsm_request_(this->manual_valve_.value());
940 this->active_req_.reset();
941 this->manual_valve_.reset();
942 this->next_req_.reset();
946 this->fsm_transition_to_shutdown_();
948 this->clear_queued_valves();
949 this->repeat_count_ = 0;
954 if (this->paused_valve_.has_value() || !this->active_req_.has_request()) {
957 this->paused_valve_ = this->active_valve();
958 this->resume_duration_ = this->time_remaining_active_valve();
959 this->shutdown(
false);
960 ESP_LOGD(TAG,
"Paused valve %zu with %" PRIu32
" seconds remaining", this->paused_valve_.value_or(0),
961 this->resume_duration_.value_or(0));
965 if (this->standby()) {
966 ESP_LOGD(TAG,
"resume called but standby is enabled; no action taken");
970 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
972 if (!this->valve_cycle_complete_(this->paused_valve_.value())) {
973 ESP_LOGD(TAG,
"Resuming valve %zu with %" PRIu32
" seconds remaining", this->paused_valve_.value_or(0),
974 this->resume_duration_.value_or(0));
975 this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
977 this->reset_resume();
979 ESP_LOGD(TAG,
"No valve to resume!");
984 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
987 this->start_full_cycle();
992 this->paused_valve_.reset();
993 this->resume_duration_.reset();
997 if (this->is_a_valid_valve(valve_number)) {
998 return this->valve_[valve_number].controller_switch->get_name().c_str();
1004 if (this->active_req_.has_request()) {
1005 return this->active_req_.request_is_from();
1011 if (!this->valve_overlap_ && this->prev_req_.has_request() &&
1012 (this->prev_req_.valve_operator()->state() ==
STARTING || this->prev_req_.valve_operator()->state() ==
ACTIVE)) {
1013 return this->prev_req_.valve_as_opt();
1015 return this->active_req_.valve_as_opt();
1021 if (!this->queued_valves_.empty()) {
1022 return this->queued_valves_.back().valve_number;
1032 return ((valve_number >= 0) && (valve_number < this->number_of_valves()));
1036 if (pump_switch ==
nullptr) {
1046 if ((vo.state() !=
BYPASS) && (vo.pump_switch() !=
nullptr)) {
1048 if ((vo.pump_switch()->off_switch() == pump_switch->
off_switch()) &&
1049 (vo.pump_switch()->on_switch() == pump_switch->
on_switch())) {
1052 if ((vo.state() ==
ACTIVE) ||
1053 ((vo.state() ==
STARTING) && this->start_delay_ && this->start_delay_is_valve_delay_) ||
1054 ((vo.state() ==
STOPPING) && this->stop_delay_ && this->stop_delay_is_valve_delay_)) {
1060 if (!this->valve_overlap_ && !this->pump_switch_off_during_valve_open_delay_ && this->switching_delay_.has_value() &&
1061 this->active_req_.has_request() && (this->state_ !=
STOPPING)) {
1064 return (pump_switch->
off_switch() == this->valve_pump_switch(this->active_req_.valve())->off_switch()) &&
1065 (pump_switch->
on_switch() == this->valve_pump_switch(this->active_req_.valve())->on_switch());
1071 if (pump_switch ==
nullptr) {
1075 bool hold_pump_on =
false;
1077 for (
auto &controller : this->other_controllers_) {
1078 if (controller !=
this) {
1079 if (controller->pump_in_use(pump_switch)) {
1080 hold_pump_on =
true;
1090 ESP_LOGD(TAG,
"Leaving pump on because another controller instance is using it");
1095 }
else if (!hold_pump_on && !this->pump_in_use(pump_switch)) {
1097 }
else if (hold_pump_on) {
1103 uint32_t total_time_remaining = 0;
1106 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1109 if (this->valve_overlap_) {
1110 total_time_remaining -= this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1112 total_time_remaining += this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1115 return total_time_remaining;
1119 uint32_t total_time_remaining = 0;
1120 uint32_t valve_count = 0;
1123 if (this->valve_is_enabled_(
valve)) {
1124 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1130 if (this->valve_overlap_) {
1131 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1133 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1137 return total_time_remaining;
1141 uint32_t total_time_remaining = 0;
1142 uint32_t enabled_valve_count = 0;
1143 uint32_t incomplete_valve_count = 0;
1146 if (this->valve_is_enabled_(
valve)) {
1147 enabled_valve_count++;
1148 if (!this->valve_cycle_complete_(
valve)) {
1149 if (!this->active_valve().has_value() || (
valve != this->active_valve().value())) {
1150 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1151 incomplete_valve_count++;
1154 if (this->active_req_.valve_operator() ==
nullptr) {
1155 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1156 incomplete_valve_count++;
1163 if (incomplete_valve_count >= enabled_valve_count) {
1164 incomplete_valve_count--;
1166 if (incomplete_valve_count) {
1167 if (this->valve_overlap_) {
1168 total_time_remaining -= this->switching_delay_.value_or(0) * incomplete_valve_count;
1170 total_time_remaining += this->switching_delay_.value_or(0) * incomplete_valve_count;
1174 return total_time_remaining;
1178 uint32_t total_time_remaining = 0;
1179 uint32_t valve_count = 0;
1181 for (
auto &
valve : this->queued_valves_) {
1182 if (
valve.run_duration) {
1183 total_time_remaining +=
valve.run_duration;
1185 total_time_remaining += this->valve_run_duration_adjusted(
valve.valve_number);
1191 if (this->valve_overlap_) {
1192 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1194 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1198 return total_time_remaining;
1202 if (this->active_req_.has_request()) {
1203 if (this->active_req_.valve_operator() !=
nullptr) {
1204 return this->active_req_.valve_operator()->time_remaining();
1207 if (this->prev_req_.has_request()) {
1208 if (this->prev_req_.valve_operator() !=
nullptr) {
1209 return this->prev_req_.valve_operator()->time_remaining();
1216 if (!this->time_remaining_active_valve().has_value() && this->state_ ==
IDLE) {
1220 auto total_time_remaining = this->time_remaining_active_valve().value_or(0);
1221 if (this->auto_advance()) {
1222 total_time_remaining += this->total_cycle_time_enabled_incomplete_valves();
1223 if (this->repeat().value_or(0) > 0) {
1224 total_time_remaining +=
1225 (this->total_cycle_time_enabled_valves() * (this->repeat().value_or(0) - this->repeat_count().value_or(0)));
1229 if (this->queue_enabled()) {
1230 total_time_remaining += this->total_queue_time();
1232 return total_time_remaining;
1236 if (this->state_ !=
IDLE) {
1240 for (
auto &controller : this->other_controllers_) {
1241 if (controller !=
this) {
1242 if (controller->controller_state() !=
IDLE) {
1251 if (this->is_a_valid_valve(valve_number)) {
1252 return this->valve_[valve_number].controller_switch;
1258 if (this->is_a_valid_valve(valve_number)) {
1259 return this->valve_[valve_number].enable_switch;
1265 if (this->is_a_valid_valve(valve_number)) {
1266 return &this->valve_[valve_number].valve_switch;
1272 if (this->is_a_valid_valve(valve_number) && this->valve_[valve_number].pump_switch_index.has_value()) {
1273 return &this->pump_[this->valve_[valve_number].pump_switch_index.value()];
1279 if (pump_index < this->pump_.size()) {
1280 return &this->pump_[pump_index];
1286 if (this->is_a_valid_valve(valve_number)) {
1287 if (this->valve_[valve_number].enable_switch !=
nullptr) {
1288 return this->valve_[valve_number].enable_switch->state;
1297 if (this->is_a_valid_valve(valve_number)) {
1298 ESP_LOGD(TAG,
"Marking valve %u complete", valve_number);
1299 this->valve_[valve_number].valve_cycle_complete =
true;
1304 if (this->is_a_valid_valve(valve_number)) {
1305 return this->valve_[valve_number].valve_cycle_complete;
1311 const bool include_complete) {
1313 size_t start = first_valve.
has_value() ? 1 : 0;
1315 if (!this->is_a_valid_valve(
valve)) {
1319 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1320 auto valve_of_interest =
valve + offset;
1321 if (!this->is_a_valid_valve(valve_of_interest)) {
1322 valve_of_interest -= this->number_of_valves();
1325 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1326 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1327 return valve_of_interest;
1334 const bool include_complete) {
1335 auto valve = first_valve.
value_or(this->number_of_valves() - 1);
1336 size_t start = first_valve.
has_value() ? 1 : 0;
1338 if (!this->is_a_valid_valve(
valve)) {
1339 valve = this->number_of_valves() - 1;
1342 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1343 auto valve_of_interest =
valve - offset;
1344 if (!this->is_a_valid_valve(valve_of_interest)) {
1345 valve_of_interest += this->number_of_valves();
1348 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1349 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1350 return valve_of_interest;
1357 if (this->reverse()) {
1358 return this->previous_valve_number_(first_valve,
false,
false);
1360 return this->next_valve_number_(first_valve,
false,
false);
1364 if (this->active_req_.has_request()) {
1365 this->prev_req_ = this->active_req_;
1367 this->prev_req_.reset();
1370 if (this->next_req_.has_request()) {
1371 if (!this->next_req_.run_duration()) {
1372 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->next_req_.valve()));
1375 }
else if (this->queue_enabled() && !this->queued_valves_.empty()) {
1376 this->next_req_.set_valve(this->queued_valves_.back().valve_number);
1377 this->next_req_.set_request_from(
QUEUE);
1378 if (this->queued_valves_.back().run_duration) {
1379 this->next_req_.set_run_duration(this->queued_valves_.back().run_duration);
1380 this->queued_valves_.pop_back();
1381 }
else if (this->multiplier()) {
1382 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->queued_valves_.back().valve_number));
1383 this->queued_valves_.pop_back();
1385 this->next_req_.reset();
1387 }
else if (this->auto_advance() && this->multiplier()) {
1388 if (this->next_valve_number_in_cycle_(first_valve).has_value()) {
1390 this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1391 this->next_req_.set_request_from(
CYCLE);
1392 this->next_req_.set_run_duration(
1393 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1394 }
else if ((this->repeat_count_++ < this->repeat().value_or(0))) {
1395 ESP_LOGD(TAG,
"Repeating - starting cycle %" PRIu32
" of %" PRIu32, this->repeat_count_ + 1,
1396 this->repeat().value_or(0) + 1);
1398 this->prep_full_cycle_();
1399 if (this->next_valve_number_in_cycle_().has_value()) {
1400 this->next_req_.set_valve(this->next_valve_number_in_cycle_().value_or(0));
1401 this->next_req_.set_request_from(
CYCLE);
1402 this->next_req_.set_run_duration(
1403 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_().value_or(0)));
1410 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1411 if (this->valve_is_enabled_(valve_number))
1421 if (!this->is_a_valid_valve(req->
valve())) {
1425 if (vo.state() ==
IDLE) {
1427 ESP_LOGD(TAG,
"%s is starting valve %zu for %" PRIu32
" seconds, cycle %" PRIu32
" of %" PRIu32,
1429 this->repeat().value_or(0) + 1);
1431 vo.set_controller(
this);
1432 vo.set_valve(&this->valve_[req->
valve()]);
1434 vo.set_start_delay(this->start_delay_, this->start_delay_is_valve_delay_);
1435 vo.set_stop_delay(this->stop_delay_, this->stop_delay_is_valve_delay_);
1443 for (
size_t valve_index = 0; valve_index < this->number_of_valves(); valve_index++) {
1444 if (this->valve_[valve_index].valve_switch.state()) {
1445 this->valve_[valve_index].valve_switch.turn_off();
1448 this->set_pump_state(this->valve_pump_switch(valve_index),
false);
1451 ESP_LOGD(TAG,
"All valves stopped%s", include_pump ?
", including pumps" :
"");
1455 this->set_auto_advance(
true);
1457 if (!this->any_valve_is_enabled_()) {
1458 for (
auto &
valve : this->valve_) {
1459 if (
valve.enable_switch !=
nullptr) {
1460 if (!
valve.enable_switch->state) {
1461 valve.enable_switch->turn_on();
1466 this->reset_cycle_states_();
1470 for (
auto &
valve : this->valve_) {
1471 valve.valve_cycle_complete =
false;
1476 this->next_req_.set_valve(requested_valve);
1477 this->next_req_.set_run_duration(requested_run_duration);
1484 if ((this->state_ ==
IDLE) || (this->state_ ==
ACTIVE)) {
1485 this->fsm_transition_();
1490 ESP_LOGVV(TAG,
"fsm_transition_ called; state is %s", this->state_as_str_(this->state_).c_str());
1491 switch (this->state_) {
1494 this->fsm_transition_from_shutdown_();
1499 this->fsm_transition_from_valve_run_();
1505 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1507 this->start_valve_(&this->active_req_);
1509 if (this->next_req_.has_request()) {
1511 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1519 this->active_req_.reset();
1520 this->manual_valve_.reset();
1521 this->all_valves_off_(
true);
1522 this->state_ =
IDLE;
1528 if (this->next_req_.has_request() && (this->state_ ==
IDLE)) {
1530 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1533 ESP_LOGVV(TAG,
"fsm_transition_ complete; new state is %s", this->state_as_str_(this->state_).c_str());
1537 this->load_next_valve_run_request_();
1539 if (this->next_req_.has_request()) {
1540 this->active_req_.set_valve(this->next_req_.valve());
1541 this->active_req_.set_request_from(this->next_req_.request_is_from());
1542 this->active_req_.set_run_duration(this->next_req_.run_duration());
1543 this->next_req_.reset();
1546 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1548 this->start_valve_(&this->active_req_);
1554 if (!this->active_req_.has_request()) {
1555 this->fsm_transition_to_shutdown_();
1560 if ((this->active_req_.request_is_from() ==
CYCLE) || (this->active_req_.request_is_from() ==
USER)) {
1561 this->mark_valve_cycle_complete_(this->active_req_.valve());
1564 ESP_LOGD(TAG,
"Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1570 this->load_next_valve_run_request_(this->active_req_.valve());
1572 if (this->next_req_.has_request()) {
1574 this->valve_pump_switch(this->active_req_.valve()) == this->valve_pump_switch(this->next_req_.valve());
1576 this->active_req_.set_valve(this->next_req_.valve());
1577 this->active_req_.set_request_from(this->next_req_.request_is_from());
1578 this->active_req_.set_run_duration(this->next_req_.run_duration());
1579 this->next_req_.reset();
1582 if (this->valve_overlap_ || !this->switching_delay_.has_value()) {
1584 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1586 this->start_valve_(&this->active_req_);
1588 this->set_timer_duration_(
1590 this->switching_delay_.value() * 2 +
1591 (this->pump_switch_off_during_valve_open_delay_ && same_pump ? this->stop_delay_ : 0));
1596 this->fsm_transition_to_shutdown_();
1603 this->start_delay_ + this->stop_delay_ + this->switching_delay_.value_or(0) + 1);
1646 if (this->timer_duration_(timer_index) > 0) {
1647 this->set_timeout(this->timer_[timer_index].
name, this->timer_duration_(timer_index),
1648 this->timer_cbf_(timer_index));
1649 this->timer_[timer_index].start_time =
millis();
1650 this->timer_[timer_index].active =
true;
1652 ESP_LOGVV(TAG,
"Timer %zu started for %" PRIu32
" sec", static_cast<size_t>(timer_index),
1653 this->timer_duration_(timer_index) / 1000);
1657 this->timer_[timer_index].active =
false;
1658 return this->cancel_timeout(this->timer_[timer_index].
name);
1664 this->timer_[timer_index].time = 1000 * time;
1670 return this->timer_[timer_index].func;
1675 ESP_LOGVV(TAG,
"Valve selection timer expired");
1676 if (this->manual_valve_.has_value()) {
1677 this->fsm_request_(this->manual_valve_.value());
1678 this->manual_valve_.reset();
1684 ESP_LOGVV(TAG,
"State machine timer expired");
1685 this->fsm_transition_();
1689 ESP_LOGCONFIG(TAG,
"Sprinkler Controller -- %s", this->name_.c_str());
1690 if (this->manual_selection_delay_.has_value()) {
1691 ESP_LOGCONFIG(TAG,
" Manual Selection Delay: %" PRIu32
" seconds", this->manual_selection_delay_.value_or(0));
1693 if (this->repeat().has_value()) {
1694 ESP_LOGCONFIG(TAG,
" Repeat Cycles: %" PRIu32
" times", this->repeat().value_or(0));
1696 if (this->start_delay_) {
1697 if (this->start_delay_is_valve_delay_) {
1698 ESP_LOGCONFIG(TAG,
" Pump Start Valve Delay: %" PRIu32
" seconds", this->start_delay_);
1700 ESP_LOGCONFIG(TAG,
" Pump Start Pump Delay: %" PRIu32
" seconds", this->start_delay_);
1703 if (this->stop_delay_) {
1704 if (this->stop_delay_is_valve_delay_) {
1705 ESP_LOGCONFIG(TAG,
" Pump Stop Valve Delay: %" PRIu32
" seconds", this->stop_delay_);
1707 ESP_LOGCONFIG(TAG,
" Pump Stop Pump Delay: %" PRIu32
" seconds", this->stop_delay_);
1710 if (this->switching_delay_.has_value()) {
1711 if (this->valve_overlap_) {
1712 ESP_LOGCONFIG(TAG,
" Valve Overlap: %" PRIu32
" seconds", this->switching_delay_.value_or(0));
1714 ESP_LOGCONFIG(TAG,
" Valve Open Delay: %" PRIu32
" seconds", this->switching_delay_.value_or(0));
1715 ESP_LOGCONFIG(TAG,
" Pump Switch Off During Valve Open Delay: %s",
1716 YESNO(this->pump_switch_off_during_valve_open_delay_));
1719 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1720 ESP_LOGCONFIG(TAG,
" Valve %zu:", valve_number);
1721 ESP_LOGCONFIG(TAG,
" Name: %s", this->valve_name(valve_number));
1722 ESP_LOGCONFIG(TAG,
" Run Duration: %" PRIu32
" seconds", this->valve_run_duration(valve_number));
1723 if (this->valve_[valve_number].valve_switch.pulse_duration()) {
1724 ESP_LOGCONFIG(TAG,
" Pulse Duration: %" PRIu32
" milliseconds",
1725 this->valve_[valve_number].valve_switch.pulse_duration());
1728 if (!this->pump_.empty()) {
1729 ESP_LOGCONFIG(TAG,
" Total number of pumps: %zu", this->pump_.size());
1731 if (!this->valve_.empty()) {
1732 ESP_LOGCONFIG(TAG,
" Total number of valves: %zu", 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_
const std::string MIN_STR
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
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