diff --git a/.coveragerc b/.coveragerc index c4e8b76abec..6a302011868 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1270,6 +1270,7 @@ omit = homeassistant/components/vesync/switch.py homeassistant/components/viaggiatreno/sensor.py homeassistant/components/vicare/binary_sensor.py + homeassistant/components/vicare/button.py homeassistant/components/vicare/climate.py homeassistant/components/vicare/const.py homeassistant/components/vicare/__init__.py diff --git a/homeassistant/components/vicare/binary_sensor.py b/homeassistant/components/vicare/binary_sensor.py index f167b11f72d..c96b3b819b3 100644 --- a/homeassistant/components/vicare/binary_sensor.py +++ b/homeassistant/components/vicare/binary_sensor.py @@ -34,6 +34,7 @@ _LOGGER = logging.getLogger(__name__) SENSOR_CIRCULATION_PUMP_ACTIVE = "circulationpump_active" SENSOR_BURNER_ACTIVE = "burner_active" +SENSOR_CHARGING_ACTIVE = "charging_active" SENSOR_COMPRESSOR_ACTIVE = "compressor_active" SENSOR_SOLAR_PUMP_ACTIVE = "solar_pump_active" @@ -79,6 +80,12 @@ GLOBAL_SENSORS: tuple[ViCareBinarySensorEntityDescription, ...] = ( device_class=BinarySensorDeviceClass.POWER, value_getter=lambda api: api.getSolarPumpActive(), ), + ViCareBinarySensorEntityDescription( + key=SENSOR_CHARGING_ACTIVE, + name="Domestic Hot Water Charging active", + device_class=BinarySensorDeviceClass.RUNNING, + value_getter=lambda api: api.getDomesticHotWaterChargingActive(), + ), ) diff --git a/homeassistant/components/vicare/button.py b/homeassistant/components/vicare/button.py new file mode 100644 index 00000000000..35133b55bd1 --- /dev/null +++ b/homeassistant/components/vicare/button.py @@ -0,0 +1,114 @@ +"""Viessmann ViCare sensor device.""" +from __future__ import annotations + +from contextlib import suppress +from dataclasses import dataclass +import logging + +from PyViCare.PyViCareUtils import ( + PyViCareInvalidDataError, + PyViCareNotSupportedFeatureError, + PyViCareRateLimitError, +) +import requests + +from homeassistant.components.button import ButtonEntity, ButtonEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ENTITY_CATEGORY_CONFIG +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ViCareRequiredKeysMixin +from .const import DOMAIN, VICARE_API, VICARE_DEVICE_CONFIG, VICARE_NAME + +_LOGGER = logging.getLogger(__name__) + +BUTTON_DHW_ACTIVATE_ONETIME_CHARGE = "activate_onetimecharge" + + +@dataclass +class ViCareButtonEntityDescription(ButtonEntityDescription, ViCareRequiredKeysMixin): + """Describes ViCare button sensor entity.""" + + +BUTTON_DESCRIPTIONS: tuple[ViCareButtonEntityDescription, ...] = ( + ViCareButtonEntityDescription( + key=BUTTON_DHW_ACTIVATE_ONETIME_CHARGE, + name="Activate one-time charge", + icon="mdi:shower-head", + entity_category=ENTITY_CATEGORY_CONFIG, + value_getter=lambda api: api.activateOneTimeCharge(), + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Create the ViCare binary sensor devices.""" + name = VICARE_NAME + api = hass.data[DOMAIN][config_entry.entry_id][VICARE_API] + + entities = [] + + for description in BUTTON_DESCRIPTIONS: + entity = ViCareButton( + f"{name} {description.name}", + api, + hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG], + description, + ) + if entity is not None: + entities.append(entity) + + async_add_entities(entities) + + +class ViCareButton(ButtonEntity): + """Representation of a ViCare button.""" + + entity_description: ViCareButtonEntityDescription + + def __init__( + self, name, api, device_config, description: ViCareButtonEntityDescription + ): + """Initialize the sensor.""" + self.entity_description = description + self._device_config = device_config + self._api = api + + def press(self) -> None: + """Handle the button press.""" + try: + with suppress(PyViCareNotSupportedFeatureError): + self.entity_description.value_getter(self._api) + except requests.exceptions.ConnectionError: + _LOGGER.error("Unable to retrieve data from ViCare server") + except ValueError: + _LOGGER.error("Unable to decode data from ViCare server") + except PyViCareRateLimitError as limit_exception: + _LOGGER.error("Vicare API rate limit exceeded: %s", limit_exception) + except PyViCareInvalidDataError as invalid_data_exception: + _LOGGER.error("Invalid data from Vicare server: %s", invalid_data_exception) + + @property + def device_info(self): + """Return device info for this device.""" + return { + "identifiers": {(DOMAIN, self._device_config.getConfig().serial)}, + "name": self._device_config.getModel(), + "manufacturer": "Viessmann", + "model": (DOMAIN, self._device_config.getModel()), + } + + @property + def unique_id(self): + """Return unique ID for this device.""" + tmp_id = ( + f"{self._device_config.getConfig().serial}-{self.entity_description.key}" + ) + if hasattr(self._api, "id"): + return f"{tmp_id}-{self._api.id}" + return tmp_id diff --git a/homeassistant/components/vicare/const.py b/homeassistant/components/vicare/const.py index 608679b2185..2325f246bc5 100644 --- a/homeassistant/components/vicare/const.py +++ b/homeassistant/components/vicare/const.py @@ -7,6 +7,7 @@ from homeassistant.const import ENERGY_KILO_WATT_HOUR, VOLUME_CUBIC_METERS, Plat DOMAIN = "vicare" PLATFORMS = [ + Platform.BUTTON, Platform.CLIMATE, Platform.SENSOR, Platform.BINARY_SENSOR,