#include "valve.h" #include "esphome/core/defines.h" #include "esphome/core/controller_registry.h" #include "esphome/core/log.h" #include "esphome/core/progmem.h" #include namespace esphome { namespace valve { static const char *const TAG = "valve"; const float VALVE_OPEN = 1.0f; const float VALVE_CLOSED = 0.0f; const LogString *valve_command_to_str(float pos) { if (pos == VALVE_OPEN) { return LOG_STR("OPEN"); } else if (pos == VALVE_CLOSED) { return LOG_STR("CLOSE"); } else { return LOG_STR("UNKNOWN"); } } // Valve operation strings indexed by ValveOperation enum (0-2): IDLE, OPENING, CLOSING, plus UNKNOWN PROGMEM_STRING_TABLE(ValveOperationStrings, "IDLE", "OPENING", "CLOSING", "UNKNOWN"); const LogString *valve_operation_to_str(ValveOperation op) { return ValveOperationStrings::get_log_str(static_cast(op), ValveOperationStrings::LAST_INDEX); } Valve::Valve() : position{VALVE_OPEN} {} ValveCall::ValveCall(Valve *parent) : parent_(parent) {} ValveCall &ValveCall::set_command(const char *command) { if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("OPEN")) == 0) { this->set_command_open(); } else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("CLOSE")) == 0) { this->set_command_close(); } else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("STOP")) == 0) { this->set_command_stop(); } else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TOGGLE")) == 0) { this->set_command_toggle(); } else { ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command); } return *this; } ValveCall &ValveCall::set_command_open() { this->position_ = VALVE_OPEN; return *this; } ValveCall &ValveCall::set_command_close() { this->position_ = VALVE_CLOSED; return *this; } ValveCall &ValveCall::set_command_stop() { this->stop_ = true; return *this; } ValveCall &ValveCall::set_command_toggle() { this->toggle_ = true; return *this; } ValveCall &ValveCall::set_position(float position) { this->position_ = position; return *this; } void ValveCall::perform() { ESP_LOGV(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); auto traits = this->parent_->get_traits(); this->validate_(); if (this->stop_) { ESP_LOGV(TAG, " Command: STOP"); } if (this->position_.has_value()) { if (traits.get_supports_position()) { ESP_LOGV(TAG, " Position: %.0f%%", *this->position_ * 100.0f); } else { ESP_LOGV(TAG, " Command: %s", LOG_STR_ARG(valve_command_to_str(*this->position_))); } } if (this->toggle_.has_value()) { ESP_LOGV(TAG, " Command: TOGGLE"); } this->parent_->control(*this); } const optional &ValveCall::get_position() const { return this->position_; } const optional &ValveCall::get_toggle() const { return this->toggle_; } void ValveCall::validate_() { auto traits = this->parent_->get_traits(); if (this->position_.has_value()) { auto pos = *this->position_; if (!traits.get_supports_position() && pos != VALVE_OPEN && pos != VALVE_CLOSED) { ESP_LOGW(TAG, "'%s' - This valve device does not support setting position!", this->parent_->get_name().c_str()); this->position_.reset(); } else if (pos < 0.0f || pos > 1.0f) { ESP_LOGW(TAG, "'%s' - Position %.2f is out of range [0.0 - 1.0]", this->parent_->get_name().c_str(), pos); this->position_ = clamp(pos, 0.0f, 1.0f); } } if (this->toggle_.has_value()) { if (!traits.get_supports_toggle()) { ESP_LOGW(TAG, "'%s' - This valve device does not support toggle!", this->parent_->get_name().c_str()); this->toggle_.reset(); } } if (this->stop_) { if (this->position_.has_value()) { ESP_LOGW(TAG, "Cannot set position when stopping a valve!"); this->position_.reset(); } if (this->toggle_.has_value()) { ESP_LOGW(TAG, "Cannot set toggle when stopping a valve!"); this->toggle_.reset(); } } } ValveCall &ValveCall::set_stop(bool stop) { this->stop_ = stop; return *this; } bool ValveCall::get_stop() const { return this->stop_; } ValveCall Valve::make_call() { return {this}; } void Valve::publish_state(bool save) { this->position = clamp(this->position, 0.0f, 1.0f); ESP_LOGV(TAG, "'%s' >>", this->name_.c_str()); auto traits = this->get_traits(); if (traits.get_supports_position()) { ESP_LOGV(TAG, " Position: %.0f%%", this->position * 100.0f); } else { if (this->position == VALVE_OPEN) { ESP_LOGV(TAG, " State: OPEN"); } else if (this->position == VALVE_CLOSED) { ESP_LOGV(TAG, " State: CLOSED"); } else { ESP_LOGV(TAG, " State: UNKNOWN"); } } ESP_LOGV(TAG, " Current Operation: %s", LOG_STR_ARG(valve_operation_to_str(this->current_operation))); this->state_callback_.call(); #if defined(USE_VALVE) && defined(USE_CONTROLLER_REGISTRY) ControllerRegistry::notify_valve_update(this); #endif if (save) { ValveRestoreState restore{}; memset(&restore, 0, sizeof(restore)); restore.position = this->position; this->rtc_.save(&restore); } } optional Valve::restore_state_() { this->rtc_ = this->make_entity_preference(); ValveRestoreState recovered{}; if (!this->rtc_.load(&recovered)) return {}; return recovered; } bool Valve::is_fully_open() const { return this->position == VALVE_OPEN; } bool Valve::is_fully_closed() const { return this->position == VALVE_CLOSED; } ValveCall ValveRestoreState::to_call(Valve *valve) { auto call = valve->make_call(); call.set_position(this->position); return call; } void ValveRestoreState::apply(Valve *valve) { valve->position = this->position; valve->publish_state(); } } // namespace valve } // namespace esphome