Implement more Comfoconnect sensors (#28817)

* Rework Comfoconnect sensor platform

* Sort ATTRS and fix icon

* Add unique_id to fan

* Use a different signal per sensor type

* Add more logging

* Swap to be sure.

* Remove -fan suffix from unique_id
pull/28851/head
Michaël Arnauts 2019-11-18 00:39:49 +01:00 committed by Martin Hjelmare
parent 0c48b8ec52
commit 3d5b007c6b
3 changed files with 242 additions and 105 deletions

View File

@ -1,12 +1,7 @@
"""Support to control a Zehnder ComfoAir Q350/450/600 ventilation unit."""
import logging
from pycomfoconnect import (
SENSOR_TEMPERATURE_EXTRACT,
SENSOR_TEMPERATURE_OUTDOOR,
Bridge,
ComfoConnect,
)
from pycomfoconnect import Bridge, ComfoConnect
import voluptuous as vol
from homeassistant.const import (
@ -24,14 +19,7 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = "comfoconnect"
SIGNAL_COMFOCONNECT_UPDATE_RECEIVED = "comfoconnect_update_received"
ATTR_CURRENT_TEMPERATURE = "current_temperature"
ATTR_CURRENT_HUMIDITY = "current_humidity"
ATTR_OUTSIDE_TEMPERATURE = "outside_temperature"
ATTR_OUTSIDE_HUMIDITY = "outside_humidity"
ATTR_AIR_FLOW_SUPPLY = "air_flow_supply"
ATTR_AIR_FLOW_EXHAUST = "air_flow_exhaust"
SIGNAL_COMFOCONNECT_UPDATE_RECEIVED = "comfoconnect_update_received_{}"
CONF_USER_AGENT = "user_agent"
@ -105,6 +93,7 @@ class ComfoConnectBridge:
self.data = {}
self.name = name
self.hass = hass
self.unique_id = bridge.uuid.hex()
self.comfoconnect = ComfoConnect(
bridge=bridge,
@ -125,13 +114,8 @@ class ComfoConnectBridge:
self.comfoconnect.disconnect()
def sensor_callback(self, var, value):
"""Call function for sensor updates."""
_LOGGER.debug("Got value from bridge: %d = %d", var, value)
if var in [SENSOR_TEMPERATURE_EXTRACT, SENSOR_TEMPERATURE_OUTDOOR]:
self.data[var] = value / 10
else:
self.data[var] = value
# Notify listeners that we have received an update
dispatcher_send(self.hass, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED, var)
"""Notify listeners that we have received an update."""
_LOGGER.debug("Received update for %s: %s", var, value)
dispatcher_send(
self.hass, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED.format(var), value
)

View File

@ -43,24 +43,34 @@ class ComfoConnectFan(FanEntity):
async def async_added_to_hass(self):
"""Register for sensor updates."""
_LOGGER.debug("Registering for fan speed")
async_dispatcher_connect(
self.hass,
SIGNAL_COMFOCONNECT_UPDATE_RECEIVED.format(SENSOR_FAN_SPEED_MODE),
self._handle_update,
)
await self.hass.async_add_executor_job(
self._ccb.comfoconnect.register_sensor, SENSOR_FAN_SPEED_MODE
)
async_dispatcher_connect(
self.hass, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED, self._handle_update
)
def _handle_update(self, var):
def _handle_update(self, value):
"""Handle update callbacks."""
if var == SENSOR_FAN_SPEED_MODE:
_LOGGER.debug("Received update for %s", var)
self.schedule_update_ha_state()
_LOGGER.debug(
"Handle update for fan speed (%d): %s", SENSOR_FAN_SPEED_MODE, value
)
self._ccb.data[SENSOR_FAN_SPEED_MODE] = value
self.schedule_update_ha_state()
@property
def should_poll(self) -> bool:
"""Do not poll."""
return False
@property
def unique_id(self):
"""Return a unique_id for this entity."""
return self._ccb.unique_id
@property
def name(self):
"""Return the name of the fan."""

View File

@ -2,93 +2,214 @@
import logging
from pycomfoconnect import (
SENSOR_BYPASS_STATE,
SENSOR_DAYS_TO_REPLACE_FILTER,
SENSOR_FAN_EXHAUST_DUTY,
SENSOR_FAN_EXHAUST_FLOW,
SENSOR_FAN_EXHAUST_SPEED,
SENSOR_FAN_SUPPLY_DUTY,
SENSOR_FAN_SUPPLY_FLOW,
SENSOR_FAN_SUPPLY_SPEED,
SENSOR_HUMIDITY_EXHAUST,
SENSOR_HUMIDITY_EXTRACT,
SENSOR_HUMIDITY_OUTDOOR,
SENSOR_HUMIDITY_SUPPLY,
SENSOR_POWER_CURRENT,
SENSOR_TEMPERATURE_EXHAUST,
SENSOR_TEMPERATURE_EXTRACT,
SENSOR_TEMPERATURE_OUTDOOR,
SENSOR_TEMPERATURE_SUPPLY,
)
import voluptuous as vol
from homeassistant.const import CONF_RESOURCES, TEMP_CELSIUS
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
ATTR_DEVICE_CLASS,
CONF_RESOURCES,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE,
POWER_WATT,
TEMP_CELSIUS,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from . import (
ATTR_AIR_FLOW_EXHAUST,
ATTR_AIR_FLOW_SUPPLY,
ATTR_CURRENT_HUMIDITY,
ATTR_CURRENT_TEMPERATURE,
ATTR_OUTSIDE_HUMIDITY,
ATTR_OUTSIDE_TEMPERATURE,
DOMAIN,
SIGNAL_COMFOCONNECT_UPDATE_RECEIVED,
ComfoConnectBridge,
)
from . import DOMAIN, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED, ComfoConnectBridge
ATTR_AIR_FLOW_EXHAUST = "air_flow_exhaust"
ATTR_AIR_FLOW_SUPPLY = "air_flow_supply"
ATTR_BYPASS_STATE = "bypass_state"
ATTR_CURRENT_HUMIDITY = "current_humidity"
ATTR_CURRENT_TEMPERATURE = "current_temperature"
ATTR_DAYS_TO_REPLACE_FILTER = "days_to_replace_filter"
ATTR_EXHAUST_FAN_DUTY = "exhaust_fan_duty"
ATTR_EXHAUST_FAN_SPEED = "exhaust_fan_speed"
ATTR_EXHAUST_HUMIDITY = "exhaust_humidity"
ATTR_EXHAUST_TEMPERATURE = "exhaust_temperature"
ATTR_OUTSIDE_HUMIDITY = "outside_humidity"
ATTR_OUTSIDE_TEMPERATURE = "outside_temperature"
ATTR_POWER_CURRENT = "power_usage"
ATTR_SUPPLY_FAN_DUTY = "supply_fan_duty"
ATTR_SUPPLY_FAN_SPEED = "supply_fan_speed"
ATTR_SUPPLY_HUMIDITY = "supply_humidity"
ATTR_SUPPLY_TEMPERATURE = "supply_temperature"
_LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {}
ATTR_ICON = "icon"
ATTR_ID = "id"
ATTR_LABEL = "label"
ATTR_MULTIPLIER = "multiplier"
ATTR_UNIT = "unit"
SENSOR_TYPES = {
ATTR_CURRENT_TEMPERATURE: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Inside Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ID: SENSOR_TEMPERATURE_EXTRACT,
ATTR_MULTIPLIER: 0.1,
},
ATTR_CURRENT_HUMIDITY: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Inside Humidity",
ATTR_UNIT: "%",
ATTR_ICON: "mdi:water-percent",
ATTR_ID: SENSOR_HUMIDITY_EXTRACT,
},
ATTR_OUTSIDE_TEMPERATURE: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Outside Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ID: SENSOR_TEMPERATURE_OUTDOOR,
ATTR_MULTIPLIER: 0.1,
},
ATTR_OUTSIDE_HUMIDITY: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Outside Humidity",
ATTR_UNIT: "%",
ATTR_ICON: "mdi:water-percent",
ATTR_ID: SENSOR_HUMIDITY_OUTDOOR,
},
ATTR_SUPPLY_TEMPERATURE: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Supply Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ID: SENSOR_TEMPERATURE_SUPPLY,
ATTR_MULTIPLIER: 0.1,
},
ATTR_SUPPLY_HUMIDITY: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Supply Humidity",
ATTR_UNIT: "%",
ATTR_ICON: "mdi:water-percent",
ATTR_ID: SENSOR_HUMIDITY_SUPPLY,
},
ATTR_SUPPLY_FAN_SPEED: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Supply Fan Speed",
ATTR_UNIT: "rpm",
ATTR_ICON: "mdi:fan",
ATTR_ID: SENSOR_FAN_SUPPLY_SPEED,
},
ATTR_SUPPLY_FAN_DUTY: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Supply Fan Duty",
ATTR_UNIT: "%",
ATTR_ICON: "mdi:fan",
ATTR_ID: SENSOR_FAN_SUPPLY_DUTY,
},
ATTR_EXHAUST_FAN_SPEED: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Exhaust Fan Speed",
ATTR_UNIT: "rpm",
ATTR_ICON: "mdi:fan",
ATTR_ID: SENSOR_FAN_EXHAUST_SPEED,
},
ATTR_EXHAUST_FAN_DUTY: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Exhaust Fan Duty",
ATTR_UNIT: "%",
ATTR_ICON: "mdi:fan",
ATTR_ID: SENSOR_FAN_EXHAUST_DUTY,
},
ATTR_EXHAUST_TEMPERATURE: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Exhaust Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ID: SENSOR_TEMPERATURE_EXHAUST,
ATTR_MULTIPLIER: 0.1,
},
ATTR_EXHAUST_HUMIDITY: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Exhaust Humidity",
ATTR_UNIT: "%",
ATTR_ICON: "mdi:water-percent",
ATTR_ID: SENSOR_HUMIDITY_EXHAUST,
},
ATTR_AIR_FLOW_SUPPLY: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Supply airflow",
ATTR_UNIT: "m³/h",
ATTR_ICON: "mdi:fan",
ATTR_ID: SENSOR_FAN_SUPPLY_FLOW,
},
ATTR_AIR_FLOW_EXHAUST: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Exhaust airflow",
ATTR_UNIT: "m³/h",
ATTR_ICON: "mdi:fan",
ATTR_ID: SENSOR_FAN_EXHAUST_FLOW,
},
ATTR_BYPASS_STATE: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Bypass State",
ATTR_UNIT: "%",
ATTR_ICON: "mdi:camera-iris",
ATTR_ID: SENSOR_BYPASS_STATE,
},
ATTR_DAYS_TO_REPLACE_FILTER: {
ATTR_DEVICE_CLASS: None,
ATTR_LABEL: "Days to replace filter",
ATTR_UNIT: "days",
ATTR_ICON: "mdi:calendar",
ATTR_ID: SENSOR_DAYS_TO_REPLACE_FILTER,
},
ATTR_POWER_CURRENT: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
ATTR_LABEL: "Power usage",
ATTR_UNIT: POWER_WATT,
ATTR_ICON: "mdi:flash",
ATTR_ID: SENSOR_POWER_CURRENT,
},
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_RESOURCES, default=[]): vol.All(
cv.ensure_list, [vol.In(SENSOR_TYPES)]
),
}
)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the ComfoConnect fan platform."""
global SENSOR_TYPES
SENSOR_TYPES = {
ATTR_CURRENT_TEMPERATURE: [
"Inside Temperature",
TEMP_CELSIUS,
"mdi:thermometer",
SENSOR_TEMPERATURE_EXTRACT,
],
ATTR_CURRENT_HUMIDITY: [
"Inside Humidity",
"%",
"mdi:water-percent",
SENSOR_HUMIDITY_EXTRACT,
],
ATTR_OUTSIDE_TEMPERATURE: [
"Outside Temperature",
TEMP_CELSIUS,
"mdi:thermometer",
SENSOR_TEMPERATURE_OUTDOOR,
],
ATTR_OUTSIDE_HUMIDITY: [
"Outside Humidity",
"%",
"mdi:water-percent",
SENSOR_HUMIDITY_OUTDOOR,
],
ATTR_AIR_FLOW_SUPPLY: [
"Supply airflow",
"m³/h",
"mdi:air-conditioner",
SENSOR_FAN_SUPPLY_FLOW,
],
ATTR_AIR_FLOW_EXHAUST: [
"Exhaust airflow",
"m³/h",
"mdi:air-conditioner",
SENSOR_FAN_EXHAUST_FLOW,
],
}
ccb = hass.data[DOMAIN]
sensors = []
for resource in config[CONF_RESOURCES]:
sensor_type = resource.lower()
if sensor_type not in SENSOR_TYPES:
_LOGGER.warning("Sensor type: %s is not a valid sensor", sensor_type)
continue
sensors.append(
ComfoConnectSensor(
name=f"{ccb.name} {SENSOR_TYPES[sensor_type][0]}",
name=f"{ccb.name} {SENSOR_TYPES[resource][ATTR_LABEL]}",
ccb=ccb,
sensor_type=sensor_type,
sensor_type=resource,
)
)
@ -102,23 +223,35 @@ class ComfoConnectSensor(Entity):
"""Initialize the ComfoConnect sensor."""
self._ccb = ccb
self._sensor_type = sensor_type
self._sensor_id = SENSOR_TYPES[self._sensor_type][3]
self._sensor_id = SENSOR_TYPES[self._sensor_type][ATTR_ID]
self._name = name
async def async_added_to_hass(self):
"""Register for sensor updates."""
_LOGGER.debug(
"Registering for sensor %s (%d)", self._sensor_type, self._sensor_id,
)
async_dispatcher_connect(
self.hass,
SIGNAL_COMFOCONNECT_UPDATE_RECEIVED.format(self._sensor_id),
self._handle_update,
)
await self.hass.async_add_executor_job(
self._ccb.comfoconnect.register_sensor, self._sensor_id
)
async_dispatcher_connect(
self.hass, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED, self._handle_update
)
def _handle_update(self, var):
def _handle_update(self, value):
"""Handle update callbacks."""
if var == self._sensor_id:
_LOGGER.debug("Received update for %s", var)
self.schedule_update_ha_state()
_LOGGER.debug(
"Handle update for sensor %s (%d): %s",
self._sensor_type,
self._sensor_id,
value,
)
self._ccb.data[self._sensor_id] = round(
value * SENSOR_TYPES[self._sensor_type].get(ATTR_MULTIPLIER, 1), 2
)
self.schedule_update_ha_state()
@property
def state(self):
@ -133,6 +266,11 @@ class ComfoConnectSensor(Entity):
"""Do not poll."""
return False
@property
def unique_id(self):
"""Return a unique_id for this entity."""
return f"{self._ccb.unique_id}-{self._sensor_type}"
@property
def name(self):
"""Return the name of the sensor."""
@ -140,10 +278,15 @@ class ComfoConnectSensor(Entity):
@property
def icon(self):
"""Return the icon to use in the frontend, if any."""
return SENSOR_TYPES[self._sensor_type][2]
"""Return the icon to use in the frontend."""
return SENSOR_TYPES[self._sensor_type][ATTR_ICON]
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return SENSOR_TYPES[self._sensor_type][1]
"""Return the unit of measurement of this entity."""
return SENSOR_TYPES[self._sensor_type][ATTR_UNIT]
@property
def device_class(self):
"""Return the device_class."""
return SENSOR_TYPES[self._sensor_type][ATTR_DEVICE_CLASS]