1 """Viessmann ViCare climate device."""
3 from __future__
import annotations
5 from contextlib
import suppress
9 from PyViCare.PyViCareDevice
import Device
as PyViCareDevice
10 from PyViCare.PyViCareDeviceConfig
import PyViCareDeviceConfig
11 from PyViCare.PyViCareHeatingDevice
import HeatingCircuit
as PyViCareHeatingCircuit
12 from PyViCare.PyViCareUtils
import (
14 PyViCareInvalidDataError,
15 PyViCareNotSupportedFeatureError,
16 PyViCareRateLimitError,
19 import voluptuous
as vol
40 from .const
import DEVICE_LIST, DOMAIN
41 from .entity
import ViCareEntity
42 from .types
import HeatingProgram, ViCareDevice
43 from .utils
import get_burners, get_circuits, get_compressors, get_device_serial
45 _LOGGER = logging.getLogger(__name__)
47 SERVICE_SET_VICARE_MODE =
"set_vicare_mode"
48 SERVICE_SET_VICARE_MODE_ATTR_MODE =
"vicare_mode"
50 VICARE_MODE_DHW =
"dhw"
51 VICARE_MODE_HEATING =
"heating"
52 VICARE_MODE_HEATINGCOOLING =
"heatingCooling"
53 VICARE_MODE_DHWANDHEATING =
"dhwAndHeating"
54 VICARE_MODE_DHWANDHEATINGCOOLING =
"dhwAndHeatingCooling"
55 VICARE_MODE_FORCEDREDUCED =
"forcedReduced"
56 VICARE_MODE_FORCEDNORMAL =
"forcedNormal"
57 VICARE_MODE_OFF =
"standby"
59 VICARE_HOLD_MODE_AWAY =
"away"
60 VICARE_HOLD_MODE_HOME =
"home"
61 VICARE_HOLD_MODE_OFF =
"off"
63 VICARE_TEMP_HEATING_MIN = 3
64 VICARE_TEMP_HEATING_MAX = 37
66 VICARE_TO_HA_HVAC_HEATING: dict[str, HVACMode] = {
67 VICARE_MODE_FORCEDREDUCED: HVACMode.OFF,
68 VICARE_MODE_OFF: HVACMode.OFF,
69 VICARE_MODE_DHW: HVACMode.OFF,
70 VICARE_MODE_DHWANDHEATINGCOOLING: HVACMode.AUTO,
71 VICARE_MODE_DHWANDHEATING: HVACMode.AUTO,
72 VICARE_MODE_HEATINGCOOLING: HVACMode.AUTO,
73 VICARE_MODE_HEATING: HVACMode.AUTO,
74 VICARE_MODE_FORCEDNORMAL: HVACMode.HEAT,
77 CHANGABLE_HEATING_PROGRAMS = [
78 HeatingProgram.COMFORT,
79 HeatingProgram.COMFORT_HEATING,
85 device_list: list[ViCareDevice],
86 ) -> list[ViCareClimate]:
87 """Create ViCare climate entities for a device."""
95 for device
in device_list
102 config_entry: ConfigEntry,
103 async_add_entities: AddEntitiesCallback,
105 """Set up the ViCare climate platform."""
107 platform = entity_platform.async_get_current_platform()
109 platform.async_register_entity_service(
110 SERVICE_SET_VICARE_MODE,
111 {vol.Required(SERVICE_SET_VICARE_MODE_ATTR_MODE): cv.string},
115 device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
118 await hass.async_add_executor_job(
126 """Representation of the ViCare heating climate device."""
128 _attr_precision = PRECISION_TENTHS
129 _attr_supported_features = (
130 ClimateEntityFeature.TARGET_TEMPERATURE
131 | ClimateEntityFeature.PRESET_MODE
132 | ClimateEntityFeature.TURN_OFF
133 | ClimateEntityFeature.TURN_ON
135 _attr_temperature_unit = UnitOfTemperature.CELSIUS
136 _attr_min_temp = VICARE_TEMP_HEATING_MIN
137 _attr_max_temp = VICARE_TEMP_HEATING_MAX
138 _attr_target_temperature_step = PRECISION_WHOLE
139 _attr_translation_key =
"heating"
140 _current_action: bool |
None =
None
141 _current_mode: str |
None =
None
142 _current_program: str |
None =
None
143 _enable_turn_on_off_backwards_compatibility =
False
147 device_serial: str |
None,
148 device_config: PyViCareDeviceConfig,
149 device: PyViCareDevice,
150 circuit: PyViCareHeatingCircuit,
152 """Initialize the climate device."""
157 self._attributes: dict[str, Any] = {}
158 self._attributes[
"vicare_programs"] = self._api.getPrograms()
161 for heating_program
in self._attributes[
"vicare_programs"]
162 if (preset := HeatingProgram.to_ha_preset(heating_program))
is not None
166 """Let HA know there has been an update from the ViCare API."""
168 _room_temperature =
None
169 with suppress(PyViCareNotSupportedFeatureError):
170 self._attributes[
"room_temperature"] = _room_temperature = (
171 self._api.getRoomTemperature()
174 _supply_temperature =
None
175 with suppress(PyViCareNotSupportedFeatureError):
176 _supply_temperature = self._api.getSupplyTemperature()
178 if _room_temperature
is not None:
180 elif _supply_temperature
is not None:
185 with suppress(PyViCareNotSupportedFeatureError):
187 self._api.getActiveProgram()
190 with suppress(PyViCareNotSupportedFeatureError):
193 with suppress(PyViCareNotSupportedFeatureError):
194 self._attributes[
"active_vicare_mode"] = self.
_current_mode_current_mode = (
195 self._api.getActiveMode()
198 with suppress(PyViCareNotSupportedFeatureError):
199 self._attributes[
"heating_curve_slope"] = (
200 self._api.getHeatingCurveSlope()
203 with suppress(PyViCareNotSupportedFeatureError):
204 self._attributes[
"heating_curve_shift"] = (
205 self._api.getHeatingCurveShift()
208 with suppress(PyViCareNotSupportedFeatureError):
209 self._attributes[
"vicare_modes"] = self._api.getModes()
213 with suppress(PyViCareNotSupportedFeatureError):
217 with suppress(PyViCareNotSupportedFeatureError):
223 except requests.exceptions.ConnectionError:
224 _LOGGER.error(
"Unable to retrieve data from ViCare server")
225 except PyViCareRateLimitError
as limit_exception:
226 _LOGGER.error(
"Vicare API rate limit exceeded: %s", limit_exception)
228 _LOGGER.error(
"Unable to decode data from ViCare server")
229 except PyViCareInvalidDataError
as invalid_data_exception:
230 _LOGGER.error(
"Invalid data from Vicare server: %s", invalid_data_exception)
234 """Return current hvac mode."""
237 return VICARE_TO_HA_HVAC_HEATING.get(self.
_current_mode_current_mode,
None)
240 """Set a new hvac mode on the ViCare API."""
241 if "vicare_modes" not in self._attributes:
242 raise ValueError(
"Cannot set hvac mode when vicare_modes are not known")
245 if vicare_mode
is None:
246 raise ValueError(f
"Cannot set invalid hvac mode: {hvac_mode}")
248 _LOGGER.debug(
"Setting hvac mode to %s / %s", hvac_mode, vicare_mode)
249 self._api.setMode(vicare_mode)
252 """Return the corresponding vicare mode for an hvac_mode."""
253 if "vicare_modes" not in self._attributes:
256 supported_modes = self._attributes[
"vicare_modes"]
257 for key, value
in VICARE_TO_HA_HVAC_HEATING.items():
258 if key
in supported_modes
and value == hvac_mode:
264 """Return the list of available hvac modes."""
265 if "vicare_modes" not in self._attributes:
268 supported_modes = self._attributes[
"vicare_modes"]
270 for key, value
in VICARE_TO_HA_HVAC_HEATING.items():
271 if value
in hvac_modes:
273 if key
in supported_modes:
274 hvac_modes.append(value)
279 """Return the current hvac action."""
281 return HVACAction.HEATING
282 return HVACAction.IDLE
285 """Set new target temperatures."""
286 if (temp := kwargs.get(ATTR_TEMPERATURE))
is not None:
287 self._api.setProgramTemperature(self.
_current_program_current_program, temp)
292 """Return the current preset mode, e.g., home, away, temp."""
296 """Set new preset mode and deactivate any existing programs."""
297 target_program = HeatingProgram.from_ha_preset(
298 preset_mode, self._attributes[
"vicare_programs"]
300 if target_program
is None:
302 translation_domain=DOMAIN,
303 translation_key=
"program_unknown",
304 translation_placeholders={
305 "preset": preset_mode,
317 except PyViCareCommandError
as err:
319 translation_domain=DOMAIN,
320 translation_key=
"program_not_deactivated",
321 translation_placeholders={
326 _LOGGER.debug(
"Setting preset to %s / %s", preset_mode, target_program)
327 if target_program
in CHANGABLE_HEATING_PROGRAMS:
328 _LOGGER.debug(
"activating %s", target_program)
330 self._api.activateProgram(target_program)
331 except PyViCareCommandError
as err:
333 translation_domain=DOMAIN,
334 translation_key=
"program_not_activated",
335 translation_placeholders={
336 "program": target_program,
342 """Show Device Attributes."""
343 return self._attributes
346 """Service function to set vicare modes directly."""
347 if vicare_mode
not in self._attributes[
"vicare_modes"]:
348 raise ValueError(f
"Cannot set invalid vicare mode: {vicare_mode}.")
350 self._api.setMode(vicare_mode)
_attr_current_temperature
list[HVACMode] hvac_modes(self)
HVACAction hvac_action(self)
dict[str, Any] extra_state_attributes(self)
None set_vicare_mode(self, vicare_mode)
None set_temperature(self, **Any kwargs)
None __init__(self, str|None device_serial, PyViCareDeviceConfig device_config, PyViCareDevice device, PyViCareHeatingCircuit circuit)
None set_hvac_mode(self, HVACMode hvac_mode)
str|None vicare_mode_from_hvac_mode(self, hvac_mode)
string _attr_translation_key
None set_preset_mode(self, str preset_mode)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
list[ViCareClimate] _build_entities(list[ViCareDevice] device_list)
str|None get_device_serial(PyViCareDevice device)
list[PyViCareHeatingDeviceComponent] get_compressors(PyViCareDevice device)
list[PyViCareHeatingDeviceComponent] get_circuits(PyViCareDevice device)
list[PyViCareHeatingDeviceComponent] get_burners(PyViCareDevice device)