ESPHome 2026.9.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
14static const char *const 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 auto traits = this->get_traits();
165 ESP_LOGV(TAG,
166 "'%s' >>\n"
167 " Mode: %s",
168 this->name_.c_str(), LOG_STR_ARG(water_heater_mode_to_string(this->mode_)));
169 if (!std::isnan(this->current_temperature_)) {
170 ESP_LOGV(TAG, " Current Temperature: %.2f°C", this->current_temperature_);
171 }
173 ESP_LOGV(TAG, " Target Temperature: Low: %.2f°C High: %.2f°C", this->target_temperature_low_,
175 } else if (!std::isnan(this->target_temperature_)) {
176 ESP_LOGV(TAG, " Target Temperature: %.2f°C", this->target_temperature_);
177 }
178 if (this->state_ & WATER_HEATER_STATE_AWAY) {
179 ESP_LOGV(TAG, " Away: YES");
180 }
182 ESP_LOGV(TAG, " On: %s", (this->state_ & WATER_HEATER_STATE_ON) ? LOG_STR_LITERAL("YES") : LOG_STR_LITERAL("NO"));
183 }
184
185#if defined(USE_WATER_HEATER) && defined(USE_CONTROLLER_REGISTRY)
186 ControllerRegistry::notify_water_heater_update(this);
187#endif
188
189 SavedWaterHeaterState saved{};
190 saved.mode = this->mode_;
192 saved.target_temperature_low = this->target_temperature_low_;
193 saved.target_temperature_high = this->target_temperature_high_;
194 } else {
195 saved.target_temperature = this->target_temperature_;
196 }
197 saved.state = this->state_;
198 this->pref_.save(&saved);
199}
200
201optional<WaterHeaterCall> WaterHeater::restore_state_() {
203 SavedWaterHeaterState recovered{};
204 if (!this->pref_.load(&recovered))
205 return {};
206
207 auto traits = this->get_traits();
208 auto call = this->make_call();
209 call.set_mode(recovered.mode);
211 call.set_target_temperature_low(recovered.target_temperature_low);
212 call.set_target_temperature_high(recovered.target_temperature_high);
213 } else {
214 call.set_target_temperature(recovered.target_temperature);
215 }
216 call.set_away((recovered.state & WATER_HEATER_STATE_AWAY) != 0);
217 call.set_on((recovered.state & WATER_HEATER_STATE_ON) != 0);
218 return call;
219}
220
222 auto traits = this->traits();
223#ifdef USE_WATER_HEATER_VISUAL_OVERRIDES
224 if (!std::isnan(this->visual_min_temperature_override_)) {
226 }
227 if (!std::isnan(this->visual_max_temperature_override_)) {
229 }
230 if (!std::isnan(this->visual_target_temperature_step_override_)) {
232 }
233#endif
234 return traits;
235}
236
237// Water heater mode strings indexed by WaterHeaterMode enum (0-6): OFF, ECO, ELECTRIC, PERFORMANCE, HIGH_DEMAND,
238// HEAT_PUMP, GAS
239PROGMEM_STRING_TABLE(WaterHeaterModeStrings, "OFF", "ECO", "ELECTRIC", "PERFORMANCE", "HIGH_DEMAND", "HEAT_PUMP", "GAS",
240 "UNKNOWN");
241
243 return WaterHeaterModeStrings::get_log_str(static_cast<uint8_t>(mode), WaterHeaterModeStrings::LAST_INDEX);
244}
245
246void WaterHeater::dump_traits_(const char *tag) {
247 auto traits = this->get_traits();
248 ESP_LOGCONFIG(tag,
249 " Min Temperature: %.1f°C\n"
250 " Max Temperature: %.1f°C\n"
251 " Temperature Step: %.1f",
254 ESP_LOGCONFIG(tag, " Supports Two-Point Target Temperature: YES");
255 }
257 ESP_LOGCONFIG(tag, " Supports Away Mode: YES");
258 }
260 ESP_LOGCONFIG(tag, " Supports On/Off: YES");
261 }
263 ESP_LOGCONFIG(tag, " Supported Modes:");
265 ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(water_heater_mode_to_string(m)));
266 }
267 }
268}
269
270} // 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.
constexpr bool empty() const
Check if the set is empty.
constexpr const char * c_str() const
Definition string_ref.h:73
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)
uint16_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.
const void size_t len
Definition hal.h:64
static void uint32_t
uint16_t temperature
Definition sun_gtil2.cpp:12