From c5fff0dc0644ee990a1cb2c02c44aaf1c96bbfc2 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Wed, 27 Jan 2021 21:03:28 +0100 Subject: [PATCH] Add support for DomesticHotWaterProduction (#353) --- custom_components/tahoma/const.py | 2 + custom_components/tahoma/water_heater.py | 26 ++++ .../domestic_hot_water_production.py | 134 ++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 custom_components/tahoma/water_heater.py create mode 100644 custom_components/tahoma/water_heater_devices/domestic_hot_water_production.py diff --git a/custom_components/tahoma/const.py b/custom_components/tahoma/const.py index 5889cddb0..5894aad67 100644 --- a/custom_components/tahoma/const.py +++ b/custom_components/tahoma/const.py @@ -7,6 +7,7 @@ from homeassistant.components.lock import DOMAIN as LOCK from homeassistant.components.sensor import DOMAIN as SENSOR from homeassistant.components.switch import DOMAIN as SWITCH +from homeassistant.components.water_heater import DOMAIN as WATER_HEATER CONF_HUB = "hub" DOMAIN = "tahoma" @@ -44,6 +45,7 @@ "ContactSensor": BINARY_SENSOR, "Curtain": COVER, "DimmerExteriorHeating": CLIMATE, # widgetName, uiClass is ExteriorHeatingSystem (not supported) + "DomesticHotWaterProduction": WATER_HEATER, # widgetName, uiClass is WaterHeatingSystem (not supported) "DomesticHotWaterTank": SWITCH, # widgetName, uiClass is WaterHeatingSystem (not supported) "DoorLock": LOCK, "ElectricitySensor": SENSOR, diff --git a/custom_components/tahoma/water_heater.py b/custom_components/tahoma/water_heater.py new file mode 100644 index 000000000..847076109 --- /dev/null +++ b/custom_components/tahoma/water_heater.py @@ -0,0 +1,26 @@ +"""Support for TaHoma water heater devices.""" +from homeassistant.components.water_heater import DOMAIN as WATER_HEATER + +from .const import DOMAIN +from .water_heater_devices.domestic_hot_water_production import ( + DomesticHotWaterProduction, +) + +TYPE = { + "DomesticHotWaterProduction": DomesticHotWaterProduction, +} + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up the TaHoma water heater from a config entry.""" + data = hass.data[DOMAIN][entry.entry_id] + coordinator = data["coordinator"] + + water_heater_devices = [device for device in data["entities"].get(WATER_HEATER)] + + entities = [ + TYPE[device.widget](device.deviceurl, coordinator) + for device in water_heater_devices + if device.widget in TYPE + ] + async_add_entities(entities) diff --git a/custom_components/tahoma/water_heater_devices/domestic_hot_water_production.py b/custom_components/tahoma/water_heater_devices/domestic_hot_water_production.py new file mode 100644 index 000000000..d40b58711 --- /dev/null +++ b/custom_components/tahoma/water_heater_devices/domestic_hot_water_production.py @@ -0,0 +1,134 @@ +"""Support for DomesticHotWaterProduction.""" +from homeassistant.components.climate.const import SUPPORT_TARGET_TEMPERATURE +from homeassistant.components.water_heater import ( + STATE_ECO, + SUPPORT_AWAY_MODE, + SUPPORT_OPERATION_MODE, + WaterHeaterEntity, +) +from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS + +from ..tahoma_device import TahomaDevice + +CORE_MAXIMAL_TEMPERATURE_MANUAL_MODE_STATE = "core:MaximalTemperatureManualModeState" +CORE_MINIMAL_TEMPERATURE_MANUAL_MODE_STATE = "core:MinimalTemperatureManualModeState" +CORE_TARGET_TEMPERATURE_STATE = "core:TargetTemperatureState" +CORE_OPERATING_MODE_STATE = "core:OperatingModeState" + +IO_DHW_MODE_STATE = "io:DHWModeState" +IO_MIDDLE_WATER_TEMPERATURE_STATE = "io:MiddleWaterTemperatureState" + +STATE_MANUAL = "manual" +STATE_AUTO = "auto" +STATE_ABSENCE = "absence" +STATE_RELAUNCH = "relaunch" + +COMMAND_SET_TARGET_TEMPERATURE = "setTargetTemperature" +COMMAND_SET_DHW_MODE = "setDHWMode" +COMMAND_SET_CURRENT_OPERATING_MODE = "setCurrentOperatingMode" + +MODE_AUTO = "autoMode" +MODE_MANUAL_ECO_ACTIVE = "manualEcoActive" +MODE_MANUAL_ECO_INACTIVE = "manualEcoInactive" + +MAP_OPERATION_MODES = { + MODE_MANUAL_ECO_ACTIVE: STATE_ECO, + MODE_MANUAL_ECO_INACTIVE: STATE_MANUAL, + MODE_AUTO: STATE_AUTO, +} + +MAP_REVERSE_OPERATION_MODES = {v: k for k, v in MAP_OPERATION_MODES.items()} + + +class DomesticHotWaterProduction(TahomaDevice, WaterHeaterEntity): + """Representation of a DomesticHotWaterProduction Water Heater.""" + + @property + def temperature_unit(self) -> str: + """Return the unit of measurement used by the platform.""" + return TEMP_CELSIUS + + @property + def min_temp(self): + """Return the minimum temperature.""" + return self.select_state(CORE_MINIMAL_TEMPERATURE_MANUAL_MODE_STATE) + + @property + def max_temp(self): + """Return the maximum temperature.""" + return self.select_state(CORE_MAXIMAL_TEMPERATURE_MANUAL_MODE_STATE) + + @property + def current_operation(self): + """Return current operation ie. eco, electric, performance, ...""" + return MAP_OPERATION_MODES[self.select_state(IO_DHW_MODE_STATE)] + + @property + def operation_list(self): + """Return the list of available operation modes.""" + return [*MAP_REVERSE_OPERATION_MODES] + + @property + def current_temperature(self): + """Return the current temperature.""" + return self.select_state(IO_MIDDLE_WATER_TEMPERATURE_STATE) + + @property + def target_temperature(self): + """Return the temperature we try to reach.""" + return self.select_state(CORE_TARGET_TEMPERATURE_STATE) + + @property + def target_temperature_high(self): + """Return the highbound target temperature we try to reach.""" + return self.select_state(CORE_MAXIMAL_TEMPERATURE_MANUAL_MODE_STATE) + + @property + def target_temperature_low(self): + """Return the lowbound target temperature we try to reach.""" + return self.select_state(CORE_MINIMAL_TEMPERATURE_MANUAL_MODE_STATE) + + async def async_set_temperature(self, **kwargs): + """Set new target temperature.""" + target_temperature = kwargs.get(ATTR_TEMPERATURE) + await self.async_execute_command( + COMMAND_SET_TARGET_TEMPERATURE, target_temperature + ) + + async def async_set_operation_mode(self, operation_mode): + """Set new target operation mode.""" + await self.async_execute_command( + COMMAND_SET_DHW_MODE, MAP_REVERSE_OPERATION_MODES[operation_mode] + ) + + @property + def supported_features(self): + """Return the list of supported features.""" + return SUPPORT_OPERATION_MODE | SUPPORT_AWAY_MODE | SUPPORT_TARGET_TEMPERATURE + + @property + def is_away_mode_on(self): + """Return true if away mode is on.""" + return ( + self.select_state(CORE_OPERATING_MODE_STATE).get(STATE_ABSENCE) == STATE_ON + ) + + async def async_turn_away_mode_on(self): + """Turn away mode on.""" + await self.async_execute_command( + COMMAND_SET_CURRENT_OPERATING_MODE, + { + STATE_RELAUNCH: STATE_OFF, + STATE_ABSENCE: STATE_ON, + }, + ) + + async def async_turn_away_mode_off(self): + """Turn away mode off.""" + await self.async_execute_command( + COMMAND_SET_CURRENT_OPERATING_MODE, + { + STATE_RELAUNCH: STATE_OFF, + STATE_ABSENCE: STATE_OFF, + }, + )