Skip to content
This repository was archived by the owner on Aug 15, 2024. It is now read-only.

Commit 1725e40

Browse files
authored
Merge pull request #154 from oischinger/thermostat_support
Thermostat support
2 parents 6e76f43 + 65fd645 commit 1725e40

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

custom_components/vicare/climate.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import logging
66
from typing import Any
77

8+
from PyViCare.PyViCareRadiatorActuator import RadiatorActuator
89
from PyViCare.PyViCareUtils import (
910
PyViCareCommandError,
1011
PyViCareInvalidDataError,
@@ -26,6 +27,7 @@
2627
from homeassistant.config_entries import ConfigEntry
2728
from homeassistant.const import (
2829
ATTR_TEMPERATURE,
30+
PRECISION_HALVES,
2931
PRECISION_TENTHS,
3032
PRECISION_WHOLE,
3133
UnitOfTemperature,
@@ -120,6 +122,7 @@ async def async_setup_entry(
120122
api = device.asAutoDetectDevice()
121123

122124
circuits = await hass.async_add_executor_job(get_circuits, api)
125+
# Devices with circuits will get one climate entity per circuit
123126
for circuit in circuits:
124127
suffix = ""
125128
if len(circuits) > 1:
@@ -133,6 +136,17 @@ async def async_setup_entry(
133136
)
134137
entities.append(entity)
135138

139+
# RadiatorActuator have no circuits but also create a climate entity
140+
if isinstance(api, RadiatorActuator):
141+
entity = ViCareThermostat(
142+
f"{name} RadiatorActuator{suffix}",
143+
api,
144+
device,
145+
)
146+
entities.append(entity)
147+
148+
149+
136150
platform = entity_platform.async_get_current_platform()
137151

138152
platform.async_register_entity_service(
@@ -187,6 +201,7 @@ def __init__(self, name, api, circuit, device_config):
187201
self._current_temperature = None
188202
self._current_program = None
189203
self._current_action = None
204+
self.update()
190205

191206
@property
192207
def unique_id(self) -> str:
@@ -406,3 +421,111 @@ def set_vicare_mode(self, vicare_mode):
406421
def set_heating_curve(self, shift, slope):
407422
"""Service function to set vicare heating curve directly."""
408423
self._circuit.setHeatingCurve(int(shift), round(float(slope), 1))
424+
425+
426+
class ViCareThermostat(ClimateEntity):
427+
"""Representation of the ViCare heating climate device."""
428+
429+
_attr_precision = PRECISION_TENTHS
430+
_attr_supported_features = (
431+
ClimateEntityFeature.TARGET_TEMPERATURE
432+
)
433+
_attr_temperature_unit = UnitOfTemperature.CELSIUS
434+
435+
def __init__(self, name, api, device_config):
436+
"""Initialize the climate device."""
437+
self._name = name
438+
self._state = None
439+
self._api = api
440+
self._device_config = device_config
441+
self._attributes = {}
442+
self._target_temperature = None
443+
self._current_mode = None
444+
self._current_temperature = None
445+
self.update()
446+
447+
@property
448+
def unique_id(self) -> str:
449+
"""Return unique ID for this device."""
450+
return get_unique_id(self._api, self._device_config, 0)
451+
452+
@property
453+
def device_info(self) -> DeviceInfo:
454+
"""Return device info for this device."""
455+
return DeviceInfo(
456+
identifiers={
457+
(
458+
DOMAIN,
459+
get_unique_device_id(self._device_config),
460+
)
461+
},
462+
name=get_device_name(self._device_config),
463+
manufacturer="Viessmann",
464+
model=self._device_config.getModel(),
465+
configuration_url="https://developer.viessmann.com/",
466+
)
467+
468+
def update(self) -> None:
469+
"""Let HA know there has been an update from the ViCare API."""
470+
try:
471+
_room_temperature = None
472+
with suppress(PyViCareNotSupportedFeatureError):
473+
_room_temperature = self._api.getTemperature()
474+
self._current_temperature = _room_temperature
475+
476+
with suppress(PyViCareNotSupportedFeatureError):
477+
self._target_temperature = self._api.getTargetTemperature()
478+
479+
# Update the generic device attributes
480+
self._attributes = {}
481+
self._attributes["room_temperature"] = _room_temperature
482+
483+
except requests.exceptions.ConnectionError:
484+
_LOGGER.error("Unable to retrieve data from ViCare server")
485+
except PyViCareRateLimitError as limit_exception:
486+
_LOGGER.error("Vicare API rate limit exceeded: %s", limit_exception)
487+
except ValueError:
488+
_LOGGER.error("Unable to decode data from ViCare server")
489+
except PyViCareInvalidDataError as invalid_data_exception:
490+
_LOGGER.error("Invalid data from Vicare server: %s", invalid_data_exception)
491+
492+
@property
493+
def name(self):
494+
"""Return the name of the climate device."""
495+
return self._name
496+
497+
@property
498+
def current_temperature(self):
499+
"""Return the current temperature."""
500+
return self._current_temperature
501+
502+
@property
503+
def target_temperature(self):
504+
"""Return the temperature we try to reach."""
505+
return self._target_temperature
506+
507+
@property
508+
def hvac_mode(self) -> HVACMode | None:
509+
"""Return current hvac mode."""
510+
return HVACMode.AUTO
511+
512+
@property
513+
def hvac_modes(self) -> list[HVACMode]:
514+
"""Return the list of available hvac modes."""
515+
return [HVACMode.AUTO]
516+
517+
@property
518+
def target_temperature_step(self) -> float:
519+
"""Set target temperature step to wholes."""
520+
return PRECISION_HALVES
521+
522+
def set_temperature(self, **kwargs: Any) -> None:
523+
"""Set new target temperatures."""
524+
if (temp := kwargs.get(ATTR_TEMPERATURE)) is not None:
525+
self._api.setTargetTemperature(temp)
526+
self._target_temperature = temp
527+
528+
@property
529+
def extra_state_attributes(self):
530+
"""Show Device Attributes."""
531+
return self._attributes

0 commit comments

Comments
 (0)