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) {
267 if (completed_millis >
millis()) {
268 return (completed_millis -
millis()) / 1000;
314 if (this->
valve_ ==
nullptr) {
323 if (this->
valve_ ==
nullptr) {
346 : valve_number_(valve_number),
run_duration_(run_duration), valve_op_(valve_op) {}
363 if (valve_op !=
nullptr) {
402 for (
auto &p : this->pump_) {
405 for (
auto &v : this->valve_) {
406 v.valve_switch.loop();
411 if (this->prev_req_.has_request() && this->prev_req_.valve_operator()->state() ==
IDLE) {
412 this->prev_req_.reset();
417 auto new_valve_number = this->number_of_valves();
418 this->valve_.resize(new_valve_number + 1);
423 if (this->valve_pump_switch(new_valve_number) !=
nullptr) {
424 return this->valve_switch(new_valve_number)->state() && this->valve_pump_switch(new_valve_number)->state();
426 return this->valve_switch(new_valve_number)->state();
439 if (enable_sw !=
nullptr) {
447 this->controller_sw_ = controller_switch;
449 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
450 if (this->valve_[valve_number].controller_switch->
state) {
454 return this->active_req_.has_request();
457 this->sprinkler_turn_off_automation_ = make_unique<Automation<>>(controller_switch->
get_turn_off_trigger());
458 this->sprinkler_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(
this);
459 this->sprinkler_turn_off_automation_->add_actions({sprinkler_shutdown_action_.get()});
461 this->sprinkler_turn_on_automation_ = make_unique<Automation<>>(controller_switch->
get_turn_on_trigger());
462 this->sprinkler_resumeorstart_action_ = make_unique<sprinkler::ResumeOrStartAction<>>(
this);
463 this->sprinkler_turn_on_automation_->add_actions({sprinkler_resumeorstart_action_.get()});
467 this->auto_adv_sw_ = auto_adv_switch;
471 this->queue_enable_sw_ = queue_enable_switch;
475 this->reverse_sw_ = reverse_switch;
479 this->standby_sw_ = standby_switch;
481 this->sprinkler_standby_turn_on_automation_ = make_unique<Automation<>>(standby_switch->
get_turn_on_trigger());
482 this->sprinkler_standby_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(
this);
483 this->sprinkler_standby_turn_on_automation_->add_actions({sprinkler_standby_shutdown_action_.get()});
487 this->multiplier_number_ = multiplier_number;
491 this->repeat_number_ = repeat_number;
495 if (this->is_a_valid_valve(valve_number)) {
496 this->valve_[valve_number].valve_switch.set_on_switch(valve_switch);
504 if (this->is_a_valid_valve(valve_number)) {
505 this->valve_[valve_number].valve_switch.set_off_switch(valve_switch_off);
506 this->valve_[valve_number].valve_switch.set_on_switch(valve_switch_on);
507 this->valve_[valve_number].valve_switch.set_pulse_duration(pulse_duration);
513 if (this->is_a_valid_valve(valve_number)) {
514 for (
size_t i = 0; i < this->pump_.size(); i++) {
515 if (this->pump_[i].on_switch() == pump_switch) {
516 this->valve_[valve_number].pump_switch_index = i;
520 this->pump_.resize(this->pump_.size() + 1);
521 this->pump_.back().set_on_switch(pump_switch);
522 this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1;
528 if (this->is_a_valid_valve(valve_number)) {
529 for (
size_t i = 0; i < this->pump_.size(); i++) {
530 if ((this->pump_[i].off_switch() == pump_switch_off) &&
531 (this->pump_[i].on_switch() == pump_switch_on)) {
532 this->valve_[valve_number].pump_switch_index = i;
536 this->pump_.resize(this->pump_.size() + 1);
537 this->pump_.back().set_off_switch(pump_switch_off);
538 this->pump_.back().set_on_switch(pump_switch_on);
539 this->pump_.back().set_pulse_duration(pulse_duration);
540 this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1;
546 if (this->is_a_valid_valve(valve_number)) {
547 this->valve_[valve_number].run_duration_number = run_duration_number;
555 if (divider.
value() > 0) {
556 this->set_multiplier(1.0 / divider.
value());
557 this->set_repeat(divider.
value() - 1);
558 }
else if (divider.
value() == 0) {
559 this->set_multiplier(1.0);
568 this->multiplier_ = multiplier.
value();
569 if (this->multiplier_number_ ==
nullptr) {
572 if (this->multiplier_number_->state == multiplier.
value()) {
575 auto call = this->multiplier_number_->make_call();
576 call.set_value(multiplier.
value());
581 this->next_prev_ignore_disabled_ = ignore_disabled;
585 this->start_delay_is_valve_delay_ =
false;
586 this->start_delay_ = start_delay;
590 this->stop_delay_is_valve_delay_ =
false;
591 this->stop_delay_ = stop_delay;
595 this->start_delay_is_valve_delay_ =
true;
596 this->start_delay_ = start_delay;
600 this->stop_delay_is_valve_delay_ =
true;
601 this->stop_delay_ = stop_delay;
605 this->pump_switch_off_during_valve_open_delay_ = pump_switch_off_during_valve_open_delay;
609 if (valve_open_delay > 0) {
610 this->valve_overlap_ =
false;
611 this->switching_delay_ = valve_open_delay;
613 this->switching_delay_.reset();
618 if (valve_overlap > 0) {
619 this->valve_overlap_ =
true;
620 this->switching_delay_ = valve_overlap;
622 this->switching_delay_.reset();
624 this->pump_switch_off_during_valve_open_delay_ =
false;
628 if (manual_selection_delay > 0) {
629 this->manual_selection_delay_ = manual_selection_delay;
631 this->manual_selection_delay_.reset();
639 if (!this->is_a_valid_valve(valve_number.
value())) {
642 this->valve_[valve_number.
value()].run_duration = run_duration.
value();
643 if (this->valve_[valve_number.
value()].run_duration_number ==
nullptr) {
646 if (this->valve_[valve_number.
value()].run_duration_number->state == run_duration.
value()) {
649 auto call = this->valve_[valve_number.
value()].run_duration_number->make_call();
650 if (this->valve_[valve_number.
value()].run_duration_number->traits.get_unit_of_measurement() ==
MIN_STR) {
651 call.set_value(run_duration.
value() / 60.0);
653 call.set_value(run_duration.
value());
659 if (this->auto_adv_sw_ ==
nullptr) {
662 if (this->auto_adv_sw_->state == auto_advance) {
666 this->auto_adv_sw_->turn_on();
668 this->auto_adv_sw_->turn_off();
673 this->target_repeats_ = repeat;
674 if (this->repeat_number_ ==
nullptr) {
677 if (this->repeat_number_->state == repeat.
value()) {
680 auto call = this->repeat_number_->make_call();
686 if (this->queue_enable_sw_ ==
nullptr) {
689 if (this->queue_enable_sw_->state == queue_enable) {
693 this->queue_enable_sw_->turn_on();
695 this->queue_enable_sw_->turn_off();
700 if (this->reverse_sw_ ==
nullptr) {
703 if (this->reverse_sw_->state == reverse) {
707 this->reverse_sw_->turn_on();
709 this->reverse_sw_->turn_off();
714 if (this->standby_sw_ ==
nullptr) {
717 if (this->standby_sw_->state == standby) {
721 this->standby_sw_->turn_on();
723 this->standby_sw_->turn_off();
728 if (!this->is_a_valid_valve(valve_number)) {
731 if (this->valve_[valve_number].run_duration_number !=
nullptr) {
732 if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() ==
MIN_STR) {
733 return static_cast<uint32_t
>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
735 return static_cast<uint32_t
>(roundf(this->valve_[valve_number].run_duration_number->state));
738 return this->valve_[valve_number].run_duration;
744 if (this->is_a_valid_valve(valve_number)) {
745 run_duration = this->valve_run_duration(valve_number);
747 run_duration =
static_cast<uint32_t
>(roundf(run_duration * this->multiplier()));
749 if ((run_duration < this->start_delay_) || (run_duration < this->stop_delay_) ||
750 (run_duration < this->switching_delay_.value_or(0) * 2)) {
751 return std::max(this->switching_delay_.value_or(0) * 2, std::max(this->start_delay_, this->stop_delay_));
757 if (this->auto_adv_sw_ !=
nullptr) {
758 return this->auto_adv_sw_->state;
764 if (this->multiplier_number_ !=
nullptr) {
765 return this->multiplier_number_->state;
767 return this->multiplier_;
771 if (this->repeat_number_ !=
nullptr) {
772 return static_cast<uint32_t
>(roundf(this->repeat_number_->state));
774 return this->target_repeats_;
779 if (this->active_req_.has_request() && this->auto_advance()) {
780 return this->repeat_count_;
786 if (this->queue_enable_sw_ !=
nullptr) {
787 return this->queue_enable_sw_->state;
793 if (this->reverse_sw_ !=
nullptr) {
794 return this->reverse_sw_->state;
800 if (this->standby_sw_ !=
nullptr) {
801 return this->standby_sw_->state;
807 if (this->standby()) {
808 ESP_LOGD(TAG,
"start_from_queue called but standby is enabled; no action taken");
811 if (this->multiplier() == 0) {
812 ESP_LOGD(TAG,
"start_from_queue called but multiplier is set to zero; no action taken");
815 if (this->queued_valves_.empty()) {
818 if (this->queue_enabled() && this->active_valve().has_value()) {
822 this->set_auto_advance(
false);
823 this->set_queue_enable(
true);
825 this->reset_cycle_states_();
826 this->repeat_count_ = 0;
831 if (this->standby()) {
832 ESP_LOGD(TAG,
"start_full_cycle called but standby is enabled; no action taken");
835 if (this->multiplier() == 0) {
836 ESP_LOGD(TAG,
"start_full_cycle called but multiplier is set to zero; no action taken");
839 if (this->auto_advance() && this->active_valve().has_value()) {
843 this->set_queue_enable(
false);
845 this->prep_full_cycle_();
846 this->repeat_count_ = 0;
848 if (!this->active_req_.has_request()) {
854 if (this->standby()) {
855 ESP_LOGD(TAG,
"start_single_valve called but standby is enabled; no action taken");
858 if (this->multiplier() == 0) {
859 ESP_LOGD(TAG,
"start_single_valve called but multiplier is set to zero; no action taken");
862 if (!valve_number.
has_value() || (valve_number == this->active_valve())) {
866 this->set_auto_advance(
false);
867 this->set_queue_enable(
false);
869 this->reset_cycle_states_();
870 this->repeat_count_ = 0;
871 this->fsm_request_(valve_number.
value(), run_duration.
value_or(0));
876 if (this->is_a_valid_valve(valve_number.
value()) && (this->queued_valves_.size() < this->max_queue_size_)) {
878 this->queued_valves_.insert(this->queued_valves_.begin(), item);
879 ESP_LOGD(TAG,
"Valve %zu placed into queue with run duration of %" PRIu32
" seconds", valve_number.
value_or(0),
886 this->queued_valves_.clear();
887 ESP_LOGD(TAG,
"Queue cleared");
891 if (this->state_ ==
IDLE) {
892 this->reset_cycle_states_();
895 this->manual_valve_ = this->next_valve_number_(
896 this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(this->number_of_valves() - 1)),
897 !this->next_prev_ignore_disabled_,
true);
899 if (!this->manual_valve_.has_value()) {
900 ESP_LOGD(TAG,
"next_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows only " 901 "enabled valves and no valves are enabled?");
905 if (this->manual_selection_delay_.has_value()) {
909 this->fsm_request_(this->manual_valve_.value());
914 if (this->state_ ==
IDLE) {
915 this->reset_cycle_states_();
918 this->manual_valve_ =
919 this->previous_valve_number_(this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(0)),
920 !this->next_prev_ignore_disabled_,
true);
922 if (!this->manual_valve_.has_value()) {
923 ESP_LOGD(TAG,
"previous_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows " 924 "only enabled valves and no valves are enabled?");
928 if (this->manual_selection_delay_.has_value()) {
932 this->fsm_request_(this->manual_valve_.value());
938 this->active_req_.reset();
939 this->manual_valve_.reset();
940 this->next_req_.reset();
944 this->fsm_transition_to_shutdown_();
946 this->clear_queued_valves();
947 this->repeat_count_ = 0;
952 if (this->paused_valve_.has_value() || !this->active_req_.has_request()) {
955 this->paused_valve_ = this->active_valve();
956 this->resume_duration_ = this->time_remaining_active_valve();
957 this->shutdown(
false);
958 ESP_LOGD(TAG,
"Paused valve %zu with %" PRIu32
" seconds remaining", this->paused_valve_.value_or(0),
959 this->resume_duration_.value_or(0));
963 if (this->standby()) {
964 ESP_LOGD(TAG,
"resume called but standby is enabled; no action taken");
968 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
970 if (!this->valve_cycle_complete_(this->paused_valve_.value())) {
971 ESP_LOGD(TAG,
"Resuming valve %zu with %" PRIu32
" seconds remaining", this->paused_valve_.value_or(0),
972 this->resume_duration_.value_or(0));
973 this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
975 this->reset_resume();
977 ESP_LOGD(TAG,
"No valve to resume!");
982 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
985 this->start_full_cycle();
990 this->paused_valve_.reset();
991 this->resume_duration_.reset();
995 if (this->is_a_valid_valve(valve_number)) {
996 return this->valve_[valve_number].controller_switch->get_name().c_str();
1002 if (this->active_req_.has_request()) {
1003 return this->active_req_.request_is_from();
1009 if (!this->valve_overlap_ && this->prev_req_.has_request() &&
1010 (this->prev_req_.valve_operator()->state() ==
STARTING || this->prev_req_.valve_operator()->state() ==
ACTIVE)) {
1011 return this->prev_req_.valve_as_opt();
1013 return this->active_req_.valve_as_opt();
1019 if (!this->queued_valves_.empty()) {
1020 return this->queued_valves_.back().valve_number;
1030 return ((valve_number >= 0) && (valve_number < this->number_of_valves()));
1034 if (pump_switch ==
nullptr) {
1044 if ((vo.state() !=
BYPASS) && (vo.pump_switch() !=
nullptr)) {
1046 if ((vo.pump_switch()->off_switch() == pump_switch->
off_switch()) &&
1047 (vo.pump_switch()->on_switch() == pump_switch->
on_switch())) {
1050 if ((vo.state() ==
ACTIVE) ||
1051 ((vo.state() ==
STARTING) && this->start_delay_ && this->start_delay_is_valve_delay_) ||
1052 ((vo.state() ==
STOPPING) && this->stop_delay_ && this->stop_delay_is_valve_delay_)) {
1058 if (!this->valve_overlap_ && !this->pump_switch_off_during_valve_open_delay_ && this->switching_delay_.has_value() &&
1059 this->active_req_.has_request() && (this->state_ !=
STOPPING)) {
1062 return (pump_switch->
off_switch() == this->valve_pump_switch(this->active_req_.valve())->off_switch()) &&
1063 (pump_switch->
on_switch() == this->valve_pump_switch(this->active_req_.valve())->on_switch());
1069 if (pump_switch ==
nullptr) {
1073 bool hold_pump_on =
false;
1075 for (
auto &controller : this->other_controllers_) {
1076 if (controller !=
this) {
1077 if (controller->pump_in_use(pump_switch)) {
1078 hold_pump_on =
true;
1088 ESP_LOGD(TAG,
"Leaving pump on because another controller instance is using it");
1093 }
else if (!hold_pump_on && !this->pump_in_use(pump_switch)) {
1095 }
else if (hold_pump_on) {
1101 uint32_t total_time_remaining = 0;
1104 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1107 if (this->valve_overlap_) {
1108 total_time_remaining -= this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1110 total_time_remaining += this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
1113 return total_time_remaining;
1117 uint32_t total_time_remaining = 0;
1118 uint32_t valve_count = 0;
1121 if (this->valve_is_enabled_(
valve)) {
1122 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1128 if (this->valve_overlap_) {
1129 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1131 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1135 return total_time_remaining;
1139 uint32_t total_time_remaining = 0;
1140 uint32_t enabled_valve_count = 0;
1141 uint32_t incomplete_valve_count = 0;
1144 if (this->valve_is_enabled_(
valve)) {
1145 enabled_valve_count++;
1146 if (!this->valve_cycle_complete_(
valve)) {
1147 if (!this->active_valve().has_value() || (
valve != this->active_valve().value())) {
1148 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1149 incomplete_valve_count++;
1152 if (this->active_req_.valve_operator() ==
nullptr) {
1153 total_time_remaining += this->valve_run_duration_adjusted(
valve);
1154 incomplete_valve_count++;
1161 if (incomplete_valve_count >= enabled_valve_count) {
1162 incomplete_valve_count--;
1164 if (incomplete_valve_count) {
1165 if (this->valve_overlap_) {
1166 total_time_remaining -= this->switching_delay_.value_or(0) * incomplete_valve_count;
1168 total_time_remaining += this->switching_delay_.value_or(0) * incomplete_valve_count;
1172 return total_time_remaining;
1176 uint32_t total_time_remaining = 0;
1177 uint32_t valve_count = 0;
1179 for (
auto &
valve : this->queued_valves_) {
1180 if (
valve.run_duration) {
1181 total_time_remaining +=
valve.run_duration;
1183 total_time_remaining += this->valve_run_duration_adjusted(
valve.valve_number);
1189 if (this->valve_overlap_) {
1190 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1192 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1196 return total_time_remaining;
1200 if (this->active_req_.has_request()) {
1201 if (this->active_req_.valve_operator() !=
nullptr) {
1202 return this->active_req_.valve_operator()->time_remaining();
1205 if (this->prev_req_.has_request()) {
1206 if (this->prev_req_.valve_operator() !=
nullptr) {
1207 return this->prev_req_.valve_operator()->time_remaining();
1214 if (!this->time_remaining_active_valve().has_value() && this->state_ ==
IDLE) {
1218 auto total_time_remaining = this->time_remaining_active_valve().value_or(0);
1219 if (this->auto_advance()) {
1220 total_time_remaining += this->total_cycle_time_enabled_incomplete_valves();
1221 if (this->repeat().value_or(0) > 0) {
1222 total_time_remaining +=
1223 (this->total_cycle_time_enabled_valves() * (this->repeat().value_or(0) - this->repeat_count().value_or(0)));
1227 if (this->queue_enabled()) {
1228 total_time_remaining += this->total_queue_time();
1230 return total_time_remaining;
1234 if (this->state_ !=
IDLE) {
1238 for (
auto &controller : this->other_controllers_) {
1239 if (controller !=
this) {
1240 if (controller->controller_state() !=
IDLE) {
1249 if (this->is_a_valid_valve(valve_number)) {
1250 return this->valve_[valve_number].controller_switch;
1256 if (this->is_a_valid_valve(valve_number)) {
1257 return this->valve_[valve_number].enable_switch;
1263 if (this->is_a_valid_valve(valve_number)) {
1264 return &this->valve_[valve_number].valve_switch;
1270 if (this->is_a_valid_valve(valve_number) && this->valve_[valve_number].pump_switch_index.has_value()) {
1271 return &this->pump_[this->valve_[valve_number].pump_switch_index.value()];
1277 if (pump_index < this->pump_.size()) {
1278 return &this->pump_[pump_index];
1284 if (this->is_a_valid_valve(valve_number)) {
1285 if (this->valve_[valve_number].enable_switch !=
nullptr) {
1286 return this->valve_[valve_number].enable_switch->state;
1295 if (this->is_a_valid_valve(valve_number)) {
1296 ESP_LOGD(TAG,
"Marking valve %u complete", valve_number);
1297 this->valve_[valve_number].valve_cycle_complete =
true;
1302 if (this->is_a_valid_valve(valve_number)) {
1303 return this->valve_[valve_number].valve_cycle_complete;
1309 const bool include_complete) {
1311 size_t start = first_valve.
has_value() ? 1 : 0;
1313 if (!this->is_a_valid_valve(
valve)) {
1317 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1318 auto valve_of_interest =
valve + offset;
1319 if (!this->is_a_valid_valve(valve_of_interest)) {
1320 valve_of_interest -= this->number_of_valves();
1323 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1324 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1325 return valve_of_interest;
1332 const bool include_complete) {
1333 auto valve = first_valve.
value_or(this->number_of_valves() - 1);
1334 size_t start = first_valve.
has_value() ? 1 : 0;
1336 if (!this->is_a_valid_valve(
valve)) {
1337 valve = this->number_of_valves() - 1;
1340 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1341 auto valve_of_interest =
valve - offset;
1342 if (!this->is_a_valid_valve(valve_of_interest)) {
1343 valve_of_interest += this->number_of_valves();
1346 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1347 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1348 return valve_of_interest;
1355 if (this->reverse()) {
1356 return this->previous_valve_number_(first_valve,
false,
false);
1358 return this->next_valve_number_(first_valve,
false,
false);
1362 if (this->active_req_.has_request()) {
1363 this->prev_req_ = this->active_req_;
1365 this->prev_req_.reset();
1368 if (this->next_req_.has_request()) {
1369 if (!this->next_req_.run_duration()) {
1370 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->next_req_.valve()));
1373 }
else if (this->queue_enabled() && !this->queued_valves_.empty()) {
1374 this->next_req_.set_valve(this->queued_valves_.back().valve_number);
1375 this->next_req_.set_request_from(
QUEUE);
1376 if (this->queued_valves_.back().run_duration) {
1377 this->next_req_.set_run_duration(this->queued_valves_.back().run_duration);
1378 this->queued_valves_.pop_back();
1379 }
else if (this->multiplier()) {
1380 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->queued_valves_.back().valve_number));
1381 this->queued_valves_.pop_back();
1383 this->next_req_.reset();
1385 }
else if (this->auto_advance() && this->multiplier()) {
1386 if (this->next_valve_number_in_cycle_(first_valve).has_value()) {
1388 this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1389 this->next_req_.set_request_from(
CYCLE);
1390 this->next_req_.set_run_duration(
1391 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1392 }
else if ((this->repeat_count_++ < this->repeat().value_or(0))) {
1393 ESP_LOGD(TAG,
"Repeating - starting cycle %" PRIu32
" of %" PRIu32, this->repeat_count_ + 1,
1394 this->repeat().value_or(0) + 1);
1396 this->prep_full_cycle_();
1397 if (this->next_valve_number_in_cycle_().has_value()) {
1398 this->next_req_.set_valve(this->next_valve_number_in_cycle_().value_or(0));
1399 this->next_req_.set_request_from(
CYCLE);
1400 this->next_req_.set_run_duration(
1401 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_().value_or(0)));
1408 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1409 if (this->valve_is_enabled_(valve_number))
1419 if (!this->is_a_valid_valve(req->
valve())) {
1423 if (vo.state() ==
IDLE) {
1425 ESP_LOGD(TAG,
"%s is starting valve %zu for %" PRIu32
" seconds, cycle %" PRIu32
" of %" PRIu32,
1427 this->repeat().value_or(0) + 1);
1429 vo.set_controller(
this);
1430 vo.set_valve(&this->valve_[req->
valve()]);
1432 vo.set_start_delay(this->start_delay_, this->start_delay_is_valve_delay_);
1433 vo.set_stop_delay(this->stop_delay_, this->stop_delay_is_valve_delay_);
1441 for (
size_t valve_index = 0; valve_index < this->number_of_valves(); valve_index++) {
1442 if (this->valve_[valve_index].valve_switch.state()) {
1443 this->valve_[valve_index].valve_switch.turn_off();
1446 this->set_pump_state(this->valve_pump_switch(valve_index),
false);
1449 ESP_LOGD(TAG,
"All valves stopped%s", include_pump ?
", including pumps" :
"");
1453 this->set_auto_advance(
true);
1455 if (!this->any_valve_is_enabled_()) {
1456 for (
auto &
valve : this->valve_) {
1457 if (
valve.enable_switch !=
nullptr) {
1458 if (!
valve.enable_switch->state) {
1459 valve.enable_switch->turn_on();
1464 this->reset_cycle_states_();
1468 for (
auto &
valve : this->valve_) {
1469 valve.valve_cycle_complete =
false;
1474 this->next_req_.set_valve(requested_valve);
1475 this->next_req_.set_run_duration(requested_run_duration);
1482 if ((this->state_ ==
IDLE) || (this->state_ ==
ACTIVE)) {
1483 this->fsm_transition_();
1488 ESP_LOGVV(TAG,
"fsm_transition_ called; state is %s", this->state_as_str_(this->state_).c_str());
1489 switch (this->state_) {
1492 this->fsm_transition_from_shutdown_();
1497 this->fsm_transition_from_valve_run_();
1503 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1505 this->start_valve_(&this->active_req_);
1507 if (this->next_req_.has_request()) {
1509 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1517 this->active_req_.reset();
1518 this->manual_valve_.reset();
1519 this->all_valves_off_(
true);
1520 this->state_ =
IDLE;
1526 if (this->next_req_.has_request() && (this->state_ ==
IDLE)) {
1528 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1531 ESP_LOGVV(TAG,
"fsm_transition_ complete; new state is %s", this->state_as_str_(this->state_).c_str());
1535 this->load_next_valve_run_request_();
1537 if (this->next_req_.has_request()) {
1538 this->active_req_.set_valve(this->next_req_.valve());
1539 this->active_req_.set_request_from(this->next_req_.request_is_from());
1540 this->active_req_.set_run_duration(this->next_req_.run_duration());
1541 this->next_req_.reset();
1544 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1546 this->start_valve_(&this->active_req_);
1552 if (!this->active_req_.has_request()) {
1553 this->fsm_transition_to_shutdown_();
1558 if ((this->active_req_.request_is_from() ==
CYCLE) || (this->active_req_.request_is_from() ==
USER)) {
1559 this->mark_valve_cycle_complete_(this->active_req_.valve());
1562 ESP_LOGD(TAG,
"Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1568 this->load_next_valve_run_request_(this->active_req_.valve());
1570 if (this->next_req_.has_request()) {
1572 this->valve_pump_switch(this->active_req_.valve()) == this->valve_pump_switch(this->next_req_.valve());
1574 this->active_req_.set_valve(this->next_req_.valve());
1575 this->active_req_.set_request_from(this->next_req_.request_is_from());
1576 this->active_req_.set_run_duration(this->next_req_.run_duration());
1577 this->next_req_.reset();
1580 if (this->valve_overlap_ || !this->switching_delay_.has_value()) {
1582 this->active_req_.run_duration() - this->switching_delay_.value_or(0));
1584 this->start_valve_(&this->active_req_);
1586 this->set_timer_duration_(
1588 this->switching_delay_.value() * 2 +
1589 (this->pump_switch_off_during_valve_open_delay_ && same_pump ? this->stop_delay_ : 0));
1594 this->fsm_transition_to_shutdown_();
1601 this->start_delay_ + this->stop_delay_ + this->switching_delay_.value_or(0) + 1);
1644 if (this->timer_duration_(timer_index) > 0) {
1645 this->set_timeout(this->timer_[timer_index].
name, this->timer_duration_(timer_index),
1646 this->timer_cbf_(timer_index));
1647 this->timer_[timer_index].start_time =
millis();
1648 this->timer_[timer_index].active =
true;
1650 ESP_LOGVV(TAG,
"Timer %zu started for %" PRIu32
" sec", static_cast<size_t>(timer_index),
1651 this->timer_duration_(timer_index) / 1000);
1655 this->timer_[timer_index].active =
false;
1656 return this->cancel_timeout(this->timer_[timer_index].
name);
1662 this->timer_[timer_index].time = 1000 * time;
1668 return this->timer_[timer_index].func;
1673 ESP_LOGVV(TAG,
"Valve selection timer expired");
1674 if (this->manual_valve_.has_value()) {
1675 this->fsm_request_(this->manual_valve_.value());
1676 this->manual_valve_.reset();
1682 ESP_LOGVV(TAG,
"State machine timer expired");
1683 this->fsm_transition_();
1687 ESP_LOGCONFIG(TAG,
"Sprinkler Controller -- %s", this->name_.c_str());
1688 if (this->manual_selection_delay_.has_value()) {
1689 ESP_LOGCONFIG(TAG,
" Manual Selection Delay: %" PRIu32
" seconds", this->manual_selection_delay_.value_or(0));
1691 if (this->repeat().has_value()) {
1692 ESP_LOGCONFIG(TAG,
" Repeat Cycles: %" PRIu32
" times", this->repeat().value_or(0));
1694 if (this->start_delay_) {
1695 if (this->start_delay_is_valve_delay_) {
1696 ESP_LOGCONFIG(TAG,
" Pump Start Valve Delay: %" PRIu32
" seconds", this->start_delay_);
1698 ESP_LOGCONFIG(TAG,
" Pump Start Pump Delay: %" PRIu32
" seconds", this->start_delay_);
1701 if (this->stop_delay_) {
1702 if (this->stop_delay_is_valve_delay_) {
1703 ESP_LOGCONFIG(TAG,
" Pump Stop Valve Delay: %" PRIu32
" seconds", this->stop_delay_);
1705 ESP_LOGCONFIG(TAG,
" Pump Stop Pump Delay: %" PRIu32
" seconds", this->stop_delay_);
1708 if (this->switching_delay_.has_value()) {
1709 if (this->valve_overlap_) {
1710 ESP_LOGCONFIG(TAG,
" Valve Overlap: %" PRIu32
" seconds", this->switching_delay_.value_or(0));
1712 ESP_LOGCONFIG(TAG,
" Valve Open Delay: %" PRIu32
" seconds", this->switching_delay_.value_or(0));
1713 ESP_LOGCONFIG(TAG,
" Pump Switch Off During Valve Open Delay: %s",
1714 YESNO(this->pump_switch_off_during_valve_open_delay_));
1717 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1718 ESP_LOGCONFIG(TAG,
" Valve %zu:", valve_number);
1719 ESP_LOGCONFIG(TAG,
" Name: %s", this->valve_name(valve_number));
1720 ESP_LOGCONFIG(TAG,
" Run Duration: %" PRIu32
" seconds", this->valve_run_duration(valve_number));
1721 if (this->valve_[valve_number].valve_switch.pulse_duration()) {
1722 ESP_LOGCONFIG(TAG,
" Pulse Duration: %" PRIu32
" milliseconds",
1723 this->valve_[valve_number].valve_switch.pulse_duration());
1726 if (!this->pump_.empty()) {
1727 ESP_LOGCONFIG(TAG,
" Total number of pumps: %zu", this->pump_.size());
1729 if (!this->valve_.empty()) {
1730 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