diff --git a/homeassistant/components/vicare/__init__.py b/homeassistant/components/vicare/__init__.py index 335e89eb873..d498ada704d 100644 --- a/homeassistant/components/vicare/__init__.py +++ b/homeassistant/components/vicare/__init__.py @@ -19,9 +19,10 @@ from homeassistant.helpers.storage import STORAGE_DIR _LOGGER = logging.getLogger(__name__) -VICARE_PLATFORMS = ["climate", "water_heater"] +VICARE_PLATFORMS = ["climate", "sensor", "binary_sensor", "water_heater"] DOMAIN = "vicare" +PYVICARE_ERROR = "error" VICARE_API = "api" VICARE_NAME = "name" VICARE_HEATING_TYPE = "heating_type" diff --git a/homeassistant/components/vicare/binary_sensor.py b/homeassistant/components/vicare/binary_sensor.py new file mode 100644 index 00000000000..a35339c12a9 --- /dev/null +++ b/homeassistant/components/vicare/binary_sensor.py @@ -0,0 +1,123 @@ +"""Viessmann ViCare sensor device.""" +import logging + +import requests + +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_POWER, + BinarySensorDevice, +) +from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME + +from . import ( + DOMAIN as VICARE_DOMAIN, + PYVICARE_ERROR, + VICARE_API, + VICARE_HEATING_TYPE, + VICARE_NAME, + HeatingType, +) + +_LOGGER = logging.getLogger(__name__) + +CONF_GETTER = "getter" + +SENSOR_CIRCULATION_PUMP_ACTIVE = "circulationpump_active" +SENSOR_BURNER_ACTIVE = "burner_active" +SENSOR_COMPRESSOR_ACTIVE = "compressor_active" + +SENSOR_TYPES = { + SENSOR_CIRCULATION_PUMP_ACTIVE: { + CONF_NAME: "Circulation pump active", + CONF_DEVICE_CLASS: DEVICE_CLASS_POWER, + CONF_GETTER: lambda api: api.getCirculationPumpActive(), + }, + # gas sensors + SENSOR_BURNER_ACTIVE: { + CONF_NAME: "Burner active", + CONF_DEVICE_CLASS: DEVICE_CLASS_POWER, + CONF_GETTER: lambda api: api.getBurnerActive(), + }, + # heatpump sensors + SENSOR_COMPRESSOR_ACTIVE: { + CONF_NAME: "Compressor active", + CONF_DEVICE_CLASS: DEVICE_CLASS_POWER, + CONF_GETTER: lambda api: api.getCompressorActive(), + }, +} + +SENSORS_GENERIC = [SENSOR_CIRCULATION_PUMP_ACTIVE] + +SENSORS_BY_HEATINGTYPE = { + HeatingType.gas: [SENSOR_BURNER_ACTIVE], + HeatingType.heatpump: [SENSOR_COMPRESSOR_ACTIVE], +} + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Create the ViCare sensor devices.""" + if discovery_info is None: + return + + 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, sensor + ) + for sensor in sensors + ] + ) + + +class ViCareBinarySensor(BinarySensorDevice): + """Representation of a ViCare sensor.""" + + def __init__(self, name, api, sensor_type): + """Initialize the sensor.""" + self._sensor = SENSOR_TYPES[sensor_type] + self._name = f"{name} {self._sensor[CONF_NAME]}" + self._api = api + self._sensor_type = sensor_type + self._state = None + + @property + def available(self): + """Return True if entity is available.""" + return self._state is not None and self._state != PYVICARE_ERROR + + @property + def unique_id(self): + """Return a unique ID.""" + return f"{self._api.service.id}-{self._sensor_type}" + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def is_on(self): + """Return the state of the sensor.""" + return self._state + + @property + def device_class(self): + """Return the class of this device, from component DEVICE_CLASSES.""" + return self._sensor[CONF_DEVICE_CLASS] + + def update(self): + """Update state of sensor.""" + try: + self._state = self._sensor[CONF_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") diff --git a/homeassistant/components/vicare/climate.py b/homeassistant/components/vicare/climate.py index ce88ea8e3e7..ddfb28478df 100644 --- a/homeassistant/components/vicare/climate.py +++ b/homeassistant/components/vicare/climate.py @@ -19,6 +19,7 @@ from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS from . import ( DOMAIN as VICARE_DOMAIN, + PYVICARE_ERROR, VICARE_API, VICARE_HEATING_TYPE, VICARE_NAME, @@ -77,8 +78,6 @@ HA_TO_VICARE_PRESET_HEATING = { PRESET_ECO: VICARE_PROGRAM_ECO, } -PYVICARE_ERROR = "error" - def setup_platform(hass, config, add_entities, discovery_info=None): """Create the ViCare climate devices.""" @@ -138,8 +137,6 @@ class ViCareClimate(ClimateEntity): # Update the generic device attributes self._attributes = {} self._attributes["room_temperature"] = _room_temperature - self._attributes["supply_temperature"] = _supply_temperature - self._attributes["outside_temperature"] = self._api.getOutsideTemperature() self._attributes["active_vicare_program"] = self._current_program self._attributes["active_vicare_mode"] = self._current_mode self._attributes["heating_curve_slope"] = self._api.getHeatingCurveSlope() @@ -150,25 +147,14 @@ class ViCareClimate(ClimateEntity): self._attributes["date_last_service"] = self._api.getLastServiceDate() self._attributes["error_history"] = self._api.getErrorHistory() self._attributes["active_error"] = self._api.getActiveError() - self._attributes[ - "circulationpump_active" - ] = self._api.getCirculationPumpActive() # Update the specific device attributes if self._heating_type == HeatingType.gas: self._current_action = self._api.getBurnerActive() - self._attributes["burner_modulation"] = self._api.getBurnerModulation() - self._attributes[ - "boiler_temperature" - ] = self._api.getBoilerTemperature() - elif self._heating_type == HeatingType.heatpump: self._current_action = self._api.getCompressorActive() - self._attributes[ - "return_temperature" - ] = self._api.getReturnTemperature() except requests.exceptions.ConnectionError: _LOGGER.error("Unable to retrieve data from ViCare server") except ValueError: diff --git a/homeassistant/components/vicare/manifest.json b/homeassistant/components/vicare/manifest.json index 6fc0dfdd119..a91867b7a19 100644 --- a/homeassistant/components/vicare/manifest.json +++ b/homeassistant/components/vicare/manifest.json @@ -3,5 +3,5 @@ "name": "Viessmann ViCare", "documentation": "https://www.home-assistant.io/integrations/vicare", "codeowners": ["@oischinger"], - "requirements": ["PyViCare==0.1.10"] + "requirements": ["PyViCare==0.2.0"] } diff --git a/homeassistant/components/vicare/sensor.py b/homeassistant/components/vicare/sensor.py new file mode 100644 index 00000000000..35ce6dc787b --- /dev/null +++ b/homeassistant/components/vicare/sensor.py @@ -0,0 +1,289 @@ +"""Viessmann ViCare sensor device.""" +import logging + +import requests + +from homeassistant.const import ( + CONF_DEVICE_CLASS, + CONF_ICON, + CONF_NAME, + CONF_UNIT_OF_MEASUREMENT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_TEMPERATURE, + ENERGY_KILO_WATT_HOUR, + POWER_WATT, + TEMP_CELSIUS, + UNIT_PERCENTAGE, +) +from homeassistant.helpers.entity import Entity + +from . import ( + DOMAIN as VICARE_DOMAIN, + PYVICARE_ERROR, + VICARE_API, + VICARE_HEATING_TYPE, + VICARE_NAME, + HeatingType, +) + +_LOGGER = logging.getLogger(__name__) + +CONF_GETTER = "getter" + +SENSOR_TYPE_TEMPERATURE = "temperature" + +SENSOR_OUTSIDE_TEMPERATURE = "outside_temperature" +SENSOR_SUPPLY_TEMPERATURE = "supply_temperature" +SENSOR_RETURN_TEMPERATURE = "return_temperature" + +# gas sensors +SENSOR_BOILER_TEMPERATURE = "boiler_temperature" +SENSOR_BURNER_MODULATION = "burner_modulation" +SENSOR_BURNER_STARTS = "burner_starts" +SENSOR_BURNER_HOURS = "burner_hours" +SENSOR_BURNER_POWER = "burner_power" +SENSOR_DHW_GAS_CONSUMPTION_TODAY = "hotwater_gas_consumption_today" +SENSOR_DHW_GAS_CONSUMPTION_THIS_WEEK = "hotwater_gas_consumption_heating_this_week" +SENSOR_DHW_GAS_CONSUMPTION_THIS_MONTH = "hotwater_gas_consumption_heating_this_month" +SENSOR_DHW_GAS_CONSUMPTION_THIS_YEAR = "hotwater_gas_consumption_heating_this_year" +SENSOR_GAS_CONSUMPTION_TODAY = "gas_consumption_heating_today" +SENSOR_GAS_CONSUMPTION_THIS_WEEK = "gas_consumption_heating_this_week" +SENSOR_GAS_CONSUMPTION_THIS_MONTH = "gas_consumption_heating_this_month" +SENSOR_GAS_CONSUMPTION_THIS_YEAR = "gas_consumption_heating_this_year" + +# heatpump sensors +SENSOR_COMPRESSOR_STARTS = "compressor_starts" +SENSOR_COMPRESSOR_HOURS = "compressor_hours" + +SENSOR_TYPES = { + SENSOR_OUTSIDE_TEMPERATURE: { + CONF_NAME: "Outside Temperature", + CONF_ICON: None, + CONF_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, + CONF_GETTER: lambda api: api.getOutsideTemperature(), + CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, + }, + SENSOR_SUPPLY_TEMPERATURE: { + CONF_NAME: "Supply Temperature", + CONF_ICON: None, + CONF_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, + CONF_GETTER: lambda api: api.getSupplyTemperature(), + CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, + }, + # gas sensors + SENSOR_BOILER_TEMPERATURE: { + CONF_NAME: "Boiler Temperature", + CONF_ICON: None, + CONF_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, + CONF_GETTER: lambda api: api.getBoilerTemperature(), + CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, + }, + SENSOR_BURNER_MODULATION: { + CONF_NAME: "Burner modulation", + CONF_ICON: "mdi:percent", + CONF_UNIT_OF_MEASUREMENT: UNIT_PERCENTAGE, + CONF_GETTER: lambda api: api.getBurnerModulation(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_DHW_GAS_CONSUMPTION_TODAY: { + CONF_NAME: "Hot water gas consumption today", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionDomesticHotWaterToday(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_DHW_GAS_CONSUMPTION_THIS_WEEK: { + CONF_NAME: "Hot water gas consumption this week", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionDomesticHotWaterThisWeek(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_DHW_GAS_CONSUMPTION_THIS_MONTH: { + CONF_NAME: "Hot water gas consumption this month", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionDomesticHotWaterThisMonth(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_DHW_GAS_CONSUMPTION_THIS_YEAR: { + CONF_NAME: "Hot water gas consumption this year", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionDomesticHotWaterThisYear(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_GAS_CONSUMPTION_TODAY: { + CONF_NAME: "Heating gas consumption today", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionHeatingToday(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_GAS_CONSUMPTION_THIS_WEEK: { + CONF_NAME: "Heating gas consumption this week", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionHeatingThisWeek(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_GAS_CONSUMPTION_THIS_MONTH: { + CONF_NAME: "Heating gas consumption this month", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionHeatingThisMonth(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_GAS_CONSUMPTION_THIS_YEAR: { + CONF_NAME: "Heating gas consumption this year", + CONF_ICON: "mdi:power", + CONF_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + CONF_GETTER: lambda api: api.getGasConsumptionHeatingThisYear(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_BURNER_STARTS: { + CONF_NAME: "Burner Starts", + CONF_ICON: "mdi:counter", + CONF_UNIT_OF_MEASUREMENT: None, + CONF_GETTER: lambda api: api.getBurnerStarts(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_BURNER_HOURS: { + CONF_NAME: "Burner Hours", + CONF_ICON: "mdi:counter", + CONF_UNIT_OF_MEASUREMENT: None, + CONF_GETTER: lambda api: api.getBurnerHours(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_BURNER_POWER: { + CONF_NAME: "Burner Current Power", + CONF_ICON: None, + CONF_UNIT_OF_MEASUREMENT: POWER_WATT, + CONF_GETTER: lambda api: api.getCurrentPower(), + CONF_DEVICE_CLASS: DEVICE_CLASS_POWER, + }, + # heatpump sensors + SENSOR_COMPRESSOR_STARTS: { + CONF_NAME: "Compressor Starts", + CONF_ICON: "mdi:counter", + CONF_UNIT_OF_MEASUREMENT: None, + CONF_GETTER: lambda api: api.getCompressorStarts(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_COMPRESSOR_HOURS: { + CONF_NAME: "Compressor Hours", + CONF_ICON: "mdi:counter", + CONF_UNIT_OF_MEASUREMENT: None, + CONF_GETTER: lambda api: api.getCompressorHours(), + CONF_DEVICE_CLASS: None, + }, + SENSOR_RETURN_TEMPERATURE: { + CONF_NAME: "Return Temperature", + CONF_ICON: None, + CONF_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, + CONF_GETTER: lambda api: api.getReturnTemperature(), + CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, + }, +} + +SENSORS_GENERIC = [SENSOR_OUTSIDE_TEMPERATURE, SENSOR_SUPPLY_TEMPERATURE] + +SENSORS_BY_HEATINGTYPE = { + HeatingType.gas: [ + SENSOR_BOILER_TEMPERATURE, + SENSOR_BURNER_HOURS, + SENSOR_BURNER_MODULATION, + SENSOR_BURNER_STARTS, + SENSOR_BURNER_POWER, + SENSOR_DHW_GAS_CONSUMPTION_TODAY, + SENSOR_DHW_GAS_CONSUMPTION_THIS_WEEK, + SENSOR_DHW_GAS_CONSUMPTION_THIS_MONTH, + SENSOR_DHW_GAS_CONSUMPTION_THIS_YEAR, + SENSOR_GAS_CONSUMPTION_TODAY, + SENSOR_GAS_CONSUMPTION_THIS_WEEK, + SENSOR_GAS_CONSUMPTION_THIS_MONTH, + SENSOR_GAS_CONSUMPTION_THIS_YEAR, + ], + HeatingType.heatpump: [ + SENSOR_COMPRESSOR_HOURS, + SENSOR_COMPRESSOR_STARTS, + SENSOR_RETURN_TEMPERATURE, + ], +} + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Create the ViCare sensor devices.""" + if discovery_info is None: + return + + 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( + [ + ViCareSensor(hass.data[VICARE_DOMAIN][VICARE_NAME], vicare_api, sensor) + for sensor in sensors + ] + ) + + +class ViCareSensor(Entity): + """Representation of a ViCare sensor.""" + + def __init__(self, name, api, sensor_type): + """Initialize the sensor.""" + self._sensor = SENSOR_TYPES[sensor_type] + self._name = f"{name} {self._sensor[CONF_NAME]}" + self._api = api + self._sensor_type = sensor_type + self._state = None + + @property + def available(self): + """Return True if entity is available.""" + return self._state is not None and self._state != PYVICARE_ERROR + + @property + def unique_id(self): + """Return a unique ID.""" + return f"{self._api.service.id}-{self._sensor_type}" + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return self._sensor[CONF_ICON] + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._sensor[CONF_UNIT_OF_MEASUREMENT] + + @property + def device_class(self): + """Return the class of this device, from component DEVICE_CLASSES.""" + return self._sensor[CONF_DEVICE_CLASS] + + def update(self): + """Update state of sensor.""" + try: + self._state = self._sensor[CONF_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") diff --git a/homeassistant/components/vicare/water_heater.py b/homeassistant/components/vicare/water_heater.py index c6aa5205f24..cbecf7fdaf2 100644 --- a/homeassistant/components/vicare/water_heater.py +++ b/homeassistant/components/vicare/water_heater.py @@ -9,7 +9,13 @@ from homeassistant.components.water_heater import ( ) from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS -from . import DOMAIN as VICARE_DOMAIN, VICARE_API, VICARE_HEATING_TYPE, VICARE_NAME +from . import ( + DOMAIN as VICARE_DOMAIN, + PYVICARE_ERROR, + VICARE_API, + VICARE_HEATING_TYPE, + VICARE_NAME, +) _LOGGER = logging.getLogger(__name__) @@ -40,8 +46,6 @@ HA_TO_VICARE_HVAC_DHW = { OPERATION_MODE_ON: VICARE_MODE_DHW, } -PYVICARE_ERROR = "error" - def setup_platform(hass, config, add_entities, discovery_info=None): """Create the ViCare water_heater devices.""" diff --git a/requirements_all.txt b/requirements_all.txt index 6b75869b671..e67aaeda020 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -85,7 +85,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.4.0 # homeassistant.components.vicare -PyViCare==0.1.10 +PyViCare==0.2.0 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.12.4