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

View File

@ -8,6 +8,13 @@ from typing import Any
from PyViCare.PyViCareDevice import Device as PyViCareDevice from PyViCare.PyViCareDevice import Device as PyViCareDevice
from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
from homeassistant.components.climate import (
PRESET_COMFORT,
PRESET_ECO,
PRESET_HOME,
PRESET_SLEEP,
)
class HeatingProgram(enum.StrEnum): class HeatingProgram(enum.StrEnum):
"""ViCare preset heating programs. """ViCare preset heating programs.
@ -24,6 +31,38 @@ class HeatingProgram(enum.StrEnum):
REDUCED_HEATING = "reducedHeating" REDUCED_HEATING = "reducedHeating"
STANDBY = "standby" 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) @dataclass(frozen=True)
class ViCareDevice: class ViCareDevice: