ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
water_heater.cpp
Go to the documentation of this file.
1#include "water_heater.h"
2#include "esphome/core/log.h"
3
4#include <cinttypes>
8
9#include <cmath>
10#include <cstring>
11
13
14ESPHOME_LOG_TAG(TAG, "water_heater");
15
16void log_water_heater(const char *tag, const char *prefix, const char *type, WaterHeater *obj) {
17 if (obj != nullptr) {
18 ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
19 }
20}
21
22WaterHeaterCall::WaterHeaterCall(WaterHeater *parent) : parent_(parent) {}
23
28
29WaterHeaterCall &WaterHeaterCall::set_mode(const char *mode) { return this->set_mode(mode, strlen(mode)); }
30
32 if (len == 3 && ESPHOME_strncasecmp_P(mode, ESPHOME_PSTR("OFF"), 3) == 0) {
34 } else if (len == 3 && ESPHOME_strncasecmp_P(mode, ESPHOME_PSTR("ECO"), 3) == 0) {
36 } else if (len == 8 && ESPHOME_strncasecmp_P(mode, ESPHOME_PSTR("ELECTRIC"), 8) == 0) {
38 } else if (len == 11 && ESPHOME_strncasecmp_P(mode, ESPHOME_PSTR("PERFORMANCE"), 11) == 0) {
40 } else if (len == 11 && ESPHOME_strncasecmp_P(mode, ESPHOME_PSTR("HIGH_DEMAND"), 11) == 0) {
42 } else if (len == 9 && ESPHOME_strncasecmp_P(mode, ESPHOME_PSTR("HEAT_PUMP"), 9) == 0) {
44 } else if (len == 3 && ESPHOME_strncasecmp_P(mode, ESPHOME_PSTR("GAS"), 3) == 0) {
46 } else {
47 ESP_LOGW(TAG, "'%s' - Unrecognized mode %.*s", this->parent_->get_name().c_str(), (int) len, mode);
48 }
49 return *this;
50}
51
56
61
66
68 if (away) {
70 } else {
71 this->state_ &= ~WATER_HEATER_STATE_AWAY;
72 }
74 return *this;
75}
76
78 if (on) {
80 } else {
81 this->state_ &= ~WATER_HEATER_STATE_ON;
82 }
84 return *this;
85}
86
88 ESP_LOGV(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
89 this->validate_();
90 if (this->mode_.has_value()) {
91 ESP_LOGV(TAG, " Mode: %s", LOG_STR_ARG(water_heater_mode_to_string(*this->mode_)));
92 }
93 if (!std::isnan(this->target_temperature_)) {
94 ESP_LOGV(TAG, " Target Temperature: %.2f", this->target_temperature_);
95 }
96 if (!std::isnan(this->target_temperature_low_)) {
97 ESP_LOGV(TAG, " Target Temperature Low: %.2f", this->target_temperature_low_);
98 }
99 if (!std::isnan(this->target_temperature_high_)) {
100 ESP_LOGV(TAG, " Target Temperature High: %.2f", this->target_temperature_high_);
101 }
103 ESP_LOGV(TAG, " Away: %s",
104 (this->state_ & WATER_HEATER_STATE_AWAY) ? LOG_STR_LITERAL("YES") : LOG_STR_LITERAL("NO"));
105 }
107 ESP_LOGV(TAG, " On: %s", (this->state_ & WATER_HEATER_STATE_ON) ? LOG_STR_LITERAL("YES") : LOG_STR_LITERAL("NO"));
108 }
109 this->parent_->control(*this);
110}
111
113 auto traits = this->parent_->get_traits();
114 if (this->mode_.has_value()) {
115 if (!traits.supports_mode(*this->mode_)) {
116 ESP_LOGW(TAG, "'%s' - Mode %" PRIu32 " not supported", this->parent_->get_name().c_str(),
117 static_cast<uint32_t>(*this->mode_));
118 this->mode_.reset();
119 }
120 }
121 if (!std::isnan(this->target_temperature_)) {
122 if (traits.get_supports_two_point_target_temperature()) {
123 ESP_LOGW(TAG, "'%s' - Cannot set target temperature for device with two-point target temperature",
124 this->parent_->get_name().c_str());
125 this->target_temperature_ = NAN;
126 } else if (this->target_temperature_ < traits.get_min_temperature() ||
127 this->target_temperature_ > traits.get_max_temperature()) {
128 ESP_LOGW(TAG, "'%s' - Target temperature %.1f is out of range [%.1f - %.1f]", this->parent_->get_name().c_str(),
129 this->target_temperature_, traits.get_min_temperature(), traits.get_max_temperature());
130 this->target_temperature_ =
131 std::max(traits.get_min_temperature(), std::min(this->target_temperature_, traits.get_max_temperature()));
132 }
133 }
134 if (!std::isnan(this->target_temperature_low_) || !std::isnan(this->target_temperature_high_)) {
135 if (!traits.get_supports_two_point_target_temperature()) {
136 ESP_LOGW(TAG, "'%s' - Cannot set low/high target temperature", this->parent_->get_name().c_str());
137 this->target_temperature_low_ = NAN;
138 this->target_temperature_high_ = NAN;
139 }
140 }
141 if (!std::isnan(this->target_temperature_low_) && !std::isnan(this->target_temperature_high_)) {
143 ESP_LOGW(TAG, "'%s' - Target temperature low %.2f must be less than high %.2f", this->parent_->get_name().c_str(),
144 this->target_temperature_low_, this->target_temperature_high_);
145 this->target_temperature_low_ = NAN;
146 this->target_temperature_high_ = NAN;
147 }
148 }
149 if (!traits.get_supports_away_mode()) {
150 if (this->state_ & WATER_HEATER_STATE_AWAY) {
151 ESP_LOGW(TAG, "'%s' - Away mode not supported", this->parent_->get_name().c_str());
152 }
153 this->state_ &= ~WATER_HEATER_STATE_AWAY;
154 this->state_mask_ &= ~WATER_HEATER_STATE_AWAY;
155 }
156 // If ON/OFF not supported, device is always on - clear the flag silently
157 if (!traits.has_feature_flags(WATER_HEATER_SUPPORTS_ON_OFF)) {
158 this->state_ &= ~WATER_HEATER_STATE_ON;
159 this->state_mask_ &= ~WATER_HEATER_STATE_ON;
160 }
161}
162
164 this->set_has_state(true);
165 auto traits = this->get_traits();
166 ESP_LOGV(TAG,
167 "'%s' >>\n"
168 " Mode: %s",
169 this->name_.c_str(), LOG_STR_ARG(water_heater_mode_to_string(this->mode_)));
170 if (!std::isnan(this->current_temperature_)) {
171 ESP_LOGV(TAG, " Current Temperature: %.2f°C", this->current_temperature_);
172 }
174 ESP_LOGV(TAG, " Target Temperature: Low: %.2f°C High: %.2f°C", this->target_temperature_low_,
176 } else if (!std::isnan(this->target_temperature_)) {
177 ESP_LOGV(TAG, " Target Temperature: %.2f°C", this->target_temperature_);
178 }
179 if (this->state_ & WATER_HEATER_STATE_AWAY) {
180 ESP_LOGV(TAG, " Away: YES");
181 }
183 ESP_LOGV(TAG, " On: %s", (this->state_ & WATER_HEATER_STATE_ON) ? LOG_STR_LITERAL("YES") : LOG_STR_LITERAL("NO"));
184 }
185
186#if defined(USE_WATER_HEATER) && defined(USE_CONTROLLER_REGISTRY)
187 ControllerRegistry::notify_water_heater_update(this);
188#endif
189
190 SavedWaterHeaterState saved{};
191 saved.mode = this->mode_;
193 saved.target_temperature_low = this->target_temperature_low_;
194 saved.target_temperature_high = this->target_temperature_high_;
195 } else {
196 saved.target_temperature = this->target_temperature_;
197 }
198 saved.state = this->state_;
199 this->pref_.save(&saved);
200}
201
202optional<WaterHeaterCall> WaterHeater::restore_state_() {
204 SavedWaterHeaterState recovered{};
205 if (!this->pref_.load(&recovered))
206 return {};
207
208 auto traits = this->get_traits();
209 auto call = this->make_call();
210 call.set_mode(recovered.mode);
212 call.set_target_temperature_low(recovered.target_temperature_low);
213 call.set_target_temperature_high(recovered.target_temperature_high);
214 } else {
215 call.set_target_temperature(recovered.target_temperature);
216 }
217 call.set_away((recovered.state & WATER_HEATER_STATE_AWAY) != 0);
218 call.set_on((recovered.state & WATER_HEATER_STATE_ON) != 0);
219 return call;
220}
221
223 auto traits = this->traits();
224#ifdef USE_WATER_HEATER_VISUAL_OVERRIDES
225 if (!std::isnan(this->visual_min_temperature_override_)) {
227 }
228 if (!std::isnan(this->visual_max_temperature_override_)) {
230 }
231 if (!std::isnan(this->visual_target_temperature_step_override_)) {
233 }
234#endif
235 return traits;
236}
237
238// Water heater mode strings indexed by WaterHeaterMode enum (0-6): OFF, ECO, ELECTRIC, PERFORMANCE, HIGH_DEMAND,
239// HEAT_PUMP, GAS
240PROGMEM_STRING_TABLE(WaterHeaterModeStrings, "OFF", "ECO", "ELECTRIC", "PERFORMANCE", "HIGH_DEMAND", "HEAT_PUMP", "GAS",
241 "UNKNOWN");
242
244 return WaterHeaterModeStrings::get_log_str(static_cast<uint8_t>(mode), WaterHeaterModeStrings::LAST_INDEX);
245}
246
247void WaterHeater::dump_traits_(const char *tag) {
248 auto traits = this->get_traits();
249 ESP_LOGCONFIG(tag,
250 " Min Temperature: %.1f°C\n"
251 " Max Temperature: %.1f°C\n"
252 " Temperature Step: %.1f",
255 ESP_LOGCONFIG(tag, " Supports Two-Point Target Temperature: YES");
256 }
258 ESP_LOGCONFIG(tag, " Supports Away Mode: YES");
259 }
261 ESP_LOGCONFIG(tag, " Supports On/Off: YES");
262 }
264 ESP_LOGCONFIG(tag, " Supported Modes:");
266 ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(water_heater_mode_to_string(m)));
267 }
268 }
269}
270
271} // namespace esphome::water_heater
BedjetMode mode
BedJet operating mode.
uint8_t m
Definition bl0906.h:1
const StringRef & get_name() const
Definition entity_base.h:71
ESPPreferenceObject make_entity_preference(uint32_t version=0)
Create a preference object for storing this entity's state/settings.
void set_has_state(bool state)
constexpr bool empty() const
Check if the set is empty.
constexpr const char * c_str() const
Definition string_ref.h:78
WaterHeaterCall & set_away(bool away)
WaterHeaterCall & set_target_temperature_high(float temperature)
WaterHeaterCall & set_mode(WaterHeaterMode mode)
WaterHeaterCall & set_target_temperature_low(float temperature)
WaterHeaterCall & set_on(bool on)
optional< WaterHeaterMode > mode_
WaterHeaterCall & set_target_temperature(float temperature)
virtual void control(const WaterHeaterCall &call)=0
optional< WaterHeaterCall > restore_state_()
Restore the state of the water heater, call this from your setup() method.
virtual WaterHeaterCallInternal make_call()=0
void dump_traits_(const char *tag)
Log the traits of this water heater for dump_config().
virtual WaterHeaterTraits get_traits()
virtual WaterHeaterTraits traits()=0
const WaterHeaterModeMask & get_supported_modes() const
void set_min_temperature(float min_temperature)
bool has_feature_flags(uint32_t flags) const
void set_target_temperature_step(float target_temperature_step)
void set_max_temperature(float max_temperature)
uint8_t type
PROGMEM_STRING_TABLE(WaterHeaterModeStrings, "OFF", "ECO", "ELECTRIC", "PERFORMANCE", "HIGH_DEMAND", "HEAT_PUMP", "GAS", "UNKNOWN")
void log_water_heater(const char *tag, const char *prefix, const char *type, WaterHeater *obj)
@ WATER_HEATER_STATE_ON
Water heater is on (not in standby)
@ WATER_HEATER_STATE_AWAY
Away/vacation mode is currently active.
@ WATER_HEATER_SUPPORTS_ON_OFF
The water heater can be turned on/off.
const LogString * water_heater_mode_to_string(WaterHeaterMode mode)
Convert the given WaterHeaterMode to a human-readable string for logging.
ESPHOME_LOG_TAG(TAG, "water_heater")
const void size_t len
Definition hal.h:68
static void uint32_t
uint16_t temperature
Definition sun_gtil2.cpp:12