47 auto s = (*this->
f_)();
75 if (!this->
f_.has_value()) {
84 : controller_(controller), valve_(valve) {}
116 if (controller !=
nullptr) {
122 if (valve !=
nullptr) {
203 return (this->
run_duration_ - (elapsed - this->start_delay_)) / 1000;
208 if (elapsed < total_duration) {
209 return (total_duration - elapsed) / 1000;
226 if ((this->
valve_ ==
nullptr) || (pump ==
nullptr)) {
241 if ((this->
valve_ ==
nullptr) || (pump ==
nullptr)) {
287 : valve_number_(valve_number), run_duration_(run_duration), valve_op_(valve_op) {}
301 if (valve_op !=
nullptr) {
331void Sprinkler::setup() {
332 this->all_valves_off_(
true);
334 this->disable_loop();
337void Sprinkler::loop() {
338 for (
auto &vo : this->valve_op_) {
341 if (this->prev_req_.has_request()) {
342 if (this->prev_req_.has_valve_operator() && this->prev_req_.valve_operator()->state() == IDLE) {
343 this->prev_req_.reset();
345 }
else if (this->state_ == IDLE) {
347 this->disable_loop();
352 auto new_valve_number = this->number_of_valves();
353 this->valve_.resize(new_valve_number + 1);
358 auto *valve = this->valve_switch(new_valve_number);
359 auto *pump = this->valve_pump_switch(new_valve_number);
360 if (valve ==
nullptr) {
363 if (pump !=
nullptr) {
364 return valve->state && pump->state;
379 if (enable_sw !=
nullptr) {
384void Sprinkler::add_controller(
Sprinkler *other_controller) { this->other_controllers_.push_back(other_controller); }
387 this->controller_sw_ = controller_switch;
389 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
390 if (this->valve_[valve_number].controller_switch->
state) {
394 return this->active_req_.has_request();
397 this->sprinkler_turn_off_automation_ = make_unique<Automation<>>(controller_switch->
get_turn_off_trigger());
398 this->sprinkler_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(
this);
399 this->sprinkler_turn_off_automation_->add_actions({sprinkler_shutdown_action_.get()});
401 this->sprinkler_turn_on_automation_ = make_unique<Automation<>>(controller_switch->
get_turn_on_trigger());
402 this->sprinkler_resumeorstart_action_ = make_unique<sprinkler::ResumeOrStartAction<>>(
this);
403 this->sprinkler_turn_on_automation_->add_actions({sprinkler_resumeorstart_action_.get()});
407 this->standby_sw_ = standby_switch;
409 this->sprinkler_standby_turn_on_automation_ = make_unique<Automation<>>(standby_switch->
get_turn_on_trigger());
410 this->sprinkler_standby_shutdown_action_ = make_unique<sprinkler::ShutdownAction<>>(
this);
411 this->sprinkler_standby_turn_on_automation_->add_actions({sprinkler_standby_shutdown_action_.get()});
415 if (this->is_a_valid_valve(valve_number)) {
416 this->valve_[valve_number].valve_switch = valve_switch;
417 this->valve_[valve_number].run_duration = run_duration;
421void Sprinkler::configure_valve_pump_switch(
size_t valve_number,
switch_::Switch *pump_switch) {
422 if (this->is_a_valid_valve(valve_number)) {
423 for (
size_t i = 0; i < this->pump_.size(); i++) {
424 if (this->pump_[i] == pump_switch) {
425 this->valve_[valve_number].pump_switch_index = i;
429 this->pump_.push_back(pump_switch);
430 this->valve_[valve_number].pump_switch_index = this->pump_.size() - 1;
434void Sprinkler::configure_valve_run_duration_number(
size_t valve_number,
436 if (this->is_a_valid_valve(valve_number)) {
437 this->valve_[valve_number].run_duration_number = run_duration_number;
441void Sprinkler::set_divider(optional<uint32_t> divider) {
442 if (!divider.has_value()) {
445 if (divider.value() > 0) {
446 this->set_multiplier(1.0 / divider.value());
447 this->set_repeat(divider.value() - 1);
448 }
else if (divider.value() == 0) {
449 this->set_multiplier(1.0);
454void Sprinkler::set_multiplier(
const optional<float> multiplier) {
455 if ((!multiplier.has_value()) || (multiplier.value() < 0)) {
458 this->multiplier_ = multiplier.value();
459 if (this->multiplier_number_ ==
nullptr) {
462 if (this->multiplier_number_->state == multiplier.value()) {
465 auto call = this->multiplier_number_->make_call();
466 call.set_value(multiplier.value());
470void Sprinkler::set_pump_start_delay(
uint32_t start_delay) {
471 this->start_delay_is_valve_delay_ =
false;
472 this->start_delay_ = start_delay;
475void Sprinkler::set_pump_stop_delay(
uint32_t stop_delay) {
476 this->stop_delay_is_valve_delay_ =
false;
477 this->stop_delay_ = stop_delay;
480void Sprinkler::set_valve_start_delay(
uint32_t start_delay) {
481 this->start_delay_is_valve_delay_ =
true;
482 this->start_delay_ = start_delay;
485void Sprinkler::set_valve_stop_delay(
uint32_t stop_delay) {
486 this->stop_delay_is_valve_delay_ =
true;
487 this->stop_delay_ = stop_delay;
490void Sprinkler::set_valve_open_delay(
const uint32_t valve_open_delay) {
491 if (valve_open_delay > 0) {
492 this->valve_overlap_ =
false;
493 this->switching_delay_ = valve_open_delay;
495 this->switching_delay_.reset();
499void Sprinkler::set_valve_overlap(
uint32_t valve_overlap) {
500 if (valve_overlap > 0) {
501 this->valve_overlap_ =
true;
502 this->switching_delay_ = valve_overlap;
504 this->switching_delay_.reset();
506 this->pump_switch_off_during_valve_open_delay_ =
false;
509void Sprinkler::set_manual_selection_delay(
uint32_t manual_selection_delay) {
510 if (manual_selection_delay > 0) {
511 this->manual_selection_delay_ = manual_selection_delay;
513 this->manual_selection_delay_.reset();
517void Sprinkler::set_valve_run_duration(
const optional<size_t> valve_number,
const optional<uint32_t> run_duration) {
518 if (!valve_number.has_value() || !run_duration.has_value()) {
521 if (!this->is_a_valid_valve(valve_number.value())) {
524 this->valve_[valve_number.value()].run_duration = run_duration.value();
525 if (this->valve_[valve_number.value()].run_duration_number ==
nullptr) {
528 if (this->valve_[valve_number.value()].run_duration_number->state == run_duration.value()) {
531 auto call = this->valve_[valve_number.value()].run_duration_number->make_call();
532 if (this->valve_[valve_number.value()].run_duration_number->get_unit_of_measurement_ref() ==
MIN_STR) {
533 call.set_value(run_duration.value() / 60.0);
535 call.set_value(run_duration.value());
540void Sprinkler::set_auto_advance(
const bool auto_advance) {
541 if (this->auto_adv_sw_ ==
nullptr) {
544 if (this->auto_adv_sw_->state == auto_advance) {
547 this->auto_adv_sw_->control(auto_advance);
550void Sprinkler::set_repeat(optional<uint32_t> repeat) {
551 this->target_repeats_ = repeat;
552 if (this->repeat_number_ ==
nullptr) {
555 if (this->repeat_number_->state == repeat.value_or(0)) {
558 auto call = this->repeat_number_->make_call();
559 call.set_value(repeat.value_or(0));
563void Sprinkler::set_queue_enable(
bool queue_enable) {
564 if (this->queue_enable_sw_ ==
nullptr) {
567 if (this->queue_enable_sw_->state == queue_enable) {
570 this->queue_enable_sw_->control(queue_enable);
573void Sprinkler::set_reverse(
const bool reverse) {
574 if (this->reverse_sw_ ==
nullptr) {
577 if (this->reverse_sw_->state == reverse) {
580 this->reverse_sw_->control(reverse);
583void Sprinkler::set_standby(
const bool standby) {
584 if (this->standby_sw_ ==
nullptr) {
587 if (this->standby_sw_->state == standby) {
590 this->standby_sw_->control(standby);
593uint32_t Sprinkler::valve_run_duration(
const size_t valve_number) {
594 if (!this->is_a_valid_valve(valve_number)) {
597 if (this->valve_[valve_number].run_duration_number !=
nullptr) {
598 if (this->valve_[valve_number].run_duration_number->get_unit_of_measurement_ref() ==
MIN_STR) {
599 return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
601 return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state));
604 return this->valve_[valve_number].run_duration;
607uint32_t Sprinkler::valve_run_duration_adjusted(
const size_t valve_number) {
610 if (this->is_a_valid_valve(valve_number)) {
611 run_duration = this->valve_run_duration(valve_number);
613 run_duration =
static_cast<uint32_t>(roundf(run_duration * this->multiplier()));
615 if ((run_duration < this->start_delay_) || (run_duration < this->stop_delay_) ||
616 (run_duration < this->switching_delay_.value_or(0) * 2)) {
617 return std::max({this->switching_delay_.value_or(0) * 2, this->start_delay_, this->stop_delay_});
622bool Sprinkler::auto_advance() {
623 if (this->auto_adv_sw_ !=
nullptr) {
624 return this->auto_adv_sw_->state;
629float Sprinkler::multiplier() {
630 if (this->multiplier_number_ !=
nullptr) {
631 return this->multiplier_number_->state;
633 return this->multiplier_;
636optional<uint32_t> Sprinkler::repeat() {
637 if (this->repeat_number_ !=
nullptr) {
638 return static_cast<uint32_t>(roundf(this->repeat_number_->state));
640 return this->target_repeats_;
643optional<uint32_t> Sprinkler::repeat_count() {
645 if (this->active_req_.has_request() && this->auto_advance()) {
646 return this->repeat_count_;
651bool Sprinkler::queue_enabled() {
652 if (this->queue_enable_sw_ !=
nullptr) {
653 return this->queue_enable_sw_->state;
658bool Sprinkler::reverse() {
659 if (this->reverse_sw_ !=
nullptr) {
660 return this->reverse_sw_->state;
665bool Sprinkler::standby() {
666 if (this->standby_sw_ !=
nullptr) {
667 return this->standby_sw_->state;
672void Sprinkler::start_from_queue() {
673 if (this->standby()) {
674 this->log_standby_warning_(LOG_STR(
"start_from_queue"));
677 if (this->multiplier() == 0) {
678 this->log_multiplier_zero_warning_(LOG_STR(
"start_from_queue"));
681 if (this->queued_valves_.empty()) {
684 if (this->queue_enabled() && this->active_valve().has_value()) {
688 this->set_auto_advance(
false);
689 this->set_queue_enable(
true);
691 this->reset_cycle_states_();
692 this->repeat_count_ = 0;
696void Sprinkler::start_full_cycle() {
697 if (this->standby()) {
698 this->log_standby_warning_(LOG_STR(
"start_full_cycle"));
701 if (this->multiplier() == 0) {
702 this->log_multiplier_zero_warning_(LOG_STR(
"start_full_cycle"));
705 if (this->auto_advance() && this->active_valve().has_value()) {
709 this->set_queue_enable(
false);
711 this->prep_full_cycle_();
712 this->repeat_count_ = 0;
714 if (!this->active_req_.has_request()) {
719void Sprinkler::start_single_valve(
const optional<size_t> valve_number, optional<uint32_t> run_duration) {
720 if (this->standby()) {
721 this->log_standby_warning_(LOG_STR(
"start_single_valve"));
724 if (this->multiplier() == 0) {
725 this->log_multiplier_zero_warning_(LOG_STR(
"start_single_valve"));
728 if (!valve_number.has_value() || (valve_number == this->active_valve())) {
732 this->set_auto_advance(
false);
733 this->set_queue_enable(
false);
735 this->reset_cycle_states_();
736 this->repeat_count_ = 0;
737 this->fsm_request_(valve_number.value(), run_duration.value_or(0));
740void Sprinkler::queue_valve(optional<size_t> valve_number, optional<uint32_t> run_duration) {
741 if (valve_number.has_value()) {
742 if (this->is_a_valid_valve(valve_number.value()) && (this->queued_valves_.size() < this->max_queue_size_)) {
744 this->queued_valves_.insert(this->queued_valves_.begin(), item);
745 ESP_LOGD(TAG,
"Valve %zu placed into queue with run duration of %" PRIu32
" seconds", valve_number.value_or(0),
746 run_duration.value_or(0));
751void Sprinkler::clear_queued_valves() {
752 this->queued_valves_.clear();
753 ESP_LOGD(TAG,
"Queue cleared");
756void Sprinkler::next_valve() {
757 if (this->standby()) {
758 this->log_standby_warning_(LOG_STR(
"next_valve"));
762 if (this->state_ == IDLE) {
763 this->reset_cycle_states_();
766 this->manual_valve_ = this->next_valve_number_(
767 this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(this->number_of_valves() - 1)),
768 !this->next_prev_ignore_disabled_,
true);
770 if (!this->manual_valve_.has_value()) {
771 ESP_LOGD(TAG,
"next_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows only "
772 "enabled valves and no valves are enabled?");
776 if (this->manual_selection_delay_.has_value()) {
780 this->fsm_request_(this->manual_valve_.value());
784void Sprinkler::previous_valve() {
785 if (this->standby()) {
786 this->log_standby_warning_(LOG_STR(
"previous_valve"));
790 if (this->state_ == IDLE) {
791 this->reset_cycle_states_();
794 this->manual_valve_ =
795 this->previous_valve_number_(this->manual_valve_.value_or(this->active_req_.valve_as_opt().value_or(0)),
796 !this->next_prev_ignore_disabled_,
true);
798 if (!this->manual_valve_.has_value()) {
799 ESP_LOGD(TAG,
"previous_valve was called but no valve could be started; perhaps next_prev_ignore_disabled allows "
800 "only enabled valves and no valves are enabled?");
804 if (this->manual_selection_delay_.has_value()) {
808 this->fsm_request_(this->manual_valve_.value());
812void Sprinkler::shutdown(
bool clear_queue) {
814 this->active_req_.reset();
815 this->manual_valve_.reset();
816 this->next_req_.reset();
817 for (
auto &vo : this->valve_op_) {
820 this->fsm_transition_to_shutdown_();
822 this->clear_queued_valves();
823 this->repeat_count_ = 0;
827void Sprinkler::pause() {
828 if (this->paused_valve_.has_value() || !this->active_req_.has_request()) {
831 this->paused_valve_ = this->active_valve();
832 this->resume_duration_ = this->time_remaining_active_valve();
833 this->shutdown(
false);
834 ESP_LOGD(TAG,
"Paused valve %zu with %" PRIu32
" seconds remaining", this->paused_valve_.value_or(0),
835 this->resume_duration_.value_or(0));
838void Sprinkler::resume() {
839 if (this->standby()) {
840 this->log_standby_warning_(LOG_STR(
"resume"));
844 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
845 const size_t paused_valve = *this->paused_valve_;
846 const uint32_t resume_duration = *this->resume_duration_;
848 if (!this->valve_cycle_complete_(paused_valve)) {
849 ESP_LOGD(TAG,
"Resuming valve %zu with %" PRIu32
" seconds remaining", paused_valve, resume_duration);
850 this->fsm_request_(paused_valve, resume_duration);
852 this->reset_resume();
854 ESP_LOGD(TAG,
"No valve to resume!");
858void Sprinkler::resume_or_start_full_cycle() {
859 if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
862 this->start_full_cycle();
866void Sprinkler::reset_resume() {
867 this->paused_valve_.reset();
868 this->resume_duration_.reset();
871#pragma GCC diagnostic push
872#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
873const char *Sprinkler::valve_name(
const size_t valve_number) {
874 if (this->is_a_valid_valve(valve_number)) {
875 return this->valve_[valve_number].controller_switch->get_name().c_str();
879#pragma GCC diagnostic pop
881const LogString *Sprinkler::valve_log_name(
const size_t valve_number) {
882 if (this->is_a_valid_valve(valve_number)) {
883 return this->valve_[valve_number].controller_switch->get_log_name();
888optional<SprinklerValveRunRequestOrigin> Sprinkler::active_valve_request_is_from() {
889 if (this->active_req_.has_request()) {
890 return this->active_req_.request_is_from();
895optional<size_t> Sprinkler::active_valve() {
896 if (!this->valve_overlap_ && this->prev_req_.has_request() && this->prev_req_.has_valve_operator() &&
897 (this->prev_req_.valve_operator()->state() == STARTING || this->prev_req_.valve_operator()->state() ==
ACTIVE)) {
898 return this->prev_req_.valve_as_opt();
900 return this->active_req_.valve_as_opt();
903optional<size_t> Sprinkler::queued_valve() {
904 if (!this->queued_valves_.empty()) {
905 return this->queued_valves_.back().valve_number;
910bool Sprinkler::is_a_valid_valve(
const size_t valve_number) {
return (valve_number < this->number_of_valves()); }
913 if (pump_switch ==
nullptr) {
922 for (
auto &vo : this->valve_op_) {
923 if ((vo.state() !=
BYPASS) && (vo.pump_switch() !=
nullptr)) {
925 if (vo.pump_switch() == pump_switch) {
928 if ((vo.state() ==
ACTIVE) ||
929 ((vo.state() == STARTING) && this->start_delay_ && this->start_delay_is_valve_delay_) ||
930 ((vo.state() == STOPPING) && this->stop_delay_ && this->stop_delay_is_valve_delay_)) {
936 if (!this->valve_overlap_ && !this->pump_switch_off_during_valve_open_delay_ && this->switching_delay_.has_value() &&
937 this->active_req_.has_request() && (this->state_ != STOPPING)) {
940 auto *valve_pump = this->valve_pump_switch(this->active_req_.valve());
941 if (valve_pump ==
nullptr) {
944 return pump_switch == valve_pump;
950 if (pump_switch ==
nullptr) {
954 bool hold_pump_on =
false;
956 for (
auto &controller : this->other_controllers_) {
957 if (controller !=
this) {
958 if (controller->pump_in_use(pump_switch)) {
964 ESP_LOGD(TAG,
"Leaving pump on because another controller instance is using it");
969 }
else if (!hold_pump_on && !this->pump_in_use(pump_switch)) {
977 for (
size_t valve = 0; valve < this->number_of_valves(); valve++) {
978 total_time_remaining += this->valve_run_duration_adjusted(valve);
981 if (this->valve_overlap_) {
982 total_time_remaining -= this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
984 total_time_remaining += this->switching_delay_.value_or(0) * (this->number_of_valves() - 1);
987 return total_time_remaining;
990uint32_t Sprinkler::total_cycle_time_enabled_valves() {
994 for (
size_t valve = 0; valve < this->number_of_valves(); valve++) {
995 if (this->valve_is_enabled_(valve)) {
996 total_time_remaining += this->valve_run_duration_adjusted(valve);
1002 if (this->valve_overlap_) {
1003 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1005 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1009 return total_time_remaining;
1012uint32_t Sprinkler::total_cycle_time_enabled_incomplete_valves() {
1015 uint32_t incomplete_valve_count = 0;
1017 for (
size_t valve = 0; valve < this->number_of_valves(); valve++) {
1018 if (this->valve_is_enabled_(valve)) {
1019 enabled_valve_count++;
1020 if (!this->valve_cycle_complete_(valve)) {
1021 auto active = this->active_valve();
1022 if (!active.has_value() || (valve != *active)) {
1023 total_time_remaining += this->valve_run_duration_adjusted(valve);
1024 incomplete_valve_count++;
1027 if (this->active_req_.valve_operator() ==
nullptr) {
1028 total_time_remaining += this->valve_run_duration_adjusted(valve);
1029 incomplete_valve_count++;
1036 if (incomplete_valve_count > 0 && incomplete_valve_count >= enabled_valve_count) {
1037 incomplete_valve_count--;
1039 if (incomplete_valve_count) {
1040 if (this->valve_overlap_) {
1041 total_time_remaining -= this->switching_delay_.value_or(0) * incomplete_valve_count;
1043 total_time_remaining += this->switching_delay_.value_or(0) * incomplete_valve_count;
1047 return total_time_remaining;
1054 for (
auto &valve : this->queued_valves_) {
1055 if (valve.run_duration) {
1056 total_time_remaining += valve.run_duration;
1058 total_time_remaining += this->valve_run_duration_adjusted(valve.valve_number);
1064 if (this->valve_overlap_) {
1065 total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1);
1067 total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1);
1071 return total_time_remaining;
1074optional<uint32_t> Sprinkler::time_remaining_active_valve() {
1075 if (this->active_req_.has_request()) {
1076 if (this->active_req_.valve_operator() !=
nullptr) {
1077 return this->active_req_.valve_operator()->time_remaining();
1080 if (this->prev_req_.has_request()) {
1081 if (this->prev_req_.valve_operator() !=
nullptr) {
1082 return this->prev_req_.valve_operator()->time_remaining();
1088optional<uint32_t> Sprinkler::time_remaining_current_operation() {
1089 if (!this->time_remaining_active_valve().has_value() && this->state_ == IDLE) {
1093 auto total_time_remaining = this->time_remaining_active_valve().value_or(0);
1094 if (this->auto_advance()) {
1095 total_time_remaining += this->total_cycle_time_enabled_incomplete_valves();
1096 if (this->repeat().value_or(0) > 0) {
1097 total_time_remaining +=
1098 (this->total_cycle_time_enabled_valves() * (this->repeat().value_or(0) - this->repeat_count().value_or(0)));
1102 if (this->queue_enabled()) {
1103 total_time_remaining += this->total_queue_time();
1105 return total_time_remaining;
1108bool Sprinkler::any_controller_is_active() {
1109 if (this->state_ != IDLE) {
1113 for (
auto &controller : this->other_controllers_) {
1114 if (controller !=
this) {
1115 if (controller->controller_state() != IDLE) {
1124 if (this->is_a_valid_valve(valve_number)) {
1125 return this->valve_[valve_number].controller_switch;
1131 if (this->is_a_valid_valve(valve_number)) {
1132 return this->valve_[valve_number].enable_switch;
1138 if (this->is_a_valid_valve(valve_number)) {
1139 return this->valve_[valve_number].valve_switch;
1145 if (this->is_a_valid_valve(valve_number)) {
1146 auto idx = this->valve_[valve_number].pump_switch_index;
1147 if (idx.has_value()) {
1148 return this->pump_[*idx];
1155 if (pump_index < this->pump_.size()) {
1156 return this->pump_[pump_index];
1161bool Sprinkler::valve_is_enabled_(
const size_t valve_number) {
1162 if (this->is_a_valid_valve(valve_number)) {
1163 if (this->valve_[valve_number].enable_switch !=
nullptr) {
1164 return this->valve_[valve_number].enable_switch->state;
1172void Sprinkler::mark_valve_cycle_complete_(
const size_t valve_number) {
1173 if (this->is_a_valid_valve(valve_number)) {
1174 ESP_LOGD(TAG,
"Marking valve %u complete", valve_number);
1175 this->valve_[valve_number].valve_cycle_complete =
true;
1179bool Sprinkler::valve_cycle_complete_(
const size_t valve_number) {
1180 if (this->is_a_valid_valve(valve_number)) {
1181 return this->valve_[valve_number].valve_cycle_complete;
1186optional<size_t> Sprinkler::next_valve_number_(
const optional<size_t> first_valve,
const bool include_disabled,
1187 const bool include_complete) {
1188 auto valve = first_valve.value_or(0);
1189 size_t start = first_valve.has_value() ? 1 : 0;
1191 if (!this->is_a_valid_valve(valve)) {
1195 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1196 auto valve_of_interest = valve + offset;
1197 if (!this->is_a_valid_valve(valve_of_interest)) {
1198 valve_of_interest -= this->number_of_valves();
1201 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1202 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1203 return valve_of_interest;
1209optional<size_t> Sprinkler::previous_valve_number_(
const optional<size_t> first_valve,
const bool include_disabled,
1210 const bool include_complete) {
1211 auto valve = first_valve.value_or(this->number_of_valves() - 1);
1212 size_t start = first_valve.has_value() ? 1 : 0;
1214 if (!this->is_a_valid_valve(valve)) {
1215 valve = this->number_of_valves() - 1;
1218 for (
size_t offset = start; offset < this->number_of_valves(); offset++) {
1219 auto valve_of_interest = valve - offset;
1220 if (!this->is_a_valid_valve(valve_of_interest)) {
1221 valve_of_interest += this->number_of_valves();
1224 if ((this->valve_is_enabled_(valve_of_interest) || include_disabled) &&
1225 (!this->valve_cycle_complete_(valve_of_interest) || include_complete)) {
1226 return valve_of_interest;
1232optional<size_t> Sprinkler::next_valve_number_in_cycle_(
const optional<size_t> first_valve) {
1233 if (this->reverse()) {
1234 return this->previous_valve_number_(first_valve,
false,
false);
1236 return this->next_valve_number_(first_valve,
false,
false);
1239void Sprinkler::load_next_valve_run_request_(
const optional<size_t> first_valve) {
1240 if (this->active_req_.has_request()) {
1241 this->prev_req_ = this->active_req_;
1243 this->prev_req_.reset();
1246 if (this->next_req_.has_request()) {
1247 if (!this->next_req_.run_duration()) {
1248 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->next_req_.valve()));
1251 }
else if (this->queue_enabled() && !this->queued_valves_.empty()) {
1252 this->next_req_.set_valve(this->queued_valves_.back().valve_number);
1253 this->next_req_.set_request_from(
QUEUE);
1254 if (this->queued_valves_.back().run_duration) {
1255 this->next_req_.set_run_duration(this->queued_valves_.back().run_duration);
1256 this->queued_valves_.pop_back();
1257 }
else if (this->multiplier()) {
1258 this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->queued_valves_.back().valve_number));
1259 this->queued_valves_.pop_back();
1261 this->next_req_.reset();
1263 }
else if (this->auto_advance() && this->multiplier()) {
1264 if (this->next_valve_number_in_cycle_(first_valve).has_value()) {
1266 this->next_req_.set_valve(this->next_valve_number_in_cycle_(first_valve).value_or(0));
1267 this->next_req_.set_request_from(
CYCLE);
1268 this->next_req_.set_run_duration(
1269 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
1270 }
else if ((this->repeat_count_++ < this->repeat().value_or(0))) {
1271 ESP_LOGD(TAG,
"Repeating - starting cycle %" PRIu32
" of %" PRIu32, this->repeat_count_ + 1,
1272 this->repeat().value_or(0) + 1);
1274 this->prep_full_cycle_();
1275 if (this->next_valve_number_in_cycle_().has_value()) {
1276 this->next_req_.set_valve(this->next_valve_number_in_cycle_().value_or(0));
1277 this->next_req_.set_request_from(
CYCLE);
1278 this->next_req_.set_run_duration(
1279 this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_().value_or(0)));
1285bool Sprinkler::any_valve_is_enabled_() {
1286 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1287 if (this->valve_is_enabled_(valve_number))
1297 if (!this->is_a_valid_valve(req->
valve())) {
1301 this->enable_loop();
1302 for (
auto &vo : this->valve_op_) {
1303 if (vo.state() == IDLE) {
1305 ESP_LOGD(TAG,
"%s is starting valve %zu for %" PRIu32
" seconds, cycle %" PRIu32
" of %" PRIu32,
1307 this->repeat_count_ + 1, this->repeat().value_or(0) + 1);
1309 vo.set_controller(
this);
1310 vo.set_valve(&this->valve_[req->
valve()]);
1311 vo.set_run_duration(run_duration);
1312 vo.set_start_delay(this->start_delay_, this->start_delay_is_valve_delay_);
1313 vo.set_stop_delay(this->stop_delay_, this->stop_delay_is_valve_delay_);
1320void Sprinkler::all_valves_off_(
const bool include_pump) {
1321 for (
size_t valve_index = 0; valve_index < this->number_of_valves(); valve_index++) {
1322 auto *valve_sw = this->valve_[valve_index].valve_switch;
1323 if ((valve_sw !=
nullptr) && valve_sw->state) {
1324 valve_sw->turn_off();
1327 this->set_pump_state(this->valve_pump_switch(valve_index),
false);
1330 ESP_LOGD(TAG,
"All valves stopped%s", include_pump ? LOG_STR_LITERAL(
", including pumps") :
"");
1333void Sprinkler::prep_full_cycle_() {
1334 this->set_auto_advance(
true);
1336 if (!this->any_valve_is_enabled_()) {
1337 for (
auto &valve : this->valve_) {
1338 if (valve.enable_switch !=
nullptr) {
1339 if (!valve.enable_switch->state) {
1340 valve.enable_switch->turn_on();
1345 this->reset_cycle_states_();
1348void Sprinkler::reset_cycle_states_() {
1349 for (
auto &valve : this->valve_) {
1350 valve.valve_cycle_complete =
false;
1354void Sprinkler::fsm_request_(
size_t requested_valve,
uint32_t requested_run_duration) {
1355 this->next_req_.set_valve(requested_valve);
1356 this->next_req_.set_run_duration(requested_run_duration);
1362void Sprinkler::fsm_kick_() {
1363 if ((this->state_ == IDLE) || (this->state_ ==
ACTIVE)) {
1364 this->fsm_transition_();
1368void Sprinkler::fsm_transition_() {
1369 ESP_LOGVV(TAG,
"fsm_transition_ called; state is %s", LOG_STR_ARG(this->state_as_str_(this->state_)));
1370 switch (this->state_) {
1373 this->fsm_transition_from_shutdown_();
1378 this->fsm_transition_from_valve_run_();
1383 uint32_t timer_duration = this->active_req_.run_duration();
1384 if (timer_duration > this->switching_delay_.value_or(0)) {
1385 timer_duration -= this->switching_delay_.value_or(0);
1389 this->start_valve_(&this->active_req_);
1391 if (this->next_req_.has_request()) {
1393 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1401 this->active_req_.reset();
1402 this->manual_valve_.reset();
1403 this->all_valves_off_(
true);
1404 this->state_ = IDLE;
1410 if (this->next_req_.has_request() && (this->state_ == IDLE)) {
1412 this->set_timer_duration_(
sprinkler::TIMER_SM, this->manual_selection_delay_.value_or(1));
1415 ESP_LOGVV(TAG,
"fsm_transition_ complete; new state is %s", LOG_STR_ARG(this->state_as_str_(this->state_)));
1418void Sprinkler::fsm_transition_from_shutdown_() {
1419 this->load_next_valve_run_request_();
1421 if (this->next_req_.has_request()) {
1422 this->active_req_.set_valve(this->next_req_.valve());
1423 this->active_req_.set_request_from(this->next_req_.request_is_from());
1424 this->active_req_.set_run_duration(this->next_req_.run_duration());
1425 this->next_req_.reset();
1427 uint32_t timer_duration = this->active_req_.run_duration();
1428 if (timer_duration > this->switching_delay_.value_or(0)) {
1429 timer_duration -= this->switching_delay_.value_or(0);
1433 this->start_valve_(&this->active_req_);
1438void Sprinkler::fsm_transition_from_valve_run_() {
1439 if (!this->active_req_.has_request()) {
1440 this->fsm_transition_to_shutdown_();
1445 if ((this->active_req_.request_is_from() ==
CYCLE) || (this->active_req_.request_is_from() ==
USER)) {
1446 this->mark_valve_cycle_complete_(this->active_req_.valve());
1449 ESP_LOGD(TAG,
"Valve cycle interrupted - NOT flagging valve as complete and stopping current valve");
1450 for (
auto &vo : this->valve_op_) {
1455 this->load_next_valve_run_request_(this->active_req_.valve());
1457 if (this->next_req_.has_request()) {
1458 auto *active_pump = this->valve_pump_switch(this->active_req_.valve());
1459 auto *next_pump = this->valve_pump_switch(this->next_req_.valve());
1460 bool same_pump = (active_pump !=
nullptr) && (next_pump !=
nullptr) && (active_pump == next_pump);
1462 this->active_req_.set_valve(this->next_req_.valve());
1463 this->active_req_.set_request_from(this->next_req_.request_is_from());
1464 this->active_req_.set_run_duration(this->next_req_.run_duration());
1465 this->next_req_.reset();
1468 if (this->valve_overlap_ || !this->switching_delay_.has_value()) {
1469 uint32_t timer_duration = this->active_req_.run_duration();
1470 if (timer_duration > this->switching_delay_.value_or(0)) {
1471 timer_duration -= this->switching_delay_.value_or(0);
1475 this->start_valve_(&this->active_req_);
1477 this->set_timer_duration_(
1479 this->switching_delay_.value() * 2 +
1480 (this->pump_switch_off_during_valve_open_delay_ && same_pump ? this->stop_delay_ : 0));
1482 this->state_ = STARTING;
1485 this->fsm_transition_to_shutdown_();
1489void Sprinkler::fsm_transition_to_shutdown_() {
1490 this->state_ = STOPPING;
1492 this->start_delay_ + this->stop_delay_ + this->switching_delay_.value_or(0) + 1);
1496void Sprinkler::log_standby_warning_(
const LogString *method_name) {
1497 ESP_LOGW(TAG,
"%s called but standby is enabled; no action taken", LOG_STR_ARG(method_name));
1500void Sprinkler::log_multiplier_zero_warning_(
const LogString *method_name) {
1501 ESP_LOGW(TAG,
"%s called but multiplier is set to zero; no action taken", LOG_STR_ARG(method_name));
1505PROGMEM_STRING_TABLE(SprinklerRequestOriginStrings,
"USER",
"CYCLE",
"QUEUE",
"UNKNOWN");
1508 return SprinklerRequestOriginStrings::get_log_str(
static_cast<uint8_t
>(origin),
1509 SprinklerRequestOriginStrings::LAST_INDEX);
1513PROGMEM_STRING_TABLE(SprinklerStateStrings,
"IDLE",
"STARTING",
"ACTIVE",
"STOPPING",
"BYPASS",
"UNKNOWN");
1516 return SprinklerStateStrings::get_log_str(
static_cast<uint8_t
>(
state), SprinklerStateStrings::LAST_INDEX);
1520 if (this->timer_duration_(timer_index) > 0) {
1521 this->set_timeout(
static_cast<uint32_t>(timer_index), this->timer_duration_(timer_index),
1522 this->timer_cbf_(timer_index));
1523 this->timer_[timer_index].start_time =
millis();
1524 this->timer_[timer_index].active =
true;
1526 ESP_LOGVV(TAG,
"Timer %zu started for %" PRIu32
" sec",
static_cast<size_t>(timer_index),
1527 this->timer_duration_(timer_index) / 1000);
1531 this->timer_[timer_index].active =
false;
1532 return this->cancel_timeout(
static_cast<uint32_t>(timer_index));
1535bool Sprinkler::timer_active_(
const SprinklerTimerIndex timer_index) {
return this->timer_[timer_index].active; }
1538 this->timer_[timer_index].time = 1000 * time;
1544 return this->timer_[timer_index].func;
1547void Sprinkler::valve_selection_callback_() {
1549 ESP_LOGVV(TAG,
"Valve selection timer expired");
1550 if (this->manual_valve_.has_value()) {
1551 this->fsm_request_(this->manual_valve_.value());
1552 this->manual_valve_.reset();
1556void Sprinkler::sm_timer_callback_() {
1558 ESP_LOGVV(TAG,
"State machine timer expired");
1559 this->fsm_transition_();
1562void Sprinkler::dump_config() {
1563 ESP_LOGCONFIG(TAG,
"Sprinkler Controller -- %s", this->name_);
1564 if (this->manual_selection_delay_.has_value()) {
1565 ESP_LOGCONFIG(TAG,
" Manual Selection Delay: %" PRIu32
" seconds", this->manual_selection_delay_.value_or(0));
1567 if (this->repeat().has_value()) {
1568 ESP_LOGCONFIG(TAG,
" Repeat Cycles: %" PRIu32
" times", this->repeat().value_or(0));
1570 if (this->start_delay_) {
1571 if (this->start_delay_is_valve_delay_) {
1572 ESP_LOGCONFIG(TAG,
" Pump Start Valve Delay: %" PRIu32
" seconds", this->start_delay_);
1574 ESP_LOGCONFIG(TAG,
" Pump Start Pump Delay: %" PRIu32
" seconds", this->start_delay_);
1577 if (this->stop_delay_) {
1578 if (this->stop_delay_is_valve_delay_) {
1579 ESP_LOGCONFIG(TAG,
" Pump Stop Valve Delay: %" PRIu32
" seconds", this->stop_delay_);
1581 ESP_LOGCONFIG(TAG,
" Pump Stop Pump Delay: %" PRIu32
" seconds", this->stop_delay_);
1584 if (this->switching_delay_.has_value()) {
1585 if (this->valve_overlap_) {
1586 ESP_LOGCONFIG(TAG,
" Valve Overlap: %" PRIu32
" seconds", this->switching_delay_.value_or(0));
1589 " Valve Open Delay: %" PRIu32
" seconds\n"
1590 " Pump Switch Off During Valve Open Delay: %s",
1591 this->switching_delay_.value_or(0), YESNO(this->pump_switch_off_during_valve_open_delay_));
1594 for (
size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
1598 " Run Duration: %" PRIu32
" seconds",
1599 valve_number, LOG_STR_ARG(this->valve_log_name(valve_number)),
1600 this->valve_run_duration(valve_number));
1602 if (!this->pump_.empty()) {
1603 ESP_LOGCONFIG(TAG,
" Total number of pumps: %zu", this->pump_.size());
1605 if (!this->valve_.empty()) {
1606 ESP_LOGCONFIG(TAG,
" Total number of valves: %zu", this->valve_.size());
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void disable_loop()
Disable this component's loop.
ESPPreferenceObject make_entity_preference(uint32_t version=0)
Create a preference object for storing this entity's state/settings.
void stop_action()
Stop any action connected to this trigger.
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
void publish_state(float state)
float get_min_value() const
Trigger< float > set_trigger_
void dump_config() override
ESPPreferenceObject pref_
void control(float value) override
Trigger turn_off_trigger_
SprinklerControllerSwitch()
Trigger * get_turn_off_trigger()
float get_setup_priority() const override
void dump_config() override
void set_state_lambda(std::function< optional< bool >()> &&f)
Trigger * get_turn_on_trigger()
void write_state(bool state) override
optional< std::function< optional< bool >()> > f_
void set_pump_state(switch_::Switch *pump_switch, bool state)
switches on/off a pump "safely" by checking that the new state will not conflict with another control...
switch_::Switch * valve_pump_switch_by_pump_index(size_t pump_index)
returns a pointer to a valve's pump switch object
void sm_timer_callback_()
void valve_selection_callback_()
callback functions for timers
switch_::Switch * pump_switch()
bool stop_delay_is_valve_delay_
void set_valve(SprinklerValve *valve)
void set_run_duration(uint32_t run_duration)
uint32_t time_remaining()
bool start_delay_is_valve_delay_
optional< uint32_t > stop_millis_
void set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay)
optional< uint32_t > start_millis_
void set_controller(Sprinkler *controller)
void set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay)
SprinklerValveRunRequestOrigin origin_
SprinklerValveRunRequestOrigin request_is_from()
SprinklerValveOperator * valve_op_
SprinklerValveOperator * valve_operator()
void set_valve(size_t valve_number)
void set_run_duration(uint32_t run_duration)
optional< size_t > valve_as_opt()
SprinklerValveRunRequest()
void set_valve_operator(SprinklerValveOperator *valve_op)
bool has_valve_operator()
Base class for all switches.
void turn_on()
Turn this switch on.
void turn_off()
Turn this switch off.
bool state
The current reported state of the binary sensor.
void publish_state(bool state)
Publish a state to the front-end from the back-end.
optional< bool > get_initial_state_with_restore_mode()
Returns the initial state of the switch, after applying restore mode rules.
constexpr float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
constexpr const char * MIN_STR
SprinklerValveRunRequestOrigin
ESPHOME_LOG_TAG(TAG, "sprinkler")
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
std::unique_ptr< StartSingleValveAction<> > valve_resumeorstart_action
switch_::Switch * valve_switch
std::unique_ptr< Automation<> > valve_turn_off_automation
SprinklerControllerSwitch * enable_switch
std::unique_ptr< ShutdownAction<> > valve_shutdown_action
SprinklerControllerSwitch * controller_switch
std::unique_ptr< Automation<> > valve_turn_on_automation
optional< size_t > pump_switch_index