diff --git a/system2mqtt.py b/system2mqtt.py index 77250ad..4774be3 100644 --- a/system2mqtt.py +++ b/system2mqtt.py @@ -32,6 +32,11 @@ CONFIG_DEFAULTS = { } class System2MQTT: + @staticmethod + def _timestamp() -> str: + """Get formatted timestamp for logging.""" + return datetime.now().strftime('%Y-%m-%d %H:%M:%S') + def __init__(self, config_path: str = None): self.config = self._load_config(config_path) self.hostname = socket.gethostname() @@ -88,10 +93,11 @@ class System2MQTT: else: config[k] = v except Exception as e: - print(f"Warning: failed to load config file {config_path}: {e}") - print("Proceeding with defaults and environment overrides.") + print(f"⚠️ [{self._timestamp()}] WARNING: Failed to load config file '{config_path}': {e}") + print(f"ℹ️ [{self._timestamp()}] INFO: Proceeding with defaults and environment overrides") else: - print(f"Config file '{config_path}' not found; using defaults and environment variables if set.") + print(f"ℹ️ [{self._timestamp()}] INFO: Config file '{config_path}' not found") + print(f"ℹ️ [{self._timestamp()}] INFO: Using defaults and environment variables") # Ensure necessary sub-keys exist config.setdefault('mqtt', CONFIG_DEFAULTS['mqtt'].copy()) @@ -100,6 +106,9 @@ class System2MQTT: # Apply environment variable overrides self._merge_env_overrides(config) + + # Log loaded config path + print(f"ℹ️ [{self._timestamp()}] INFO: Loaded configuration from '{config_path}'") return config @@ -116,7 +125,7 @@ class System2MQTT: try: config['mqtt']['port'] = int(os.environ['MQTT_PORT']) except ValueError: - print("Warning: MQTT_PORT is not an integer; ignoring env override") + print(f"⚠️ [{self._timestamp()}] WARNING: MQTT_PORT environment variable is not an integer; ignoring override") if 'MQTT_USERNAME' in os.environ: config['mqtt']['username'] = os.environ['MQTT_USERNAME'] if 'MQTT_PASSWORD' in os.environ: @@ -131,7 +140,7 @@ class System2MQTT: try: config['collectors']['default_interval'] = int(os.environ['COLLECTORS_DEFAULT_INTERVAL']) except ValueError: - print("Warning: COLLECTORS_DEFAULT_INTERVAL is not an integer; ignoring env override") + print(f"⚠️ [{self._timestamp()}] WARNING: COLLECTORS_DEFAULT_INTERVAL is not an integer; ignoring override") # Per-collector overrides for key, val in os.environ.items(): @@ -143,7 +152,7 @@ class System2MQTT: try: config['collectors']['intervals'][name] = int(val) except ValueError: - print(f"Warning: {key} must be an integer; ignoring") + print(f"⚠️ [{self._timestamp()}] WARNING: Collector interval '{key}' must be an integer; ignoring") def _setup_mqtt_client(self) -> mqtt.Client: """Setup paho-mqtt client with configuration (callback API v2 when available).""" @@ -168,15 +177,21 @@ class System2MQTT: except Exception: rc_val = 0 if rc_val == 0: - print("Connected to MQTT broker") + mqtt_host = self.config['mqtt']['host'] + mqtt_port = self.config['mqtt']['port'] + print(f"✓ [{self._timestamp()}] SUCCESS: Connected to MQTT broker at {mqtt_host}:{mqtt_port}") self.connected = True else: - print(f"Failed to connect to MQTT broker with code: {rc_val}") + mqtt_host = self.config['mqtt']['host'] + mqtt_port = self.config['mqtt']['port'] + print(f"✗ [{self._timestamp()}] ERROR: Failed to connect to MQTT broker at {mqtt_host}:{mqtt_port} (code: {rc_val})") self.connected = False def _on_disconnect(self, client, userdata, rc, reason_code=None, properties=None): """Callback when disconnected (paho v2).""" - print("Disconnected from MQTT broker") + mqtt_host = self.config['mqtt']['host'] + mqtt_port = self.config['mqtt']['port'] + print(f"⚠️ [{self._timestamp()}] INFO: Disconnected from MQTT broker at {mqtt_host}:{mqtt_port}") self.connected = False def _get_device_info(self) -> Dict[str, Any]: @@ -226,14 +241,14 @@ class System2MQTT: 'name': module_name, 'interval': interval }) - print(f"Loaded collector: {module_name} (interval: {interval}s)") + print(f"✓ [{self._timestamp()}] SUCCESS: Loaded collector '{module_name}' (update interval: {interval}s)") return collectors async def process_collector_data(self, data: Dict[str, Any]): """Process data from collectors and publish to MQTT.""" if not self.connected: - print("Not connected to MQTT broker") + print(f"⚠️ [{self._timestamp()}] WARNING: Cannot process collector data - not connected to MQTT broker") return # Publish discovery messages for each entity @@ -295,11 +310,12 @@ class System2MQTT: try: data = collector['module'].collect_metrics() + entity_count = len(data.get('entities', [])) await self.process_collector_data(data) self.last_run[collector['name']] = datetime.now() - print(f"Updated {collector['name']} metrics") + print(f"✓ [{self._timestamp()}] SUCCESS: Updated {entity_count} metrics from '{collector['name']}'") except Exception as e: - print(f"Error collecting metrics from {collector['name']}: {e}") + print(f"✗ [{self._timestamp()}] ERROR: Failed to collect metrics from '{collector['name']}': {e}") async def connect(self): """Connect to MQTT broker using paho-mqtt and wait briefly for on_connect.""" @@ -313,7 +329,9 @@ class System2MQTT: break await asyncio.sleep(0.1) except Exception as e: - print(f"Error connecting to MQTT broker: {e}") + mqtt_host = self.config['mqtt']['host'] + mqtt_port = self.config['mqtt']['port'] + print(f"✗ [{self._timestamp()}] FATAL: Cannot connect to MQTT broker at {mqtt_host}:{mqtt_port}: {e}") sys.exit(1) async def disconnect(self): @@ -327,6 +345,7 @@ class System2MQTT: async def async_main(): """Async main function.""" + print(f"🚀 [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] INFO: Starting system2mqtt...") system2mqtt = System2MQTT() await system2mqtt.connect() @@ -340,9 +359,10 @@ async def async_main(): await asyncio.sleep(1) except KeyboardInterrupt: - print("\nShutting down...") + print(f"\n⚠️ [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] INFO: Received shutdown signal, stopping...") finally: await system2mqtt.disconnect() + print(f"✓ [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] INFO: Shutdown complete") if __name__ == "__main__": asyncio.run(async_main()) \ No newline at end of file