import paho.mqtt.client as mqtt import json import threading from flask import Flask, render_template import os import datetime # Import für Datums- und Zeitstempel # ========================================================== # KONFIGURATION & DATEIEN # ========================================================== CHIP_NAMES_FILE = "data.json" STATE_SAVE_FILE = "current_status.json" MQTT_TOPIC = None MQTT_BROKER = None MQTT_PORT = None 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 (Hier wird der Time Active Wert aus der JSON gelesen) 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'))) time_active = chip_data.get('time_active') if time_active: # Wir speichern den Wert aus der JSON, um ihn beim Start zu laden RFID_STATUS[chip_id] = { "Name": chip_name, "ID": chip_id, "Status": initial_status, "Time_Active": time_active } else: # Fallback: Wenn kein Zeitstempel gefunden wird, setze ihn auf jetzt. RFID_STATUS[chip_id] = { "Name": chip_name, "ID": chip_id, "Status": initial_status, "Time_Active": datetime.datetime.now().isoformat() } print(f"✅ Starte mit {len(RFID_STATUS)} Chips.") return True except FileNotFoundError: # ... (Error Handling bleibt gleich) ... 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: # ... (Error Handling bleibt gleich) ... print(f"❌ KRITISCHER FEHLER: Die Datei {CHIP_NAMES_FILE} ist kein gültiges JSON!") return False except ValueError as e: # ... (Error Handling bleibt gleich) ... print(f"❌ KONFIGURIERUNGSFEHLER: {e}") return False # ========================================================== # 💾 Funktion zur Zustandsspeicherung (Speichert Status UND Zeit) # ========================================================== 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']), "time_active": data['Time_Active'] # Speichern des Zeitstempels } for data in RFID_STATUS.values() ] } try: with status_lock: 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: print(f"[FEHLER!] Konnte den Zustand nicht speichern: {e}") # ========================================================== # ⚙️ Angepasste Funktion (Status-Update, Logging & Time Stamping) # ========================================================== 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 # Überprüfung, ob sich der Status tatsächlich geändert hat if new_status != old_status: logging_message = "" 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.") # Wichtig: Status und Zeitstempel aktualisieren RFID_STATUS[chip_id]['Status'] = new_status RFID_STATUS[chip_id]['Time_Active'] = datetime.datetime.now().isoformat() save_state() # ========================================================== # MQTT LOGIK (Bleibt unverändert) # ========================================================== 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.""" # ... (Code bleibt gleich) ... client = mqtt.Client("RFID_Tracker") client.on_connect = on_connect client.on_message = on_message print("\n[MQTT] Starte Verbindung zum Broker...") try: 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}/") app.run(host='0.0.0.0', port=5000, debug=True, use_reloader=False)