""" Support for CO2 sensor connected to a serial port. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.mhz19/ """ import logging from datetime import timedelta import voluptuous as vol from homeassistant.const import ( ATTR_TEMPERATURE, CONF_NAME, CONF_MONITORED_CONDITIONS, TEMP_FAHRENHEIT) from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.util.temperature import celsius_to_fahrenheit from homeassistant.util import Throttle REQUIREMENTS = ['pmsensor==0.4'] _LOGGER = logging.getLogger(__name__) CONF_SERIAL_DEVICE = 'serial_device' MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) DEFAULT_NAME = 'CO2 Sensor' ATTR_CO2_CONCENTRATION = 'co2_concentration' SENSOR_TEMPERATURE = 'temperature' SENSOR_CO2 = 'co2' SENSOR_TYPES = { SENSOR_TEMPERATURE: ['Temperature', None], SENSOR_CO2: ['CO2', 'ppm'] } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_SERIAL_DEVICE): cv.string, vol.Optional(CONF_MONITORED_CONDITIONS, default=[SENSOR_CO2]): vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), }) def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the available CO2 sensors.""" from pmsensor import co2sensor try: co2sensor.read_mh_z19(config.get(CONF_SERIAL_DEVICE)) except OSError as err: _LOGGER.error("Could not open serial connection to %s (%s)", config.get(CONF_SERIAL_DEVICE), err) return False SENSOR_TYPES[SENSOR_TEMPERATURE][1] = hass.config.units.temperature_unit data = MHZClient(co2sensor, config.get(CONF_SERIAL_DEVICE)) dev = [] name = config.get(CONF_NAME) for variable in config[CONF_MONITORED_CONDITIONS]: dev.append( MHZ19Sensor(data, variable, SENSOR_TYPES[variable][1], name)) add_devices(dev, True) return True class MHZ19Sensor(Entity): """Representation of an CO2 sensor.""" def __init__(self, mhz_client, sensor_type, temp_unit, name): """Initialize a new PM sensor.""" self._mhz_client = mhz_client self._sensor_type = sensor_type self._temp_unit = temp_unit self._name = name self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] self._ppm = None self._temperature = None @property def name(self): """Return the name of the sensor.""" return '{}: {}'.format(self._name, SENSOR_TYPES[self._sensor_type][0]) @property def state(self): """Return the state of the sensor.""" return self._ppm if self._sensor_type == SENSOR_CO2 \ else self._temperature @property def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" return self._unit_of_measurement def update(self): """Read from sensor and update the state.""" self._mhz_client.update() data = self._mhz_client.data self._temperature = data.get(SENSOR_TEMPERATURE) if self._temperature is not None and \ self._temp_unit == TEMP_FAHRENHEIT: self._temperature = round( celsius_to_fahrenheit(self._temperature), 1) self._ppm = data.get(SENSOR_CO2) @property def device_state_attributes(self): """Return the state attributes.""" result = {} if self._sensor_type == SENSOR_TEMPERATURE and self._ppm is not None: result[ATTR_CO2_CONCENTRATION] = self._ppm if self._sensor_type == SENSOR_CO2 and self._temperature is not None: result[ATTR_TEMPERATURE] = self._temperature return result class MHZClient(object): """Get the latest data from the MH-Z sensor.""" def __init__(self, co2sensor, serial): """Initialize the sensor.""" self.co2sensor = co2sensor self._serial = serial self.data = dict() @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Get the latest data the MH-Z19 sensor.""" self.data = {} try: result = self.co2sensor.read_mh_z19_with_temperature(self._serial) if result is None: return co2, temperature = result except OSError as err: _LOGGER.error("Could not open serial connection to %s (%s)", self._serial, err) return if temperature is not None: self.data[SENSOR_TEMPERATURE] = temperature if co2 is not None and 0 < co2 <= 5000: self.data[SENSOR_CO2] = co2