import paho.mqtt.client as mqtt import json import threading from flask import Flask, render_template import os # ========================================================== # KONFIGURATION & DATEIEN # ========================================================== CHIP_NAMES_FILE = "data.json" STATE_SAVE_FILE = "current_status.json" # Die Datei, in die der Zustand geschrieben wird MQTT_TOPIC = None MQTT_BROKER = None MQTT_PORT = None # Global State Management: Schlüssel ist die eindeutige ID. RFID_STATUS = {} status_lock = threading.Lock() app = Flask(__name__) def load_config(): """Liest alle Konfigurationsdaten (MQTT-Settings und Chip-Status) aus data.json.""" global RFID_STATUS, MQTT_BROKER, MQTT_TOPIC, MQTT_PORT print("--- Starte Konfigurationsladen ---") try: with open(CHIP_NAMES_FILE, 'r', encoding='utf-8') as f: data = json.load(f) # 1. MQTT Konfiguration auslesen if "mqtt_config" not in data or not isinstance(data["mqtt_config"], dict): raise ValueError("Die JSON-Struktur ist falsch. Fehlendes 'mqtt_config' Key.") mqtt_cfg = data["mqtt_config"] MQTT_BROKER = str(mqtt_cfg.get('broker', 'localhost')) MQTT_PORT = int(mqtt_cfg.get('port', 1883)) MQTT_TOPIC = str(mqtt_cfg.get('topic', 'Gruppe 14')) print(f"✅ MQTT-Einstellungen geladen: Broker={MQTT_BROKER}, Topic={MQTT_TOPIC}") # 2. Chip-Status initialisieren if "chips" not in data or not isinstance(data["chips"], list): raise ValueError("Die JSON-Struktur ist falsch. Fehlendes 'chips' Key.") for chip_data in data["chips"]: chip_id = str(chip_data.get('id', 'UNKNOWN')) chip_name = str(chip_data.get('name', 'N/A')) initial_status = int(str(chip_data.get('status', '0'))) RFID_STATUS[chip_id] = { "Name": chip_name, "ID": chip_id, "Status": initial_status } print(f"✅ Starte mit {len(RFID_STATUS)} Chips, geladen aus {CHIP_NAMES_FILE}.") return True except FileNotFoundError: print("❌ KRITISCHER FEHLER: Die Datei data.json wurde nicht gefunden!") print("Bitte erstellen Sie diese Datei und verwenden Sie die korrekte Struktur.") return False except json.JSONDecodeError: print(f"❌ KRITISCHER FEHLER: Die Datei {CHIP_NAMES_FILE} ist kein gültiges JSON!") return False except ValueError as e: print(f"❌ KONFIGURIERUNGSFEHLER: {e}") return False # ========================================================== # 💾 Funktion zur Zustandsspeicherung (Korrigierter Block) # ========================================================== def save_state(): """Speichert den gesamten aktuellen RFID_STATUS in die Konfigurationsdatei.""" global RFID_STATUS data_to_save = { "chips": [ { "name": data['Name'], "id": data['ID'], "status": str(data['Status']) } for data in RFID_STATUS.values() ] } try: with status_lock: # Innerstes Level der Indentation ist hier entscheidend! with open(STATE_SAVE_FILE, 'w', encoding='utf-8') as f: json.dump(data_to_save, f, indent=4) print(f"\n[💾 SPEICHERT] Status erfolgreich in {STATE_SAVE_FILE} gespeichert.") except Exception as e: # Dieser Block muss korrekt unter dem 'try' stehen und einen Fehler abfangen. print(f"[FEHLER!] Konnte den Zustand nicht speichern: {e}") # ========================================================== # ⚙️ Angepasste Funktion (Status-Update und Logging) # ========================================================== def update_status(chip_id): """Aktualisiert den Status, loggt die Änderung UND speichert den Zustand.""" global RFID_STATUS if chip_id not in RFID_STATUS: print(f"[WARNUNG] Unbekannte oder ungültige ID erkannt: '{chip_id}'. Ignoriere den Statuswechsel.") return with status_lock: old_status = RFID_STATUS[chip_id]['Status'] new_status = 1 - old_status RFID_STATUS[chip_id]['Status'] = new_status # Logging der Änderung und Speichern des Zustands if old_status == 0 and new_status == 1: logging_message = "✅ AKTIVIERUNG DETEKTIERT! (Von Inaktiv zu Aktiv)" print(f"\n[!!! STATUS GEÄNDERT !!!] {logging_message} Chip ID '{chip_id}'. Neuer Status: 1.") elif old_status == 1 and new_status == 0: logging_message = "⚠️ DEAKTIVIERUNG DETEKTIERT! (Von Aktiv zu Inaktiv)" print(f"\n[!!! STATUS GEÄNDERT !!!] {logging_message} Chip ID '{chip_id}'. Neuer Status: 0.") # Speichern des Zustandes nach jeder Änderung save_state() # ========================================================== # MQTT LOGIK (Verwendet die globalen Variablen) # ========================================================== def on_connect(client, userdata, flags, rc): """Callback bei erfolgreicher Verbindung.""" if rc == 0: print("\n✅ MQTT verbunden. Abonniere Topic:", MQTT_TOPIC) client.subscribe(MQTT_TOPIC) else: print(f"\n❌ MQTT Verbindung fehlgeschlagen mit Code {rc}") def on_message(client, userdata, msg): """Callback bei empfangener Nachricht (Plain Text Payload = Chip ID).""" payload = msg.payload.decode().strip() if not payload: print("[WARNUNG] Empfangen leerer Payload.") return chip_id = payload print(f"\n[MQTT] Plain Text ID empfangen: {chip_id}") update_status(chip_id) def run_mqtt_client(): """Startet den MQTT-Client im Hintergrundthread mit den konfigurierten Daten.""" client = mqtt.Client("RFID_Tracker") client.on_connect = on_connect client.on_message = on_message print("\n[MQTT] Starte Verbindung zum Broker...") try: # Nutzung der globalen Variablen (die aus data.json kommen) client.connect(MQTT_BROKER, MQTT_PORT, 60) client.loop_start() except Exception as e: print(f"\n!!! KRITISCHER FEHLER !!! Broker-Verbindung fehlgeschlagen ({e}). Prüfen Sie die Einstellungen in data.json.") # ========================================================== # WEB-SERVER LOGIK (Flask) - Bleibt unverändert # ========================================================== @app.route('/') def index(): """Die Hauptseite, die den aktuellen Status aller RFID Karten anzeigt.""" global RFID_STATUS with status_lock: current_status = dict(RFID_STATUS) return render_template('index.html', rfid_data=current_status, mqtt_topic=MQTT_TOPIC) if __name__ == '__main__': # Initialisierung des Chip-Zustandes UND der MQTT Konfiguration if not load_config(): print("\n[🛑 ABBRUCH] Das Programm kann wegen eines Fehlers bei der Konfiguration nicht gestartet werden.") exit() mqtt_thread = threading.Thread(target=run_mqtt_client) mqtt_thread.start() # Speichere den initialen Zustand, um die JSON-Datenbank zu befüllen save_state() print("\n============================================================") print(" *** RFID STATUS TRACKER AKTIV ***") print("============================================================") print(f"Statusseite läuft unter: http://0.0.0.0:{MQTT_PORT}/") # Start des Webservers auf allen Interfaces (für externen Zugriff) app.run(host='0.0.0.0', port=5000, debug=True, use_reloader=False)