From ddbfed354e2a2d878e8c730059487961f97c847f Mon Sep 17 00:00:00 2001
From: Nyro <cedric@nyro.dev>
Date: Fri, 4 Nov 2022 10:42:58 +0100
Subject: [PATCH] Add Overkiz AtlanticPassAPCDHW (#78665)

* Add Overkiz AtlanticPassAPCDHW

* Remove unnecessary line

* Improve atlantic pass_apcdhw for operation and target temprature

* Remove async_execute_commands

* Fix small code issues for Overkiz AtlanticPassAPCDHW

* Update homeassistant/components/overkiz/const.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py

Co-authored-by: Quentame <polletquentin74@me.com>

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py

Co-authored-by: Quentame <polletquentin74@me.com>

* Fix small issues

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>
Co-authored-by: Quentame <polletquentin74@me.com>
---
 .coveragerc                                   |   2 +
 homeassistant/components/overkiz/const.py     |   2 +
 .../components/overkiz/water_heater.py        |  28 ++++
 .../overkiz/water_heater_entities/__init__.py |   8 +
 .../atlantic_pass_apc_dhw.py                  | 145 ++++++++++++++++++
 5 files changed, 185 insertions(+)
 create mode 100644 homeassistant/components/overkiz/water_heater.py
 create mode 100644 homeassistant/components/overkiz/water_heater_entities/__init__.py
 create mode 100644 homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py

diff --git a/.coveragerc b/.coveragerc
index eb60f320a74..84f061fbd35 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -953,6 +953,8 @@ omit =
     homeassistant/components/overkiz/sensor.py
     homeassistant/components/overkiz/siren.py
     homeassistant/components/overkiz/switch.py
+    homeassistant/components/overkiz/water_heater.py
+    homeassistant/components/overkiz/water_heater_entities/*
     homeassistant/components/ovo_energy/__init__.py
     homeassistant/components/ovo_energy/const.py
     homeassistant/components/ovo_energy/sensor.py
diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py
index 70477bbcdb2..4645b058182 100644
--- a/homeassistant/components/overkiz/const.py
+++ b/homeassistant/components/overkiz/const.py
@@ -32,6 +32,7 @@ PLATFORMS: list[Platform] = [
     Platform.SENSOR,
     Platform.SIREN,
     Platform.SWITCH,
+    Platform.WATER_HEATER,
 ]
 
 IGNORED_OVERKIZ_DEVICES: list[UIClass | UIWidget] = [
@@ -64,6 +65,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = {
     UIWidget.ATLANTIC_ELECTRICAL_HEATER: Platform.CLIMATE,  # widgetName, uiClass is HeatingSystem (not supported)
     UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: Platform.CLIMATE,  # widgetName, uiClass is HeatingSystem (not supported)
     UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: Platform.CLIMATE,  # widgetName, uiClass is HeatingSystem (not supported)
+    UIWidget.ATLANTIC_PASS_APC_DHW: Platform.WATER_HEATER,  # widgetName, uiClass is WaterHeatingSystem (not supported)
     UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: Platform.CLIMATE,  # widgetName, uiClass is HeatingSystem (not supported)
     UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: Platform.CLIMATE,  # widgetName, uiClass is HeatingSystem (not supported)
     UIWidget.DOMESTIC_HOT_WATER_TANK: Platform.SWITCH,  # widgetName, uiClass is WaterHeatingSystem (not supported)
diff --git a/homeassistant/components/overkiz/water_heater.py b/homeassistant/components/overkiz/water_heater.py
new file mode 100644
index 00000000000..e22f442c266
--- /dev/null
+++ b/homeassistant/components/overkiz/water_heater.py
@@ -0,0 +1,28 @@
+"""Support for Overkiz water heater devices."""
+from __future__ import annotations
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import Platform
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers.entity_platform import AddEntitiesCallback
+
+from . import HomeAssistantOverkizData
+from .const import DOMAIN
+from .water_heater_entities import WIDGET_TO_WATER_HEATER_ENTITY
+
+
+async def async_setup_entry(
+    hass: HomeAssistant,
+    entry: ConfigEntry,
+    async_add_entities: AddEntitiesCallback,
+) -> None:
+    """Set up the Overkiz DHW from a config entry."""
+    data: HomeAssistantOverkizData = hass.data[DOMAIN][entry.entry_id]
+
+    async_add_entities(
+        WIDGET_TO_WATER_HEATER_ENTITY[device.widget](
+            device.device_url, data.coordinator
+        )
+        for device in data.platforms[Platform.WATER_HEATER]
+        if device.widget in WIDGET_TO_WATER_HEATER_ENTITY
+    )
diff --git a/homeassistant/components/overkiz/water_heater_entities/__init__.py b/homeassistant/components/overkiz/water_heater_entities/__init__.py
new file mode 100644
index 00000000000..e03585da56d
--- /dev/null
+++ b/homeassistant/components/overkiz/water_heater_entities/__init__.py
@@ -0,0 +1,8 @@
+"""Water heater entities for the Overkiz (by Somfy) integration."""
+from pyoverkiz.enums.ui import UIWidget
+
+from .atlantic_pass_apc_dhw import AtlanticPassAPCDHW
+
+WIDGET_TO_WATER_HEATER_ENTITY = {
+    UIWidget.ATLANTIC_PASS_APC_DHW: AtlanticPassAPCDHW,
+}
diff --git a/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py b/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py
new file mode 100644
index 00000000000..7c2ea6ff2d8
--- /dev/null
+++ b/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py
@@ -0,0 +1,145 @@
+"""Support for Atlantic Pass APC DHW."""
+
+from typing import Any, cast
+
+from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState
+
+from homeassistant.components.water_heater import (
+    STATE_ECO,
+    STATE_HEAT_PUMP,
+    STATE_OFF,
+    STATE_PERFORMANCE,
+    WaterHeaterEntity,
+    WaterHeaterEntityFeature,
+)
+from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
+
+from ..entity import OverkizEntity
+
+
+class AtlanticPassAPCDHW(OverkizEntity, WaterHeaterEntity):
+    """Representation of Atlantic Pass APC DHW."""
+
+    _attr_temperature_unit = TEMP_CELSIUS
+    _attr_supported_features = (
+        WaterHeaterEntityFeature.TARGET_TEMPERATURE
+        | WaterHeaterEntityFeature.OPERATION_MODE
+        | WaterHeaterEntityFeature.AWAY_MODE
+    )
+    _attr_operation_list = [STATE_OFF, STATE_HEAT_PUMP, STATE_PERFORMANCE]
+
+    @property
+    def target_temperature(self) -> float:
+        """Return the temperature corresponding to the PRESET."""
+        if self.is_boost_mode_on:
+            return cast(
+                float,
+                self.executor.select_state(
+                    OverkizState.CORE_COMFORT_TARGET_DWH_TEMPERATURE
+                ),
+            )
+
+        if self.is_eco_mode_on:
+            return cast(
+                float,
+                self.executor.select_state(
+                    OverkizState.CORE_ECO_TARGET_DWH_TEMPERATURE
+                ),
+            )
+
+        return cast(
+            float,
+            self.executor.select_state(OverkizState.CORE_TARGET_DWH_TEMPERATURE),
+        )
+
+    async def async_set_temperature(self, **kwargs: Any) -> None:
+        """Set new temperature."""
+        temperature = kwargs[ATTR_TEMPERATURE]
+
+        if self.is_eco_mode_on:
+            await self.executor.async_execute_command(
+                OverkizCommand.SET_ECO_TARGET_DHW_TEMPERATURE, temperature
+            )
+            await self.executor.async_execute_command(
+                OverkizCommand.REFRESH_ECO_TARGET_DWH_TEMPERATURE
+            )
+        else:
+            await self.executor.async_execute_command(
+                OverkizCommand.SET_COMFORT_TARGET_DHW_TEMPERATURE, temperature
+            )
+            await self.executor.async_execute_command(
+                OverkizCommand.REFRESH_COMFORT_TARGET_DWH_TEMPERATURE
+            )
+        await self.executor.async_execute_command(
+            OverkizCommand.REFRESH_TARGET_DWH_TEMPERATURE
+        )
+
+    @property
+    def is_boost_mode_on(self) -> bool:
+        """Return true if boost mode is on."""
+        return (
+            self.executor.select_state(OverkizState.CORE_BOOST_ON_OFF)
+            == OverkizCommandParam.ON
+        )
+
+    @property
+    def is_eco_mode_on(self) -> bool:
+        """Return true if eco mode is on."""
+        return (
+            self.executor.select_state(OverkizState.IO_PASS_APCDWH_MODE)
+            == OverkizCommandParam.ECO
+        )
+
+    @property
+    def is_away_mode_on(self) -> bool:
+        """Return true if away mode is on."""
+        return (
+            self.executor.select_state(OverkizState.CORE_DWH_ON_OFF)
+            == OverkizCommandParam.OFF
+        )
+
+    @property
+    def current_operation(self) -> str:
+        """Return current operation."""
+        if self.is_boost_mode_on:
+            return STATE_PERFORMANCE
+        if self.is_eco_mode_on:
+            return STATE_ECO
+        if self.is_away_mode_on:
+            return STATE_OFF
+        return STATE_HEAT_PUMP
+
+    async def async_set_operation_mode(self, operation_mode: str) -> None:
+        """Set new operation mode."""
+        boost_state = OverkizCommandParam.OFF
+        regular_state = OverkizCommandParam.OFF
+        if operation_mode == STATE_PERFORMANCE:
+            boost_state = OverkizCommandParam.ON
+            regular_state = OverkizCommandParam.ON
+        elif operation_mode == STATE_HEAT_PUMP:
+            regular_state = OverkizCommandParam.ON
+
+        await self.executor.async_execute_command(
+            OverkizCommand.SET_BOOST_ON_OFF_STATE, boost_state
+        )
+        await self.executor.async_execute_command(
+            OverkizCommand.SET_DHW_ON_OFF_STATE, regular_state
+        )
+
+    async def async_turn_away_mode_on(self) -> None:
+        """Turn away mode on."""
+        await self.executor.async_execute_command(
+            OverkizCommand.SET_BOOST_ON_OFF_STATE, OverkizCommandParam.OFF
+        )
+        await self.executor.async_execute_command(
+            OverkizCommand.SET_DHW_ON_OFF_STATE, OverkizCommandParam.OFF
+        )
+
+    async def async_turn_away_mode_off(self) -> None:
+        """Turn away mode off."""
+        await self.executor.async_execute_command(
+            OverkizCommand.SET_BOOST_ON_OFF_STATE, OverkizCommandParam.OFF
+        )
+        await self.executor.async_execute_command(
+            OverkizCommand.SET_DHW_ON_OFF_STATE, OverkizCommandParam.ON
+        )
-- 
GitLab