Add ability to change heating programs for heat pumps in ViCare integration (#110924)

* heating programs

* fix heating program

* fix heating program

* remove commented code

* simplify

* Update types.py

* update vicare_programs in init
This commit is contained in:
Christopher Fenner 2024-05-14 17:01:34 +02:00 committed by GitHub
parent 83f5133065
commit eca67eb901
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 27 deletions

View File

@ -19,10 +19,6 @@ import requests
import voluptuous as vol
from homeassistant.components.climate import (
PRESET_COMFORT,
PRESET_ECO,
PRESET_HOME,
PRESET_SLEEP,
ClimateEntity,
ClimateEntityFeature,
HVACAction,
@ -78,14 +74,11 @@ VICARE_TO_HA_HVAC_HEATING: dict[str, HVACMode] = {
VICARE_MODE_FORCEDNORMAL: HVACMode.HEAT,
}
VICARE_TO_HA_PRESET_HEATING = {
HeatingProgram.COMFORT: PRESET_COMFORT,
HeatingProgram.ECO: PRESET_ECO,
HeatingProgram.NORMAL: PRESET_HOME,
HeatingProgram.REDUCED: PRESET_SLEEP,
}
HA_TO_VICARE_PRESET_HEATING = {v: k for k, v in VICARE_TO_HA_PRESET_HEATING.items()}
CHANGABLE_HEATING_PROGRAMS = [
HeatingProgram.COMFORT,
HeatingProgram.COMFORT_HEATING,
HeatingProgram.ECO,
]
def _build_entities(
@ -143,7 +136,6 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
_attr_min_temp = VICARE_TEMP_HEATING_MIN
_attr_max_temp = VICARE_TEMP_HEATING_MAX
_attr_target_temperature_step = PRECISION_WHOLE
_attr_preset_modes = list(HA_TO_VICARE_PRESET_HEATING)
_current_action: bool | None = None
_current_mode: str | None = None
_enable_turn_on_off_backwards_compatibility = False
@ -162,6 +154,13 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
self._current_program = None
self._attr_translation_key = translation_key
self._attributes["vicare_programs"] = self._circuit.getPrograms()
self._attr_preset_modes = [
preset
for heating_program in self._attributes["vicare_programs"]
if (preset := HeatingProgram.to_ha_preset(heating_program)) is not None
]
def update(self) -> None:
"""Let HA know there has been an update from the ViCare API."""
try:
@ -293,11 +292,13 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
@property
def preset_mode(self):
"""Return the current preset mode, e.g., home, away, temp."""
return VICARE_TO_HA_PRESET_HEATING.get(self._current_program)
return HeatingProgram.to_ha_preset(self._current_program)
def set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode and deactivate any existing programs."""
target_program = HA_TO_VICARE_PRESET_HEATING.get(preset_mode)
target_program = HeatingProgram.from_ha_preset(
preset_mode, self._attributes["vicare_programs"]
)
if target_program is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
@ -308,12 +309,10 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
)
_LOGGER.debug("Current preset %s", self._current_program)
if self._current_program and self._current_program not in [
HeatingProgram.NORMAL,
HeatingProgram.REDUCED,
HeatingProgram.STANDBY,
]:
# We can't deactivate "normal", "reduced" or "standby"
if (
self._current_program
and self._current_program in CHANGABLE_HEATING_PROGRAMS
):
_LOGGER.debug("deactivating %s", self._current_program)
try:
self._circuit.deactivateProgram(self._current_program)
@ -327,12 +326,7 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
) from err
_LOGGER.debug("Setting preset to %s / %s", preset_mode, target_program)
if target_program not in [
HeatingProgram.NORMAL,
HeatingProgram.REDUCED,
HeatingProgram.STANDBY,
]:
# And we can't explicitly activate "normal", "reduced" or "standby", either
if target_program in CHANGABLE_HEATING_PROGRAMS:
_LOGGER.debug("activating %s", target_program)
try:
self._circuit.activateProgram(target_program)

View File

@ -8,6 +8,13 @@ from typing import Any
from PyViCare.PyViCareDevice import Device as PyViCareDevice
from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
from homeassistant.components.climate import (
PRESET_COMFORT,
PRESET_ECO,
PRESET_HOME,
PRESET_SLEEP,
)
class HeatingProgram(enum.StrEnum):
"""ViCare preset heating programs.
@ -24,6 +31,38 @@ class HeatingProgram(enum.StrEnum):
REDUCED_HEATING = "reducedHeating"
STANDBY = "standby"
@staticmethod
def to_ha_preset(program: str) -> str | None:
"""Return the mapped Home Assistant preset for the ViCare heating program."""
try:
heating_program = HeatingProgram(program)
except ValueError:
# ignore unsupported / unmapped programs
return None
return VICARE_TO_HA_PRESET_HEATING.get(heating_program) if program else None
@staticmethod
def from_ha_preset(
ha_preset: str, supported_heating_programs: list[str]
) -> str | None:
"""Return the mapped ViCare heating program for the Home Assistant preset."""
for program in supported_heating_programs:
if VICARE_TO_HA_PRESET_HEATING.get(HeatingProgram(program)) == ha_preset:
return program
return None
VICARE_TO_HA_PRESET_HEATING = {
HeatingProgram.COMFORT: PRESET_COMFORT,
HeatingProgram.COMFORT_HEATING: PRESET_COMFORT,
HeatingProgram.ECO: PRESET_ECO,
HeatingProgram.NORMAL: PRESET_HOME,
HeatingProgram.NORMAL_HEATING: PRESET_HOME,
HeatingProgram.REDUCED: PRESET_SLEEP,
HeatingProgram.REDUCED_HEATING: PRESET_SLEEP,
}
@dataclass(frozen=True)
class ViCareDevice: