10#include "esphome/core/log.h"
18static const char *
const TAG =
"home_io_control.water_heater";
23struct WaterHeaterModeMapping {
25 water_heater::WaterHeaterMode mode;
27constexpr WaterHeaterModeMapping WATER_HEATER_MODE_MAP[] = {
35constexpr float WATER_HEATER_TARGET_STEP_C = 1.0F;
38constexpr float WH_PER_KWH = 1000.0F;
43 for (
const auto &mapping : WATER_HEATER_MODE_MAP) {
44 if (mapping.dhw == mode)
47 return water_heater::WATER_HEATER_MODE_HEAT_PUMP;
51 for (
const auto &mapping : WATER_HEATER_MODE_MAP) {
52 if (mapping.mode == mode) {
68 "Device %s is declared as %s, which is not a water heater (io_device_type: domestic_hot_water) — "
82 water_heater::WaterHeaterTraits
traits;
86 water_heater::WATER_HEATER_SUPPORTS_CURRENT_TEMPERATURE | water_heater::WATER_HEATER_SUPPORTS_TARGET_TEMPERATURE |
87 water_heater::WATER_HEATER_SUPPORTS_OPERATION_MODE | water_heater::WATER_HEATER_SUPPORTS_AWAY_MODE);
88 traits.set_min_temperature(DHW_TARGET_MIN_C);
89 traits.set_max_temperature(DHW_TARGET_MAX_C);
90 traits.set_target_temperature_step(WATER_HEATER_TARGET_STEP_C);
91 traits.set_supported_modes({water_heater::WATER_HEATER_MODE_HEAT_PUMP, water_heater::WATER_HEATER_MODE_ECO,
92 water_heater::WATER_HEATER_MODE_PERFORMANCE});
101 if (call.get_mode().has_value()) {
105 ESP_LOGW(
TAG,
"Device %s: mode %" PRIu32
" is not one this heater offers", this->
device_id_.c_str(),
106 static_cast<uint32_t
>(*call.get_mode()));
109 this->set_mode_(*call.get_mode());
115 if (call.get_away().has_value())
118 const float target = call.get_target_temperature();
123 const bool away = call.get_away().value_or(this->is_away());
124 if (!std::isnan(target) && away) {
125 ESP_LOGW(
TAG,
"Device %s: target ignored while away, the heater holds no target then", this->
device_id_.c_str());
126 }
else if (!std::isnan(target) && this->
observed_ && this->mode_ == water_heater::WATER_HEATER_MODE_HEAT_PUMP) {
127 ESP_LOGW(
TAG,
"Device %s: target ignored in automatic mode, the heater chooses its own", this->
device_id_.c_str());
131 this->set_target_temperature_(target);
136 this->publish_state();
140 bool changed =
false;
142 const auto differs = [](
float a,
float b) {
return !(std::isnan(a) && std::isnan(b)) && a != b; };
144 if (differs(this->current_temperature_, readings.
water_c)) {
145 this->set_current_temperature(readings.
water_c);
152 const float target = (automatic || readings.
away == 1) ? NAN : readings.
target_c;
153 if (differs(this->target_temperature_, target)) {
154 this->set_target_temperature_(target);
157 if (readings.
mode >= 0) {
159 if (mode != this->mode_) {
160 this->set_mode_(mode);
164 if (readings.
away >= 0 && (readings.
away != 0) != this->is_away()) {
165 this->set_state_flag_(water_heater::WATER_HEATER_STATE_AWAY, readings.
away != 0);
177 this->publish_state();
181 LOG_WATER_HEATER(
"",
"IO-Homecontrol Water Heater",
this);
182 ESP_LOGCONFIG(
TAG,
" Device ID: %s", this->
device_id_.c_str());
183 ESP_LOGCONFIG(
TAG,
" Target temperature range: %.0f-%.0f C",
static_cast<double>(DHW_TARGET_MIN_C),
184 static_cast<double>(DHW_TARGET_MAX_C));
186 TAG,
" Status Poll Interval: %" PRIu32
" ms",
188 ESP_LOGCONFIG(
TAG,
" Status: readings, target and mode confirmed on hardware; away mode experimental");
196 this->publish_state(
static_cast<float>(dev.
dhw.
energy_wh) / WH_PER_KWH);
201 ESP_LOGCONFIG(
TAG,
"IO-Homecontrol Water Heater Energy:");
202 ESP_LOGCONFIG(
TAG,
" Device ID: %s", this->
device_id_.c_str());
218 this->invalidate_state();
221 this->publish_state(value != 0);
226 const char *reading =
"heat pump running";
231 reading =
"electric backup running";
234 ESP_LOGCONFIG(
TAG,
"IO-Homecontrol Water Heater %s:", reading);
235 ESP_LOGCONFIG(
TAG,
" Device ID: %s", this->
device_id_.c_str());
243 this->publish_state(dev.
dhw.
boost != 0);
252 ESP_LOGW(
TAG,
"Device %s: boost %s was not acknowledged", this->
device_id_.c_str(), state ?
"on" :
"off");
257 ESP_LOGCONFIG(
TAG,
"IO-Homecontrol Water Heater Boost:");
258 ESP_LOGCONFIG(
TAG,
" Device ID: %s", this->
device_id_.c_str());
259 ESP_LOGCONFIG(
TAG,
" Status: experimental (a boost started here writes duration 1; the state is the heater's FP16)");
void register_companion_binding_(const std::function< void(const IoDevice &)> &publish)
Shared setup() body: subscribe to this device's updates and publish the initial state.
IOHomeControlComponent * parent_
uint32_t status_poll_interval_ms_
void register_device_binding_(Component *self, DeviceCapabilityClass entity_class, std::optional< bool > invert_override, std::function< void(const std::string &, const IoDevice &)> on_update, bool dimmable=false)
Perform the shared setup() registration ritual.
IOHomeControlComponent * parent_
virtual bool set_water_heater_mode(const std::string &device_id, DhwMode mode)
Set a water heater's operating mode: a write of register 0x0006 (encode_dhw_mode_write()).
virtual bool set_water_heater_boost(const std::string &device_id, bool on)
Start or end a water heater's boost: a duration write of register 0x0000, then an EXECUTE with the op...
virtual bool set_water_heater_target(const std::string &device_id, float celsius)
Set a water heater's target temperature.
virtual bool set_water_heater_away(const std::string &device_id, bool on)
Start or end a water heater's away (holiday) mode: a duration write of register 0x0005 (255,...
void setup() override
Subscribe to the device's updates and publish what is already known.
@ ELECTRIC_BACKUP
Register 0x0021 byte 2: the electric element heats.
@ HEAT_PUMP
Register 0x0021 bytes 3/4: the heat pump runs.
void dump_config() override
Dump sensor configuration to the log.
void write_state(bool state) override
Start or end the boost; the read-back publishes the state.
void setup() override
Subscribe to the device's updates and publish what is already known.
void dump_config() override
Dump switch configuration to the log.
void setup() override
Subscribe to the device's updates and publish what is already known.
void dump_config() override
Dump sensor configuration to the log.
void dump_config() override
Dump configuration to the log.
void on_device_update_(const std::string &id, const IoDevice &dev)
Inbound device-update hook: fold IoDevice::dhw into the entity and publish when a shown field changed...
void setup() override
Register the entity with the shared hub and schedule the first poll of the heater.
bool observed_
True once a reading has been folded in.
water_heater::WaterHeaterTraits traits() override
Current and target temperature, the three modes, and the target range DHW_TARGET_MIN_C....
void control(const water_heater::WaterHeaterCall &call) override
Apply a Home Assistant request: a mode is written to register 0x0006, away mode goes through the oper...
bool sync_from_readings_(const DhwReadings &readings)
Copy the readings into the entity's fields.
DeviceFeatures device_features(DeviceType type, uint8_t subtype, DeviceModel model)
Capability bits of a (type, subtype, model).
DhwMode
Operating mode as register 0x0006 holds it.
@ MANUAL_ECO_ON
Manual mode with ECO on (heat pump only).
@ MANUAL_ECO_OFF
Manual mode with ECO off (the electric element may help).
@ AUTO
The heater's automatic mode (its own schedule and target).
static constexpr const char * TAG
@ UNKNOWN
Unknown/unspecified device.
constexpr bool has_feature(DeviceFeatures set, DeviceFeatures bit)
True when every bit of bit is set in set.
bool dhw_mode_for(water_heater::WaterHeaterMode mode, DhwMode &out)
Heater mode for an ESPHome water heater mode.
water_heater::WaterHeaterMode water_heater_mode_for(DhwMode mode)
ESPHome water heater mode for a heater mode.
@ WATER_HEATER
Domestic hot water heater (device_feature::WATER_HEATER).
std::string format_device_kind(DeviceType type, uint8_t subtype)
Human-readable description of a (type, subtype) for logs and reports.
static const char *const TAG
The IO node-type and subtype table: names, entity class, platform and behaviour flags.
Per-device poll scheduling, failure backoff, and follow-up-poll state machine.
What the hub has read from a domestic hot water heater.
int64_t energy_wh
Energy since installation in Wh; -1 until read.
float target_c
Target temperature (status reply main parameter); NaN until read or when the main parameter is not a ...
int8_t boost
Boost on (1) or off (0) as FP16 reports it; -1 until read.
int16_t mode
DhwMode value; -1 until read.
int8_t element
DhwHeatingState::element as 0/1; -1 until read.
int8_t away
Away on (1) or off (0) as FP16 reports it; -1 until read.
int8_t heat_pump
DhwHeatingState::heat_pump as 0/1; -1 until read.
float water_c
Water temperature (register 0x0017); NaN until read.
Runtime state of a paired IO‑Homecontrol device.
DhwReadings dhw
What the hub has read from a water heater (device_feature::WATER_HEATER): family-0x00 registers and t...