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_idpull/28851/head
parent
0c48b8ec52
commit
3d5b007c6b
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue