Update PyVicare to 2.13.0 (#57700)
* Update PyVicare to 2.x With PyViCare 2.8.1 a breaking change was introduced which required changes on sensor and binary_sensor platforms: - Circuit, Burner and Compressor have been separated out from the "main" device - Multiple circuits and burners allow "duplicate sensors". We add the circuit or burner number as suffix now At the same time the sensors are now created only when available: During entity creation we can check if the value is provided for the user's device. Sensors are not created by heating type anymore but instead the new API structure is reflected, providing device, burner or circuit sensors. For details of breaking changes from PyViCare 1.x to 2.x please see https://github.com/somm15/PyViCare#breaking-changes-in-version-2x * Integrate review comments * variables cleanup * Update unique ids The unique ids shall not depend on the name but on the entity description key (which should not change) and the id of the circuit, burner or device.
This commit is contained in:
parent
be4b1d15ec
commit
66ae116023
8 changed files with 661 additions and 441 deletions
|
@ -1,16 +1,12 @@
|
||||||
"""The ViCare integration."""
|
"""The ViCare integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import enum
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Generic, TypeVar
|
from typing import Callable
|
||||||
|
|
||||||
|
from PyViCare.PyViCare import PyViCare
|
||||||
from PyViCare.PyViCareDevice import Device
|
from PyViCare.PyViCareDevice import Device
|
||||||
from PyViCare.PyViCareFuelCell import FuelCell
|
|
||||||
from PyViCare.PyViCareGazBoiler import GazBoiler
|
|
||||||
from PyViCare.PyViCareHeatPump import HeatPump
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -24,55 +20,51 @@ from homeassistant.helpers import discovery
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.storage import STORAGE_DIR
|
from homeassistant.helpers.storage import STORAGE_DIR
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
CONF_CIRCUIT,
|
||||||
|
CONF_HEATING_TYPE,
|
||||||
|
DEFAULT_HEATING_TYPE,
|
||||||
|
DOMAIN,
|
||||||
|
HEATING_TYPE_TO_CREATOR_METHOD,
|
||||||
|
PLATFORMS,
|
||||||
|
VICARE_API,
|
||||||
|
VICARE_CIRCUITS,
|
||||||
|
VICARE_DEVICE_CONFIG,
|
||||||
|
VICARE_NAME,
|
||||||
|
HeatingType,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLATFORMS = ["climate", "sensor", "binary_sensor", "water_heater"]
|
|
||||||
|
|
||||||
DOMAIN = "vicare"
|
|
||||||
VICARE_API = "api"
|
|
||||||
VICARE_NAME = "name"
|
|
||||||
VICARE_HEATING_TYPE = "heating_type"
|
|
||||||
|
|
||||||
CONF_CIRCUIT = "circuit"
|
|
||||||
CONF_HEATING_TYPE = "heating_type"
|
|
||||||
DEFAULT_HEATING_TYPE = "generic"
|
|
||||||
|
|
||||||
|
|
||||||
ApiT = TypeVar("ApiT", bound=Device)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
class ViCareRequiredKeysMixin(Generic[ApiT]):
|
class ViCareRequiredKeysMixin:
|
||||||
"""Mixin for required keys."""
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
value_getter: Callable[[ApiT], bool]
|
value_getter: Callable[[Device], bool]
|
||||||
|
|
||||||
|
|
||||||
class HeatingType(enum.Enum):
|
|
||||||
"""Possible options for heating type."""
|
|
||||||
|
|
||||||
generic = "generic"
|
|
||||||
gas = "gas"
|
|
||||||
heatpump = "heatpump"
|
|
||||||
fuelcell = "fuelcell"
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
DOMAIN: vol.Schema(
|
DOMAIN: vol.All(
|
||||||
{
|
cv.deprecated(CONF_CIRCUIT),
|
||||||
vol.Required(CONF_USERNAME): cv.string,
|
vol.Schema(
|
||||||
vol.Required(CONF_PASSWORD): cv.string,
|
{
|
||||||
vol.Required(CONF_CLIENT_ID): cv.string,
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
vol.Optional(CONF_SCAN_INTERVAL, default=60): vol.All(
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
cv.time_period, lambda value: value.total_seconds()
|
vol.Required(CONF_CLIENT_ID): cv.string,
|
||||||
),
|
vol.Optional(CONF_SCAN_INTERVAL, default=60): vol.All(
|
||||||
vol.Optional(CONF_CIRCUIT): int,
|
cv.time_period, lambda value: value.total_seconds()
|
||||||
vol.Optional(CONF_NAME, default="ViCare"): cv.string,
|
),
|
||||||
vol.Optional(CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE): cv.enum(
|
vol.Optional(
|
||||||
HeatingType
|
CONF_CIRCUIT
|
||||||
),
|
): int, # Ignored: All circuits are now supported. Will be removed when switching to Setup via UI.
|
||||||
}
|
vol.Optional(CONF_NAME, default="ViCare"): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE
|
||||||
|
): cv.enum(HeatingType),
|
||||||
|
}
|
||||||
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
@ -83,34 +75,40 @@ def setup(hass, config):
|
||||||
"""Create the ViCare component."""
|
"""Create the ViCare component."""
|
||||||
conf = config[DOMAIN]
|
conf = config[DOMAIN]
|
||||||
params = {"token_file": hass.config.path(STORAGE_DIR, "vicare_token.save")}
|
params = {"token_file": hass.config.path(STORAGE_DIR, "vicare_token.save")}
|
||||||
if conf.get(CONF_CIRCUIT) is not None:
|
|
||||||
params["circuit"] = conf[CONF_CIRCUIT]
|
|
||||||
|
|
||||||
params["cacheDuration"] = conf.get(CONF_SCAN_INTERVAL)
|
params["cacheDuration"] = conf.get(CONF_SCAN_INTERVAL)
|
||||||
params["client_id"] = conf.get(CONF_CLIENT_ID)
|
params["client_id"] = conf.get(CONF_CLIENT_ID)
|
||||||
heating_type = conf[CONF_HEATING_TYPE]
|
|
||||||
|
|
||||||
try:
|
|
||||||
if heating_type == HeatingType.gas:
|
|
||||||
vicare_api = GazBoiler(conf[CONF_USERNAME], conf[CONF_PASSWORD], **params)
|
|
||||||
elif heating_type == HeatingType.heatpump:
|
|
||||||
vicare_api = HeatPump(conf[CONF_USERNAME], conf[CONF_PASSWORD], **params)
|
|
||||||
elif heating_type == HeatingType.fuelcell:
|
|
||||||
vicare_api = FuelCell(conf[CONF_USERNAME], conf[CONF_PASSWORD], **params)
|
|
||||||
else:
|
|
||||||
vicare_api = Device(conf[CONF_USERNAME], conf[CONF_PASSWORD], **params)
|
|
||||||
except AttributeError:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Failed to create PyViCare API client. Please check your credentials"
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
hass.data[DOMAIN] = {}
|
hass.data[DOMAIN] = {}
|
||||||
hass.data[DOMAIN][VICARE_API] = vicare_api
|
|
||||||
hass.data[DOMAIN][VICARE_NAME] = conf[CONF_NAME]
|
hass.data[DOMAIN][VICARE_NAME] = conf[CONF_NAME]
|
||||||
hass.data[DOMAIN][VICARE_HEATING_TYPE] = heating_type
|
setup_vicare_api(hass, conf, hass.data[DOMAIN])
|
||||||
|
|
||||||
|
hass.data[DOMAIN][CONF_HEATING_TYPE] = conf[CONF_HEATING_TYPE]
|
||||||
|
|
||||||
for platform in PLATFORMS:
|
for platform in PLATFORMS:
|
||||||
discovery.load_platform(hass, platform, DOMAIN, {}, config)
|
discovery.load_platform(hass, platform, DOMAIN, {}, config)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def setup_vicare_api(hass, conf, entity_data):
|
||||||
|
"""Set up PyVicare API."""
|
||||||
|
vicare_api = PyViCare()
|
||||||
|
vicare_api.setCacheDuration(conf[CONF_SCAN_INTERVAL])
|
||||||
|
vicare_api.initWithCredentials(
|
||||||
|
conf[CONF_USERNAME],
|
||||||
|
conf[CONF_PASSWORD],
|
||||||
|
conf[CONF_CLIENT_ID],
|
||||||
|
hass.config.path(STORAGE_DIR, "vicare_token.save"),
|
||||||
|
)
|
||||||
|
|
||||||
|
device = vicare_api.devices[0]
|
||||||
|
for device in vicare_api.devices:
|
||||||
|
_LOGGER.info(
|
||||||
|
"Found device: %s (online: %s)", device.getModel(), str(device.isOnline())
|
||||||
|
)
|
||||||
|
entity_data[VICARE_DEVICE_CONFIG] = device
|
||||||
|
entity_data[VICARE_API] = getattr(
|
||||||
|
device, HEATING_TYPE_TO_CREATOR_METHOD[conf[CONF_HEATING_TYPE]]
|
||||||
|
)()
|
||||||
|
entity_data[VICARE_CIRCUITS] = entity_data[VICARE_API].circuits
|
||||||
|
|
|
@ -4,12 +4,12 @@ from __future__ import annotations
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from PyViCare.PyViCare import PyViCareNotSupportedFeatureError, PyViCareRateLimitError
|
from PyViCare.PyViCareUtils import (
|
||||||
from PyViCare.PyViCareDevice import Device
|
PyViCareInvalidDataError,
|
||||||
from PyViCare.PyViCareGazBoiler import GazBoiler
|
PyViCareNotSupportedFeatureError,
|
||||||
from PyViCare.PyViCareHeatPump import HeatPump
|
PyViCareRateLimitError,
|
||||||
|
)
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
|
@ -18,36 +18,25 @@ from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorEntityDescription,
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import (
|
from . import ViCareRequiredKeysMixin
|
||||||
DOMAIN as VICARE_DOMAIN,
|
from .const import DOMAIN, VICARE_API, VICARE_DEVICE_CONFIG, VICARE_NAME
|
||||||
VICARE_API,
|
|
||||||
VICARE_HEATING_TYPE,
|
|
||||||
VICARE_NAME,
|
|
||||||
ApiT,
|
|
||||||
HeatingType,
|
|
||||||
ViCareRequiredKeysMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SENSOR_CIRCULATION_PUMP_ACTIVE = "circulationpump_active"
|
SENSOR_CIRCULATION_PUMP_ACTIVE = "circulationpump_active"
|
||||||
|
|
||||||
# gas sensors
|
|
||||||
SENSOR_BURNER_ACTIVE = "burner_active"
|
SENSOR_BURNER_ACTIVE = "burner_active"
|
||||||
|
|
||||||
# heatpump sensors
|
|
||||||
SENSOR_COMPRESSOR_ACTIVE = "compressor_active"
|
SENSOR_COMPRESSOR_ACTIVE = "compressor_active"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ViCareBinarySensorEntityDescription(
|
class ViCareBinarySensorEntityDescription(
|
||||||
BinarySensorEntityDescription, ViCareRequiredKeysMixin[ApiT]
|
BinarySensorEntityDescription, ViCareRequiredKeysMixin
|
||||||
):
|
):
|
||||||
"""Describes ViCare binary sensor entity."""
|
"""Describes ViCare binary sensor entity."""
|
||||||
|
|
||||||
|
|
||||||
SENSOR_TYPES_GENERIC: tuple[ViCareBinarySensorEntityDescription[Device]] = (
|
CIRCUIT_SENSORS: tuple[ViCareBinarySensorEntityDescription, ...] = (
|
||||||
ViCareBinarySensorEntityDescription[Device](
|
ViCareBinarySensorEntityDescription(
|
||||||
key=SENSOR_CIRCULATION_PUMP_ACTIVE,
|
key=SENSOR_CIRCULATION_PUMP_ACTIVE,
|
||||||
name="Circulation pump active",
|
name="Circulation pump active",
|
||||||
device_class=DEVICE_CLASS_POWER,
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
@ -55,80 +44,133 @@ SENSOR_TYPES_GENERIC: tuple[ViCareBinarySensorEntityDescription[Device]] = (
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSOR_TYPES_GAS: tuple[ViCareBinarySensorEntityDescription[GazBoiler]] = (
|
BURNER_SENSORS: tuple[ViCareBinarySensorEntityDescription, ...] = (
|
||||||
ViCareBinarySensorEntityDescription[GazBoiler](
|
ViCareBinarySensorEntityDescription(
|
||||||
key=SENSOR_BURNER_ACTIVE,
|
key=SENSOR_BURNER_ACTIVE,
|
||||||
name="Burner active",
|
name="Burner active",
|
||||||
device_class=DEVICE_CLASS_POWER,
|
device_class=DEVICE_CLASS_POWER,
|
||||||
value_getter=lambda api: api.getBurnerActive(),
|
value_getter=lambda api: api.getActive(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSOR_TYPES_HEATPUMP: tuple[ViCareBinarySensorEntityDescription[HeatPump]] = (
|
COMPRESSOR_SENSORS: tuple[ViCareBinarySensorEntityDescription, ...] = (
|
||||||
ViCareBinarySensorEntityDescription[HeatPump](
|
ViCareBinarySensorEntityDescription(
|
||||||
key=SENSOR_COMPRESSOR_ACTIVE,
|
key=SENSOR_COMPRESSOR_ACTIVE,
|
||||||
name="Compressor active",
|
name="Compressor active",
|
||||||
device_class=DEVICE_CLASS_POWER,
|
device_class=DEVICE_CLASS_POWER,
|
||||||
value_getter=lambda api: api.getCompressorActive(),
|
value_getter=lambda api: api.getActive(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSORS_GENERIC = [SENSOR_CIRCULATION_PUMP_ACTIVE]
|
|
||||||
|
|
||||||
SENSORS_BY_HEATINGTYPE = {
|
def _build_entity(name, vicare_api, device_config, sensor):
|
||||||
HeatingType.gas: [SENSOR_BURNER_ACTIVE],
|
"""Create a ViCare binary sensor entity."""
|
||||||
HeatingType.heatpump: [SENSOR_COMPRESSOR_ACTIVE],
|
try:
|
||||||
HeatingType.fuelcell: [SENSOR_BURNER_ACTIVE],
|
sensor.value_getter(vicare_api)
|
||||||
}
|
_LOGGER.debug("Found entity %s", name)
|
||||||
|
except PyViCareNotSupportedFeatureError:
|
||||||
|
_LOGGER.info("Feature not supported %s", name)
|
||||||
|
return None
|
||||||
|
except AttributeError:
|
||||||
|
_LOGGER.debug("Attribute Error %s", name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return ViCareBinarySensor(
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
name,
|
||||||
"""Create the ViCare sensor devices."""
|
vicare_api,
|
||||||
if discovery_info is None:
|
device_config,
|
||||||
return
|
sensor,
|
||||||
|
|
||||||
vicare_api = hass.data[VICARE_DOMAIN][VICARE_API]
|
|
||||||
heating_type = hass.data[VICARE_DOMAIN][VICARE_HEATING_TYPE]
|
|
||||||
|
|
||||||
sensors = SENSORS_GENERIC.copy()
|
|
||||||
|
|
||||||
if heating_type != HeatingType.generic:
|
|
||||||
sensors.extend(SENSORS_BY_HEATINGTYPE[heating_type])
|
|
||||||
|
|
||||||
add_entities(
|
|
||||||
[
|
|
||||||
ViCareBinarySensor(
|
|
||||||
hass.data[VICARE_DOMAIN][VICARE_NAME], vicare_api, description
|
|
||||||
)
|
|
||||||
for description in (
|
|
||||||
*SENSOR_TYPES_GENERIC,
|
|
||||||
*SENSOR_TYPES_GAS,
|
|
||||||
*SENSOR_TYPES_HEATPUMP,
|
|
||||||
)
|
|
||||||
if description.key in sensors
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
DescriptionT = Union[
|
async def _entities_from_descriptions(
|
||||||
ViCareBinarySensorEntityDescription[Device],
|
hass, name, all_devices, sensor_descriptions, iterables
|
||||||
ViCareBinarySensorEntityDescription[GazBoiler],
|
):
|
||||||
ViCareBinarySensorEntityDescription[HeatPump],
|
"""Create entities from descriptions and list of burners/circuits."""
|
||||||
]
|
for description in sensor_descriptions:
|
||||||
|
for current in iterables:
|
||||||
|
suffix = ""
|
||||||
|
if len(iterables) > 1:
|
||||||
|
suffix = f" {current.id}"
|
||||||
|
entity = await hass.async_add_executor_job(
|
||||||
|
_build_entity,
|
||||||
|
f"{name} {description.name}{suffix}",
|
||||||
|
current,
|
||||||
|
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
||||||
|
description,
|
||||||
|
)
|
||||||
|
if entity is not None:
|
||||||
|
all_devices.append(entity)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
|
"""Create the ViCare binary sensor devices."""
|
||||||
|
if discovery_info is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
name = hass.data[DOMAIN][VICARE_NAME]
|
||||||
|
api = hass.data[DOMAIN][VICARE_API]
|
||||||
|
|
||||||
|
all_devices = []
|
||||||
|
|
||||||
|
for description in CIRCUIT_SENSORS:
|
||||||
|
for circuit in api.circuits:
|
||||||
|
suffix = ""
|
||||||
|
if len(api.circuits) > 1:
|
||||||
|
suffix = f" {circuit.id}"
|
||||||
|
entity = await hass.async_add_executor_job(
|
||||||
|
_build_entity,
|
||||||
|
f"{name} {description.name}{suffix}",
|
||||||
|
circuit,
|
||||||
|
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
||||||
|
description,
|
||||||
|
)
|
||||||
|
if entity is not None:
|
||||||
|
all_devices.append(entity)
|
||||||
|
|
||||||
|
try:
|
||||||
|
_entities_from_descriptions(
|
||||||
|
hass, name, all_devices, BURNER_SENSORS, api.burners
|
||||||
|
)
|
||||||
|
except PyViCareNotSupportedFeatureError:
|
||||||
|
_LOGGER.info("No burners found")
|
||||||
|
|
||||||
|
try:
|
||||||
|
_entities_from_descriptions(
|
||||||
|
hass, name, all_devices, COMPRESSOR_SENSORS, api.compressors
|
||||||
|
)
|
||||||
|
except PyViCareNotSupportedFeatureError:
|
||||||
|
_LOGGER.info("No compressors found")
|
||||||
|
|
||||||
|
async_add_entities(all_devices)
|
||||||
|
|
||||||
|
|
||||||
class ViCareBinarySensor(BinarySensorEntity):
|
class ViCareBinarySensor(BinarySensorEntity):
|
||||||
"""Representation of a ViCare sensor."""
|
"""Representation of a ViCare sensor."""
|
||||||
|
|
||||||
entity_description: DescriptionT
|
entity_description: ViCareBinarySensorEntityDescription
|
||||||
|
|
||||||
def __init__(self, name, api, description: DescriptionT):
|
def __init__(
|
||||||
|
self, name, api, device_config, description: ViCareBinarySensorEntityDescription
|
||||||
|
):
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_name = f"{name} {description.name}"
|
self._attr_name = name
|
||||||
self._api = api
|
self._api = api
|
||||||
|
self.entity_description = description
|
||||||
|
self._device_config = device_config
|
||||||
self._state = None
|
self._state = None
|
||||||
|
|
||||||
|
@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
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
"""Return True if entity is available."""
|
"""Return True if entity is available."""
|
||||||
|
@ -136,8 +178,13 @@ class ViCareBinarySensor(BinarySensorEntity):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return a unique ID."""
|
"""Return unique ID for this device."""
|
||||||
return f"{self._api.service.id}-{self.entity_description.key}"
|
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
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
|
@ -155,3 +202,5 @@ class ViCareBinarySensor(BinarySensorEntity):
|
||||||
_LOGGER.error("Unable to decode data from ViCare server")
|
_LOGGER.error("Unable to decode data from ViCare server")
|
||||||
except PyViCareRateLimitError as limit_exception:
|
except PyViCareRateLimitError as limit_exception:
|
||||||
_LOGGER.error("Vicare API rate limit exceeded: %s", 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)
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyViCare.PyViCare import PyViCareNotSupportedFeatureError, PyViCareRateLimitError
|
from PyViCare.PyViCareUtils import (
|
||||||
|
PyViCareInvalidDataError,
|
||||||
|
PyViCareNotSupportedFeatureError,
|
||||||
|
PyViCareRateLimitError,
|
||||||
|
)
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -21,12 +25,13 @@ from homeassistant.components.climate.const import (
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
||||||
from homeassistant.helpers import entity_platform
|
from homeassistant.helpers import entity_platform
|
||||||
|
|
||||||
from . import (
|
from .const import (
|
||||||
DOMAIN as VICARE_DOMAIN,
|
CONF_HEATING_TYPE,
|
||||||
|
DOMAIN,
|
||||||
VICARE_API,
|
VICARE_API,
|
||||||
VICARE_HEATING_TYPE,
|
VICARE_CIRCUITS,
|
||||||
|
VICARE_DEVICE_CONFIG,
|
||||||
VICARE_NAME,
|
VICARE_NAME,
|
||||||
HeatingType,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -87,23 +92,38 @@ HA_TO_VICARE_PRESET_HEATING = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_entity(name, vicare_api, circuit, device_config, heating_type):
|
||||||
|
"""Create a ViCare climate entity."""
|
||||||
|
_LOGGER.debug("Found device %s", name)
|
||||||
|
return ViCareClimate(name, vicare_api, device_config, circuit, heating_type)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass, hass_config, async_add_entities, discovery_info=None
|
hass, hass_config, async_add_entities, discovery_info=None
|
||||||
):
|
):
|
||||||
"""Create the ViCare climate devices."""
|
"""Create the ViCare climate devices."""
|
||||||
|
# Legacy setup. Remove after configuration.yaml deprecation end
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
vicare_api = hass.data[VICARE_DOMAIN][VICARE_API]
|
|
||||||
heating_type = hass.data[VICARE_DOMAIN][VICARE_HEATING_TYPE]
|
name = hass.data[DOMAIN][VICARE_NAME]
|
||||||
async_add_entities(
|
all_devices = []
|
||||||
[
|
|
||||||
ViCareClimate(
|
for circuit in hass.data[DOMAIN][VICARE_CIRCUITS]:
|
||||||
f"{hass.data[VICARE_DOMAIN][VICARE_NAME]} Heating",
|
suffix = ""
|
||||||
vicare_api,
|
if len(hass.data[DOMAIN][VICARE_CIRCUITS]) > 1:
|
||||||
heating_type,
|
suffix = f" {circuit.id}"
|
||||||
)
|
entity = _build_entity(
|
||||||
]
|
f"{name} Heating{suffix}",
|
||||||
)
|
hass.data[DOMAIN][VICARE_API],
|
||||||
|
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
||||||
|
circuit,
|
||||||
|
hass.data[DOMAIN][CONF_HEATING_TYPE],
|
||||||
|
)
|
||||||
|
if entity is not None:
|
||||||
|
all_devices.append(entity)
|
||||||
|
|
||||||
|
async_add_entities(all_devices)
|
||||||
|
|
||||||
platform = entity_platform.async_get_current_platform()
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
|
||||||
|
@ -121,11 +141,13 @@ async def async_setup_platform(
|
||||||
class ViCareClimate(ClimateEntity):
|
class ViCareClimate(ClimateEntity):
|
||||||
"""Representation of the ViCare heating climate device."""
|
"""Representation of the ViCare heating climate device."""
|
||||||
|
|
||||||
def __init__(self, name, api, heating_type):
|
def __init__(self, name, api, circuit, device_config, heating_type):
|
||||||
"""Initialize the climate device."""
|
"""Initialize the climate device."""
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = None
|
self._state = None
|
||||||
self._api = api
|
self._api = api
|
||||||
|
self._circuit = circuit
|
||||||
|
self._device_config = device_config
|
||||||
self._attributes = {}
|
self._attributes = {}
|
||||||
self._target_temperature = None
|
self._target_temperature = None
|
||||||
self._current_mode = None
|
self._current_mode = None
|
||||||
|
@ -134,16 +156,31 @@ class ViCareClimate(ClimateEntity):
|
||||||
self._heating_type = heating_type
|
self._heating_type = heating_type
|
||||||
self._current_action = None
|
self._current_action = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return unique ID for this device."""
|
||||||
|
return f"{self._device_config.getConfig().serial}-climate-{self._circuit.id}"
|
||||||
|
|
||||||
|
@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()),
|
||||||
|
}
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""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:
|
||||||
_room_temperature = None
|
_room_temperature = None
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
_room_temperature = self._api.getRoomTemperature()
|
_room_temperature = self._circuit.getRoomTemperature()
|
||||||
|
|
||||||
_supply_temperature = None
|
_supply_temperature = None
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
_supply_temperature = self._api.getSupplyTemperature()
|
_supply_temperature = self._circuit.getSupplyTemperature()
|
||||||
|
|
||||||
if _room_temperature is not None:
|
if _room_temperature is not None:
|
||||||
self._current_temperature = _room_temperature
|
self._current_temperature = _room_temperature
|
||||||
|
@ -153,13 +190,13 @@ class ViCareClimate(ClimateEntity):
|
||||||
self._current_temperature = None
|
self._current_temperature = None
|
||||||
|
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._current_program = self._api.getActiveProgram()
|
self._current_program = self._circuit.getActiveProgram()
|
||||||
|
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._target_temperature = self._api.getCurrentDesiredTemperature()
|
self._target_temperature = self._circuit.getCurrentDesiredTemperature()
|
||||||
|
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._current_mode = self._api.getActiveMode()
|
self._current_mode = self._circuit.getActiveMode()
|
||||||
|
|
||||||
# Update the generic device attributes
|
# Update the generic device attributes
|
||||||
self._attributes = {}
|
self._attributes = {}
|
||||||
|
@ -171,26 +208,33 @@ class ViCareClimate(ClimateEntity):
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._attributes[
|
self._attributes[
|
||||||
"heating_curve_slope"
|
"heating_curve_slope"
|
||||||
] = self._api.getHeatingCurveSlope()
|
] = self._circuit.getHeatingCurveSlope()
|
||||||
|
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._attributes[
|
self._attributes[
|
||||||
"heating_curve_shift"
|
"heating_curve_shift"
|
||||||
] = self._api.getHeatingCurveShift()
|
] = self._circuit.getHeatingCurveShift()
|
||||||
|
|
||||||
|
self._current_action = False
|
||||||
# Update the specific device attributes
|
# Update the specific device attributes
|
||||||
if self._heating_type == HeatingType.gas:
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
for burner in self._api.burners:
|
||||||
self._current_action = self._api.getBurnerActive()
|
self._current_action = self._current_action or burner.getActive()
|
||||||
elif self._heating_type == HeatingType.heatpump:
|
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._current_action = self._api.getCompressorActive()
|
for compressor in self._api.compressors:
|
||||||
|
self._current_action = (
|
||||||
|
self._current_action or compressor.getActive()
|
||||||
|
)
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
_LOGGER.error("Unable to retrieve data from ViCare server")
|
_LOGGER.error("Unable to retrieve data from ViCare server")
|
||||||
except PyViCareRateLimitError as limit_exception:
|
except PyViCareRateLimitError as limit_exception:
|
||||||
_LOGGER.error("Vicare API rate limit exceeded: %s", limit_exception)
|
_LOGGER.error("Vicare API rate limit exceeded: %s", limit_exception)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.error("Unable to decode data from ViCare server")
|
_LOGGER.error("Unable to decode data from ViCare server")
|
||||||
|
except PyViCareInvalidDataError as invalid_data_exception:
|
||||||
|
_LOGGER.error("Invalid data from Vicare server: %s", invalid_data_exception)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self):
|
def supported_features(self):
|
||||||
|
@ -231,7 +275,7 @@ class ViCareClimate(ClimateEntity):
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER.debug("Setting hvac mode to %s / %s", hvac_mode, vicare_mode)
|
_LOGGER.debug("Setting hvac mode to %s / %s", hvac_mode, vicare_mode)
|
||||||
self._api.setMode(vicare_mode)
|
self._circuit.setMode(vicare_mode)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hvac_modes(self):
|
def hvac_modes(self):
|
||||||
|
@ -263,7 +307,7 @@ class ViCareClimate(ClimateEntity):
|
||||||
def set_temperature(self, **kwargs):
|
def set_temperature(self, **kwargs):
|
||||||
"""Set new target temperatures."""
|
"""Set new target temperatures."""
|
||||||
if (temp := kwargs.get(ATTR_TEMPERATURE)) is not None:
|
if (temp := kwargs.get(ATTR_TEMPERATURE)) is not None:
|
||||||
self._api.setProgramTemperature(self._current_program, temp)
|
self._circuit.setProgramTemperature(self._current_program, temp)
|
||||||
self._target_temperature = temp
|
self._target_temperature = temp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -285,8 +329,8 @@ class ViCareClimate(ClimateEntity):
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER.debug("Setting preset to %s / %s", preset_mode, vicare_program)
|
_LOGGER.debug("Setting preset to %s / %s", preset_mode, vicare_program)
|
||||||
self._api.deactivateProgram(self._current_program)
|
self._circuit.deactivateProgram(self._current_program)
|
||||||
self._api.activateProgram(vicare_program)
|
self._circuit.activateProgram(vicare_program)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self):
|
def extra_state_attributes(self):
|
||||||
|
@ -298,4 +342,4 @@ class ViCareClimate(ClimateEntity):
|
||||||
if vicare_mode not in VICARE_TO_HA_HVAC_HEATING:
|
if vicare_mode not in VICARE_TO_HA_HVAC_HEATING:
|
||||||
raise ValueError(f"Cannot set invalid vicare mode: {vicare_mode}")
|
raise ValueError(f"Cannot set invalid vicare mode: {vicare_mode}")
|
||||||
|
|
||||||
self._api.setMode(vicare_mode)
|
self._circuit.setMode(vicare_mode)
|
||||||
|
|
39
homeassistant/components/vicare/const.py
Normal file
39
homeassistant/components/vicare/const.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
"""Constants for the ViCare integration."""
|
||||||
|
import enum
|
||||||
|
|
||||||
|
DOMAIN = "vicare"
|
||||||
|
|
||||||
|
PLATFORMS = ["climate", "sensor", "binary_sensor", "water_heater"]
|
||||||
|
|
||||||
|
VICARE_DEVICE_CONFIG = "device_conf"
|
||||||
|
VICARE_API = "api"
|
||||||
|
VICARE_NAME = "name"
|
||||||
|
VICARE_CIRCUITS = "circuits"
|
||||||
|
|
||||||
|
CONF_CIRCUIT = "circuit"
|
||||||
|
CONF_HEATING_TYPE = "heating_type"
|
||||||
|
|
||||||
|
DEFAULT_SCAN_INTERVAL = 60
|
||||||
|
|
||||||
|
|
||||||
|
class HeatingType(enum.Enum):
|
||||||
|
"""Possible options for heating type."""
|
||||||
|
|
||||||
|
auto = "auto"
|
||||||
|
gas = "gas"
|
||||||
|
oil = "oil"
|
||||||
|
pellets = "pellets"
|
||||||
|
heatpump = "heatpump"
|
||||||
|
fuelcell = "fuelcell"
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_HEATING_TYPE = HeatingType.auto
|
||||||
|
|
||||||
|
HEATING_TYPE_TO_CREATOR_METHOD = {
|
||||||
|
HeatingType.auto: "asAutoDetectDevice",
|
||||||
|
HeatingType.gas: "asGazBoiler",
|
||||||
|
HeatingType.fuelcell: "asFuelCell",
|
||||||
|
HeatingType.heatpump: "asHeatPump",
|
||||||
|
HeatingType.oil: "asOilBoiler",
|
||||||
|
HeatingType.pellets: "asPelletsBoiler",
|
||||||
|
}
|
|
@ -3,6 +3,6 @@
|
||||||
"name": "Viessmann ViCare",
|
"name": "Viessmann ViCare",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/vicare",
|
"documentation": "https://www.home-assistant.io/integrations/vicare",
|
||||||
"codeowners": ["@oischinger"],
|
"codeowners": ["@oischinger"],
|
||||||
"requirements": ["PyViCare==1.0.0"],
|
"requirements": ["PyViCare==2.13.0"],
|
||||||
"iot_class": "cloud_polling"
|
"iot_class": "cloud_polling"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,19 @@ from __future__ import annotations
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from PyViCare.PyViCare import PyViCareNotSupportedFeatureError, PyViCareRateLimitError
|
from PyViCare.PyViCareUtils import (
|
||||||
from PyViCare.PyViCareDevice import Device
|
PyViCareInvalidDataError,
|
||||||
from PyViCare.PyViCareFuelCell import FuelCell
|
PyViCareNotSupportedFeatureError,
|
||||||
from PyViCare.PyViCareGazBoiler import GazBoiler
|
PyViCareRateLimitError,
|
||||||
from PyViCare.PyViCareHeatPump import HeatPump
|
)
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
from homeassistant.components.sensor import (
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
DEVICE_CLASS_ENERGY,
|
DEVICE_CLASS_ENERGY,
|
||||||
DEVICE_CLASS_POWER,
|
DEVICE_CLASS_POWER,
|
||||||
|
@ -24,21 +27,13 @@ from homeassistant.const import (
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
TIME_HOURS,
|
TIME_HOURS,
|
||||||
)
|
)
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from . import (
|
from . import ViCareRequiredKeysMixin
|
||||||
DOMAIN as VICARE_DOMAIN,
|
from .const import DOMAIN, VICARE_API, VICARE_DEVICE_CONFIG, VICARE_NAME
|
||||||
VICARE_API,
|
|
||||||
VICARE_HEATING_TYPE,
|
|
||||||
VICARE_NAME,
|
|
||||||
ApiT,
|
|
||||||
HeatingType,
|
|
||||||
ViCareRequiredKeysMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SENSOR_TYPE_TEMPERATURE = "temperature"
|
|
||||||
|
|
||||||
SENSOR_OUTSIDE_TEMPERATURE = "outside_temperature"
|
SENSOR_OUTSIDE_TEMPERATURE = "outside_temperature"
|
||||||
SENSOR_SUPPLY_TEMPERATURE = "supply_temperature"
|
SENSOR_SUPPLY_TEMPERATURE = "supply_temperature"
|
||||||
SENSOR_RETURN_TEMPERATURE = "return_temperature"
|
SENSOR_RETURN_TEMPERATURE = "return_temperature"
|
||||||
|
@ -76,308 +71,340 @@ SENSOR_POWER_PRODUCTION_THIS_YEAR = "power_production_this_year"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ViCareSensorEntityDescription(
|
class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysMixin):
|
||||||
SensorEntityDescription, ViCareRequiredKeysMixin[ApiT]
|
|
||||||
):
|
|
||||||
"""Describes ViCare sensor entity."""
|
"""Describes ViCare sensor entity."""
|
||||||
|
|
||||||
|
|
||||||
SENSOR_TYPES_GENERIC: tuple[ViCareSensorEntityDescription[Device], ...] = (
|
GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
|
||||||
ViCareSensorEntityDescription[Device](
|
ViCareSensorEntityDescription(
|
||||||
key=SENSOR_OUTSIDE_TEMPERATURE,
|
key=SENSOR_OUTSIDE_TEMPERATURE,
|
||||||
name="Outside Temperature",
|
name="Outside Temperature",
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
value_getter=lambda api: api.getOutsideTemperature(),
|
value_getter=lambda api: api.getOutsideTemperature(),
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
),
|
),
|
||||||
ViCareSensorEntityDescription[Device](
|
ViCareSensorEntityDescription(
|
||||||
key=SENSOR_SUPPLY_TEMPERATURE,
|
|
||||||
name="Supply Temperature",
|
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
|
||||||
value_getter=lambda api: api.getSupplyTemperature(),
|
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
SENSOR_TYPES_GAS: tuple[ViCareSensorEntityDescription[GazBoiler], ...] = (
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_BOILER_TEMPERATURE,
|
|
||||||
name="Boiler Temperature",
|
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
|
||||||
value_getter=lambda api: api.getBoilerTemperature(),
|
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_BURNER_MODULATION,
|
|
||||||
name="Burner modulation",
|
|
||||||
icon="mdi:percent",
|
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
|
||||||
value_getter=lambda api: api.getBurnerModulation(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_DHW_GAS_CONSUMPTION_TODAY,
|
|
||||||
name="Hot water gas consumption today",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterToday(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_DHW_GAS_CONSUMPTION_THIS_WEEK,
|
|
||||||
name="Hot water gas consumption this week",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterThisWeek(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_DHW_GAS_CONSUMPTION_THIS_MONTH,
|
|
||||||
name="Hot water gas consumption this month",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterThisMonth(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_DHW_GAS_CONSUMPTION_THIS_YEAR,
|
|
||||||
name="Hot water gas consumption this year",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterThisYear(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_GAS_CONSUMPTION_TODAY,
|
|
||||||
name="Heating gas consumption today",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionHeatingToday(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_GAS_CONSUMPTION_THIS_WEEK,
|
|
||||||
name="Heating gas consumption this week",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionHeatingThisWeek(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_GAS_CONSUMPTION_THIS_MONTH,
|
|
||||||
name="Heating gas consumption this month",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionHeatingThisMonth(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_GAS_CONSUMPTION_THIS_YEAR,
|
|
||||||
name="Heating gas consumption this year",
|
|
||||||
icon="mdi:power",
|
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
|
||||||
value_getter=lambda api: api.getGasConsumptionHeatingThisYear(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_BURNER_STARTS,
|
|
||||||
name="Burner Starts",
|
|
||||||
icon="mdi:counter",
|
|
||||||
value_getter=lambda api: api.getBurnerStarts(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[GazBoiler](
|
|
||||||
key=SENSOR_BURNER_HOURS,
|
|
||||||
name="Burner Hours",
|
|
||||||
icon="mdi:counter",
|
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
|
||||||
value_getter=lambda api: api.getBurnerHours(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
SENSOR_TYPES_HEATPUMP: tuple[ViCareSensorEntityDescription[HeatPump], ...] = (
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_COMPRESSOR_STARTS,
|
|
||||||
name="Compressor Starts",
|
|
||||||
icon="mdi:counter",
|
|
||||||
value_getter=lambda api: api.getCompressorStarts(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_COMPRESSOR_HOURS,
|
|
||||||
name="Compressor Hours",
|
|
||||||
icon="mdi:counter",
|
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
|
||||||
value_getter=lambda api: api.getCompressorHours(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS1,
|
|
||||||
name="Compressor Hours Load Class 1",
|
|
||||||
icon="mdi:counter",
|
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
|
||||||
value_getter=lambda api: api.getCompressorHoursLoadClass1(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS2,
|
|
||||||
name="Compressor Hours Load Class 2",
|
|
||||||
icon="mdi:counter",
|
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
|
||||||
value_getter=lambda api: api.getCompressorHoursLoadClass2(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS3,
|
|
||||||
name="Compressor Hours Load Class 3",
|
|
||||||
icon="mdi:counter",
|
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
|
||||||
value_getter=lambda api: api.getCompressorHoursLoadClass3(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS4,
|
|
||||||
name="Compressor Hours Load Class 4",
|
|
||||||
icon="mdi:counter",
|
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
|
||||||
value_getter=lambda api: api.getCompressorHoursLoadClass4(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS5,
|
|
||||||
name="Compressor Hours Load Class 5",
|
|
||||||
icon="mdi:counter",
|
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
|
||||||
value_getter=lambda api: api.getCompressorHoursLoadClass5(),
|
|
||||||
),
|
|
||||||
ViCareSensorEntityDescription[HeatPump](
|
|
||||||
key=SENSOR_RETURN_TEMPERATURE,
|
key=SENSOR_RETURN_TEMPERATURE,
|
||||||
name="Return Temperature",
|
name="Return Temperature",
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
value_getter=lambda api: api.getReturnTemperature(),
|
value_getter=lambda api: api.getReturnTemperature(),
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
),
|
),
|
||||||
)
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_BOILER_TEMPERATURE,
|
||||||
SENSOR_TYPES_FUELCELL: tuple[ViCareSensorEntityDescription[FuelCell], ...] = (
|
name="Boiler Temperature",
|
||||||
ViCareSensorEntityDescription[FuelCell](
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
|
value_getter=lambda api: api.getBoilerTemperature(),
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_DHW_GAS_CONSUMPTION_TODAY,
|
||||||
|
name="Hot water gas consumption today",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterToday(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_DHW_GAS_CONSUMPTION_THIS_WEEK,
|
||||||
|
name="Hot water gas consumption this week",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterThisWeek(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_DHW_GAS_CONSUMPTION_THIS_MONTH,
|
||||||
|
name="Hot water gas consumption this month",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterThisMonth(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_DHW_GAS_CONSUMPTION_THIS_YEAR,
|
||||||
|
name="Hot water gas consumption this year",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionDomesticHotWaterThisYear(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_GAS_CONSUMPTION_TODAY,
|
||||||
|
name="Heating gas consumption today",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionHeatingToday(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_GAS_CONSUMPTION_THIS_WEEK,
|
||||||
|
name="Heating gas consumption this week",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionHeatingThisWeek(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_GAS_CONSUMPTION_THIS_MONTH,
|
||||||
|
name="Heating gas consumption this month",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionHeatingThisMonth(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_GAS_CONSUMPTION_THIS_YEAR,
|
||||||
|
name="Heating gas consumption this year",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
value_getter=lambda api: api.getGasConsumptionHeatingThisYear(),
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
key=SENSOR_POWER_PRODUCTION_CURRENT,
|
key=SENSOR_POWER_PRODUCTION_CURRENT,
|
||||||
name="Power production current",
|
name="Power production current",
|
||||||
native_unit_of_measurement=POWER_WATT,
|
native_unit_of_measurement=POWER_WATT,
|
||||||
value_getter=lambda api: api.getPowerProductionCurrent(),
|
value_getter=lambda api: api.getPowerProductionCurrent(),
|
||||||
device_class=DEVICE_CLASS_POWER,
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
ViCareSensorEntityDescription[FuelCell](
|
ViCareSensorEntityDescription(
|
||||||
key=SENSOR_POWER_PRODUCTION_TODAY,
|
key=SENSOR_POWER_PRODUCTION_TODAY,
|
||||||
name="Power production today",
|
name="Power production today",
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
value_getter=lambda api: api.getPowerProductionToday(),
|
value_getter=lambda api: api.getPowerProductionToday(),
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
ViCareSensorEntityDescription[FuelCell](
|
ViCareSensorEntityDescription(
|
||||||
key=SENSOR_POWER_PRODUCTION_THIS_WEEK,
|
key=SENSOR_POWER_PRODUCTION_THIS_WEEK,
|
||||||
name="Power production this week",
|
name="Power production this week",
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
value_getter=lambda api: api.getPowerProductionThisWeek(),
|
value_getter=lambda api: api.getPowerProductionThisWeek(),
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
ViCareSensorEntityDescription[FuelCell](
|
ViCareSensorEntityDescription(
|
||||||
key=SENSOR_POWER_PRODUCTION_THIS_MONTH,
|
key=SENSOR_POWER_PRODUCTION_THIS_MONTH,
|
||||||
name="Power production this month",
|
name="Power production this month",
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
value_getter=lambda api: api.getPowerProductionThisMonth(),
|
value_getter=lambda api: api.getPowerProductionThisMonth(),
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
ViCareSensorEntityDescription[FuelCell](
|
ViCareSensorEntityDescription(
|
||||||
key=SENSOR_POWER_PRODUCTION_THIS_YEAR,
|
key=SENSOR_POWER_PRODUCTION_THIS_YEAR,
|
||||||
name="Power production this year",
|
name="Power production this year",
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
value_getter=lambda api: api.getPowerProductionThisYear(),
|
value_getter=lambda api: api.getPowerProductionThisYear(),
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSORS_GENERIC = [SENSOR_OUTSIDE_TEMPERATURE, SENSOR_SUPPLY_TEMPERATURE]
|
CIRCUIT_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_SUPPLY_TEMPERATURE,
|
||||||
|
name="Supply Temperature",
|
||||||
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
|
value_getter=lambda api: api.getSupplyTemperature(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
SENSORS_BY_HEATINGTYPE = {
|
BURNER_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
|
||||||
HeatingType.gas: [
|
ViCareSensorEntityDescription(
|
||||||
SENSOR_BOILER_TEMPERATURE,
|
key=SENSOR_BURNER_STARTS,
|
||||||
SENSOR_BURNER_HOURS,
|
name="Burner Starts",
|
||||||
SENSOR_BURNER_MODULATION,
|
icon="mdi:counter",
|
||||||
SENSOR_BURNER_STARTS,
|
value_getter=lambda api: api.getStarts(),
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_TODAY,
|
),
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_THIS_WEEK,
|
ViCareSensorEntityDescription(
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_THIS_MONTH,
|
key=SENSOR_BURNER_HOURS,
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_THIS_YEAR,
|
name="Burner Hours",
|
||||||
SENSOR_GAS_CONSUMPTION_TODAY,
|
icon="mdi:counter",
|
||||||
SENSOR_GAS_CONSUMPTION_THIS_WEEK,
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
SENSOR_GAS_CONSUMPTION_THIS_MONTH,
|
value_getter=lambda api: api.getHours(),
|
||||||
SENSOR_GAS_CONSUMPTION_THIS_YEAR,
|
),
|
||||||
],
|
ViCareSensorEntityDescription(
|
||||||
HeatingType.heatpump: [
|
key=SENSOR_BURNER_MODULATION,
|
||||||
SENSOR_COMPRESSOR_STARTS,
|
name="Burner Modulation",
|
||||||
SENSOR_COMPRESSOR_HOURS,
|
icon="mdi:percent",
|
||||||
SENSOR_COMPRESSOR_HOURS_LOADCLASS1,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
SENSOR_COMPRESSOR_HOURS_LOADCLASS2,
|
value_getter=lambda api: api.getModulation(),
|
||||||
SENSOR_COMPRESSOR_HOURS_LOADCLASS3,
|
),
|
||||||
SENSOR_COMPRESSOR_HOURS_LOADCLASS4,
|
)
|
||||||
SENSOR_COMPRESSOR_HOURS_LOADCLASS5,
|
|
||||||
SENSOR_RETURN_TEMPERATURE,
|
COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
|
||||||
],
|
ViCareSensorEntityDescription(
|
||||||
HeatingType.fuelcell: [
|
key=SENSOR_COMPRESSOR_STARTS,
|
||||||
# gas
|
name="Compressor Starts",
|
||||||
SENSOR_BOILER_TEMPERATURE,
|
icon="mdi:counter",
|
||||||
SENSOR_BURNER_HOURS,
|
value_getter=lambda api: api.getStarts(),
|
||||||
SENSOR_BURNER_MODULATION,
|
),
|
||||||
SENSOR_BURNER_STARTS,
|
ViCareSensorEntityDescription(
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_TODAY,
|
key=SENSOR_COMPRESSOR_HOURS,
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_THIS_WEEK,
|
name="Compressor Hours",
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_THIS_MONTH,
|
icon="mdi:counter",
|
||||||
SENSOR_DHW_GAS_CONSUMPTION_THIS_YEAR,
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
SENSOR_GAS_CONSUMPTION_TODAY,
|
value_getter=lambda api: api.getHours(),
|
||||||
SENSOR_GAS_CONSUMPTION_THIS_WEEK,
|
),
|
||||||
SENSOR_GAS_CONSUMPTION_THIS_MONTH,
|
ViCareSensorEntityDescription(
|
||||||
SENSOR_GAS_CONSUMPTION_THIS_YEAR,
|
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS1,
|
||||||
# fuel cell
|
name="Compressor Hours Load Class 1",
|
||||||
SENSOR_POWER_PRODUCTION_CURRENT,
|
icon="mdi:counter",
|
||||||
SENSOR_POWER_PRODUCTION_TODAY,
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
SENSOR_POWER_PRODUCTION_THIS_WEEK,
|
value_getter=lambda api: api.getHoursLoadClass1(),
|
||||||
SENSOR_POWER_PRODUCTION_THIS_MONTH,
|
),
|
||||||
SENSOR_POWER_PRODUCTION_THIS_YEAR,
|
ViCareSensorEntityDescription(
|
||||||
],
|
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS2,
|
||||||
}
|
name="Compressor Hours Load Class 2",
|
||||||
|
icon="mdi:counter",
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
|
value_getter=lambda api: api.getHoursLoadClass2(),
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS3,
|
||||||
|
name="Compressor Hours Load Class 3",
|
||||||
|
icon="mdi:counter",
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
|
value_getter=lambda api: api.getHoursLoadClass3(),
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS4,
|
||||||
|
name="Compressor Hours Load Class 4",
|
||||||
|
icon="mdi:counter",
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
|
value_getter=lambda api: api.getHoursLoadClass4(),
|
||||||
|
),
|
||||||
|
ViCareSensorEntityDescription(
|
||||||
|
key=SENSOR_COMPRESSOR_HOURS_LOADCLASS5,
|
||||||
|
name="Compressor Hours Load Class 5",
|
||||||
|
icon="mdi:counter",
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
|
value_getter=lambda api: api.getHoursLoadClass5(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def _build_entity(name, vicare_api, device_config, sensor):
|
||||||
|
"""Create a ViCare sensor entity."""
|
||||||
|
_LOGGER.debug("Found device %s", name)
|
||||||
|
try:
|
||||||
|
sensor.value_getter(vicare_api)
|
||||||
|
_LOGGER.debug("Found entity %s", name)
|
||||||
|
except PyViCareNotSupportedFeatureError:
|
||||||
|
_LOGGER.info("Feature not supported %s", name)
|
||||||
|
return None
|
||||||
|
except AttributeError:
|
||||||
|
_LOGGER.debug("Attribute Error %s", name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return ViCareSensor(
|
||||||
|
name,
|
||||||
|
vicare_api,
|
||||||
|
device_config,
|
||||||
|
sensor,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _entities_from_descriptions(
|
||||||
|
hass, name, all_devices, sensor_descriptions, iterables
|
||||||
|
):
|
||||||
|
"""Create entities from descriptions and list of burners/circuits."""
|
||||||
|
for description in sensor_descriptions:
|
||||||
|
for current in iterables:
|
||||||
|
suffix = ""
|
||||||
|
if len(iterables) > 1:
|
||||||
|
suffix = f" {current.id}"
|
||||||
|
entity = await hass.async_add_executor_job(
|
||||||
|
_build_entity,
|
||||||
|
f"{name} {description.name}{suffix}",
|
||||||
|
current,
|
||||||
|
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
||||||
|
description,
|
||||||
|
)
|
||||||
|
if entity is not None:
|
||||||
|
all_devices.append(entity)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Create the ViCare sensor devices."""
|
"""Create the ViCare sensor devices."""
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
vicare_api = hass.data[VICARE_DOMAIN][VICARE_API]
|
name = hass.data[DOMAIN][VICARE_NAME]
|
||||||
heating_type = hass.data[VICARE_DOMAIN][VICARE_HEATING_TYPE]
|
api = hass.data[DOMAIN][VICARE_API]
|
||||||
|
|
||||||
sensors = SENSORS_GENERIC.copy()
|
all_devices = []
|
||||||
|
for description in GLOBAL_SENSORS:
|
||||||
|
entity = await hass.async_add_executor_job(
|
||||||
|
_build_entity,
|
||||||
|
f"{name} {description.name}",
|
||||||
|
api,
|
||||||
|
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
||||||
|
description,
|
||||||
|
)
|
||||||
|
if entity is not None:
|
||||||
|
all_devices.append(entity)
|
||||||
|
|
||||||
if heating_type != HeatingType.generic:
|
for description in CIRCUIT_SENSORS:
|
||||||
sensors.extend(SENSORS_BY_HEATINGTYPE[heating_type])
|
for circuit in api.circuits:
|
||||||
|
suffix = ""
|
||||||
add_entities(
|
if len(api.circuits) > 1:
|
||||||
[
|
suffix = f" {circuit.id}"
|
||||||
ViCareSensor(hass.data[VICARE_DOMAIN][VICARE_NAME], vicare_api, description)
|
entity = await hass.async_add_executor_job(
|
||||||
for description in (
|
_build_entity,
|
||||||
*SENSOR_TYPES_GENERIC,
|
f"{name} {description.name}{suffix}",
|
||||||
*SENSOR_TYPES_GAS,
|
circuit,
|
||||||
*SENSOR_TYPES_HEATPUMP,
|
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
||||||
*SENSOR_TYPES_FUELCELL,
|
description,
|
||||||
)
|
)
|
||||||
if description.key in sensors
|
if entity is not None:
|
||||||
]
|
all_devices.append(entity)
|
||||||
)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
_entities_from_descriptions(
|
||||||
|
hass, name, all_devices, BURNER_SENSORS, api.burners
|
||||||
|
)
|
||||||
|
except PyViCareNotSupportedFeatureError:
|
||||||
|
_LOGGER.info("No burners found")
|
||||||
|
|
||||||
DescriptionT = Union[
|
try:
|
||||||
ViCareSensorEntityDescription[Device],
|
_entities_from_descriptions(
|
||||||
ViCareSensorEntityDescription[GazBoiler],
|
hass, name, all_devices, COMPRESSOR_SENSORS, api.compressors
|
||||||
ViCareSensorEntityDescription[HeatPump],
|
)
|
||||||
ViCareSensorEntityDescription[FuelCell],
|
except PyViCareNotSupportedFeatureError:
|
||||||
]
|
_LOGGER.info("No compressors found")
|
||||||
|
|
||||||
|
async_add_entities(all_devices)
|
||||||
|
|
||||||
|
|
||||||
class ViCareSensor(SensorEntity):
|
class ViCareSensor(SensorEntity):
|
||||||
"""Representation of a ViCare sensor."""
|
"""Representation of a ViCare sensor."""
|
||||||
|
|
||||||
entity_description: DescriptionT
|
entity_description: ViCareSensorEntityDescription
|
||||||
|
|
||||||
def __init__(self, name, api, description: DescriptionT):
|
def __init__(
|
||||||
|
self, name, api, device_config, description: ViCareSensorEntityDescription
|
||||||
|
):
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_name = f"{name} {description.name}"
|
self._attr_name = name
|
||||||
self._api = api
|
self._api = api
|
||||||
|
self._device_config = device_config
|
||||||
self._state = None
|
self._state = None
|
||||||
|
self._last_reset = dt_util.utcnow()
|
||||||
|
|
||||||
|
@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
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
|
@ -386,16 +413,27 @@ class ViCareSensor(SensorEntity):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return a unique ID."""
|
"""Return unique ID for this device."""
|
||||||
return f"{self._api.service.id}-{self.entity_description.key}"
|
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
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self):
|
def native_value(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_reset(self):
|
||||||
|
"""Return the time when the sensor was last reset."""
|
||||||
|
return self._last_reset
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update state of sensor."""
|
"""Update state of sensor."""
|
||||||
|
self._last_reset = dt_util.start_of_local_day()
|
||||||
try:
|
try:
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._state = self.entity_description.value_getter(self._api)
|
self._state = self.entity_description.value_getter(self._api)
|
||||||
|
@ -405,3 +443,5 @@ class ViCareSensor(SensorEntity):
|
||||||
_LOGGER.error("Unable to decode data from ViCare server")
|
_LOGGER.error("Unable to decode data from ViCare server")
|
||||||
except PyViCareRateLimitError as limit_exception:
|
except PyViCareRateLimitError as limit_exception:
|
||||||
_LOGGER.error("Vicare API rate limit exceeded: %s", 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)
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyViCare.PyViCare import PyViCareNotSupportedFeatureError, PyViCareRateLimitError
|
from PyViCare.PyViCareUtils import (
|
||||||
|
PyViCareInvalidDataError,
|
||||||
|
PyViCareNotSupportedFeatureError,
|
||||||
|
PyViCareRateLimitError,
|
||||||
|
)
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.components.water_heater import (
|
from homeassistant.components.water_heater import (
|
||||||
|
@ -11,7 +15,14 @@ from homeassistant.components.water_heater import (
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
||||||
|
|
||||||
from . import DOMAIN as VICARE_DOMAIN, VICARE_API, VICARE_HEATING_TYPE, VICARE_NAME
|
from .const import (
|
||||||
|
CONF_HEATING_TYPE,
|
||||||
|
DOMAIN,
|
||||||
|
VICARE_API,
|
||||||
|
VICARE_CIRCUITS,
|
||||||
|
VICARE_DEVICE_CONFIG,
|
||||||
|
VICARE_NAME,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -43,31 +54,53 @@ HA_TO_VICARE_HVAC_DHW = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def _build_entity(name, vicare_api, circuit, device_config, heating_type):
|
||||||
|
"""Create a ViCare water_heater entity."""
|
||||||
|
_LOGGER.debug("Found device %s", name)
|
||||||
|
return ViCareWater(
|
||||||
|
name,
|
||||||
|
vicare_api,
|
||||||
|
circuit,
|
||||||
|
device_config,
|
||||||
|
heating_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Create the ViCare water_heater devices."""
|
"""Create the ViCare water_heater devices."""
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
vicare_api = hass.data[VICARE_DOMAIN][VICARE_API]
|
|
||||||
heating_type = hass.data[VICARE_DOMAIN][VICARE_HEATING_TYPE]
|
name = hass.data[DOMAIN][VICARE_NAME]
|
||||||
add_entities(
|
|
||||||
[
|
all_devices = []
|
||||||
ViCareWater(
|
for circuit in hass.data[DOMAIN][VICARE_CIRCUITS]:
|
||||||
f"{hass.data[VICARE_DOMAIN][VICARE_NAME]} Water",
|
suffix = ""
|
||||||
vicare_api,
|
if len(hass.data[DOMAIN][VICARE_CIRCUITS]) > 1:
|
||||||
heating_type,
|
suffix = f" {circuit.id}"
|
||||||
)
|
entity = _build_entity(
|
||||||
]
|
f"{name} Water{suffix}",
|
||||||
)
|
hass.data[DOMAIN][VICARE_API],
|
||||||
|
circuit,
|
||||||
|
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
||||||
|
hass.data[DOMAIN][CONF_HEATING_TYPE],
|
||||||
|
)
|
||||||
|
if entity is not None:
|
||||||
|
all_devices.append(entity)
|
||||||
|
|
||||||
|
async_add_entities(all_devices)
|
||||||
|
|
||||||
|
|
||||||
class ViCareWater(WaterHeaterEntity):
|
class ViCareWater(WaterHeaterEntity):
|
||||||
"""Representation of the ViCare domestic hot water device."""
|
"""Representation of the ViCare domestic hot water device."""
|
||||||
|
|
||||||
def __init__(self, name, api, heating_type):
|
def __init__(self, name, api, circuit, device_config, heating_type):
|
||||||
"""Initialize the DHW water_heater device."""
|
"""Initialize the DHW water_heater device."""
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = None
|
self._state = None
|
||||||
self._api = api
|
self._api = api
|
||||||
|
self._circuit = circuit
|
||||||
|
self._device_config = device_config
|
||||||
self._attributes = {}
|
self._attributes = {}
|
||||||
self._target_temperature = None
|
self._target_temperature = None
|
||||||
self._current_temperature = None
|
self._current_temperature = None
|
||||||
|
@ -84,11 +117,11 @@ class ViCareWater(WaterHeaterEntity):
|
||||||
|
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._target_temperature = (
|
self._target_temperature = (
|
||||||
self._api.getDomesticHotWaterConfiguredTemperature()
|
self._api.getDomesticHotWaterDesiredTemperature()
|
||||||
)
|
)
|
||||||
|
|
||||||
with suppress(PyViCareNotSupportedFeatureError):
|
with suppress(PyViCareNotSupportedFeatureError):
|
||||||
self._current_mode = self._api.getActiveMode()
|
self._current_mode = self._circuit.getActiveMode()
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
_LOGGER.error("Unable to retrieve data from ViCare server")
|
_LOGGER.error("Unable to retrieve data from ViCare server")
|
||||||
|
@ -96,6 +129,23 @@ class ViCareWater(WaterHeaterEntity):
|
||||||
_LOGGER.error("Vicare API rate limit exceeded: %s", limit_exception)
|
_LOGGER.error("Vicare API rate limit exceeded: %s", limit_exception)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.error("Unable to decode data from ViCare server")
|
_LOGGER.error("Unable to decode data from ViCare server")
|
||||||
|
except PyViCareInvalidDataError as invalid_data_exception:
|
||||||
|
_LOGGER.error("Invalid data from Vicare server: %s", invalid_data_exception)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return unique ID for this device."""
|
||||||
|
return f"{self._device_config.getConfig().serial}-water-{self._circuit.id}"
|
||||||
|
|
||||||
|
@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
|
@property
|
||||||
def supported_features(self):
|
def supported_features(self):
|
||||||
|
|
|
@ -55,7 +55,7 @@ PyTransportNSW==0.1.1
|
||||||
PyTurboJPEG==1.6.1
|
PyTurboJPEG==1.6.1
|
||||||
|
|
||||||
# homeassistant.components.vicare
|
# homeassistant.components.vicare
|
||||||
PyViCare==1.0.0
|
PyViCare==2.13.0
|
||||||
|
|
||||||
# homeassistant.components.xiaomi_aqara
|
# homeassistant.components.xiaomi_aqara
|
||||||
PyXiaomiGateway==0.13.4
|
PyXiaomiGateway==0.13.4
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue