Files
Home-Assistant/custom_components/midea_ac_lan/water_heater.py
T
2026-07-08 10:43:39 -04:00

456 lines
15 KiB
Python

"""Midea Water Heater entries."""
import functools as ft
import logging
from typing import Any, ClassVar, cast
from homeassistant.components.water_heater import (
WaterHeaterEntity,
WaterHeaterEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_TEMPERATURE,
CONF_DEVICE_ID,
CONF_SWITCHES,
PRECISION_HALVES,
PRECISION_WHOLE,
STATE_OFF,
STATE_ON,
Platform,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from midealocal.device import DeviceType
from midealocal.devices.c3 import DeviceAttributes as C3Attributes
from midealocal.devices.c3 import MideaC3Device
from midealocal.devices.cd import DeviceAttributes as CDAttributes
from midealocal.devices.cd import MideaCDDevice
from midealocal.devices.e2 import DeviceAttributes as E2Attributes
from midealocal.devices.e2 import MideaE2Device
from midealocal.devices.e3 import MideaE3Device
from midealocal.devices.e6 import DeviceAttributes as E6Attributes
from midealocal.devices.e6 import MideaE6Device
from .const import DEVICES, DOMAIN
from .midea_devices import MIDEA_DEVICES
from .midea_entity import MideaEntity
_LOGGER = logging.getLogger(__name__)
E2_TEMPERATURE_MAX = 75
E2_TEMPERATURE_MIN = 30
E3_TEMPERATURE_MAX = 65
E3_TEMPERATURE_MIN = 35
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up water heater entries."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
devs: list[
MideaE2WaterHeater
| MideaE3WaterHeater
| MideaE6WaterHeater
| MideaC3WaterHeater
| MideaCDWaterHeater
] = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
if config["type"] == Platform.WATER_HEATER and (
config.get("default") or entity_key in extra_switches
):
if device.device_type == DeviceType.E2:
devs.append(MideaE2WaterHeater(device, entity_key))
elif device.device_type == DeviceType.E3:
devs.append(MideaE3WaterHeater(device, entity_key))
elif device.device_type == DeviceType.E6:
devs.append(MideaE6WaterHeater(device, entity_key, config["use"]))
elif device.device_type == DeviceType.C3:
devs.append(MideaC3WaterHeater(device, entity_key))
elif device.device_type == DeviceType.CD:
devs.append(MideaCDWaterHeater(device, entity_key))
async_add_entities(devs)
type MideaWaterHeaterDevice = (
MideaE2Device | MideaE3Device | MideaC3Device | MideaE6Device | MideaCDDevice
)
class MideaWaterHeater(MideaEntity, WaterHeaterEntity):
"""Midea Water Heater Entries Base Class."""
_device: MideaWaterHeaterDevice
def __init__(self, device: MideaWaterHeaterDevice, entity_key: str) -> None:
"""Midea Water Heater entity init."""
super().__init__(device, entity_key)
self._operations: list[str] = []
@property
def supported_features(self) -> WaterHeaterEntityFeature:
"""Midea Water Heater supported features."""
return WaterHeaterEntityFeature.TARGET_TEMPERATURE
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Midea Water Heater extra state attributes."""
attrs: dict[str, Any] = self._device.attributes
if hasattr(self._device, "temperature_step"):
attrs["target_temperature_step"] = self._device.temperature_step
return attrs
@property
def min_temp(self) -> float:
"""Midea Water Heater min temperature."""
raise NotImplementedError
@property
def max_temp(self) -> float:
"""Midea Water Heater max temperature."""
raise NotImplementedError
@property
def precision(self) -> float:
"""Midea Water Heater precision."""
return float(PRECISION_WHOLE)
@property
def temperature_unit(self) -> UnitOfTemperature:
"""Midea Water Heater temperature unix."""
return UnitOfTemperature.CELSIUS
@property
def current_operation(self) -> str | None:
"""Midea Water Heater current operation."""
return cast(
"str",
(
self._device.get_attribute("mode")
if self._device.get_attribute("power")
else STATE_OFF
),
)
@property
def current_temperature(self) -> float:
"""Midea Water Heater current temperature."""
return cast("float", self._device.get_attribute("current_temperature"))
@property
def target_temperature(self) -> float:
"""Midea Water Heater target temperature."""
return cast("float", self._device.get_attribute("target_temperature"))
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Water Heater set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
# input target_temperature should be float
temperature = float(kwargs[ATTR_TEMPERATURE])
self._device.set_attribute("target_temperature", temperature)
def set_operation_mode(self, operation_mode: str) -> None:
"""Midea Water Heater set operation mode."""
self._device.set_attribute(attr="mode", value=operation_mode)
@property
def operation_list(self) -> list[str] | None:
"""Midea Water Heater operation list."""
if not hasattr(self._device, "preset_modes"):
return None
return cast("list", self._device.preset_modes)
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Water Heater turn on."""
self._device.set_attribute(attr="power", value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea Water Heater turn off."""
self._device.set_attribute(attr="power", value=False)
async def async_turn_on(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Water Heater async turn on."""
await self.hass.async_add_executor_job(ft.partial(self.turn_on, **kwargs))
async def async_turn_off(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea Water Heater async off."""
await self.hass.async_add_executor_job(ft.partial(self.turn_off, **kwargs))
def update_state(self, status: Any) -> None: # noqa: ANN401, ARG002
"""Midea Water Heater update state."""
if not self.hass:
_LOGGER.warning(
"Water update_state skipped for %s [%s]: HASS is None",
self.name,
type(self),
)
return
self.schedule_update_ha_state()
class MideaE2WaterHeater(MideaWaterHeater):
"""Midea E2 Water Heater Entries."""
_device: MideaE2Device
def __init__(self, device: MideaE2Device, entity_key: str) -> None:
"""Midea E2 Water Heater entity init."""
super().__init__(device, entity_key)
@property
def current_operation(self) -> str:
"""Midea E2 Water Heater current operation."""
return str(
STATE_ON if self._device.get_attribute(E2Attributes.power) else STATE_OFF,
)
@property
def min_temp(self) -> float:
"""Midea E2 Water Heater min temperature."""
return E2_TEMPERATURE_MIN
@property
def max_temp(self) -> float:
"""Midea E2 Water Heater max temperature."""
return E2_TEMPERATURE_MAX
@property
def supported_features(self) -> WaterHeaterEntityFeature:
"""Midea E2 Water Heater supported features."""
return (
WaterHeaterEntityFeature.TARGET_TEMPERATURE
| WaterHeaterEntityFeature.ON_OFF
)
class MideaE3WaterHeater(MideaWaterHeater):
"""Midea E3 Water Heater Entries."""
_device: MideaE3Device
def __init__(self, device: MideaE3Device, entity_key: str) -> None:
"""Midea E3 Water Heater entity init."""
super().__init__(device, entity_key)
@property
def min_temp(self) -> float:
"""Midea E3 Water Heater min temperature."""
return E3_TEMPERATURE_MIN
@property
def max_temp(self) -> float:
"""Midea E3 Water Heater max temperature."""
return E3_TEMPERATURE_MAX
@property
def precision(self) -> float:
"""Midea E3 Water Heater precision."""
return float(
PRECISION_HALVES if self._device.precision_halves else PRECISION_WHOLE,
)
@property
def current_operation(self) -> str:
"""Midea E3 Water Heater current operation."""
return str(
STATE_ON if self._device.get_attribute("power") else STATE_OFF,
)
class MideaC3WaterHeater(MideaWaterHeater):
"""Midea C3 Water Heater Entries."""
_device: MideaC3Device
def __init__(self, device: MideaC3Device, entity_key: str) -> None:
"""Midea C3 Water Heater entity init."""
super().__init__(device, entity_key)
@property
def current_operation(self) -> str:
"""Midea C3 Water Heater current operation."""
return str(
(
STATE_ON
if self._device.get_attribute(C3Attributes.dhw_power)
else STATE_OFF
),
)
@property
def current_temperature(self) -> float:
"""Midea C3 Water Heater current temperature."""
return cast(
"float",
self._device.get_attribute(C3Attributes.tank_actual_temperature),
)
@property
def target_temperature(self) -> float:
"""Midea C3 Water Heater target temperature."""
return cast("float", self._device.get_attribute(C3Attributes.dhw_target_temp))
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea C3 Water Heater set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
temperature = float(kwargs[ATTR_TEMPERATURE])
self._device.set_attribute(C3Attributes.dhw_target_temp, temperature)
@property
def min_temp(self) -> float:
"""Midea C3 Water Heater min temperature."""
return cast("float", self._device.get_attribute(C3Attributes.dhw_temp_min))
@property
def max_temp(self) -> float:
"""Midea C3 Water Heater max temperature."""
return cast("float", self._device.get_attribute(C3Attributes.dhw_temp_max))
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea C3 Water Heater turn on."""
self._device.set_attribute(attr=C3Attributes.dhw_power, value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea C3 Water Heater turn off."""
self._device.set_attribute(attr=C3Attributes.dhw_power, value=False)
class MideaE6WaterHeater(MideaWaterHeater):
"""Midea E6 Water Heater Entries."""
_device: MideaE6Device
_powers: ClassVar[list[E6Attributes]] = [
E6Attributes.heating_power,
E6Attributes.main_power,
]
_current_temperatures: ClassVar[list[E6Attributes]] = [
E6Attributes.heating_leaving_temperature,
E6Attributes.bathing_leaving_temperature,
]
_target_temperatures: ClassVar[list[E6Attributes]] = [
E6Attributes.heating_temperature,
E6Attributes.bathing_temperature,
]
def __init__(self, device: MideaE6Device, entity_key: str, use: int) -> None:
"""Midea E6 Water Heater entity init."""
super().__init__(device, entity_key)
self._use = use
self._power_attr = MideaE6WaterHeater._powers[self._use]
self._current_temperature_attr = MideaE6WaterHeater._current_temperatures[
self._use
]
self._target_temperature_attr = MideaE6WaterHeater._target_temperatures[
self._use
]
@property
def current_operation(self) -> str:
"""Midea E6 Water Heater current operation."""
if self._use == 0: # for heating
return str(
(
STATE_ON
if self._device.get_attribute(E6Attributes.main_power)
and self._device.get_attribute(E6Attributes.heating_power)
else STATE_OFF
),
)
# for bathing
return str(
(
STATE_ON
if self._device.get_attribute(E6Attributes.main_power)
else STATE_OFF
),
)
@property
def current_temperature(self) -> float:
"""Midea E6 Water Heater current temperature."""
return cast("float", self._device.get_attribute(self._current_temperature_attr))
@property
def target_temperature(self) -> float:
"""Midea E6 Water Heater target temperature."""
return cast("float", self._device.get_attribute(self._target_temperature_attr))
def set_temperature(self, **kwargs: Any) -> None: # noqa: ANN401
"""Midea E6 Water Heater set temperature."""
if ATTR_TEMPERATURE not in kwargs:
return
temperature = float(kwargs[ATTR_TEMPERATURE])
self._device.set_attribute(self._target_temperature_attr, temperature)
@property
def min_temp(self) -> float:
"""Midea E6 Water Heater min temperature."""
min_temperature = cast(
"list[str]",
self._device.get_attribute(E6Attributes.min_temperature),
)
return cast(
"float",
min_temperature[self._use],
)
@property
def max_temp(self) -> float:
"""Midea E6 Water Heater max temperature."""
max_temperature = cast(
"list[str]",
self._device.get_attribute(E6Attributes.max_temperature),
)
return cast(
"float",
max_temperature[self._use],
)
def turn_on(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea E6 Water Heater turn on."""
self._device.set_attribute(attr=self._power_attr, value=True)
def turn_off(self, **kwargs: Any) -> None: # noqa: ANN401, ARG002
"""Midea E6 Water Heater turn off."""
self._device.set_attribute(attr=self._power_attr, value=False)
class MideaCDWaterHeater(MideaWaterHeater):
"""Midea CD Water Heater Entries."""
_device: MideaCDDevice
def __init__(self, device: MideaCDDevice, entity_key: str) -> None:
"""Midea CD Water Heater entity init."""
super().__init__(device, entity_key)
@property
def supported_features(self) -> WaterHeaterEntityFeature:
"""Midea CD Water Heater supported features."""
return (
WaterHeaterEntityFeature.TARGET_TEMPERATURE
| WaterHeaterEntityFeature.OPERATION_MODE
)
@property
def min_temp(self) -> float:
"""Midea CD Water Heater min temperature."""
return cast("float", self._device.get_attribute(CDAttributes.min_temperature))
@property
def max_temp(self) -> float:
"""Midea CD Water Heater max temperature."""
return cast("float", self._device.get_attribute(CDAttributes.max_temperature))