From d4ce4b534c9566d0d1330b13c449317e7b343777 Mon Sep 17 00:00:00 2001 From: sighmon Date: Sat, 4 Apr 2020 21:20:54 +1030 Subject: [PATCH 1/4] #2 Add InfluxDB Cloud support --- enviroplus_exporter.py | 52 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/enviroplus_exporter.py b/enviroplus_exporter.py index 58803f6..0915404 100755 --- a/enviroplus_exporter.py +++ b/enviroplus_exporter.py @@ -1,9 +1,11 @@ #!/usr/bin/env python +import os import random import time import logging import argparse +from influxdb_client import InfluxDBClient from prometheus_client import start_http_server, Gauge, Histogram from bme280 import BME280 @@ -53,6 +55,15 @@ REDUCING_HIST = Histogram('reducing_measurements', 'Histogram of reducing measurements', buckets=(0, 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000, 1300000, 1400000, 1500000)) NH3_HIST = Histogram('nh3_measurements', 'Histogram of nh3 measurements', buckets=(0, 10000, 110000, 210000, 310000, 410000, 510000, 610000, 710000, 810000, 910000, 1010000, 1110000, 1210000, 1310000, 1410000, 1510000, 1610000, 1710000, 1810000, 1910000, 2000000)) +# Setup InfluxDB +# You can generate an InfluxDB Token from the Tokens Tab in the InfluxDB Cloud UI +INFLUXDB_URL = os.getenv('INFLUXDB_URL', '') +INFLUXDB_TOKEN = os.getenv('INFLUXDB_TOKEN', '') +INFLUXDB_BUCKETID = os.getenv('INFLUXDB_BUCKETID', '') +INFLUXDB_TIME_BETWEEN_POSTS = int(os.getenv('INFLUXDB_TIME_BETWEEN_POSTS', '5')) +influxdb_client = InfluxDBClient(url=INFLUXDB_URL, token=INFLUXDB_TOKEN) +influxdb_api = influxdb_client.write_api() + # Get the temperature of the CPU for compensation def get_cpu_temperature(): with open("/sys/class/thermal/thermal_zone0/temp", "r") as f: @@ -121,11 +132,45 @@ def get_particulates(): PM10.set(pms_data.pm_ug_per_m3(10)) +def post_to_influxdb(): + """Post all sensor data to InfluxDB""" + while True: + time.sleep(INFLUXDB_TIME_BETWEEN_POSTS) + name = 'enviroplus' + tag = ['location', 'adelaide'] + fields = {} + sequence = [] + epoch_time_now = round(time.time()) + fields['temperature'] = TEMPERATURE.collect()[0].samples[0].value + fields['humidity'] = HUMIDITY.collect()[0].samples[0].value + fields['pressure'] = PRESSURE.collect()[0].samples[0].value + fields['oxidising'] = OXIDISING.collect()[0].samples[0].value + fields['reducing'] = REDUCING.collect()[0].samples[0].value + fields['nh3'] = NH3.collect()[0].samples[0].value + fields['lux'] = LUX.collect()[0].samples[0].value + fields['proximity'] = PROXIMITY.collect()[0].samples[0].value + fields['pm1'] = PM1.collect()[0].samples[0].value + fields['pm25'] = PM25.collect()[0].samples[0].value + fields['pm10'] = PM10.collect()[0].samples[0].value + for field_name in fields: + sequence.append(f'{name},{tag[0]}={tag[1]} {field_name}={fields[field_name]} {epoch_time_now}') + influxdb_api.write('bucketID', INFLUXDB_BUCKETID, sequence) + + +def str_to_bool(value): + if value.lower() in {'false', 'f', '0', 'no', 'n'}: + return False + elif value.lower() in {'true', 't', '1', 'yes', 'y'}: + return True + raise ValueError(f'{value} is not a valid boolean value') + + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("-b", "--bind", metavar='ADDRESS', default='0.0.0.0', help="Specify alternate bind address [default: 0.0.0.0]") parser.add_argument("-p", "--port", metavar='PORT', default=8000, type=int, help="Specify alternate port [default: 8000]") parser.add_argument("-f", "--factor", metavar='FACTOR', type=float, help="The compensation factor to get better temperature results when the Enviro+ pHAT is too close to the Raspberry Pi board") + parser.add_argument("-i", "--influxdb", metavar='INFLUXDB', type=str_to_bool, default='false', help="Post sensor data to InfluxDB [default: false]") args = parser.parse_args() # Start up the server to expose the metrics. @@ -135,8 +180,13 @@ def get_particulates(): if args.factor: logging.info("Using compensating algorithm (factor={}) to account for heat leakage from Raspberry Pi board".format(args.factor)) - logging.info("Listening on http://{}:{}".format(args.bind, args.port)) + if args.influxdb: + # Post to InfluxDB on another thread + logging.info("Sensor data will be posted to InfluxDB every {} seconds".format(INFLUXDB_TIME_BETWEEN_POSTS)) + influx_thread = Thread(target=post_to_influxdb) + influx_thread.start() + logging.info("Listening on http://{}:{}".format(args.bind, args.port)) while True: get_temperature(args.factor) From 8485ce71e34aea971dc6466e9f641e1f9748cdb5 Mon Sep 17 00:00:00 2001 From: sighmon Date: Sat, 4 Apr 2020 21:50:12 +1030 Subject: [PATCH 2/4] #2 Import threading --- enviroplus_exporter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/enviroplus_exporter.py b/enviroplus_exporter.py index 0915404..e8a9c86 100755 --- a/enviroplus_exporter.py +++ b/enviroplus_exporter.py @@ -4,6 +4,7 @@ import time import logging import argparse +from threading import Thread from influxdb_client import InfluxDBClient from prometheus_client import start_http_server, Gauge, Histogram From 5068bb5aba79ed1d7aec1b915fdd8b41797ded04 Mon Sep 17 00:00:00 2001 From: sighmon Date: Sat, 4 Apr 2020 22:48:01 +1030 Subject: [PATCH 3/4] #2 Fix posting data_points --- enviroplus_exporter.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/enviroplus_exporter.py b/enviroplus_exporter.py index e8a9c86..7d5e15e 100755 --- a/enviroplus_exporter.py +++ b/enviroplus_exporter.py @@ -6,7 +6,8 @@ import argparse from threading import Thread -from influxdb_client import InfluxDBClient +from influxdb_client import InfluxDBClient, Point +from influxdb_client.client.write_api import SYNCHRONOUS from prometheus_client import start_http_server, Gauge, Histogram from bme280 import BME280 @@ -60,10 +61,12 @@ # You can generate an InfluxDB Token from the Tokens Tab in the InfluxDB Cloud UI INFLUXDB_URL = os.getenv('INFLUXDB_URL', '') INFLUXDB_TOKEN = os.getenv('INFLUXDB_TOKEN', '') -INFLUXDB_BUCKETID = os.getenv('INFLUXDB_BUCKETID', '') +INFLUXDB_ORG_ID = os.getenv('INFLUXDB_ORG_ID', '') +INFLUXDB_BUCKET = os.getenv('INFLUXDB_BUCKET', '') +INFLUXDB_SENSOR_LOCATION = os.getenv('INFLUXDB_SENSOR_LOCATION', 'Adelaide') INFLUXDB_TIME_BETWEEN_POSTS = int(os.getenv('INFLUXDB_TIME_BETWEEN_POSTS', '5')) -influxdb_client = InfluxDBClient(url=INFLUXDB_URL, token=INFLUXDB_TOKEN) -influxdb_api = influxdb_client.write_api() +influxdb_client = InfluxDBClient(url=INFLUXDB_URL, token=INFLUXDB_TOKEN, org=INFLUXDB_ORG_ID) +influxdb_api = influxdb_client.write_api(write_options=SYNCHRONOUS) # Get the temperature of the CPU for compensation def get_cpu_temperature(): @@ -140,7 +143,7 @@ def post_to_influxdb(): name = 'enviroplus' tag = ['location', 'adelaide'] fields = {} - sequence = [] + data_points = [] epoch_time_now = round(time.time()) fields['temperature'] = TEMPERATURE.collect()[0].samples[0].value fields['humidity'] = HUMIDITY.collect()[0].samples[0].value @@ -154,8 +157,8 @@ def post_to_influxdb(): fields['pm25'] = PM25.collect()[0].samples[0].value fields['pm10'] = PM10.collect()[0].samples[0].value for field_name in fields: - sequence.append(f'{name},{tag[0]}={tag[1]} {field_name}={fields[field_name]} {epoch_time_now}') - influxdb_api.write('bucketID', INFLUXDB_BUCKETID, sequence) + data_points.append(Point('enviroplus').tag('location', INFLUXDB_SENSOR_LOCATION).field(field_name, fields[field_name])) + influxdb_api.write(bucket=INFLUXDB_BUCKET, record=data_points) def str_to_bool(value): From 2ff06184b9ca6ab8e3c317648d74599a6a9791df Mon Sep 17 00:00:00 2001 From: sighmon Date: Sun, 5 Apr 2020 12:39:03 +0930 Subject: [PATCH 4/4] #2 Remove f-string formatting --- enviroplus_exporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enviroplus_exporter.py b/enviroplus_exporter.py index 7d5e15e..0c15eb8 100755 --- a/enviroplus_exporter.py +++ b/enviroplus_exporter.py @@ -166,7 +166,7 @@ def str_to_bool(value): return False elif value.lower() in {'true', 't', '1', 'yes', 'y'}: return True - raise ValueError(f'{value} is not a valid boolean value') + raise ValueError('{} is not a valid boolean value'.format(value)) if __name__ == '__main__':