diff --git a/homeassistant/components/overkiz/climate_entities/__init__.py b/homeassistant/components/overkiz/climate_entities/__init__.py index e70315e099d..9d54c04422a 100644 --- a/homeassistant/components/overkiz/climate_entities/__init__.py +++ b/homeassistant/components/overkiz/climate_entities/__init__.py @@ -10,6 +10,7 @@ from .atlantic_heat_recovery_ventilation import AtlanticHeatRecoveryVentilation from .atlantic_pass_apc_heating_zone import AtlanticPassAPCHeatingZone from .atlantic_pass_apc_zone_control import AtlanticPassAPCZoneControl from .somfy_thermostat import SomfyThermostat +from .valve_heating_temperature_interface import ValveHeatingTemperatureInterface WIDGET_TO_CLIMATE_ENTITY = { UIWidget.ATLANTIC_ELECTRICAL_HEATER: AtlanticElectricalHeater, @@ -21,4 +22,5 @@ WIDGET_TO_CLIMATE_ENTITY = { UIWidget.ATLANTIC_PASS_APC_HEATING_ZONE: AtlanticPassAPCHeatingZone, UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: AtlanticPassAPCZoneControl, UIWidget.SOMFY_THERMOSTAT: SomfyThermostat, + UIWidget.VALVE_HEATING_TEMPERATURE_INTERFACE: ValveHeatingTemperatureInterface, } diff --git a/homeassistant/components/overkiz/climate_entities/valve_heating_temperature_interface.py b/homeassistant/components/overkiz/climate_entities/valve_heating_temperature_interface.py new file mode 100644 index 00000000000..fdaf0d61f1f --- /dev/null +++ b/homeassistant/components/overkiz/climate_entities/valve_heating_temperature_interface.py @@ -0,0 +1,137 @@ +"""Support for ValveHeatingTemperatureInterface.""" +from __future__ import annotations + +from typing import Any, cast + +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState + +from homeassistant.components.climate import ( + PRESET_AWAY, + PRESET_COMFORT, + PRESET_ECO, + PRESET_NONE, + ClimateEntity, + ClimateEntityFeature, + HVACAction, + HVACMode, + UnitOfTemperature, +) +from homeassistant.const import ATTR_TEMPERATURE + +from ..const import DOMAIN +from ..coordinator import OverkizDataUpdateCoordinator +from ..entity import OverkizEntity + +PRESET_MANUAL = "manual" +PRESET_FROST_PROTECTION = "frost_protection" + +OVERKIZ_TO_HVAC_ACTION: dict[str, HVACAction] = { + OverkizCommandParam.OPEN: HVACAction.HEATING, + OverkizCommandParam.CLOSED: HVACAction.IDLE, +} + +OVERKIZ_TO_PRESET_MODE: dict[str, str] = { + OverkizCommandParam.GEOFENCING_MODE: PRESET_NONE, + OverkizCommandParam.SUDDEN_DROP_MODE: PRESET_NONE, + OverkizCommandParam.AWAY: PRESET_AWAY, + OverkizCommandParam.COMFORT: PRESET_COMFORT, + OverkizCommandParam.ECO: PRESET_ECO, + OverkizCommandParam.FROSTPROTECTION: PRESET_FROST_PROTECTION, + OverkizCommandParam.MANUAL: PRESET_MANUAL, +} +PRESET_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_PRESET_MODE.items()} + +TEMPERATURE_SENSOR_DEVICE_INDEX = 2 + + +class ValveHeatingTemperatureInterface(OverkizEntity, ClimateEntity): + """Representation of Valve Heating Temperature Interface device.""" + + _attr_hvac_mode = HVACMode.HEAT + _attr_hvac_modes = [HVACMode.HEAT] + _attr_preset_modes = [*PRESET_MODE_TO_OVERKIZ] + _attr_supported_features = ( + ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE + ) + _attr_temperature_unit = UnitOfTemperature.CELSIUS + _attr_translation_key = DOMAIN + + def __init__( + self, device_url: str, coordinator: OverkizDataUpdateCoordinator + ) -> None: + """Init method.""" + super().__init__(device_url, coordinator) + self.temperature_device = self.executor.linked_device( + TEMPERATURE_SENSOR_DEVICE_INDEX + ) + + self._attr_min_temp = cast( + float, self.executor.select_state(OverkizState.CORE_MIN_SETPOINT) + ) + self._attr_max_temp = cast( + float, self.executor.select_state(OverkizState.CORE_MAX_SETPOINT) + ) + + @property + def hvac_action(self) -> str: + """Return the current running hvac operation.""" + return OVERKIZ_TO_HVAC_ACTION[ + cast(str, self.executor.select_state(OverkizState.CORE_OPEN_CLOSED_VALVE)) + ] + + @property + def target_temperature(self) -> float: + """Return the temperature.""" + return cast( + float, self.executor.select_state(OverkizState.CORE_TARGET_TEMPERATURE) + ) + + @property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + if temperature := self.temperature_device.states[OverkizState.CORE_TEMPERATURE]: + return temperature.value_as_float + + return None + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new temperature.""" + temperature = kwargs[ATTR_TEMPERATURE] + + await self.executor.async_execute_command( + OverkizCommand.SET_DEROGATION, + float(temperature), + OverkizCommandParam.FURTHER_NOTICE, + ) + + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set new target hvac mode.""" + return + + @property + def preset_mode(self) -> str: + """Return the current preset mode, e.g., home, away, temp.""" + return OVERKIZ_TO_PRESET_MODE[ + cast( + str, self.executor.select_state(OverkizState.IO_DEROGATION_HEATING_MODE) + ) + ] + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set new preset mode.""" + + # If we want to switch to manual mode via a preset, we need to pass in a temperature + # Manual mode will be on automatically if an user sets a temperature + if preset_mode == PRESET_MANUAL: + if current_temperature := self.current_temperature: + await self.executor.async_execute_command( + OverkizCommand.SET_DEROGATION, + current_temperature, + OverkizCommandParam.FURTHER_NOTICE, + ) + else: + await self.executor.async_execute_command( + OverkizCommand.SET_DEROGATION, + PRESET_MODE_TO_OVERKIZ[preset_mode], + OverkizCommandParam.FURTHER_NOTICE, + ) diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index d176a137544..806ba435c20 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -83,6 +83,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIWidget.STATEFUL_ALARM_CONTROLLER: Platform.ALARM_CONTROL_PANEL, # widgetName, uiClass is Alarm (not supported) UIWidget.STATELESS_EXTERIOR_HEATING: Platform.SWITCH, # widgetName, uiClass is ExteriorHeatingSystem (not supported) UIWidget.TSKALARM_CONTROLLER: Platform.ALARM_CONTROL_PANEL, # widgetName, uiClass is Alarm (not supported) + UIWidget.VALVE_HEATING_TEMPERATURE_INTERFACE: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) } # Map Overkiz camelCase to Home Assistant snake_case for translation