summaryrefslogtreecommitdiff
path: root/src/app/bme680-max44009-logger/bme680-max44009-client.py
blob: 3d836f1d3bf2414c577fd3a5d5dd26109233853e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env python3

import json
import paho.mqtt.client as mqtt
import re
import requests
import serial
import serial.threaded
import sys
import time

location = "wohnzimmer"


class SerialReader(serial.threaded.Protocol):
    """
    Character- to line-wise data buffer for serial interfaces.

    Reads in new data whenever it becomes available and exposes a line-based
    interface to applications.
    """

    def __init__(self, callback):
        """Create a new SerialReader object."""
        self.callback = callback
        self.recv_buf = ""

    def __call__(self):
        return self

    def data_received(self, data):
        """Append newly received serial data to the line buffer."""
        try:
            str_data = data.decode("UTF-8")
            self.recv_buf += str_data

            # We may get anything between \r\n, \n\r and simple \n newlines.
            # We assume that \n is always present and use str.strip to remove leading/trailing \r symbols
            # Note: Do not call str.strip on lines[-1]! Otherwise, lines may be mangled
            lines = self.recv_buf.split("\n")
            if len(lines) > 1:
                self.recv_buf = lines[-1]
                for line in lines[:-1]:
                    self.callback(str.strip(line))

        except UnicodeDecodeError:
            pass
            # sys.stderr.write('UART output contains garbage: {data}\n'.format(data = data))


class SerialMonitor:
    """SerialMonitor captures serial output for a specific amount of time."""

    def __init__(self, port: str, baud: int, callback):
        """
        Create a new SerialMonitor connected to port at the specified baud rate.

        Communication uses no parity, no flow control, and one stop bit.
        Data collection starts immediately.
        """
        self.ser = serial.serial_for_url(port, do_not_open=True)
        self.ser.baudrate = baud
        self.ser.parity = "N"
        self.ser.rtscts = False
        self.ser.xonxoff = False

        try:
            self.ser.open()
        except serial.SerialException as e:
            sys.stderr.write(
                "Could not open serial port {}: {}\n".format(self.ser.name, e)
            )
            sys.exit(1)

        self.reader = SerialReader(callback=callback)
        self.worker = serial.threaded.ReaderThread(self.ser, self.reader)
        self.worker.start()

    def close(self):
        """Close serial connection."""
        self.worker.stop()
        self.ser.close()


if __name__ == "__main__":

    mqtt = mqtt.Client()
    mqtt.connect("mqtt.derf0.net")

    step = 0
    got_data = False
    max_accel = 0
    max_magnet = 0
    vcc = 0
    temperature = 0
    humidity = 0
    pressure = 0
    gas = 0
    brightness = 0

    def parse_line(line):

        global got_data
        global vcc
        global temperature
        global humidity
        global pressure
        global gas
        global brightness

        match = re.match("BME680 temperature: ([^ ]+)", line)
        if match:
            temperature = float(match.group(1))

        match = re.match("BME680 humidity: ([^ ]+)", line)
        if match:
            humidity = float(match.group(1))

        match = re.match("BME680 pressure: ([^ ]+)", line)
        if match:
            pressure = float(match.group(1))

        match = re.match("BME680 gas resistance: ([^ ]+)", line)
        if match:
            gas = match.group(1)

        match = re.match("VCC: ([^ ]+)", line)
        if match:
            vcc = int(match.group(1))

        match = re.match("MAX44009: ([^ ]+)", line)
        if match:
            got_data = True
            brightness = float(match.group(1))

            requests.post(
                "http://influxdb.derf0.net:8086/write?db=sensors",
                f"bme680,area=hm17,location={location} temperature_celsius={temperature},humidity_relpercent={humidity},pressure_hpa={pressure},air_quality_ohm={gas}",
            )
            requests.post(
                "http://influxdb.derf0.net:8086/write?db=sensors",
                f"max44009,area=hm17,location={location} illuminance_lux={brightness}",
            )

            mqtt.publish(
                f"sensor/hm17/{location}/brightness_lux",
                brightness
            )
            mqtt.publish(
                f"sensor/hm17/{location}/bme680",
                json.dumps(
                    {
                        "temperature_celsius": round(temperature, 1),
                        "humidity_percent": round(humidity, 1),
                        "pressure_hpa": pressure,
                        "iaq_ohm": gas,
                    }
                ),
            )

            temperature = None
            humidity = None
            pressure = None
            gas = None
            vcc = None
            brightness = None

    monitor = SerialMonitor("/dev/ttyUSB0", 57600, parse_line)

    try:
        while True:
            time.sleep(5)

            step += 1

            if step == 4:
                if not got_data:
                    print("Error: received no data for 20 seconds", file=sys.stderr)
                    sys.exit(1)
                got_data = False
                step = 0

    except KeyboardInterrupt:
        monitor.close()
        mqtt.disconnect()