diff --git a/backend/app/app.py b/backend/app/app.py index a98d9d3d..3388abff 100644 --- a/backend/app/app.py +++ b/backend/app/app.py @@ -1283,76 +1283,85 @@ def kiosk_restart_system(): @measure_execution_time(logger=printers_logger, task_name="Drucker-Status-Prüfung") def check_printer_status(ip_address: str, timeout: int = 7) -> Tuple[str, bool]: """ - Überprüft den Status eines Druckers über TP-Link Tapo P110-Steckdosenabfrage. - + Überprüft den Status eines Druckers anhand der IP-Adresse. + Gibt den Status und die Erreichbarkeit zurück. + Args: - ip_address: IP-Adresse der Drucker-Steckdose - timeout: Timeout in Sekunden (Standard: 7) - + ip_address: IP-Adresse des Druckers oder der Steckdose + timeout: Timeout in Sekunden + Returns: - Tuple[str, bool]: (Status, Aktiv) - Status ist "online" oder "offline", Aktiv ist True/False + Tuple[str, bool]: (Status, Erreichbarkeit) """ - if not ip_address or ip_address.strip() == "": - printers_logger.debug(f"Keine IP-Adresse angegeben") - return "offline", False + status = "offline" + reachable = False try: - # TCP-Verbindung zu Port 80 oder 443 testen (statt Ping) - try: - import socket - # Zuerst Port 80 versuchen - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(timeout) - result = sock.connect_ex((ip_address.strip(), 80)) - sock.close() - - connection_ok = result == 0 - - # Falls Port 80 nicht erfolgreich, Port 443 testen - if not connection_ok: + # Überprüfen, ob die Steckdose online ist + import socket + + # Erst Port 9999 versuchen (Tapo-Standard) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(timeout) + result = sock.connect_ex((ip_address, 9999)) + sock.close() + + if result == 0: + reachable = True + try: + # TP-Link Tapo Steckdose mit PyP100 überprüfen + from PyP100 import PyP100 + p100 = PyP100.P100(ip_address, TAPO_USERNAME, TAPO_PASSWORD) + p100.handshake() # Authentifizierung + p100.login() # Login + + # Geräteinformationen abrufen + device_info = p100.getDeviceInfo() + + # Status auswerten + if device_info.get('device_on', False): + status = "online" + else: + status = "standby" + + printers_logger.info(f"✅ Tapo-Steckdose {ip_address}: Status = {status}") + except Exception as e: + printers_logger.error(f"❌ Fehler bei Tapo-Status-Check für {ip_address}: {str(e)}") + reachable = False + status = "error" + else: + # Alternativ HTTP/HTTPS versuchen + try: + # HTTP auf Port 80 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) - result = sock.connect_ex((ip_address.strip(), 443)) + result = sock.connect_ex((ip_address, 80)) sock.close() - connection_ok = result == 0 - except: - connection_ok = False - - if not connection_ok: - printers_logger.debug(f"Keine Verbindung zu {ip_address} möglich") - return "offline", False - - # TP-Link Tapo P100-Verbindung aufbauen - try: - # Passwort aus config/settings.py (mit 'A' am Ende) - from config.settings import TAPO_USERNAME, TAPO_PASSWORD - - # Verbindung aufbauen - from PyP100 import PyP100 - p100 = PyP100.P100(ip_address, TAPO_USERNAME, TAPO_PASSWORD) - p100.handshake() # Authentifizierung - p100.login() # Login - - # Geräteinformationen abrufen - device_info = p100.getDeviceInfo() - - # Status auswerten - device_on = device_info.get('device_on', False) - - if device_on: - printers_logger.debug(f"Steckdose {ip_address} ist eingeschaltet") - return "online", True - else: - printers_logger.debug(f"Steckdose {ip_address} ist ausgeschaltet") - return "offline", False - - except Exception as e: - printers_logger.debug(f"Fehler bei Tapo-Verbindung zu {ip_address}: {str(e)}") - return "offline", False - + + if result == 0: + reachable = True + status = "online" # Standarddrucker ohne Tapo-Steckdose + else: + # HTTPS auf Port 443 + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(timeout) + result = sock.connect_ex((ip_address, 443)) + sock.close() + + if result == 0: + reachable = True + status = "online" + except Exception as e: + printers_logger.error(f"❌ Fehler bei Socket-Check für {ip_address}: {str(e)}") + reachable = False + status = "error" + except Exception as e: - printers_logger.error(f"Fehler bei Status-Check für {ip_address}: {str(e)}") - return "offline", False + printers_logger.error(f"❌ Fehler bei Verbindungsprüfung zu {ip_address}: {str(e)}") + status = "error" + reachable = False + + return status, reachable @measure_execution_time(logger=printers_logger, task_name="Mehrere-Drucker-Status-Prüfung") def check_multiple_printers_status(printers: List[Dict], timeout: int = 7) -> Dict[int, Tuple[str, bool]]: @@ -4541,7 +4550,7 @@ def upload_asset(): } # Datei speichern - result = file_manager.save_file(file, 'assets', current_user.id, 'asset', metadata) + result = save_asset_file(file, current_user.id, metadata) if result: relative_path, absolute_path, file_metadata = result @@ -4593,7 +4602,7 @@ def upload_log(): } # Datei speichern - result = file_manager.save_file(file, 'logs', current_user.id, 'log', metadata) + result = save_log_file(file, current_user.id, metadata) if result: relative_path, absolute_path, file_metadata = result @@ -4645,7 +4654,7 @@ def upload_backup(): } # Datei speichern - result = file_manager.save_file(file, 'backups', current_user.id, 'backup', metadata) + result = save_backup_file(file, current_user.id, metadata) if result: relative_path, absolute_path, file_metadata = result @@ -4696,7 +4705,7 @@ def upload_temp_file(): } # Datei speichern - result = file_manager.save_file(file, 'temp', current_user.id, 'temp', metadata) + result = save_temp_file(file, current_user.id, metadata) if result: relative_path, absolute_path, file_metadata = result diff --git a/backend/app/database/myp.db-shm b/backend/app/database/myp.db-shm index 9e8a1136..762cfb11 100644 Binary files a/backend/app/database/myp.db-shm and b/backend/app/database/myp.db-shm differ diff --git a/backend/app/templates/printers.html b/backend/app/templates/printers.html index fa244d75..a15a57a9 100644 --- a/backend/app/templates/printers.html +++ b/backend/app/templates/printers.html @@ -517,7 +517,7 @@
- 0% aller Drucker + 0% % aller Drucker
diff --git a/backend/app/test_tapo_direkt.py b/backend/app/test_tapo_direkt.py index c54cda69..2aadf124 100644 --- a/backend/app/test_tapo_direkt.py +++ b/backend/app/test_tapo_direkt.py @@ -14,7 +14,7 @@ from datetime import datetime # Anmeldedaten für Tapo-Steckdosen TAPO_USERNAME = "till.tomczak@mercedes-benz.com" -TAPO_PASSWORD = "744563017196" +TAPO_PASSWORD = "744563017196A" # Standard-IPs für Tapo-Steckdosen # (falls nicht verfügbar, passen Sie diese an die tatsächlichen IPs in Ihrem Netzwerk an) diff --git a/backend/app/utils/file_manager.py b/backend/app/utils/file_manager.py index 4fc478a1..66e54338 100644 --- a/backend/app/utils/file_manager.py +++ b/backend/app/utils/file_manager.py @@ -389,6 +389,22 @@ def save_avatar_file(file, user_id: int) -> Optional[Tuple[str, str, Dict]]: """Speichert eine Avatar-Datei""" return file_manager.save_file(file, 'avatars', user_id, 'avatar') +def save_asset_file(file, user_id: int, metadata: Dict = None) -> Optional[Tuple[str, str, Dict]]: + """Speichert eine Asset-Datei""" + return file_manager.save_file(file, 'assets', user_id, 'asset', metadata) + +def save_log_file(file, user_id: int, metadata: Dict = None) -> Optional[Tuple[str, str, Dict]]: + """Speichert eine Log-Datei""" + return file_manager.save_file(file, 'logs', user_id, 'log', metadata) + +def save_backup_file(file, user_id: int, metadata: Dict = None) -> Optional[Tuple[str, str, Dict]]: + """Speichert eine Backup-Datei""" + return file_manager.save_file(file, 'backups', user_id, 'backup', metadata) + +def save_temp_file(file, user_id: int, metadata: Dict = None) -> Optional[Tuple[str, str, Dict]]: + """Speichert eine temporäre Datei""" + return file_manager.save_file(file, 'temp', user_id, 'temp', metadata) + def delete_file(relative_path: str) -> bool: """Löscht eine Datei""" return file_manager.delete_file(relative_path) diff --git a/backend/app/utils/job_scheduler.py b/backend/app/utils/job_scheduler.py index 80497b3b..ac5e69e4 100644 --- a/backend/app/utils/job_scheduler.py +++ b/backend/app/utils/job_scheduler.py @@ -33,6 +33,7 @@ class BackgroundTaskScheduler: self._stop_event = threading.Event() self._running = False self._start_time: Optional[datetime] = None + self.logger = get_scheduler_logger() def register_task(self, task_id: str, @@ -55,9 +56,8 @@ class BackgroundTaskScheduler: Returns: bool: True wenn erfolgreich, False wenn die ID bereits existiert """ - logger = get_scheduler_logger() if task_id in self._tasks: - logger.error(f"Task mit ID {task_id} existiert bereits") + self.logger.error(f"Task mit ID {task_id} existiert bereits") return False self._tasks[task_id] = { @@ -70,7 +70,7 @@ class BackgroundTaskScheduler: "next_run": datetime.now() if enabled else None } - logger.info(f"Task {task_id} registriert: Intervall {interval}s, Enabled: {enabled}") + self.logger.info(f"Task {task_id} registriert: Intervall {interval}s, Enabled: {enabled}") return True def update_task(self, @@ -92,9 +92,8 @@ class BackgroundTaskScheduler: Returns: bool: True wenn erfolgreich, False wenn die ID nicht existiert """ - logger = get_scheduler_logger() if task_id not in self._tasks: - logger.error(f"Task mit ID {task_id} existiert nicht") + self.logger.error(f"Task mit ID {task_id} existiert nicht") return False task = self._tasks[task_id] @@ -115,7 +114,7 @@ class BackgroundTaskScheduler: else: task["next_run"] = None - logger.info(f"Task {task_id} aktualisiert: Intervall {task['interval']}s, Enabled: {task['enabled']}") + self.logger.info(f"Task {task_id} aktualisiert: Intervall {task['interval']}s, Enabled: {task['enabled']}") return True def remove_task(self, task_id: str) -> bool: @@ -128,13 +127,12 @@ class BackgroundTaskScheduler: Returns: bool: True wenn erfolgreich, False wenn die ID nicht existiert """ - logger = get_scheduler_logger() if task_id not in self._tasks: - logger.error(f"Task mit ID {task_id} existiert nicht") + self.logger.error(f"Task mit ID {task_id} existiert nicht") return False del self._tasks[task_id] - logger.info(f"Task {task_id} entfernt") + self.logger.info(f"Task {task_id} entfernt") return True def get_task_info(self, task_id: Optional[str] = None) -> Union[Dict, List[Dict]]: @@ -217,9 +215,8 @@ class BackgroundTaskScheduler: Returns: bool: True wenn erfolgreich gestartet, False wenn bereits läuft """ - logger = get_scheduler_logger() if self._running: - logger.warning("Scheduler läuft bereits") + self.logger.warning("Scheduler läuft bereits") return False self._stop_event.clear() @@ -229,7 +226,7 @@ class BackgroundTaskScheduler: self._running = True self._start_time = datetime.now() - logger.info("Scheduler gestartet") + self.logger.info("Scheduler gestartet") return True def stop(self) -> bool: @@ -239,9 +236,8 @@ class BackgroundTaskScheduler: Returns: bool: True wenn erfolgreich gestoppt, False wenn nicht läuft """ - logger = get_scheduler_logger() if not self._running: - logger.warning("Scheduler läuft nicht") + self.logger.warning("Scheduler läuft nicht") return False self._stop_event.set() @@ -250,7 +246,7 @@ class BackgroundTaskScheduler: self._running = False self._start_time = None - logger.info("Scheduler gestoppt") + self.logger.info("Scheduler gestoppt") return True def is_running(self) -> bool: @@ -264,8 +260,7 @@ class BackgroundTaskScheduler: def _run(self) -> None: """Hauptloop des Schedulers.""" - logger = get_scheduler_logger() - logger.info("Scheduler-Thread gestartet") + self.logger.info("Scheduler-Thread gestartet") while not self._stop_event.is_set(): now = datetime.now() @@ -276,221 +271,249 @@ class BackgroundTaskScheduler: if now >= task["next_run"]: try: - logger.debug(f"Führe Task {task_id} aus") + self.logger.debug(f"Führe Task {task_id} aus") task["func"](*task["args"], **task["kwargs"]) task["last_run"] = now task["next_run"] = now + timedelta(seconds=task["interval"]) - logger.debug(f"Task {task_id} erfolgreich ausgeführt, nächste Ausführung: {task['next_run']}") + self.logger.debug(f"Task {task_id} erfolgreich ausgeführt, nächste Ausführung: {task['next_run']}") except Exception as e: - logger.error(f"Fehler bei Ausführung von Task {task_id}: {str(e)}") + self.logger.error(f"Fehler bei Ausführung von Task {task_id}: {str(e)}") # Trotzdem nächste Ausführung planen task["next_run"] = now + timedelta(seconds=task["interval"]) # Schlafenszeit berechnen (1 Sekunde oder weniger) time.sleep(1) - logger.info("Scheduler-Thread beendet") + self.logger.info("Scheduler-Thread beendet") + def toggle_plug(self, ip: str, state: bool, username: str = None, password: str = None) -> bool: + """ + Schaltet eine TP-Link Tapo P100/P110-Steckdose ein oder aus. + + Args: + ip: IP-Adresse der Steckdose + state: True = Ein, False = Aus + username: Benutzername für die Steckdose (optional) + password: Passwort für die Steckdose (optional) + + Returns: + bool: True wenn erfolgreich geschaltet + """ + try: + # PyP100 importieren + try: + from PyP100 import PyP100 + except ImportError: + self.logger.error("❌ PyP100-Modul nicht installiert - Steckdose kann nicht geschaltet werden") + return False + + # Anmeldedaten aus Einstellungen verwenden, falls nicht angegeben + if not username or not password: + from config.settings import TAPO_USERNAME, TAPO_PASSWORD + username = TAPO_USERNAME + password = TAPO_PASSWORD + self.logger.debug(f"🔧 Verwende globale Tapo-Anmeldedaten für {ip}") + + # P100-Verbindung herstellen (P100 statt P110 verwenden) + p100 = PyP100.P100(ip, username, password) + + # Handshake und Login durchführen + p100.handshake() + p100.login() + + # Steckdose schalten + if state: + p100.turnOn() + self.logger.info(f"✅ Tapo-Steckdose {ip} erfolgreich eingeschaltet") + else: + p100.turnOff() + self.logger.info(f"✅ Tapo-Steckdose {ip} erfolgreich ausgeschaltet") + + return True + + except Exception as e: + action = "ein" if state else "aus" + self.logger.error(f"❌ Fehler beim {action}schalten der Tapo-Steckdose {ip}: {str(e)}") + return False -def toggle_plug(printer_id: int, state: bool) -> bool: - """ - Schaltet eine Tapo-Steckdose ein oder aus. - - Args: - printer_id: ID des Druckers - state: True für ein, False für aus + def toggle_printer_plug(self, printer_id: int, state: bool) -> bool: + """ + Schaltet die Steckdose eines Druckers ein oder aus. - Returns: - bool: True wenn erfolgreich, False wenn fehlgeschlagen - """ - logger = get_logger("printers") - db_session = get_db_session() - - try: - printer = db_session.query(Printer).get(printer_id) - - if not printer: - logger.error(f"Drucker mit ID {printer_id} nicht gefunden") - db_session.close() - return False - - # Konfiguration validieren - if not printer.plug_ip or not printer.plug_username or not printer.plug_password: - logger.error(f"Unvollständige Steckdosen-Konfiguration für Drucker {printer.name}") - db_session.close() - return False - - # Importiere PyP100 für Tapo-Unterstützung + Args: + printer_id: ID des Druckers + state: True für ein, False für aus + + Returns: + bool: True wenn erfolgreich, False wenn fehlgeschlagen + """ try: - from PyP100 import PyP100 - except ImportError: - logger.error("PyP100-Modul nicht verfügbar - kann Tapo-Steckdosen nicht steuern") + # Drucker aus Datenbank holen + db_session = get_db_session() + printer = db_session.query(Printer).get(printer_id) + + if not printer: + self.logger.error(f"❌ Drucker mit ID {printer_id} nicht gefunden") + db_session.close() + return False + + # Konfiguration validieren + if not printer.plug_ip: + self.logger.error(f"❌ Unvollständige Steckdosen-Konfiguration für Drucker {printer.name}") + db_session.close() + return False + + # Steckdose schalten + success = self.toggle_plug( + ip=printer.plug_ip, + state=state, + username=printer.plug_username, + password=printer.plug_password + ) + + if success: + # Status in Datenbank aktualisieren + printer.status = "online" if state else "offline" + printer.last_checked = datetime.now() + db_session.commit() + self.logger.info(f"✅ Status für Drucker {printer.name} aktualisiert: {'online' if state else 'offline'}") + db_session.close() + return success + + except Exception as e: + action = "ein" if state else "aus" + self.logger.error(f"❌ Fehler beim {action}schalten der Steckdose für Drucker {printer_id}: {str(e)}") + try: + db_session.close() + except: + pass return False + + def _check_jobs(self) -> None: + """ + Überprüft und verwaltet Druckjobs: + - Startet anstehende Jobs + - Beendet abgelaufene Jobs + """ + db_session = get_db_session() - # Verwende die in der Datenbank gespeicherten Anmeldedaten - # Fallback zu config/settings.py wenn nicht vorhanden - username = printer.plug_username - password = printer.plug_password - - if not username or not password: - from config.settings import TAPO_USERNAME, TAPO_PASSWORD - username = TAPO_USERNAME - password = TAPO_PASSWORD - logger.debug(f"Verwende globale Tapo-Anmeldedaten für {printer.name}") - - # TP-Link Tapo P100 Verbindung herstellen - p100 = PyP100.P100(printer.plug_ip, username, password) - p100.handshake() # Authentifizierung - p100.login() # Login - - # Steckdose schalten - if state: - p100.turnOn() - logger.info(f"Steckdose für {printer.name} eingeschaltet") - else: - p100.turnOff() - logger.info(f"Steckdose für {printer.name} ausgeschaltet") - - # Status in Datenbank aktualisieren - printer.status = "online" if state else "offline" - printer.last_checked = datetime.now() - db_session.commit() - db_session.close() - - return True - - except Exception as e: - logger.error(f"Fehler beim Schalten der Steckdose für Drucker {printer_id}: {str(e)}") try: + now = datetime.now() + + # 1. Anstehende Jobs starten + pending_jobs = db_session.query(Job).filter( + Job.status == "scheduled", + Job.start_at <= now + ).all() + + for job in pending_jobs: + self.logger.info(f"🚀 Starte Job {job.id}: {job.name}") + + # Steckdose einschalten + if self.toggle_printer_plug(job.printer_id, True): + # Job als laufend markieren + job.status = "running" + db_session.commit() + self.logger.info(f"✅ Job {job.id} gestartet") + else: + self.logger.error(f"❌ Konnte Steckdose für Job {job.id} nicht einschalten") + + # 2. Abgelaufene Jobs beenden + running_jobs = db_session.query(Job).filter( + Job.status == "running", + Job.end_at <= now + ).all() + + for job in running_jobs: + self.logger.info(f"🏁 Beende Job {job.id}: {job.name}") + + # Steckdose ausschalten + if self.toggle_printer_plug(job.printer_id, False): + # Job als beendet markieren + job.status = "finished" + job.actual_end_time = now + db_session.commit() + self.logger.info(f"✅ Job {job.id} beendet") + else: + self.logger.error(f"❌ Konnte Steckdose für Job {job.id} nicht ausschalten") + + except Exception as e: + self.logger.error(f"❌ Fehler bei Überprüfung der Jobs: {str(e)}") + try: + db_session.rollback() + except: + pass + + finally: db_session.close() - except: - pass - return False def test_tapo_connection(ip_address: str, username: str = None, password: str = None) -> dict: """ - Testet die Verbindung zu einer Tapo-Steckdose und gibt detaillierte Informationen zurück. + Testet die Verbindung zu einer TP-Link Tapo P110-Steckdose. Args: ip_address: IP-Adresse der Steckdose - username: Benutzername (optional, verwendet globale Konfiguration als Fallback) - password: Passwort (optional, verwendet globale Konfiguration als Fallback) + username: Benutzername für die Steckdose (optional) + password: Passwort für die Steckdose (optional) Returns: - dict: Testergebnis mit Status und Informationen + dict: Ergebnis mit Status und Informationen """ - logger = get_logger("printers") + logger = get_logger("tapo") result = { "success": False, - "error": None, + "message": "", "device_info": None, - "status": "unknown" + "error": None } - # Fallback zu globalen Anmeldedaten - if not username or not password: - username = TAPO_USERNAME - password = TAPO_PASSWORD - logger.debug(f"🔧 Verwende globale Tapo-Anmeldedaten für {ip_address}") - try: - logger.debug(f"Teste Tapo-Verbindung zu {ip_address}") - p110 = PyP110.P110(ip_address, username, password) - p110.handshake() # Authentifizierung - p110.login() # Login + # Importiere PyP100 für Tapo-Unterstützung + try: + from PyP100 import PyP100 + except ImportError: + result["message"] = "PyP100-Modul nicht verfügbar" + result["error"] = "ModuleNotFound" + logger.error("PyP100-Modul nicht verfügbar - kann Tapo-Steckdosen nicht testen") + return result + + # Verwende globale Anmeldedaten falls nicht angegeben + if not username or not password: + from config.settings import TAPO_USERNAME, TAPO_PASSWORD + username = TAPO_USERNAME + password = TAPO_PASSWORD + logger.debug(f"Verwende globale Tapo-Anmeldedaten für {ip_address}") + + # TP-Link Tapo P100 Verbindung herstellen + p100 = PyP100.P100(ip_address, username, password) + p100.handshake() # Authentifizierung + p100.login() # Login # Geräteinformationen abrufen - device_info = p110.getDeviceInfo() - result["device_info"] = device_info - result["status"] = "on" if device_info.get('device_on', False) else "off" - result["success"] = True + device_info = p100.getDeviceInfo() - logger.debug(f"✅ Tapo-Verbindung zu {ip_address} erfolgreich") + result["success"] = True + result["message"] = "Verbindung erfolgreich" + result["device_info"] = device_info + + logger.info(f"Tapo-Verbindung zu {ip_address} erfolgreich: {device_info.get('nickname', 'Unbekannt')}") except Exception as e: + result["success"] = False + result["message"] = f"Verbindungsfehler: {str(e)}" result["error"] = str(e) - logger.warning(f"❌ Tapo-Verbindung zu {ip_address} fehlgeschlagen: {str(e)}") + logger.error(f"Fehler bei Tapo-Test zu {ip_address}: {str(e)}") return result -def check_jobs(): - """ - Überprüft alle geplanten und laufenden Jobs und schaltet Steckdosen entsprechend. - - Diese Funktion wird vom Scheduler regelmäßig aufgerufen und: - 1. Prüft, ob geplante Jobs gestartet werden müssen - 2. Prüft, ob laufende Jobs beendet werden müssen - 3. Aktualisiert den Status der Jobs - """ - logger = get_logger("jobs") - db_session = get_db_session() - - try: - now = datetime.now() - - # Geplante Jobs abrufen (mit 5 Minuten Puffer für vergangene Jobs) - scheduled_jobs = db_session.query(Job).options( - joinedload(Job.printer) - ).filter( - Job.status == "scheduled", - Job.start_at <= now - ).all() - - # Laufende Jobs abrufen (mit 5 Minuten Sicherheitspuffer) - running_jobs = db_session.query(Job).options( - joinedload(Job.printer) - ).filter( - Job.status == "running", - Job.end_at <= now - timedelta(minutes=5) # 5 Minuten Sicherheitspuffer - ).all() - - # Geplante Jobs starten - for job in scheduled_jobs: - logger.info(f"Starte geplanten Job {job.id}: {job.name} für Drucker {job.printer.name}") - - # Steckdose einschalten - if toggle_plug(job.printer_id, True): - # Job als laufend markieren - job.status = "running" - job.end_at = job.start_at + timedelta(minutes=job.duration_minutes) - db_session.commit() - logger.info(f"Job {job.id} gestartet: läuft bis {job.end_at}") - else: - logger.error(f"Fehler beim Starten von Job {job.id}: Steckdose konnte nicht eingeschaltet werden") - - # Beendete Jobs stoppen - for job in running_jobs: - logger.info(f"Beende laufenden Job {job.id}: {job.name} für Drucker {job.printer.name}") - - # Steckdose ausschalten - if toggle_plug(job.printer_id, False): - # Job als beendet markieren - job.status = "finished" - job.actual_end_time = now - db_session.commit() - logger.info(f"Job {job.id} beendet: tatsächliche Endzeit {job.actual_end_time}") - else: - logger.error(f"Fehler beim Beenden von Job {job.id}: Steckdose konnte nicht ausgeschaltet werden") - - db_session.close() - except Exception as e: - logger.error(f"Fehler im Job-Scheduler: {str(e)}") - db_session.close() - - -# Globaler Scheduler +# Scheduler-Instanz erzeugen scheduler = BackgroundTaskScheduler() -# Job-Überprüfungs-Task registrieren (alle 60 Sekunden) -scheduler.register_task( - task_id="check_jobs", - func=check_jobs, - interval=60, - enabled=True -) +# Standardaufgaben registrieren +scheduler.register_task("check_jobs", scheduler._check_jobs, interval=60) # Alias für Kompatibilität JobScheduler = BackgroundTaskScheduler