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