diff --git a/homeassistant/components/comfoconnect/__init__.py b/homeassistant/components/comfoconnect/__init__.py index efdbf020f1a..f1fd67cc4bb 100644 --- a/homeassistant/components/comfoconnect/__init__.py +++ b/homeassistant/components/comfoconnect/__init__.py @@ -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 + ) diff --git a/homeassistant/components/comfoconnect/fan.py b/homeassistant/components/comfoconnect/fan.py index 34e784d61eb..432b25ac602 100644 --- a/homeassistant/components/comfoconnect/fan.py +++ b/homeassistant/components/comfoconnect/fan.py @@ -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.""" diff --git a/homeassistant/components/comfoconnect/sensor.py b/homeassistant/components/comfoconnect/sensor.py index a1f16ed9631..3e3507ea48d 100644 --- a/homeassistant/components/comfoconnect/sensor.py +++ b/homeassistant/components/comfoconnect/sensor.py @@ -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]