234 lines
7.7 KiB
Python
234 lines
7.7 KiB
Python
"""Support for water heaters through the SmartThings cloud API."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from pysmartthings import Attribute, Capability, Command, SmartThings
|
|
|
|
from homeassistant.components.water_heater import (
|
|
DEFAULT_MAX_TEMP,
|
|
DEFAULT_MIN_TEMP,
|
|
STATE_ECO,
|
|
STATE_HEAT_PUMP,
|
|
STATE_HIGH_DEMAND,
|
|
STATE_PERFORMANCE,
|
|
WaterHeaterEntity,
|
|
WaterHeaterEntityFeature,
|
|
)
|
|
from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, UnitOfTemperature
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
from homeassistant.util.unit_conversion import TemperatureConverter
|
|
|
|
from . import FullDevice, SmartThingsConfigEntry
|
|
from .const import MAIN, UNIT_MAP
|
|
from .entity import SmartThingsEntity
|
|
|
|
OPERATION_MAP_TO_HA: dict[str, str] = {
|
|
"eco": STATE_ECO,
|
|
"std": STATE_HEAT_PUMP,
|
|
"force": STATE_HIGH_DEMAND,
|
|
"power": STATE_PERFORMANCE,
|
|
}
|
|
|
|
HA_TO_OPERATION_MAP = {v: k for k, v in OPERATION_MAP_TO_HA.items()}
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: SmartThingsConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Add water heaters for a config entry."""
|
|
entry_data = entry.runtime_data
|
|
async_add_entities(
|
|
SmartThingsWaterHeater(entry_data.client, device)
|
|
for device in entry_data.devices.values()
|
|
if all(
|
|
capability in device.status[MAIN]
|
|
for capability in (
|
|
Capability.SWITCH,
|
|
Capability.AIR_CONDITIONER_MODE,
|
|
Capability.TEMPERATURE_MEASUREMENT,
|
|
Capability.CUSTOM_THERMOSTAT_SETPOINT_CONTROL,
|
|
Capability.THERMOSTAT_COOLING_SETPOINT,
|
|
Capability.SAMSUNG_CE_EHS_THERMOSTAT,
|
|
Capability.CUSTOM_OUTING_MODE,
|
|
)
|
|
)
|
|
and device.status[MAIN][Capability.TEMPERATURE_MEASUREMENT][
|
|
Attribute.TEMPERATURE
|
|
].value
|
|
is not None
|
|
)
|
|
|
|
|
|
class SmartThingsWaterHeater(SmartThingsEntity, WaterHeaterEntity):
|
|
"""Define a SmartThings Water Heater."""
|
|
|
|
_attr_name = None
|
|
_attr_translation_key = "water_heater"
|
|
|
|
def __init__(self, client: SmartThings, device: FullDevice) -> None:
|
|
"""Init the class."""
|
|
super().__init__(
|
|
client,
|
|
device,
|
|
{
|
|
Capability.SWITCH,
|
|
Capability.AIR_CONDITIONER_MODE,
|
|
Capability.TEMPERATURE_MEASUREMENT,
|
|
Capability.CUSTOM_THERMOSTAT_SETPOINT_CONTROL,
|
|
Capability.THERMOSTAT_COOLING_SETPOINT,
|
|
Capability.CUSTOM_OUTING_MODE,
|
|
},
|
|
)
|
|
unit = self._internal_state[Capability.TEMPERATURE_MEASUREMENT][
|
|
Attribute.TEMPERATURE
|
|
].unit
|
|
assert unit is not None
|
|
self._attr_temperature_unit = UNIT_MAP[unit]
|
|
|
|
@property
|
|
def supported_features(self) -> WaterHeaterEntityFeature:
|
|
"""Return the supported features."""
|
|
features = (
|
|
WaterHeaterEntityFeature.OPERATION_MODE
|
|
| WaterHeaterEntityFeature.AWAY_MODE
|
|
| WaterHeaterEntityFeature.ON_OFF
|
|
)
|
|
if self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "on":
|
|
features |= WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
|
return features
|
|
|
|
@property
|
|
def min_temp(self) -> float:
|
|
"""Return the minimum temperature."""
|
|
min_temperature = TemperatureConverter.convert(
|
|
DEFAULT_MIN_TEMP, UnitOfTemperature.FAHRENHEIT, self._attr_temperature_unit
|
|
)
|
|
return min(min_temperature, self.target_temperature_low)
|
|
|
|
@property
|
|
def max_temp(self) -> float:
|
|
"""Return the maximum temperature."""
|
|
max_temperature = TemperatureConverter.convert(
|
|
DEFAULT_MAX_TEMP, UnitOfTemperature.FAHRENHEIT, self._attr_temperature_unit
|
|
)
|
|
return max(max_temperature, self.target_temperature_high)
|
|
|
|
@property
|
|
def operation_list(self) -> list[str]:
|
|
"""Return the list of available operation modes."""
|
|
return [
|
|
STATE_OFF,
|
|
*(
|
|
OPERATION_MAP_TO_HA[mode]
|
|
for mode in self.get_attribute_value(
|
|
Capability.AIR_CONDITIONER_MODE, Attribute.SUPPORTED_AC_MODES
|
|
)
|
|
if mode in OPERATION_MAP_TO_HA
|
|
),
|
|
]
|
|
|
|
@property
|
|
def current_operation(self) -> str | None:
|
|
"""Return the current operation mode."""
|
|
if self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "off":
|
|
return STATE_OFF
|
|
return OPERATION_MAP_TO_HA.get(
|
|
self.get_attribute_value(
|
|
Capability.AIR_CONDITIONER_MODE, Attribute.AIR_CONDITIONER_MODE
|
|
)
|
|
)
|
|
|
|
@property
|
|
def current_temperature(self) -> float | None:
|
|
"""Return the current temperature."""
|
|
return self.get_attribute_value(
|
|
Capability.TEMPERATURE_MEASUREMENT, Attribute.TEMPERATURE
|
|
)
|
|
|
|
@property
|
|
def target_temperature(self) -> float | None:
|
|
"""Return the target temperature."""
|
|
return self.get_attribute_value(
|
|
Capability.THERMOSTAT_COOLING_SETPOINT, Attribute.COOLING_SETPOINT
|
|
)
|
|
|
|
@property
|
|
def target_temperature_low(self) -> float:
|
|
"""Return the minimum temperature."""
|
|
return self.get_attribute_value(
|
|
Capability.CUSTOM_THERMOSTAT_SETPOINT_CONTROL, Attribute.MINIMUM_SETPOINT
|
|
)
|
|
|
|
@property
|
|
def target_temperature_high(self) -> float:
|
|
"""Return the maximum temperature."""
|
|
return self.get_attribute_value(
|
|
Capability.CUSTOM_THERMOSTAT_SETPOINT_CONTROL, Attribute.MAXIMUM_SETPOINT
|
|
)
|
|
|
|
@property
|
|
def is_away_mode_on(self) -> bool:
|
|
"""Return if away mode is on."""
|
|
return (
|
|
self.get_attribute_value(
|
|
Capability.CUSTOM_OUTING_MODE, Attribute.OUTING_MODE
|
|
)
|
|
== "on"
|
|
)
|
|
|
|
async def async_set_operation_mode(self, operation_mode: str) -> None:
|
|
"""Set new target operation mode."""
|
|
if operation_mode == STATE_OFF:
|
|
await self.async_turn_off()
|
|
return
|
|
if self.current_operation == STATE_OFF:
|
|
await self.async_turn_on()
|
|
await self.execute_device_command(
|
|
Capability.AIR_CONDITIONER_MODE,
|
|
Command.SET_AIR_CONDITIONER_MODE,
|
|
argument=HA_TO_OPERATION_MAP[operation_mode],
|
|
)
|
|
|
|
async def async_set_temperature(self, **kwargs: Any) -> None:
|
|
"""Set new target temperature."""
|
|
await self.execute_device_command(
|
|
Capability.THERMOSTAT_COOLING_SETPOINT,
|
|
Command.SET_COOLING_SETPOINT,
|
|
argument=kwargs[ATTR_TEMPERATURE],
|
|
)
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn the water heater on."""
|
|
await self.execute_device_command(
|
|
Capability.SWITCH,
|
|
Command.ON,
|
|
)
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn the water heater off."""
|
|
await self.execute_device_command(
|
|
Capability.SWITCH,
|
|
Command.OFF,
|
|
)
|
|
|
|
async def async_turn_away_mode_on(self) -> None:
|
|
"""Turn away mode on."""
|
|
await self.execute_device_command(
|
|
Capability.CUSTOM_OUTING_MODE,
|
|
Command.SET_OUTING_MODE,
|
|
argument="on",
|
|
)
|
|
|
|
async def async_turn_away_mode_off(self) -> None:
|
|
"""Turn away mode off."""
|
|
await self.execute_device_command(
|
|
Capability.CUSTOM_OUTING_MODE,
|
|
Command.SET_OUTING_MODE,
|
|
argument="off",
|
|
)
|