From 3180c8b0fbe8bee70a0487adc995c95f218cfde7 Mon Sep 17 00:00:00 2001 From: Viorel Stirbu <viorels@gmail.com> Date: Thu, 19 Apr 2018 12:37:30 +0300 Subject: [PATCH] Add support for Sensirion SHT31 temperature/humidity sensor (#12952) --- .coveragerc | 1 + homeassistant/components/sensor/sht31.py | 152 +++++++++++++++++++++++ requirements_all.txt | 6 + 3 files changed, 159 insertions(+) create mode 100644 homeassistant/components/sensor/sht31.py diff --git a/.coveragerc b/.coveragerc index 1f86a13f6ae..eae6498cd0a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -648,6 +648,7 @@ omit = homeassistant/components/sensor/sensehat.py homeassistant/components/sensor/serial_pm.py homeassistant/components/sensor/serial.py + homeassistant/components/sensor/sht31.py homeassistant/components/sensor/shodan.py homeassistant/components/sensor/sigfox.py homeassistant/components/sensor/simulated.py diff --git a/homeassistant/components/sensor/sht31.py b/homeassistant/components/sensor/sht31.py new file mode 100644 index 00000000000..1ba6c8f90eb --- /dev/null +++ b/homeassistant/components/sensor/sht31.py @@ -0,0 +1,152 @@ +""" +Support for Sensirion SHT31 temperature and humidity sensor. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.sht31/ +""" + +from datetime import timedelta +import logging +import math + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + TEMP_CELSIUS, CONF_NAME, CONF_MONITORED_CONDITIONS) +from homeassistant.exceptions import HomeAssistantError +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.temperature import display_temp +from homeassistant.const import PRECISION_TENTHS +from homeassistant.util import Throttle + + +REQUIREMENTS = ['Adafruit-GPIO==1.0.3', + 'Adafruit-SHT31==1.0.2'] + +_LOGGER = logging.getLogger(__name__) + +CONF_I2C_ADDRESS = 'i2c_address' + +DEFAULT_NAME = 'SHT31' +DEFAULT_I2C_ADDRESS = 0x44 + +SENSOR_TEMPERATURE = 'temperature' +SENSOR_HUMIDITY = 'humidity' +SENSOR_TYPES = (SENSOR_TEMPERATURE, SENSOR_HUMIDITY) + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_I2C_ADDRESS, default=DEFAULT_I2C_ADDRESS): + vol.All(vol.Coerce(int), vol.Range(min=0x44, max=0x45)), + vol.Optional(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the sensor platform.""" + from Adafruit_SHT31 import SHT31 + + i2c_address = config.get(CONF_I2C_ADDRESS) + sensor = SHT31(address=i2c_address) + + try: + if sensor.read_status() is None: + raise ValueError("CRC error while reading SHT31 status") + except (OSError, ValueError): + raise HomeAssistantError("SHT31 sensor not detected at address %s " % + hex(i2c_address)) + sensor_client = SHTClient(sensor) + + sensor_classes = { + SENSOR_TEMPERATURE: SHTSensorTemperature, + SENSOR_HUMIDITY: SHTSensorHumidity + } + + devs = [] + for sensor_type, sensor_class in sensor_classes.items(): + name = "{} {}".format(config.get(CONF_NAME), sensor_type.capitalize()) + devs.append(sensor_class(sensor_client, name)) + + add_devices(devs) + + +class SHTClient(object): + """Get the latest data from the SHT sensor.""" + + def __init__(self, adafruit_sht): + """Initialize the sensor.""" + self.adafruit_sht = adafruit_sht + self.temperature = None + self.humidity = None + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Get the latest data from the SHT sensor.""" + temperature, humidity = self.adafruit_sht.read_temperature_humidity() + if math.isnan(temperature) or math.isnan(humidity): + _LOGGER.warning("Bad sample from sensor SHT31") + return + self.temperature = temperature + self.humidity = humidity + + +class SHTSensor(Entity): + """An abstract SHTSensor, can be either temperature or humidity.""" + + def __init__(self, sensor, name): + """Initialize the sensor.""" + self._sensor = sensor + self._name = name + self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + def update(self): + """Fetch temperature and humidity from the sensor.""" + self._sensor.update() + + +class SHTSensorTemperature(SHTSensor): + """Representation of a temperature sensor.""" + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self.hass.config.units.temperature_unit + + def update(self): + """Fetch temperature from the sensor.""" + super().update() + temp_celsius = self._sensor.temperature + if temp_celsius is not None: + self._state = display_temp(self.hass, temp_celsius, + TEMP_CELSIUS, PRECISION_TENTHS) + + +class SHTSensorHumidity(SHTSensor): + """Representation of a humidity sensor.""" + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return '%' + + def update(self): + """Fetch humidity from the sensor.""" + super().update() + humidity = self._sensor.humidity + if humidity is not None: + self._state = round(humidity) diff --git a/requirements_all.txt b/requirements_all.txt index be44da3c9b9..6b64dba2bc6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -15,6 +15,12 @@ attrs==17.4.0 # homeassistant.components.nuimo_controller --only-binary=all https://github.com/getSenic/nuimo-linux-python/archive/29fc42987f74d8090d0e2382e8f248ff5990b8c9.zip#nuimo==1.0.0 +# homeassistant.components.sensor.sht31 +Adafruit-GPIO==1.0.3 + +# homeassistant.components.sensor.sht31 +Adafruit-SHT31==1.0.2 + # homeassistant.components.bbb_gpio # Adafruit_BBIO==1.0.0 -- GitLab