diff --git a/backend/app.py b/backend/app.py index b7387e5..efc72ef 100755 --- a/backend/app.py +++ b/backend/app.py @@ -32,6 +32,7 @@ app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # Steckdosen-Konfiguration TAPO_USERNAME = os.environ.get('TAPO_USERNAME') TAPO_PASSWORD = os.environ.get('TAPO_PASSWORD') +# SOCKET_DEVICES Format: {"192.168.1.100": {"number": "1"}, "192.168.1.101": {"number": "2"}, ...} SOCKET_DEVICES = json.loads(os.environ.get('SOCKET_DEVICES', '{}')) # Logging @@ -107,9 +108,46 @@ def init_db(): db.commit() -# Initialisiere die Datenbank beim Starten der Anwendung +# Initialisierung der Steckdosen +def init_sockets(): + """ + Initialisiert die Steckdosen-Einträge in der Datenbank basierend auf SOCKET_DEVICES Umgebungsvariable. + Stellt sicher, dass alle Steckdosen zu Beginn ausgeschaltet sind. + """ + app.logger.info("Initialisiere Steckdosen aus Umgebungsvariablen") + db = get_db() + + # Alle IP-Adressen aus der Datenbank abrufen + existing_ips = {row['ip_address']: row['id'] for row in db.execute('SELECT id, ip_address FROM socket').fetchall() if row['ip_address']} + + for ip_address, device_config in SOCKET_DEVICES.items(): + socket_number = device_config.get('number', '0') + name = f"Printer {socket_number}" + description = f"3D-Drucker mit SmartPlug (IP: {ip_address})" + + if ip_address in existing_ips: + # Steckdose existiert bereits, nichts zu tun + app.logger.info(f"Steckdose mit IP {ip_address} existiert bereits in der Datenbank") + socket_id = existing_ips[ip_address] + else: + # Steckdose erstellen, wenn sie noch nicht existiert + socket = create_socket(name=name, description=description, ip_address=ip_address, status=0) + socket_id = socket['id'] + app.logger.info(f"Neue Steckdose angelegt: {name} mit IP {ip_address}") + + # Steckdose ausschalten, um sicherzustellen, dass alle Steckdosen im AUS-Zustand starten + try: + turn_off_socket(ip_address) + app.logger.info(f"Steckdose {ip_address} wurde beim Start ausgeschaltet") + except Exception as e: + app.logger.error(f"Fehler beim Ausschalten der Steckdose {ip_address}: {e}") + +# Initialisiere die Datenbank und Steckdosen beim Starten der Anwendung with app.app_context(): init_db() + # Nur initialisieren, wenn Steckdosen konfiguriert sind + if SOCKET_DEVICES: + init_sockets() app.teardown_appcontext(close_db) @@ -752,10 +790,22 @@ def abort_job(job_id): # Steckdose ausschalten, falls IP-Adresse hinterlegt ist if socket['ip_address']: - try: - turn_off_socket(socket['ip_address']) - except Exception as e: - app.logger.error(f"Fehler beim Ausschalten der Steckdose: {e}") + # Mehrmals versuchen, die Steckdose auszuschalten, um sicherzustellen, dass sie wirklich aus ist + max_attempts = 3 + for attempt in range(1, max_attempts + 1): + try: + success = turn_off_socket(socket['ip_address']) + if success: + app.logger.info(f"Steckdose {socket['ip_address']} für abgebrochenen Job {job['id']} ausgeschaltet (Versuch {attempt}).") + break + app.logger.warning(f"Konnte Steckdose {socket['ip_address']} nicht ausschalten (Versuch {attempt}/{max_attempts}).") + except Exception as e: + app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e} (Versuch {attempt}/{max_attempts})") + + # Nur wenn es nicht der letzte Versuch war, kurz warten und neu versuchen + if attempt < max_attempts: + import time + time.sleep(1) return jsonify(job_to_dict(updated_job)) @@ -784,10 +834,22 @@ def finish_job(job_id): # Steckdose ausschalten, falls IP-Adresse hinterlegt ist if socket['ip_address']: - try: - turn_off_socket(socket['ip_address']) - except Exception as e: - app.logger.error(f"Fehler beim Ausschalten der Steckdose: {e}") + # Mehrmals versuchen, die Steckdose auszuschalten, um sicherzustellen, dass sie wirklich aus ist + max_attempts = 3 + for attempt in range(1, max_attempts + 1): + try: + success = turn_off_socket(socket['ip_address']) + if success: + app.logger.info(f"Steckdose {socket['ip_address']} für beendeten Job {job['id']} ausgeschaltet (Versuch {attempt}).") + break + app.logger.warning(f"Konnte Steckdose {socket['ip_address']} nicht ausschalten (Versuch {attempt}/{max_attempts}).") + except Exception as e: + app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e} (Versuch {attempt}/{max_attempts})") + + # Nur wenn es nicht der letzte Versuch war, kurz warten und neu versuchen + if attempt < max_attempts: + import time + time.sleep(1) return jsonify(job_to_dict(updated_job)) @@ -849,11 +911,22 @@ def job_remaining_time(job_id): # Steckdose ausschalten, falls IP-Adresse hinterlegt ist if socket['ip_address']: - try: - turn_off_socket(socket['ip_address']) - app.logger.info(f"Steckdose {socket['ip_address']} für abgelaufenen Job {job['id']} automatisch ausgeschaltet.") - except Exception as e: - app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e}") + # Mehrmals versuchen, die Steckdose auszuschalten, um sicherzustellen, dass sie wirklich aus ist + max_attempts = 3 + for attempt in range(1, max_attempts + 1): + try: + success = turn_off_socket(socket['ip_address']) + if success: + app.logger.info(f"Steckdose {socket['ip_address']} für abgelaufenen Job {job['id']} automatisch ausgeschaltet (Versuch {attempt}).") + break + app.logger.warning(f"Konnte Steckdose {socket['ip_address']} nicht ausschalten (Versuch {attempt}/{max_attempts}).") + except Exception as e: + app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e} (Versuch {attempt}/{max_attempts})") + + # Nur wenn es nicht der letzte Versuch war, kurz warten und neu versuchen + if attempt < max_attempts: + import time + time.sleep(1) return jsonify({ 'remaining_minutes': remaining, @@ -977,11 +1050,22 @@ def check_jobs(): # Steckdose ausschalten, falls IP-Adresse hinterlegt ist if socket['ip_address']: - try: - turn_off_socket(socket['ip_address']) - app.logger.info(f"Steckdose {socket['ip_address']} für abgelaufenen Job {job['id']} ausgeschaltet.") - except Exception as e: - app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e}") + # Mehrmals versuchen, die Steckdose auszuschalten, um sicherzustellen, dass sie wirklich aus ist + max_attempts = 3 + for attempt in range(1, max_attempts + 1): + try: + success = turn_off_socket(socket['ip_address']) + if success: + app.logger.info(f"Steckdose {socket['ip_address']} für abgelaufenen Job {job['id']} ausgeschaltet (Versuch {attempt}).") + break + app.logger.warning(f"Konnte Steckdose {socket['ip_address']} nicht ausschalten (Versuch {attempt}/{max_attempts}).") + except Exception as e: + app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e} (Versuch {attempt}/{max_attempts})") + + # Nur wenn es nicht der letzte Versuch war, kurz warten und neu versuchen + if attempt < max_attempts: + import time + time.sleep(1) app.logger.info(f"{len(expired_jobs)} abgelaufene Jobs überprüft und Steckdosen aktualisiert.") @@ -1006,11 +1090,22 @@ def job_status(job_id): # Steckdose ausschalten, falls IP-Adresse hinterlegt ist if socket['ip_address']: - try: - turn_off_socket(socket['ip_address']) - app.logger.info(f"Steckdose {socket['ip_address']} für abgelaufenen Job {job['id']} automatisch ausgeschaltet.") - except Exception as e: - app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e}") + # Mehrmals versuchen, die Steckdose auszuschalten, um sicherzustellen, dass sie wirklich aus ist + max_attempts = 3 + for attempt in range(1, max_attempts + 1): + try: + success = turn_off_socket(socket['ip_address']) + if success: + app.logger.info(f"Steckdose {socket['ip_address']} für abgelaufenen Job {job['id']} automatisch ausgeschaltet (Versuch {attempt}).") + break + app.logger.warning(f"Konnte Steckdose {socket['ip_address']} nicht ausschalten (Versuch {attempt}/{max_attempts}).") + except Exception as e: + app.logger.error(f"Fehler beim Ausschalten der Steckdose {socket['ip_address']}: {e} (Versuch {attempt}/{max_attempts})") + + # Nur wenn es nicht der letzte Versuch war, kurz warten und neu versuchen + if attempt < max_attempts: + import time + time.sleep(1) job_status = 'aborted' if job['aborted'] else ('completed' if remaining == 0 else 'active')