ESPHome 2026.3.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"
6
7#include <cmath>
8
10
11static const char *const TAG = "water_heater";
12
13void log_water_heater(const char *tag, const char *prefix, const char *type, WaterHeater *obj) {
14 if (obj != nullptr) {
15 ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
16 }
17}
18
19WaterHeaterCall::WaterHeaterCall(WaterHeater *parent) : parent_(parent) {}
20
25
27 if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("OFF")) == 0) {
29 } else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("ECO")) == 0) {
31 } else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("ELECTRIC")) == 0) {
33 } else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("PERFORMANCE")) == 0) {
35 } else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("HIGH_DEMAND")) == 0) {
37 } else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("HEAT_PUMP")) == 0) {
39 } else if (ESPHOME_strcasecmp_P(mode, ESPHOME_PSTR("GAS")) == 0) {
41 } else {
42 ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode);
43 }
44 return *this;
45}
46
51
56
61
63 if (away) {
65 } else {
66 this->state_ &= ~WATER_HEATER_STATE_AWAY;
67 }
69 return *this;
70}
71
73 if (on) {
75 } else {
76 this->state_ &= ~WATER_HEATER_STATE_ON;
77 }
79 return *this;
80}
81
83 ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
84 this->validate_();
85 if (this->mode_.has_value()) {
86 ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(water_heater_mode_to_string(*this->mode_)));
87 }
88 if (!std::isnan(this->target_temperature_)) {
89 ESP_LOGD(TAG, " Target Temperature: %.2f", this->target_temperature_);
90 }
91 if (!std::isnan(this->target_temperature_low_)) {
92 ESP_LOGD(TAG, " Target Temperature Low: %.2f", this->target_temperature_low_);
93 }
94 if (!std::isnan(this->target_temperature_high_)) {
95 ESP_LOGD(TAG, " Target Temperature High: %.2f", this->target_temperature_high_);
96 }
98 ESP_LOGD(TAG, " Away: %s", (this->state_ & WATER_HEATER_STATE_AWAY) ? "YES" : "NO");
99 }
101 ESP_LOGD(TAG, " On: %s", (this->state_ & WATER_HEATER_STATE_ON) ? "YES" : "NO");
102 }
103 this->parent_->control(*this);
104}
105
107 auto traits = this->parent_->get_traits();
108 if (this->mode_.has_value()) {
109 if (!traits.supports_mode(*this->mode_)) {
110 ESP_LOGW(TAG, "'%s' - Mode %d not supported", this->parent_->get_name().c_str(), *this->mode_);
111 this->mode_.reset();
112 }
113 }
114 if (!std::isnan(this->target_temperature_)) {
115 if (traits.get_supports_two_point_target_temperature()) {
116 ESP_LOGW(TAG, "'%s' - Cannot set target temperature for device with two-point target temperature",
117 this->parent_->get_name().c_str());
118 this->target_temperature_ = NAN;
119 } else if (this->target_temperature_ < traits.get_min_temperature() ||
120 this->target_temperature_ > traits.get_max_temperature()) {
121 ESP_LOGW(TAG, "'%s' - Target temperature %.1f is out of range [%.1f - %.1f]", this->parent_->get_name().c_str(),
122 this->target_temperature_, traits.get_min_temperature(), traits.get_max_temperature());
123 this->target_temperature_ =
124 std::max(traits.get_min_temperature(), std::min(this->target_temperature_, traits.get_max_temperature()));
125 }
126 }
127 if (!std::isnan(this->target_temperature_low_) || !std::isnan(this->target_temperature_high_)) {
128 if (!traits.get_supports_two_point_target_temperature()) {
129 ESP_LOGW(TAG, "'%s' - Cannot set low/high target temperature", this->parent_->get_name().c_str());
130 this->target_temperature_low_ = NAN;
131 this->target_temperature_high_ = NAN;
132 }
133 }
134 if (!std::isnan(this->target_temperature_low_) && !std::isnan(this->target_temperature_high_)) {
136 ESP_LOGW(TAG, "'%s' - Target temperature low %.2f must be less than high %.2f", this->parent_->get_name().c_str(),
137 this->target_temperature_low_, this->target_temperature_high_);
138 this->target_temperature_low_ = NAN;
139 this->target_temperature_high_ = NAN;
140 }
141 }
142 if (!traits.get_supports_away_mode()) {
143 if (this->state_ & WATER_HEATER_STATE_AWAY) {
144 ESP_LOGW(TAG, "'%s' - Away mode not supported", this->parent_->get_name().c_str());
145 }
146 this->state_ &= ~WATER_HEATER_STATE_AWAY;
147 this->state_mask_ &= ~WATER_HEATER_STATE_AWAY;
148 }
149 // If ON/OFF not supported, device is always on - clear the flag silently
150 if (!traits.has_feature_flags(WATER_HEATER_SUPPORTS_ON_OFF)) {
151 this->state_ &= ~WATER_HEATER_STATE_ON;
152 this->state_mask_ &= ~WATER_HEATER_STATE_ON;
153 }
154}
155
157 auto traits = this->get_traits();
158 ESP_LOGD(TAG,
159 "'%s' >>\n"
160 " Mode: %s",
161 this->name_.c_str(), LOG_STR_ARG(water_heater_mode_to_string(this->mode_)));
162 if (!std::isnan(this->current_temperature_)) {
163 ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature_);
164 }
166 ESP_LOGD(TAG, " Target Temperature: Low: %.2f°C High: %.2f°C", this->target_temperature_low_,
168 } else if (!std::isnan(this->target_temperature_)) {
169 ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature_);
170 }
171 if (this->state_ & WATER_HEATER_STATE_AWAY) {
172 ESP_LOGD(TAG, " Away: YES");
173 }
175 ESP_LOGD(TAG, " On: %s", (this->state_ & WATER_HEATER_STATE_ON) ? "YES" : "NO");
176 }
177
178#if defined(USE_WATER_HEATER) && defined(USE_CONTROLLER_REGISTRY)
180#endif
181
182 SavedWaterHeaterState saved{};
183 saved.mode = this->mode_;
185 saved.target_temperature_low = this->target_temperature_low_;
186 saved.target_temperature_high = this->target_temperature_high_;
187 } else {
188 saved.target_temperature = this->target_temperature_;
189 }
190 saved.state = this->state_;
191 this->pref_.save(&saved);
192}
193
196 SavedWaterHeaterState recovered{};
197 if (!this->pref_.load(&recovered))
198 return {};
199
200 auto traits = this->get_traits();
201 auto call = this->make_call();
202 call.set_mode(recovered.mode);
204 call.set_target_temperature_low(recovered.target_temperature_low);
205 call.set_target_temperature_high(recovered.target_temperature_high);
206 } else {
207 call.set_target_temperature(recovered.target_temperature);
208 }
209 call.set_away((recovered.state & WATER_HEATER_STATE_AWAY) != 0);
210 call.set_on((recovered.state & WATER_HEATER_STATE_ON) != 0);
211 return call;
212}
213
215 auto traits = this->traits();
216#ifdef USE_WATER_HEATER_VISUAL_OVERRIDES
217 if (!std::isnan(this->visual_min_temperature_override_)) {
219 }
220 if (!std::isnan(this->visual_max_temperature_override_)) {
222 }
223 if (!std::isnan(this->visual_target_temperature_step_override_)) {
225 }
226#endif
227 return traits;
228}
229
230#ifdef USE_WATER_HEATER_VISUAL_OVERRIDES
231void WaterHeater::set_visual_min_temperature_override(float min_temperature_override) {
232 this->visual_min_temperature_override_ = min_temperature_override;
233}
234void WaterHeater::set_visual_max_temperature_override(float max_temperature_override) {
235 this->visual_max_temperature_override_ = max_temperature_override;
236}
237void WaterHeater::set_visual_target_temperature_step_override(float visual_target_temperature_step_override) {
238 this->visual_target_temperature_step_override_ = visual_target_temperature_step_override;
239}
240#endif
241
242// Water heater mode strings indexed by WaterHeaterMode enum (0-6): OFF, ECO, ELECTRIC, PERFORMANCE, HIGH_DEMAND,
243// HEAT_PUMP, GAS
244PROGMEM_STRING_TABLE(WaterHeaterModeStrings, "OFF", "ECO", "ELECTRIC", "PERFORMANCE", "HIGH_DEMAND", "HEAT_PUMP", "GAS",
245 "UNKNOWN");
246
248 return WaterHeaterModeStrings::get_log_str(static_cast<uint8_t>(mode), WaterHeaterModeStrings::LAST_INDEX);
249}
250
251void WaterHeater::dump_traits_(const char *tag) {
252 auto traits = this->get_traits();
253 ESP_LOGCONFIG(tag,
254 " Min Temperature: %.1f°C\n"
255 " Max Temperature: %.1f°C\n"
256 " Temperature Step: %.1f",
259 ESP_LOGCONFIG(tag, " Supports Two-Point Target Temperature: YES");
260 }
262 ESP_LOGCONFIG(tag, " Supports Away Mode: YES");
263 }
265 ESP_LOGCONFIG(tag, " Supports On/Off: YES");
266 }
268 ESP_LOGCONFIG(tag, " Supported Modes:");
270 ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(water_heater_mode_to_string(m)));
271 }
272 }
273}
274
275} // namespace esphome::water_heater
BedjetMode mode
BedJet operating mode.
uint8_t m
Definition bl0906.h:1
static void notify_water_heater_update(water_heater::WaterHeater *obj)
bool save(const T *src)
Definition preferences.h:21
const StringRef & get_name() const
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
bool has_value() const
Definition optional.h:92
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)
void set_visual_min_temperature_override(float min_temperature_override)
virtual void control(const WaterHeaterCall &call)=0
void set_visual_target_temperature_step_override(float visual_target_temperature_step_override)
void set_visual_max_temperature_override(float max_temperature_override)
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.
uint16_t temperature
Definition sun_gtil2.cpp:12