#!/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(database_path, logger): db_connection = sqlite3.connect(database_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: logger.info("Not re-creating 'weather' table") pass return db_connection def setup_serial(device_path): serial_connection = PySerialTransport(device_path) serial_connection.reset() return serial_connection 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(config, serial_connection, db_connection, logger): known_devices = config["known_devices"][config["active_location"]] while True: logger.info("(Re-)Starting receiver loop") data = serial_connection.receive_blocking() if data is None: logger.info("Didn't receive 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 not a 'SensorEvent', ignoring") continue handle = known_devices[data.device.id_string]["handle"] add_data_to_db(data, handle, db_connection) def write_pid_file(pid_file_path, logger): if os.path.exists(pid_file_path): with open(pid_file_path, "r") as pid_file: pid = pid_file.read() logger.error(f"Possibly already running with PID {pid}, exiting") sys.exit(1) with open(pid_file_path, "w") as pid_file: pid_file.write(str(os.getpid())) def load_config(config_file_path, logger): with open(config_file_path, "r") as f: config = yaml.safe_load(f.read()) logger.info(f"Loaded configuration from '{config_file_path}'") return config if __name__ == "__main__": # logger setup logging.basicConfig(format='%(asctime)-2s %(levelname)-8s %(message)s', datefmt='%Y-%m-%dT%H:%M:%S', level=logging.INFO) logger = logging.getLogger("weather-logger-receiver") # load configuration if len(sys.argv) != 2: logger.error("Missing path to configuration file, exiting") sys.exit(1) config_file_path = sys.argv[1] config = load_config(config_file_path, logger) # get known devices active_location = config["active_location"] known_devices = config["locations"][active_location] logger.info(f"Loaded known devices for location '{active_location}'") # write pid file write_pid_file(config["pid_file_path"], logger) logger.info("Wrote PID file to '{config{'pid_file_path']}'") # database setup db_connection = setup_database(config["database_path"], logger) logger.info("Set up database '{config['database_path']}'") # serial connection setup serial_connection = setup_serial(config["device_path"]) logger.info("Set up serial connection to '{config['device_path']}'") # handle shutdown def shutdown(*args): logger.info("Shutting down") serial_connection.close() db_connection.close() try: os.remove(config["pid_file_path"]) except FileNotFoundError: pass sys.exit(0) signal.signal(signal.SIGINT, shutdown) signal.signal(signal.SIGTERM, shutdown) # start main loop try: loop(known_devices, serial_connection, db_connection, logger) except Exception as e: logger.error(f"Exception: '{e}'") finally: shutdown(config, serial_connection, db_connection, logger)