17import esphome.codegen
as cg
18import esphome.config_validation
as cv
19from esphome.components
import binary_sensor, sensor, switch, water_heater
20from esphome.const
import (
26 STATE_CLASS_TOTAL_INCREASING,
30from .
import home_io_control_ns
31from .hub_validators
import FEATURE_WATER_HEATER
32from .platform_common
import (
33 create_companion_sensors,
34 inherit_esphome_device,
35 inject_companion_sensor_ids,
36 platform_schema_extension,
38 validate_device_model,
39 validate_register_poll_interval,
40 warn_if_experimental_kind,
43 CONF_HOME_IO_CONTROL_ID,
47DEPENDENCIES = [
"home_io_control"]
49IOHomeWaterHeater = home_io_control_ns.class_(
"IOHomeWaterHeater", water_heater.WaterHeater, cg.Component)
50IOHomeDhwEnergySensor = home_io_control_ns.class_(
"IOHomeDhwEnergySensor", sensor.Sensor, cg.Component)
51IOHomeDhwBinarySensor = home_io_control_ns.class_(
52 "IOHomeDhwBinarySensor", binary_sensor.BinarySensor, cg.Component
54IOHomeDhwBoostSwitch = home_io_control_ns.class_(
"IOHomeDhwBoostSwitch", switch.Switch, cg.Component)
55DhwReading = IOHomeDhwBinarySensor.enum(
"Reading", is_class=
True)
57CONF_HEAT_PUMP_RUNNING =
"heat_pump_running"
58CONF_ELECTRIC_BACKUP_RUNNING =
"electric_backup_running"
63 (CONF_HEAT_PUMP_RUNNING, DhwReading.HEAT_PUMP),
64 (CONF_ELECTRIC_BACKUP_RUNNING, DhwReading.ELECTRIC_BACKUP),
69 """Require io_device_type, and a kind that is read and commanded as a water heater.
71 The water heater's frames (family-0x00 register reads, the mode write, a target EXECUTE) go only to a
72 device whose kind carries the WATER_HEATER capability, so the kind must be known at codegen time.
74 if CONF_DEVICE_TYPE
not in config:
76 "io_device_type is required on the water_heater platform: set io_device_type: domestic_hot_water",
77 path=[CONF_DEVICE_TYPE],
81 "the water_heater platform",
83 required=FEATURE_WATER_HEATER,
84 reason=
"it drives a domestic hot water heater (io_device_type: domestic_hot_water)",
90 """Declare the auto-generated companion IDs during validation (ESPHome 2026.x StaticVector sizing).
92 The opt-in companions (sensors and the boost switch) are schemas of their own, whose IDs are declared by
95 return inject_companion_sensor_ids(config, CONF_ID)
98CONFIG_SCHEMA = cv.All(
99 water_heater.water_heater_schema(IOHomeWaterHeater)
100 .extend(platform_schema_extension())
103 cv.Optional(CONF_ENERGY): sensor.sensor_schema(
104 IOHomeDhwEnergySensor,
105 unit_of_measurement=UNIT_KILOWATT_HOURS,
107 device_class=DEVICE_CLASS_ENERGY,
108 state_class=STATE_CLASS_TOTAL_INCREASING,
109 ).extend(cv.COMPONENT_SCHEMA),
110 cv.Optional(CONF_HEAT_PUMP_RUNNING): binary_sensor.binary_sensor_schema(
111 IOHomeDhwBinarySensor, device_class=DEVICE_CLASS_RUNNING
112 ).extend(cv.COMPONENT_SCHEMA),
113 cv.Optional(CONF_ELECTRIC_BACKUP_RUNNING): binary_sensor.binary_sensor_schema(
114 IOHomeDhwBinarySensor, device_class=DEVICE_CLASS_RUNNING
115 ).extend(cv.COMPONENT_SCHEMA),
118 cv.Optional(CONF_BOOST): switch.switch_schema(
119 IOHomeDhwBoostSwitch, default_restore_mode=
"DISABLED"
120 ).extend(cv.COMPONENT_SCHEMA),
123 .extend(cv.COMPONENT_SCHEMA),
124 _validate_water_heater_kind,
125 functools.partial(validate_register_poll_interval, platform=
"water_heater"),
126 validate_device_model,
127 warn_if_experimental_kind,
128 _inject_companion_ids,
133 """A companion's config, grouped under the entity's ESPHome sub-device unless it names its own."""
134 if CONF_DEVICE_ID
not in companion:
135 inherit_esphome_device(companion, config)
140 var = cg.new_Pvariable(config[CONF_ID])
141 await cg.register_component(var, config)
142 await water_heater.register_water_heater(var, config)
144 parent = await cg.get_variable(config[CONF_HOME_IO_CONTROL_ID])
145 await wire_device_binding(var, parent, config)
146 await create_companion_sensors(config, parent)
148 if CONF_ENERGY
in config:
150 energy = await sensor.new_sensor(energy_config)
151 await cg.register_component(energy, energy_config)
152 cg.add(energy.set_parent(parent))
153 cg.add(energy.set_device_id(config[CONF_IO_DEVICE_ID]))
155 for key, reading
in _BINARY_COMPANIONS:
156 if key
not in config:
159 companion = await binary_sensor.new_binary_sensor(companion_config)
160 await cg.register_component(companion, companion_config)
161 cg.add(companion.set_parent(parent))
162 cg.add(companion.set_device_id(config[CONF_IO_DEVICE_ID]))
163 cg.add(companion.set_reading(reading))
165 if CONF_BOOST
in config:
167 boost = await switch.new_switch(boost_config)
168 await cg.register_component(boost, boost_config)
169 cg.add(boost.set_parent(parent))
170 cg.add(boost.set_device_id(config[CONF_IO_DEVICE_ID]))
_companion_config(companion, config)
_validate_water_heater_kind(config)
_inject_companion_ids(config)