diff --git a/.coveragerc b/.coveragerc index 696cd5ce3b0..360bd5f6911 100644 --- a/.coveragerc +++ b/.coveragerc @@ -870,6 +870,8 @@ omit = homeassistant/components/overkiz/__init__.py homeassistant/components/overkiz/binary_sensor.py homeassistant/components/overkiz/button.py + homeassistant/components/overkiz/climate.py + homeassistant/components/overkiz/climate_entities/* homeassistant/components/overkiz/cover.py homeassistant/components/overkiz/cover_entities/* homeassistant/components/overkiz/coordinator.py diff --git a/homeassistant/components/overkiz/climate.py b/homeassistant/components/overkiz/climate.py new file mode 100644 index 00000000000..a94c731ec8f --- /dev/null +++ b/homeassistant/components/overkiz/climate.py @@ -0,0 +1,26 @@ +"""Support for Overkiz climate 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 .climate_entities import WIDGET_TO_CLIMATE_ENTITY +from .const import DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Overkiz climate from a config entry.""" + data: HomeAssistantOverkizData = hass.data[DOMAIN][entry.entry_id] + + async_add_entities( + WIDGET_TO_CLIMATE_ENTITY[device.widget](device.device_url, data.coordinator) + for device in data.platforms[Platform.CLIMATE] + if device.widget in WIDGET_TO_CLIMATE_ENTITY + ) diff --git a/homeassistant/components/overkiz/climate_entities/__init__.py b/homeassistant/components/overkiz/climate_entities/__init__.py new file mode 100644 index 00000000000..0e98b7c7e21 --- /dev/null +++ b/homeassistant/components/overkiz/climate_entities/__init__.py @@ -0,0 +1,8 @@ +"""Climate entities for the Overkiz (by Somfy) integration.""" +from pyoverkiz.enums.ui import UIWidget + +from .atlantic_electrical_heater import AtlanticElectricalHeater + +WIDGET_TO_CLIMATE_ENTITY = { + UIWidget.ATLANTIC_ELECTRICAL_HEATER: AtlanticElectricalHeater, +} diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater.py b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater.py new file mode 100644 index 00000000000..8756b768e52 --- /dev/null +++ b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater.py @@ -0,0 +1,73 @@ +"""Support for Atlantic Electrical Heater.""" +from __future__ import annotations + +from typing import cast + +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState + +from homeassistant.components.climate import ( + HVAC_MODE_OFF, + SUPPORT_PRESET_MODE, + ClimateEntity, +) +from homeassistant.components.climate.const import ( + HVAC_MODE_HEAT, + PRESET_COMFORT, + PRESET_ECO, + PRESET_NONE, +) +from homeassistant.components.overkiz.entity import OverkizEntity +from homeassistant.const import TEMP_CELSIUS + +PRESET_FROST_PROTECTION = "frost_protection" + +OVERKIZ_TO_HVAC_MODES: dict[str, str] = { + OverkizCommandParam.ON: HVAC_MODE_HEAT, + OverkizCommandParam.COMFORT: HVAC_MODE_HEAT, + OverkizCommandParam.OFF: HVAC_MODE_OFF, +} +HVAC_MODES_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_HVAC_MODES.items()} + +OVERKIZ_TO_PRESET_MODES: dict[str, str] = { + OverkizCommandParam.OFF: PRESET_NONE, + OverkizCommandParam.FROSTPROTECTION: PRESET_FROST_PROTECTION, + OverkizCommandParam.ECO: PRESET_ECO, + OverkizCommandParam.COMFORT: PRESET_COMFORT, +} + +PRESET_MODES_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_PRESET_MODES.items()} + + +class AtlanticElectricalHeater(OverkizEntity, ClimateEntity): + """Representation of Atlantic Electrical Heater.""" + + _attr_hvac_modes = [*HVAC_MODES_TO_OVERKIZ] + _attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ] + _attr_supported_features = SUPPORT_PRESET_MODE + _attr_temperature_unit = TEMP_CELSIUS + + @property + def hvac_mode(self) -> str: + """Return hvac operation ie. heat, cool mode.""" + return OVERKIZ_TO_HVAC_MODES[ + cast(str, self.executor.select_state(OverkizState.CORE_ON_OFF)) + ] + + async def async_set_hvac_mode(self, hvac_mode: str) -> None: + """Set new target hvac mode.""" + await self.executor.async_execute_command( + OverkizCommand.SET_HEATING_LEVEL, HVAC_MODES_TO_OVERKIZ[hvac_mode] + ) + + @property + def preset_mode(self) -> str | None: + """Return the current preset mode, e.g., home, away, temp.""" + return OVERKIZ_TO_PRESET_MODES[ + cast(str, self.executor.select_state(OverkizState.IO_TARGET_HEATING_LEVEL)) + ] + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set new preset mode.""" + await self.executor.async_execute_command( + OverkizCommand.SET_HEATING_LEVEL, PRESET_MODES_TO_OVERKIZ[preset_mode] + ) diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index 60a9163df5f..7f031ef3b6a 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -21,6 +21,7 @@ UPDATE_INTERVAL_ALL_ASSUMED_STATE: Final = timedelta(minutes=60) PLATFORMS: list[Platform] = [ Platform.BINARY_SENSOR, Platform.BUTTON, + Platform.CLIMATE, Platform.COVER, Platform.LIGHT, Platform.LOCK, @@ -57,6 +58,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIClass.SWINGING_SHUTTER: Platform.COVER, UIClass.VENETIAN_BLIND: Platform.COVER, UIClass.WINDOW: Platform.COVER, + UIWidget.ATLANTIC_ELECTRICAL_HEATER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.DOMESTIC_HOT_WATER_TANK: Platform.SWITCH, # widgetName, uiClass is WaterHeatingSystem (not supported) UIWidget.MY_FOX_SECURITY_CAMERA: Platform.SWITCH, # widgetName, uiClass is Camera (not supported) UIWidget.RTD_INDOOR_SIREN: Platform.SWITCH, # widgetName, uiClass is Siren (not supported)