diff options
-rw-r--r-- | known-devices.yaml | 14 | ||||
-rw-r--r-- | requirements.txt | 3 | ||||
-rwxr-xr-x | weather-db-from-old-log | 81 | ||||
-rwxr-xr-x | weather-logger-receiver | 112 |
4 files changed, 210 insertions, 0 deletions
diff --git a/known-devices.yaml b/known-devices.yaml new file mode 100644 index 0000000..0986e39 --- /dev/null +++ b/known-devices.yaml @@ -0,0 +1,14 @@ +Berlin: + "f4:01": + handle: "inside" + type: "THGR810, THGN800" + "1f:01": + handle: "outside" + type: "BTHR918" +Heidenheim: + "07:01": + handle: "inside" + type: "THC238/268,THN132,THWR288,THRN122,THN122,AW129/131" + "58:02": + handle: "outside" + type: "THGR810, THGN800" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..752aa56 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pyRFXtrx==0.26.1 +pyserial==3.5 +PyYAML==5.4.1 diff --git a/weather-db-from-old-log b/weather-db-from-old-log new file mode 100755 index 0000000..3efa4b1 --- /dev/null +++ b/weather-db-from-old-log @@ -0,0 +1,81 @@ +#!/usr/bin/env ruby + +require 'sqlite3' + +# Berlin +#$device_id_to_handle = { +# "b7:01" => "outside", +# "ca:01" => "outside", +# "d5:01" => "outside", +# "f4:01" => "outside", +# "1d:01" => "inside", +# "1f:01" => "inside", +# "37:01" => "inside", +# "38:01" => "inside", +# "9c:01" => "inside", +# "cb:01" => "inside" +#} + +# Heidenheim +$device_id_to_handle = { + "58:02" => "outside", + "62:02" => "outside", + "07:01" => "inside" +} + +def db_execute s, d + begin + $db.execute(s, d) + rescue SQLite3::BusyException + sleep 3 + begin + $db.execute(s, d) + rescue SQLite3::BusyException + STDERR.write "DB busy, skipping data point '#{d.to_s}'\n" + end + end +end + +def db_insert timestamp, handle, temperature, humidity + db_execute("INSERT INTO weather(timestamp, handle, temperature, humidity) VALUES(?,?,?,?)", + [timestamp, handle, temperature, humidity]) +end + + +$db = SQLite3::Database.new("weather.db") + +$db.execute <<-SQL + create table weather( + id INTEGER PRIMARY KEY, + timestamp DATETIME, + handle TEXT, + temperature FLOAT, + humidity FLOAT + ); + SQL + +while line = STDIN.gets + parts = line.split "\t" + log_entry_type = parts.first.split("::")[1] + if log_entry_type == "temp" + data = {} + parts.each { |i| + key, value = i.split "::" + data[key] = value + } + data["timestamp"] = data["timestamp"].to_i + data["temperature"] = data["temperature"].to_f + data["humidity"] = data["humidity"].to_f + + handle = $device_id_to_handle[data["device_id"]] + if handle + db_insert \ + data["timestamp"], \ + handle, \ + data["temperature"], \ + data["humidity"] + end + end +end + +$db.close diff --git a/weather-logger-receiver b/weather-logger-receiver new file mode 100755 index 0000000..389a9de --- /dev/null +++ b/weather-logger-receiver @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +import logging +import os +import signal +import sqlite3 +import sys +import time +import yaml + +from RFXtrx import PySerialTransport + + +def setup_database(path="weather.db"): + db_connection = sqlite3.connect(path) + db_cursor = db_connection.cursor() + try: + db_cursor.execute("CREATE TABLE weather( \ + id INTEGER PRIMARY KEY, \ + timestamp DATETIME, \ + handle TEXT, \ + temperature FLOAT, \ + humidity FLOAT);") + except sqlite3.OperationalError: + pass + + return db_connection + + +def setup_serial(device="/dev/ttyUSB0"): + serial_connection = PySerialTransport(device) + serial_connection.reset() + + return serial_connection + + +def load_known_devices(location, path="known-devices.yaml"): + known_devices = None + with open(path, "r") as f: + known_devices = yaml.safe_load(f.read()) + + return known_devices[location] + + +def add_data_to_db(data, handle, db_connection): + db_cursor = db_connection.cursor() + db_cursor.execute(f"INSERT INTO weather \ + (timestamp, handle, temperature, humidity) \ + VALUES ( {int(time.time())}, \ + \"{handle}\", \ + {data.values['Temperature']}, \ + {data.values['Humidity']})") + db_connection.commit() + + +def loop(serial_connection, db_connection, logger): + while True: + logger.info("(Re-)Starting receiver loop") + + data = serial_connection.receive_blocking() + + if data is None: + logger.info("Received no data") + continue + + logger.info(data) + + try: + if data.device.id_string not in known_devices: + logger.info(f"Unknown device with id {device_id} and type {device_type}, ignoring") + continue + except: + logger.info("Device ID not found, likely no SensorEvent, ignoring") + continue + + handle = known_devices[data.device.id_string]["handle"] + + add_data_to_db(data, handle, db_connection) + + +if __name__ == "__main__": + logger = logging.getLogger("weather-logger-receiver") + logging.basicConfig(format='%(asctime)-2s %(levelname)-8s %(message)s', + datefmt='%Y-%m-%dT%H:%M:%S', + level=logging.INFO) + + assert len(sys.argv) == 2 + location = sys.argv[1] + known_devices = load_known_devices(location=location) + logger.info(f"Loaded known devices for location {location}") + + db_connection = setup_database() + logger.info("Set up database") + + serial_connection = setup_serial() + logger.info("Set up serial connection") + + def shutdown(*args): + logger.info("Shutting down") + serial_connection.close() + db_connection.close() + sys.exit(0) + + signal.signal(signal.SIGINT, shutdown) + signal.signal(signal.SIGTERM, shutdown) + + try: + loop(serial_connection, db_connection, logger) + except Exception as e: + logger.error(f"Exception: {e}") + finally: + shutdown(serial_connection, db_connection, logger) |