ESPHome 2026.1.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"
5
6#include <cmath>
7
9
10static const char *const TAG = "water_heater";
11
12void log_water_heater(const char *tag, const char *prefix, const char *type, WaterHeater *obj) {
13 if (obj != nullptr) {
14 ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
15 }
16}
17
18WaterHeaterCall::WaterHeaterCall(WaterHeater *parent) : parent_(parent) {}
19
24
28 } else if (str_equals_case_insensitive(mode, "ECO")) {
30 } else if (str_equals_case_insensitive(mode, "ELECTRIC")) {
32 } else if (str_equals_case_insensitive(mode, "PERFORMANCE")) {
34 } else if (str_equals_case_insensitive(mode, "HIGH_DEMAND")) {
36 } else if (str_equals_case_insensitive(mode, "HEAT_PUMP")) {
38 } else if (str_equals_case_insensitive(mode, "GAS")) {
40 } else {
41 ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str());
42 }
43 return *this;
44}
45
50
55
60
62 if (away) {
64 } else {
65 this->state_ &= ~WATER_HEATER_STATE_AWAY;
66 }
67 return *this;
68}
69
71 if (on) {
73 } else {
74 this->state_ &= ~WATER_HEATER_STATE_ON;
75 }
76 return *this;
77}
78
80 ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
81 this->validate_();
82 if (this->mode_.has_value()) {
83 ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(water_heater_mode_to_string(*this->mode_)));
84 }
85 if (!std::isnan(this->target_temperature_)) {
86 ESP_LOGD(TAG, " Target Temperature: %.2f", this->target_temperature_);
87 }
88 if (!std::isnan(this->target_temperature_low_)) {
89 ESP_LOGD(TAG, " Target Temperature Low: %.2f", this->target_temperature_low_);
90 }
91 if (!std::isnan(this->target_temperature_high_)) {
92 ESP_LOGD(TAG, " Target Temperature High: %.2f", this->target_temperature_high_);
93 }
94 if (this->state_ & WATER_HEATER_STATE_AWAY) {
95 ESP_LOGD(TAG, " Away: YES");
96 }
97 if (this->state_ & WATER_HEATER_STATE_ON) {
98 ESP_LOGD(TAG, " On: YES");
99 }
100 this->parent_->control(*this);
101}
102
104 auto traits = this->parent_->get_traits();
105 if (this->mode_.has_value()) {
106 if (!traits.supports_mode(*this->mode_)) {
107 ESP_LOGW(TAG, "'%s' - Mode %d not supported", this->parent_->get_name().c_str(), *this->mode_);
108 this->mode_.reset();
109 }
110 }
111 if (!std::isnan(this->target_temperature_)) {
112 if (traits.get_supports_two_point_target_temperature()) {
113 ESP_LOGW(TAG, "'%s' - Cannot set target temperature for device with two-point target temperature",
114 this->parent_->get_name().c_str());
115 this->target_temperature_ = NAN;
116 } else if (this->target_temperature_ < traits.get_min_temperature() ||
117 this->target_temperature_ > traits.get_max_temperature()) {
118 ESP_LOGW(TAG, "'%s' - Target temperature %.1f is out of range [%.1f - %.1f]", this->parent_->get_name().c_str(),
119 this->target_temperature_, traits.get_min_temperature(), traits.get_max_temperature());
120 this->target_temperature_ =
121 std::max(traits.get_min_temperature(), std::min(this->target_temperature_, traits.get_max_temperature()));
122 }
123 }
124 if (!std::isnan(this->target_temperature_low_) || !std::isnan(this->target_temperature_high_)) {
125 if (!traits.get_supports_two_point_target_temperature()) {
126 ESP_LOGW(TAG, "'%s' - Cannot set low/high target temperature", this->parent_->get_name().c_str());
127 this->target_temperature_low_ = NAN;
128 this->target_temperature_high_ = NAN;
129 }
130 }
131 if (!std::isnan(this->target_temperature_low_) && !std::isnan(this->target_temperature_high_)) {
133 ESP_LOGW(TAG, "'%s' - Target temperature low %.2f must be less than high %.2f", this->parent_->get_name().c_str(),
134 this->target_temperature_low_, this->target_temperature_high_);
135 this->target_temperature_low_ = NAN;
136 this->target_temperature_high_ = NAN;
137 }
138 }
139 if ((this->state_ & WATER_HEATER_STATE_AWAY) && !traits.get_supports_away_mode()) {
140 ESP_LOGW(TAG, "'%s' - Away mode not supported", this->parent_->get_name().c_str());
141 this->state_ &= ~WATER_HEATER_STATE_AWAY;
142 }
143 // If ON/OFF not supported, device is always on - clear the flag silently
144 if (!traits.has_feature_flags(WATER_HEATER_SUPPORTS_ON_OFF)) {
145 this->state_ &= ~WATER_HEATER_STATE_ON;
146 }
147}
148
152
154 auto traits = this->get_traits();
155 ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
156 ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(water_heater_mode_to_string(this->mode_)));
157 if (!std::isnan(this->current_temperature_)) {
158 ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature_);
159 }
161 ESP_LOGD(TAG, " Target Temperature: Low: %.2f°C High: %.2f°C", this->target_temperature_low_,
163 } else if (!std::isnan(this->target_temperature_)) {
164 ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature_);
165 }
166 if (this->state_ & WATER_HEATER_STATE_AWAY) {
167 ESP_LOGD(TAG, " Away: YES");
168 }
170 ESP_LOGD(TAG, " On: %s", (this->state_ & WATER_HEATER_STATE_ON) ? "YES" : "NO");
171 }
172
173#if defined(USE_WATER_HEATER) && defined(USE_CONTROLLER_REGISTRY)
175#endif
176
177 SavedWaterHeaterState saved{};
178 saved.mode = this->mode_;
180 saved.target_temperature_low = this->target_temperature_low_;
181 saved.target_temperature_high = this->target_temperature_high_;
182 } else {
183 saved.target_temperature = this->target_temperature_;
184 }
185 saved.state = this->state_;
186 this->pref_.save(&saved);
187}
188
190 SavedWaterHeaterState recovered{};
191 if (!this->pref_.load(&recovered))
192 return {};
193
194 auto traits = this->get_traits();
195 auto call = this->make_call();
196 call.set_mode(recovered.mode);
198 call.set_target_temperature_low(recovered.target_temperature_low);
199 call.set_target_temperature_high(recovered.target_temperature_high);
200 } else {
201 call.set_target_temperature(recovered.target_temperature);
202 }
203 call.set_away((recovered.state & WATER_HEATER_STATE_AWAY) != 0);
204 call.set_on((recovered.state & WATER_HEATER_STATE_ON) != 0);
205 return call;
206}
207
209 auto traits = this->traits();
210#ifdef USE_WATER_HEATER_VISUAL_OVERRIDES
211 if (!std::isnan(this->visual_min_temperature_override_)) {
213 }
214 if (!std::isnan(this->visual_max_temperature_override_)) {
216 }
217 if (!std::isnan(this->visual_target_temperature_step_override_)) {
219 }
220#endif
221 return traits;
222}
223
224#ifdef USE_WATER_HEATER_VISUAL_OVERRIDES
225void WaterHeater::set_visual_min_temperature_override(float min_temperature_override) {
226 this->visual_min_temperature_override_ = min_temperature_override;
227}
228void WaterHeater::set_visual_max_temperature_override(float max_temperature_override) {
229 this->visual_max_temperature_override_ = max_temperature_override;
230}
231void WaterHeater::set_visual_target_temperature_step_override(float visual_target_temperature_step_override) {
232 this->visual_target_temperature_step_override_ = visual_target_temperature_step_override;
233}
234#endif
235
237 switch (mode) {
239 return LOG_STR("OFF");
241 return LOG_STR("ECO");
243 return LOG_STR("ELECTRIC");
245 return LOG_STR("PERFORMANCE");
247 return LOG_STR("HIGH_DEMAND");
249 return LOG_STR("HEAT_PUMP");
251 return LOG_STR("GAS");
252 default:
253 return LOG_STR("UNKNOWN");
254 }
255}
256
257void WaterHeater::dump_traits_(const char *tag) {
258 auto traits = this->get_traits();
259 ESP_LOGCONFIG(tag,
260 " Min Temperature: %.1f°C\n"
261 " Max Temperature: %.1f°C\n"
262 " Temperature Step: %.1f",
265 ESP_LOGCONFIG(tag, " Supports Two-Point Target Temperature: YES");
266 }
268 ESP_LOGCONFIG(tag, " Supports Away Mode: YES");
269 }
271 ESP_LOGCONFIG(tag, " Supports On/Off: YES");
272 }
274 ESP_LOGCONFIG(tag, " Supported Modes:");
276 ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(water_heater_mode_to_string(m)));
277 }
278 }
279}
280
281} // 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
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
const StringRef & get_name() const
uint32_t get_preference_hash()
Get a unique hash for storing preferences/settings for this entity.
constexpr bool empty() const
Check if the set is empty.
constexpr const char * c_str() const
Definition string_ref.h:69
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)
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
optional< WaterHeaterCall > restore_state()
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
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.
ESPPreferences * global_preferences
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare strings for equality in case-insensitive manner.
Definition helpers.cpp:162
uint16_t temperature
Definition sun_gtil2.cpp:12