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