#!/usr/bin/env python3 """ Wirenboard Temperature Monitoring Script Monitors temperature sensors and controls GPIO based on threshold """ import json import time import logging from typing import Dict, Any import paho.mqtt.client as mqtt import threading # Configuration CONFIG_FILE = "/etc/wb-temperature-monitor/config.json" # Global dictionary for temperature values temperature_data: Dict[str, float] = {} config: Dict[str, Any] = {} # MQTT client mqtt_client = None # Setup logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) def load_config(): """Load configuration from file""" global config try: with open(CONFIG_FILE, 'r') as f: config = json.load(f) logger.info(f"Configuration loaded: {config}") except FileNotFoundError: logger.error(f"Config file not found: {CONFIG_FILE}") raise except json.JSONDecodeError: logger.error(f"Invalid JSON in config file: {CONFIG_FILE}") raise except Exception as e: logger.error(f"Error loading config: {e}") raise def on_connect(client, userdata, flags, rc): """MQTT connection callback""" if rc == 0: logger.info("Connected to MQTT broker") # Subscribe to all temperature sensor topics for sensor in config.get('temperature_sensors', []): topic = f"devices/{sensor}/controls/t" client.subscribe(topic) logger.info(f"Subscribed to: {topic}") else: logger.error(f"Connection failed with code: {rc}") def on_message(client, userdata, msg): """MQTT message callback - store temperature values""" try: # Extract sensor name from topic # Topic format: devices/%NAME%/controls/t parts = msg.topic.split('/') if len(parts) >= 2: sensor_name = parts[1] # Get the %NAME% part # Convert payload to float temperature = float(msg.payload.decode('utf-8')) # Store in global dictionary temperature_data[sensor_name] = temperature logger.debug(f"Updated {sensor_name}: {temperature}°C") except ValueError: logger.error(f"Invalid temperature value: {msg.payload}") except Exception as e: logger.error(f"Error processing message: {e}") def check_temperatures(): """Check temperatures against target and control GPIO""" target_temp = config.get('target_temperature') control_topic = config.get('control_topic', 'devices/wb-gpio/controls/EXT1_R3A1') if target_temp is None: logger.error("Target temperature not configured") return any_below_target = False # Check all sensors for sensor, temp in temperature_data.items(): if temp < target_temp: logger.info(f"Sensor {sensor} below target: {temp} < {target_temp}") any_below_target = True break # No need to check further if one is below # Control GPIO if any_below_target: # Send "1" to turn on/activate mqtt_client.publish(control_topic, "1", retain=True) logger.info(f"Activated: {control_topic}") else: # Send "0" to turn off/deactivate mqtt_client.publish(control_topic, "0", retain=True) logger.debug(f"Deactivated: {control_topic}") def main_cycle(): """Main 5-second cycle""" while True: try: check_temperatures() except Exception as e: logger.error(f"Error in main cycle: {e}") # Wait 5 seconds time.sleep(5) def setup_mqtt(): """Setup and connect MQTT client""" global mqtt_client mqtt_client = mqtt.Client(client_id="wb_temperature_monitor") mqtt_client.on_connect = on_connect mqtt_client.on_message = on_message # Configure from config mqtt_host = config.get('mqtt_host', 'localhost') mqtt_port = config.get('mqtt_port', 1883) mqtt_username = config.get('mqtt_username', '') mqtt_password = config.get('mqtt_password', '') if mqtt_username: mqtt_client.username_pw_set(mqtt_username, mqtt_password) try: mqtt_client.connect(mqtt_host, mqtt_port, 60) mqtt_client.loop_start() except Exception as e: logger.error(f"Failed to connect to MQTT broker: {e}") raise def main(): """Main function""" logger.info("Starting Wirenboard Temperature Monitor") # Load configuration load_config() # Setup MQTT setup_mqtt() # Start main cycle in a separate thread # This allows MQTT to run in background cycle_thread = threading.Thread(target=main_cycle, daemon=True) cycle_thread.start() # Keep main thread alive try: while True: time.sleep(1) except KeyboardInterrupt: logger.info("Shutting down...") mqtt_client.loop_stop() mqtt_client.disconnect() if __name__ == "__main__": main()