#!/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() columns = ["timestamp", "handle", "temperature"] values = [int(time.time()), handle, data.values['Temperature']] if "Humidity" in data.values: columns.append("humidity") values.append(data.values['Humidity']) db_cursor.execute(f"INSERT INTO weather \ ({','.join(columns)}) \ VALUES ({','.join(values)})") db_connection.commit() def loop(known_devices, serial_connection, db_connection, logger): 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(f"Wrote PID file to \"{config['pid_file_path']}\"") # database setup db_connection = setup_database(config["database_path"], logger) logger.info(f"Set up database \"{config['database_path']}\"") # serial connection setup serial_connection = setup_serial(config["device_path"]) logger.info(f"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)