diff --git a/octoprint_enclosure/BME680.py b/octoprint_enclosure/BME680.py new file mode 100644 index 0000000..3f21d22 --- /dev/null +++ b/octoprint_enclosure/BME680.py @@ -0,0 +1,86 @@ +import bme680 +import time + + +try: + sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY) +except IOError: + sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY) + +hum_weighting = float(0.25) # so hum effect is 25% of the total air quality score +gas_weighting = float(0.75) # so gas effect is 75% of the total air quality score + +sensor.set_humidity_oversample(bme680.OS_2X) +sensor.set_pressure_oversample(bme680.OS_2X) +sensor.set_temperature_oversample(bme680.OS_2X) +sensor.set_filter(bme680.FILTER_SIZE_3) + +sensor.set_gas_heater_temperature(320) +sensor.set_gas_heater_duration(150) +sensor.select_gas_heater_profile(0) +sensor.set_gas_status(bme680.ENABLE_GAS_MEAS) + +gas_reference = float(250000) +hum_reference = float(40) +getgasreference_count = int(0) + + +def GetGasReference(gas_reference): + # Now run the sensor for a burn-in period, then use combination of relative humidity and gas resistance to estimate indoor air quality as a percentage. + # print("Getting a new gas reference value") + readings = int(10) + while True: + sensor.get_sensor_data() + if sensor.data.heat_stable: + for i in range(1, readings): # // read gas for 10 x 0.150mS = 1.5secs + sensor.get_sensor_data() + gas_reference = gas_reference + sensor.data.gas_resistance + gas_reference = gas_reference / readings + return + +def CalculateIAQ(score): + IAQ_text = "Air quality is " + score = float((100 - score) * 5) + if score >= 301: + IAQ_text = IAQ_text + "Hazardous" + elif score >= 201 and score <= 300: + IAQ_text = IAQ_text + "Very Unhealthy" + elif score >= 176 and score <= 200: + IAQ_text = IAQ_text + "Unhealthy" + elif score >= 151 and score <= 175: + IAQ_text = IAQ_text + "Unhealthy for Sensitive Groups" + elif score >= 51 and score <= 150: + IAQ_text = IAQ_text + "Moderate" + elif score >= 00 and score <= 50: + IAQ_text = IAQ_text + "Good" + return IAQ_text + +#Calculate humidity contribution to IAQ index +current_humidity = float(sensor.data.humidity) +if (current_humidity >= 38 and current_humidity <= 42): + hum_score = float(0.25*100) # Humidity +/-5% around optimum +else: + #sub-optimal + if (current_humidity < 38): + hum_score = float(0.25/hum_reference*current_humidity*100) + else: + hum_score = ((-0.25/(100-hum_reference)*current_humidity)+0.416666)*100 + +#Calculate gas contribution to IAQ index +gas_lower_limit = float(5000) # Bad air quality limit +gas_upper_limit = float(50000) # Good air quality limit + +if gas_reference > gas_upper_limit: + gas_reference = gas_upper_limit +if gas_reference < gas_lower_limit: + gas_reference = gas_lower_limit + +gas_score = float((0.75/(gas_upper_limit-gas_lower_limit)*gas_reference -(gas_lower_limit*(0.75/(gas_upper_limit-gas_lower_limit))))*100) + +#Combine results for the final IAQ index value (0-100% where 100% is good quality air) +air_quality_score = float(hum_score + gas_score) + +GetGasReference(gas_reference) + +print('{0:0.1f}'.format(air_quality_score)) + diff --git a/octoprint_enclosure/__init__.py b/octoprint_enclosure/__init__.py index 63e3b5d..bfedb92 100644 --- a/octoprint_enclosure/__init__.py +++ b/octoprint_enclosure/__init__.py @@ -808,13 +808,13 @@ def check_enclosure_temp(self): try: sensor_data = [] for sensor in list(filter(lambda item: item['input_type'] == 'temperature_sensor', self.rpi_inputs)): - temp, hum = self.get_sensor_data(sensor) + temp, hum, airquality = self.get_sensor_data(sensor) if self._settings.get(["debug_temperature_log"]) is True: - self._logger.debug("Sensor %s Temperature: %s humidity %s", sensor['label'], temp, hum) - if temp is not None and hum is not None: + self._logger.debug("Sensor %s Temperature: %s humidity %s Airquality %s", sensor['label'], temp, hum, airquality) + if temp is not None and hum is not None and airquality is not None: sensor["temp_sensor_temp"] = temp sensor["temp_sensor_humidity"] = hum - sensor_data.append(dict(index_id=sensor['index_id'], temperature=temp, humidity=hum)) + sensor_data.append(dict(index_id=sensor['index_id'], temperature=temp, humidity=hum, airquality=airquality)) self.temperature_sensor_data = sensor_data self.handle_temp_hum_control() self.handle_temperature_events() @@ -978,45 +978,58 @@ def update_ui_inputs(self): def get_sensor_data(self, sensor): try: if self.development_mode: - temp, hum = self.read_dummy_temp() + temp, hum, airquality = self.read_dummy_temp() else: if sensor['temp_sensor_type'] in ["11", "22", "2302"]: temp, hum = self.read_dht_temp(sensor['temp_sensor_type'], sensor['gpio_pin']) elif sensor['temp_sensor_type'] == "18b20": temp = self.read_18b20_temp(sensor['ds18b20_serial']) hum = 0 + airquality = 0 elif sensor['temp_sensor_type'] == "bme280": temp, hum = self.read_bme280_temp(sensor['temp_sensor_address']) + airquality = 0 + elif sensor['temp_sensor_type'] == "bme680": + temp, hum, airquality = self.read_bme680_temp(sensor['temp_sensor_address']) elif sensor['temp_sensor_type'] == "am2320": temp, hum = self.read_am2320_temp() # sensor has fixed address + airquality = 0 elif sensor['temp_sensor_type'] == "rpi": temp = self.read_rpi_temp() # rpi CPU Temp hum = 0 + airquality = 0 elif sensor['temp_sensor_type'] == "si7021": temp, hum = self.read_si7021_temp(sensor['temp_sensor_address'], sensor['temp_sensor_i2cbus']) + airquality = 0 elif sensor['temp_sensor_type'] == "tmp102": temp = self.read_tmp102_temp(sensor['temp_sensor_address']) hum = 0 + airquality = 0 elif sensor['temp_sensor_type'] == "max31855": temp = self.read_max31855_temp(sensor['temp_sensor_address']) hum = 0 + airquality = 0 elif sensor['temp_sensor_type'] == "mcp9808": temp = self.read_mcp_temp(sensor['temp_sensor_address']) hum = 0 + airquality = 0 elif sensor['temp_sensor_type'] == "temp_raw_i2c": temp, hum = self.read_raw_i2c_temp(sensor) + airquality = 0 elif sensor['temp_sensor_type'] == "hum_raw_i2c": hum, temp = self.read_raw_i2c_temp(sensor) + airquality = 0 else: self._logger.info("temp_sensor_type no match") temp = None hum = None - if temp != -1 and hum != -1: + if temp != -1 and hum != -1 and airquality != -1: temp = round(self.to_float(temp), 1) if not sensor['use_fahrenheit'] else round( self.to_float(temp) * 1.8 + 32, 1) hum = round(self.to_float(hum), 1) - return temp, hum - return None, None + airquality = round(self.to_float(airquality), 1) + return temp, hum, airquality + return None, None, None except Exception as ex: self.log_error(ex) @@ -1053,7 +1066,7 @@ def read_dummy_temp(self): self.dummy_value = return_value - return return_value, return_value + return return_value, return_value, return_value def read_raw_i2c_temp(self, sensor): try: @@ -1143,6 +1156,31 @@ def read_bme280_temp(self, address): "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") self.log_error(ex) return (0, 0) + + def read_bme680_temp(self, address): + try: + script = os.path.dirname(os.path.realpath(__file__)) + "/BME680.py " + cmd = [sys.executable, script, str(address)] + if self._settings.get(["use_sudo"]): + cmd.insert(0, "sudo") + if self._settings.get(["debug_temperature_log"]) is True: + self._logger.debug("Temperature BME680 cmd: %s", cmd) + + stdout = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True) + output, errors = stdout.communicate() + + if self._settings.get(["debug_temperature_log"]) is True: + if len(errors) > 0: + self._logger.error("BME680 error: %s", errors) + else: + self._logger.debug("BME680 result: %s", output) + temp, hum, airq = output.split("|") + return (self.to_float(temp.strip()), self.to_float(hum.strip()), self.to_float(airq.strip())) + except Exception as ex: + self._logger.info( + "Failed to execute python scripts, try disabling use SUDO on advanced section of the plugin.") + self.log_error(ex) + return (0, 0) def read_am2320_temp(self): try: diff --git a/octoprint_enclosure/templates/enclosure_settings.jinja2 b/octoprint_enclosure/templates/enclosure_settings.jinja2 index 9744548..f4f5c4b 100644 --- a/octoprint_enclosure/templates/enclosure_settings.jinja2 +++ b/octoprint_enclosure/templates/enclosure_settings.jinja2 @@ -609,6 +609,7 @@ + diff --git a/setup.py b/setup.py index 13655f7..61d2cc5 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ plugin_license = "AGPLv3" # Any additional requirements besides OctoPrint should be listed here -plugin_requires = ["RPi.GPIO>=0.6.5", "requests>=2.7", "smbus2>=0.3.0", "gpiozero==1.6.2", "RPi.bme280"] +plugin_requires = ["RPi.GPIO>=0.6.5", "requests>=2.7", "smbus2>=0.3.0", "gpiozero==1.6.2", "RPi.bme280", "bme680"] additional_setup_parameters = {}