From df67ab995f6dcbed880e3a737e78f162f6b87dcb Mon Sep 17 00:00:00 2001 From: Balazs Keresztury Date: Mon, 23 Mar 2020 15:12:59 +0100 Subject: [PATCH] Add support for Bosch BMP280 Sensor (#30837) * Implement support for Bosch BMP280 Sensor * Fixed linting errors * Fixed case of the requirement RPi.GPIO so it is ignored in requirements * Update homeassistant/components/bmp280/manifest.json Co-Authored-By: springstan <46536646+springstan@users.noreply.github.com> * Update homeassistant/components/bmp280/sensor.py Co-Authored-By: springstan <46536646+springstan@users.noreply.github.com> * Fix linting errors * Implemented changes suggested by code review * Fixed incomplete refactoring Co-authored-by: springstan <46536646+springstan@users.noreply.github.com> --- .coveragerc | 1 + CODEOWNERS | 1 + homeassistant/components/bmp280/__init__.py | 1 + homeassistant/components/bmp280/manifest.json | 9 + homeassistant/components/bmp280/sensor.py | 158 ++++++++++++++++++ requirements_all.txt | 4 + 6 files changed, 174 insertions(+) create mode 100644 homeassistant/components/bmp280/__init__.py create mode 100644 homeassistant/components/bmp280/manifest.json create mode 100644 homeassistant/components/bmp280/sensor.py diff --git a/.coveragerc b/.coveragerc index e8f349013fc..09ff6115ce2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -75,6 +75,7 @@ omit = homeassistant/components/bluetooth_tracker/* homeassistant/components/bme280/sensor.py homeassistant/components/bme680/sensor.py + homeassistant/components/bmp280/sensor.py homeassistant/components/bmw_connected_drive/* homeassistant/components/bom/camera.py homeassistant/components/bom/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 86ad7f5c2db..86a36551f57 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -52,6 +52,7 @@ homeassistant/components/beewi_smartclim/* @alemuro homeassistant/components/bitcoin/* @fabaff homeassistant/components/bizkaibus/* @UgaitzEtxebarria homeassistant/components/blink/* @fronzbot +homeassistant/components/bmp280/* @belidzs homeassistant/components/bmw_connected_drive/* @gerard33 homeassistant/components/bom/* @maddenp homeassistant/components/braviatv/* @robbiet480 diff --git a/homeassistant/components/bmp280/__init__.py b/homeassistant/components/bmp280/__init__.py new file mode 100644 index 00000000000..0c884eafbf1 --- /dev/null +++ b/homeassistant/components/bmp280/__init__.py @@ -0,0 +1 @@ +"""The Bosch BMP280 sensor integration.""" diff --git a/homeassistant/components/bmp280/manifest.json b/homeassistant/components/bmp280/manifest.json new file mode 100644 index 00000000000..d7d3752392b --- /dev/null +++ b/homeassistant/components/bmp280/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "bmp280", + "name": "Bosch BMP280 Environmental Sensor", + "documentation": "https://www.home-assistant.io/integrations/bmp280", + "dependencies": [], + "codeowners": ["@belidzs"], + "requirements": ["adafruit-circuitpython-bmp280==3.1.1", "RPi.GPIO==0.7.0"], + "quality_scale": "silver" +} diff --git a/homeassistant/components/bmp280/sensor.py b/homeassistant/components/bmp280/sensor.py new file mode 100644 index 00000000000..613902d1cd7 --- /dev/null +++ b/homeassistant/components/bmp280/sensor.py @@ -0,0 +1,158 @@ +"""Platform for Bosch BMP280 Environmental Sensor integration.""" +from datetime import timedelta +import logging + +from adafruit_bmp280 import Adafruit_BMP280_I2C +import board +from busio import I2C +import voluptuous as vol + +from homeassistant.components.sensor import ( + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + PLATFORM_SCHEMA, +) +from homeassistant.const import CONF_NAME, PRESSURE_HPA, TEMP_CELSIUS +from homeassistant.exceptions import PlatformNotReady +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = "BMP280" +DEFAULT_SCAN_INTERVAL = timedelta(seconds=15) + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=3) + +MIN_I2C_ADDRESS = 0x76 +MAX_I2C_ADDRESS = 0x77 + +CONF_I2C_ADDRESS = "i2c_address" + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Required(CONF_I2C_ADDRESS): vol.All( + vol.Coerce(int), vol.Range(min=MIN_I2C_ADDRESS, max=MAX_I2C_ADDRESS) + ), + } +) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the sensor platform.""" + try: + # initializing I2C bus using the auto-detected pins + i2c = I2C(board.SCL, board.SDA) + # initializing the sensor + bmp280 = Adafruit_BMP280_I2C(i2c, address=config[CONF_I2C_ADDRESS]) + except ValueError as error: + # this usually happens when the board is I2C capable, but the device can't be found at the configured address + if str(error.args[0]).startswith("No I2C device at address"): + _LOGGER.error( + "%s. Hint: Check wiring and make sure that the SDO pin is tied to either ground (0x76) or VCC (0x77)!", + error.args[0], + ) + raise PlatformNotReady() + raise error + # use custom name if there's any + name = config.get(CONF_NAME) + # BMP280 has both temperature and pressure sensing capability + add_entities( + [Bmp280TemperatureSensor(bmp280, name), Bmp280PressureSensor(bmp280, name)] + ) + + +class Bmp280Sensor(Entity): + """Base class for BMP280 entities.""" + + def __init__( + self, + bmp280: Adafruit_BMP280_I2C, + name: str, + unit_of_measurement: str, + device_class: str, + ): + """Initialize the sensor.""" + self._bmp280 = bmp280 + self._name = name + self._unit_of_measurement = unit_of_measurement + self._device_class = device_class + self._state = None + self._errored = False + + @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 + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit_of_measurement + + @property + def device_class(self): + """Return the device class.""" + return self._device_class + + @property + def available(self) -> bool: + """Return if the device is currently available.""" + return not self._errored + + +class Bmp280TemperatureSensor(Bmp280Sensor): + """Representation of a Bosch BMP280 Temperature Sensor.""" + + def __init__(self, bmp280: Adafruit_BMP280_I2C, name: str): + """Initialize the entity.""" + super().__init__( + bmp280, f"{name} Temperature", TEMP_CELSIUS, DEVICE_CLASS_TEMPERATURE + ) + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Fetch new state data for the sensor.""" + try: + self._state = round(self._bmp280.temperature, 1) + if self._errored: + _LOGGER.warning("Communication restored with temperature sensor") + self._errored = False + except OSError: + # this is thrown when a working sensor is unplugged between two updates + _LOGGER.warning( + "Unable to read temperature data due to a communication problem" + ) + self._errored = True + + +class Bmp280PressureSensor(Bmp280Sensor): + """Representation of a Bosch BMP280 Barometric Pressure Sensor.""" + + def __init__(self, bmp280: Adafruit_BMP280_I2C, name: str): + """Initialize the entity.""" + super().__init__( + bmp280, f"{name} Pressure", PRESSURE_HPA, DEVICE_CLASS_PRESSURE + ) + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Fetch new state data for the sensor.""" + try: + self._state = round(self._bmp280.pressure) + if self._errored: + _LOGGER.warning("Communication restored with pressure sensor") + self._errored = False + except OSError: + # this is thrown when a working sensor is unplugged between two updates + _LOGGER.warning( + "Unable to read pressure data due to a communication problem" + ) + self._errored = True diff --git a/requirements_all.txt b/requirements_all.txt index 44422771b13..8bae90ae60c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -83,6 +83,7 @@ PyViCare==0.1.7 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.12.4 +# homeassistant.components.bmp280 # homeassistant.components.mcp23017 # homeassistant.components.rpi_gpio # RPi.GPIO==0.7.0 @@ -111,6 +112,9 @@ abodepy==0.18.1 # homeassistant.components.mcp23017 adafruit-blinka==3.9.0 +# homeassistant.components.bmp280 +adafruit-circuitpython-bmp280==3.1.1 + # homeassistant.components.mcp23017 adafruit-circuitpython-mcp230xx==2.2.2