🎉 Refactor and optimize database files, enhance error handling with new utility scripts 📚, and update documentation on fault tolerance and unattended operation. 🚀
This commit is contained in:
parent
7bea427bd6
commit
6ff407a895
270
backend/app.py
270
backend/app.py
@ -1241,6 +1241,239 @@ def kiosk_restart_system():
|
||||
kiosk_logger.error(f"Fehler beim System-Neustart: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Neustart"}), 500
|
||||
|
||||
|
||||
# ===== ERWEITERTE SYSTEM-CONTROL API-ENDPUNKTE =====
|
||||
|
||||
@app.route('/api/admin/system/restart', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_system_restart():
|
||||
"""Robuster System-Neustart mit Sicherheitsprüfungen."""
|
||||
try:
|
||||
from utils.system_control import schedule_system_restart
|
||||
|
||||
data = request.get_json() or {}
|
||||
delay_seconds = data.get('delay_seconds', 60)
|
||||
reason = data.get('reason', 'Manueller Admin-Neustart')
|
||||
force = data.get('force', False)
|
||||
|
||||
# Begrenze Verzögerung auf sinnvolle Werte
|
||||
delay_seconds = max(10, min(3600, delay_seconds)) # 10s bis 1h
|
||||
|
||||
result = schedule_system_restart(
|
||||
delay_seconds=delay_seconds,
|
||||
user_id=str(current_user.id),
|
||||
reason=reason,
|
||||
force=force
|
||||
)
|
||||
|
||||
if result.get('success'):
|
||||
app_logger.warning(f"System-Neustart geplant von Admin {current_user.username}: {reason}")
|
||||
return jsonify(result)
|
||||
else:
|
||||
return jsonify(result), 400
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei System-Neustart-Planung: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/admin/system/shutdown', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_system_shutdown():
|
||||
"""Robuster System-Shutdown mit Sicherheitsprüfungen."""
|
||||
try:
|
||||
from utils.system_control import schedule_system_shutdown
|
||||
|
||||
data = request.get_json() or {}
|
||||
delay_seconds = data.get('delay_seconds', 30)
|
||||
reason = data.get('reason', 'Manueller Admin-Shutdown')
|
||||
force = data.get('force', False)
|
||||
|
||||
# Begrenze Verzögerung auf sinnvolle Werte
|
||||
delay_seconds = max(10, min(3600, delay_seconds)) # 10s bis 1h
|
||||
|
||||
result = schedule_system_shutdown(
|
||||
delay_seconds=delay_seconds,
|
||||
user_id=str(current_user.id),
|
||||
reason=reason,
|
||||
force=force
|
||||
)
|
||||
|
||||
if result.get('success'):
|
||||
app_logger.warning(f"System-Shutdown geplant von Admin {current_user.username}: {reason}")
|
||||
return jsonify(result)
|
||||
else:
|
||||
return jsonify(result), 400
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei System-Shutdown-Planung: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/admin/kiosk/restart', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_kiosk_restart():
|
||||
"""Kiosk-Display neustarten ohne System-Neustart."""
|
||||
try:
|
||||
from utils.system_control import restart_kiosk
|
||||
|
||||
data = request.get_json() or {}
|
||||
delay_seconds = data.get('delay_seconds', 10)
|
||||
reason = data.get('reason', 'Manueller Kiosk-Neustart')
|
||||
|
||||
# Begrenze Verzögerung
|
||||
delay_seconds = max(0, min(300, delay_seconds)) # 0s bis 5min
|
||||
|
||||
result = restart_kiosk(
|
||||
delay_seconds=delay_seconds,
|
||||
user_id=str(current_user.id),
|
||||
reason=reason
|
||||
)
|
||||
|
||||
if result.get('success'):
|
||||
app_logger.info(f"Kiosk-Neustart geplant von Admin {current_user.username}: {reason}")
|
||||
return jsonify(result)
|
||||
else:
|
||||
return jsonify(result), 400
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei Kiosk-Neustart-Planung: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/admin/system/status', methods=['GET'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_system_status_extended():
|
||||
"""Erweiterte System-Status-Informationen."""
|
||||
try:
|
||||
from utils.system_control import get_system_status
|
||||
from utils.error_recovery import get_error_recovery_manager
|
||||
|
||||
# System-Control-Status
|
||||
system_status = get_system_status()
|
||||
|
||||
# Error-Recovery-Status
|
||||
error_manager = get_error_recovery_manager()
|
||||
error_stats = error_manager.get_error_statistics()
|
||||
|
||||
# Kombiniere alle Informationen
|
||||
combined_status = {
|
||||
**system_status,
|
||||
"error_recovery": error_stats,
|
||||
"resilience_features": {
|
||||
"auto_recovery_enabled": error_stats.get('auto_recovery_enabled', False),
|
||||
"monitoring_active": error_stats.get('monitoring_active', False),
|
||||
"recovery_success_rate": error_stats.get('recovery_success_rate', 0)
|
||||
}
|
||||
}
|
||||
|
||||
return jsonify(combined_status)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei System-Status-Abfrage: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/admin/system/operations', methods=['GET'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_system_operations():
|
||||
"""Gibt geplante und vergangene System-Operationen zurück."""
|
||||
try:
|
||||
from utils.system_control import get_system_control_manager
|
||||
|
||||
manager = get_system_control_manager()
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"pending_operations": manager.get_pending_operations(),
|
||||
"operation_history": manager.get_operation_history(limit=50)
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei Operations-Abfrage: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/admin/system/operations/<operation_id>/cancel', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_cancel_operation(operation_id):
|
||||
"""Bricht geplante System-Operation ab."""
|
||||
try:
|
||||
from utils.system_control import get_system_control_manager
|
||||
|
||||
manager = get_system_control_manager()
|
||||
result = manager.cancel_operation(operation_id)
|
||||
|
||||
if result.get('success'):
|
||||
app_logger.info(f"Operation {operation_id} abgebrochen von Admin {current_user.username}")
|
||||
return jsonify(result)
|
||||
else:
|
||||
return jsonify(result), 400
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Abbrechen von Operation {operation_id}: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/admin/error-recovery/status', methods=['GET'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_error_recovery_status():
|
||||
"""Gibt Error-Recovery-Status und -Statistiken zurück."""
|
||||
try:
|
||||
from utils.error_recovery import get_error_recovery_manager
|
||||
|
||||
manager = get_error_recovery_manager()
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"statistics": manager.get_error_statistics(),
|
||||
"recent_errors": manager.get_recent_errors(limit=20)
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei Error-Recovery-Status-Abfrage: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/admin/error-recovery/toggle', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_toggle_error_recovery():
|
||||
"""Aktiviert/Deaktiviert Error-Recovery-Monitoring."""
|
||||
try:
|
||||
from utils.error_recovery import get_error_recovery_manager
|
||||
|
||||
data = request.get_json() or {}
|
||||
enable = data.get('enable', True)
|
||||
|
||||
manager = get_error_recovery_manager()
|
||||
|
||||
if enable:
|
||||
manager.start_monitoring()
|
||||
message = "Error-Recovery-Monitoring aktiviert"
|
||||
else:
|
||||
manager.stop_monitoring()
|
||||
message = "Error-Recovery-Monitoring deaktiviert"
|
||||
|
||||
app_logger.info(f"{message} von Admin {current_user.username}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": message,
|
||||
"monitoring_active": manager.is_active
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Toggle von Error-Recovery: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
# ===== BENUTZER-ROUTEN (ehemals user.py) =====
|
||||
|
||||
@app.route("/user/profile", methods=["GET"])
|
||||
@ -8719,6 +8952,43 @@ if __name__ == "__main__":
|
||||
# Fallback auf die alte Methode
|
||||
shutdown_manager = None
|
||||
|
||||
# ===== INITIALISIERE FEHLERRESILIENZ-SYSTEME =====
|
||||
try:
|
||||
from utils.error_recovery import start_error_monitoring, stop_error_monitoring
|
||||
from utils.system_control import get_system_control_manager
|
||||
|
||||
# Error-Recovery-Monitoring starten
|
||||
start_error_monitoring()
|
||||
app_logger.info("✅ Error-Recovery-Monitoring gestartet")
|
||||
|
||||
# System-Control-Manager initialisieren
|
||||
system_control_manager = get_system_control_manager()
|
||||
app_logger.info("✅ System-Control-Manager initialisiert")
|
||||
|
||||
# Integriere in Shutdown-Manager
|
||||
if shutdown_manager:
|
||||
shutdown_manager.register_cleanup_function(
|
||||
func=stop_error_monitoring,
|
||||
name="Error Recovery Monitoring",
|
||||
priority=2,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ Fehlerresilienz-Systeme konnten nicht initialisiert werden: {e}")
|
||||
|
||||
# ===== KIOSK-SERVICE-OPTIMIERUNG =====
|
||||
try:
|
||||
# Stelle sicher, dass der Kiosk-Service korrekt konfiguriert ist
|
||||
kiosk_service_exists = os.path.exists('/etc/systemd/system/myp-kiosk.service')
|
||||
if not kiosk_service_exists:
|
||||
app_logger.warning("⚠️ Kiosk-Service nicht gefunden - Kiosk-Funktionen eventuell eingeschränkt")
|
||||
else:
|
||||
app_logger.info("✅ Kiosk-Service-Konfiguration gefunden")
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ Kiosk-Service-Check fehlgeschlagen: {e}")
|
||||
|
||||
# Windows-spezifisches Signal-Handling als Fallback
|
||||
def fallback_signal_handler(sig, frame):
|
||||
"""Fallback Signal-Handler für ordnungsgemäßes Shutdown."""
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
backend/docs/FEHLERRESILIENZ_WARTUNGSFREIER_BETRIEB.md
Normal file
1
backend/docs/FEHLERRESILIENZ_WARTUNGSFREIER_BETRIEB.md
Normal file
@ -0,0 +1 @@
|
||||
|
@ -112,3 +112,5 @@
|
||||
2025-06-02 14:30:43 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-02 14:34:09 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-02 14:43:36 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-02 14:50:27 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-02 14:51:08 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
|
||||
|
@ -2925,3 +2925,56 @@ WHERE jobs.status = ?) AS anon_1]
|
||||
2025-06-02 14:43:49 - [app] app - [ERROR] ERROR - Datenbank-Transaktion fehlgeschlagen: name 'func' is not defined
|
||||
2025-06-02 14:43:49 - [app] app - [ERROR] ERROR - Fehler beim Erstellen der Steckdosen-Statistiken: name 'func' is not defined
|
||||
2025-06-02 14:43:51 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_plug_schedules_calendar: User authenticated: True, User ID: 1, Is Admin: True
|
||||
2025-06-02 14:50:27 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - SQLite für Raspberry Pi optimiert (reduzierte Cache-Größe, SD-Karten I/O)
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - ✅ Timeout Force-Quit Manager geladen
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - ✅ Zentraler Shutdown-Manager initialisiert
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - 🔄 Starte Datenbank-Setup und Migrationen...
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - ✅ JobOrder-Tabelle bereits vorhanden
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - ✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen
|
||||
2025-06-02 14:50:28 - [app] app - [INFO] INFO - 🖨️ Starte automatische Steckdosen-Initialisierung...
|
||||
2025-06-02 14:50:30 - [app] app - [INFO] INFO - Steckdosen-Status geloggt: Drucker 4, Status: disconnected, Quelle: system
|
||||
2025-06-02 14:50:32 - [app] app - [INFO] INFO - Steckdosen-Status geloggt: Drucker 5, Status: disconnected, Quelle: system
|
||||
2025-06-02 14:50:32 - [app] app - [INFO] INFO - ✅ Steckdosen-Initialisierung: 0/2 Drucker erfolgreich
|
||||
2025-06-02 14:50:32 - [app] app - [WARNING] WARNING - ⚠️ 2 Drucker konnten nicht initialisiert werden
|
||||
2025-06-02 14:50:32 - [app] app - [INFO] INFO - 🔄 Debug-Modus: Queue Manager deaktiviert für Entwicklung
|
||||
2025-06-02 14:50:32 - [app] app - [INFO] INFO - Job-Scheduler gestartet
|
||||
2025-06-02 14:50:32 - [app] app - [INFO] INFO - Starte Debug-Server auf 0.0.0.0:5000 (HTTP)
|
||||
2025-06-02 14:50:32 - [app] app - [INFO] INFO - Windows-Debug-Modus: Auto-Reload deaktiviert
|
||||
2025-06-02 14:50:33 - [app] app - [INFO] INFO - Benutzer admin@mercedes-benz.com hat sich abgemeldet
|
||||
2025-06-02 14:50:44 - [app] app - [INFO] INFO - Admin-Check für Funktion admin_plug_schedules: User authenticated: True, User ID: 1, Is Admin: True
|
||||
2025-06-02 14:50:44 - [app] app - [INFO] INFO - Admin Administrator (ID: 1) öffnet Steckdosenschaltzeiten
|
||||
2025-06-02 14:50:44 - [app] app - [ERROR] ERROR - Datenbank-Transaktion fehlgeschlagen: name 'func' is not defined
|
||||
2025-06-02 14:50:44 - [app] app - [ERROR] ERROR - Fehler beim Erstellen der Steckdosen-Statistiken: name 'func' is not defined
|
||||
2025-06-02 14:50:46 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_plug_schedules_calendar: User authenticated: True, User ID: 1, Is Admin: True
|
||||
2025-06-02 14:50:46 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_plug_schedules_statistics: User authenticated: True, User ID: 1, Is Admin: True
|
||||
2025-06-02 14:50:46 - [app] app - [ERROR] ERROR - Datenbank-Transaktion fehlgeschlagen: name 'func' is not defined
|
||||
2025-06-02 14:50:46 - [app] app - [ERROR] ERROR - Fehler beim Erstellen der Steckdosen-Statistiken: name 'func' is not defined
|
||||
2025-06-02 14:51:08 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - SQLite für Raspberry Pi optimiert (reduzierte Cache-Größe, SD-Karten I/O)
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - ✅ Timeout Force-Quit Manager geladen
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - ✅ Zentraler Shutdown-Manager initialisiert
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - 🔄 Starte Datenbank-Setup und Migrationen...
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - ✅ JobOrder-Tabelle bereits vorhanden
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - ✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen
|
||||
2025-06-02 14:51:09 - [app] app - [INFO] INFO - 🖨️ Starte automatische Steckdosen-Initialisierung...
|
||||
2025-06-02 14:51:12 - [app] app - [INFO] INFO - Steckdosen-Status geloggt: Drucker 4, Status: disconnected, Quelle: system
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - Steckdosen-Status geloggt: Drucker 5, Status: disconnected, Quelle: system
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - ✅ Steckdosen-Initialisierung: 0/2 Drucker erfolgreich
|
||||
2025-06-02 14:51:14 - [app] app - [WARNING] WARNING - ⚠️ 2 Drucker konnten nicht initialisiert werden
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - 🔄 Debug-Modus: Queue Manager deaktiviert für Entwicklung
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - Job-Scheduler gestartet
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - Starte Debug-Server auf 0.0.0.0:5000 (HTTP)
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - Windows-Debug-Modus: Auto-Reload deaktiviert
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - Admin-Check für Funktion admin_plug_schedules: User authenticated: True, User ID: 1, Is Admin: True
|
||||
2025-06-02 14:51:14 - [app] app - [INFO] INFO - Admin Administrator (ID: 1) öffnet Steckdosenschaltzeiten
|
||||
2025-06-02 14:51:14 - [app] app - [ERROR] ERROR - Datenbank-Transaktion fehlgeschlagen: name 'func' is not defined
|
||||
2025-06-02 14:51:14 - [app] app - [ERROR] ERROR - Fehler beim Erstellen der Steckdosen-Statistiken: name 'func' is not defined
|
||||
2025-06-02 14:51:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_plug_schedules_calendar: User authenticated: True, User ID: 1, Is Admin: True
|
||||
2025-06-02 14:51:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_plug_schedules_statistics: User authenticated: True, User ID: 1, Is Admin: True
|
||||
2025-06-02 14:51:15 - [app] app - [ERROR] ERROR - Datenbank-Transaktion fehlgeschlagen: name 'func' is not defined
|
||||
2025-06-02 14:51:15 - [app] app - [ERROR] ERROR - Fehler beim Erstellen der Steckdosen-Statistiken: name 'func' is not defined
|
||||
|
@ -71,3 +71,5 @@
|
||||
2025-06-02 14:27:04 - [auth] auth - [WARNING] WARNING - JSON-Parsing fehlgeschlagen: 400 Bad Request: Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)
|
||||
2025-06-02 14:27:05 - [auth] auth - [INFO] INFO - Benutzer admin@mercedes-benz.com hat sich erfolgreich angemeldet
|
||||
2025-06-02 14:27:06 - [auth] auth - [INFO] INFO - 🔐 Neue Session erstellt für Benutzer admin@mercedes-benz.com von IP 127.0.0.1
|
||||
2025-06-02 14:50:38 - [auth] auth - [WARNING] WARNING - JSON-Parsing fehlgeschlagen: 400 Bad Request: Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)
|
||||
2025-06-02 14:50:38 - [auth] auth - [INFO] INFO - Benutzer admin@mercedes-benz.com hat sich erfolgreich angemeldet
|
||||
|
@ -116,3 +116,5 @@
|
||||
2025-06-02 14:30:43 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
|
||||
2025-06-02 14:34:08 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
|
||||
2025-06-02 14:43:36 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
|
||||
2025-06-02 14:50:27 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
|
||||
2025-06-02 14:51:08 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
|
||||
|
@ -449,3 +449,11 @@
|
||||
2025-06-02 14:43:37 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-02 14:43:37 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
|
||||
2025-06-02 14:43:37 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)
|
||||
2025-06-02 14:50:28 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-02 14:50:28 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-02 14:50:28 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
|
||||
2025-06-02 14:50:28 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)
|
||||
2025-06-02 14:51:09 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-02 14:51:09 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-02 14:51:09 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
|
||||
2025-06-02 14:51:09 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)
|
||||
|
@ -112,3 +112,5 @@
|
||||
2025-06-02 14:30:43 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:34:08 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:43:36 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:50:27 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:51:08 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
|
@ -109,3 +109,5 @@
|
||||
2025-06-02 14:30:44 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-02 14:34:09 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-02 14:43:37 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-02 14:50:28 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-02 14:51:09 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
|
@ -131,3 +131,8 @@ WHERE printers.id = ?]
|
||||
2025-06-02 10:03:40 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 16 von 16 (Seite 1)
|
||||
2025-06-02 10:03:47 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 16 von 16 (Seite 1)
|
||||
2025-06-02 10:26:53 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 16 von 16 (Seite 1)
|
||||
2025-06-02 14:51:31 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 16 von 16 (Seite 1)
|
||||
2025-06-02 14:51:46 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 16 von 16 (Seite 1)
|
||||
2025-06-02 14:52:01 - [jobs] jobs - [ERROR] ERROR - Fehler beim Abrufen von Jobs: tuple index out of range
|
||||
2025-06-02 14:52:01 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 16 von 16 (Seite 1)
|
||||
2025-06-02 14:52:16 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 16 von 16 (Seite 1)
|
||||
|
@ -224,3 +224,7 @@
|
||||
2025-06-02 14:34:09 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:43:37 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:43:37 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:50:28 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:50:28 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:51:09 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-02 14:51:09 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
|
||||
|
@ -222,3 +222,7 @@
|
||||
2025-06-02 14:34:09 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
|
||||
2025-06-02 14:43:37 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
|
||||
2025-06-02 14:43:37 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
|
||||
2025-06-02 14:50:28 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
|
||||
2025-06-02 14:50:28 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
|
||||
2025-06-02 14:51:09 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
|
||||
2025-06-02 14:51:09 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
|
||||
|
@ -111,3 +111,5 @@
|
||||
2025-06-02 14:30:44 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
|
||||
2025-06-02 14:34:09 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
|
||||
2025-06-02 14:43:37 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
|
||||
2025-06-02 14:50:28 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
|
||||
2025-06-02 14:51:09 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
|
||||
|
@ -3200,3 +3200,37 @@
|
||||
2025-06-02 14:44:02 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102
|
||||
2025-06-02 14:44:08 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 6/6: 192.168.0.105
|
||||
2025-06-02 14:44:14 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Steckdosen-Erkennung abgeschlossen: 0/6 Steckdosen gefunden in 36.0s
|
||||
2025-06-02 14:50:27 - [printer_monitor] printer_monitor - [INFO] INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-02 14:50:27 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-02 14:50:28 - [printer_monitor] printer_monitor - [INFO] INFO - 🚀 Starte Steckdosen-Initialisierung beim Programmstart...
|
||||
2025-06-02 14:50:29 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Starte automatische Tapo-Steckdosenerkennung...
|
||||
2025-06-02 14:50:29 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Teste 6 Standard-IPs aus der Konfiguration
|
||||
2025-06-02 14:50:29 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 1/6: 192.168.0.103
|
||||
2025-06-02 14:50:30 - [printer_monitor] printer_monitor - [WARNING] WARNING - ❌ Tapo P110 (192.168.0.103): Steckdose konnte nicht ausgeschaltet werden
|
||||
2025-06-02 14:50:32 - [printer_monitor] printer_monitor - [WARNING] WARNING - ❌ Tapo P110 (192.168.0.104): Steckdose konnte nicht ausgeschaltet werden
|
||||
2025-06-02 14:50:32 - [printer_monitor] printer_monitor - [INFO] INFO - 🎯 Steckdosen-Initialisierung abgeschlossen: 0/2 erfolgreich
|
||||
2025-06-02 14:50:35 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 2/6: 192.168.0.104
|
||||
2025-06-02 14:50:41 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 3/6: 192.168.0.100
|
||||
2025-06-02 14:50:46 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
|
||||
2025-06-02 14:50:46 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Prüfe Status von 2 aktiven Druckern...
|
||||
2025-06-02 14:50:47 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101
|
||||
2025-06-02 14:51:08 - [printer_monitor] printer_monitor - [INFO] INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-02 14:51:08 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-02 14:51:09 - [printer_monitor] printer_monitor - [INFO] INFO - 🚀 Starte Steckdosen-Initialisierung beim Programmstart...
|
||||
2025-06-02 14:51:10 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Starte automatische Tapo-Steckdosenerkennung...
|
||||
2025-06-02 14:51:10 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Teste 6 Standard-IPs aus der Konfiguration
|
||||
2025-06-02 14:51:10 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 1/6: 192.168.0.103
|
||||
2025-06-02 14:51:12 - [printer_monitor] printer_monitor - [WARNING] WARNING - ❌ Tapo P110 (192.168.0.103): Steckdose konnte nicht ausgeschaltet werden
|
||||
2025-06-02 14:51:14 - [printer_monitor] printer_monitor - [WARNING] WARNING - ❌ Tapo P110 (192.168.0.104): Steckdose konnte nicht ausgeschaltet werden
|
||||
2025-06-02 14:51:14 - [printer_monitor] printer_monitor - [INFO] INFO - 🎯 Steckdosen-Initialisierung abgeschlossen: 0/2 erfolgreich
|
||||
2025-06-02 14:51:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
|
||||
2025-06-02 14:51:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Prüfe Status von 2 aktiven Druckern...
|
||||
2025-06-02 14:51:16 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 2/6: 192.168.0.104
|
||||
2025-06-02 14:51:22 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 3/6: 192.168.0.100
|
||||
2025-06-02 14:51:24 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.103): UNREACHABLE (Ping fehlgeschlagen)
|
||||
2025-06-02 14:51:24 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.104): UNREACHABLE (Ping fehlgeschlagen)
|
||||
2025-06-02 14:51:24 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Status-Update abgeschlossen für 2 Drucker
|
||||
2025-06-02 14:51:28 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101
|
||||
2025-06-02 14:51:34 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102
|
||||
2025-06-02 14:51:40 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 6/6: 192.168.0.105
|
||||
2025-06-02 14:51:46 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Steckdosen-Erkennung abgeschlossen: 0/6 Steckdosen gefunden in 36.0s
|
||||
|
@ -6604,3 +6604,38 @@
|
||||
2025-06-02 14:43:53 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:43:53 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:43:53 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.52ms
|
||||
2025-06-02 14:44:21 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:44:21 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:44:21 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.32ms
|
||||
2025-06-02 14:44:51 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:44:51 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:44:51 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.32ms
|
||||
2025-06-02 14:45:21 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:45:21 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:45:21 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.30ms
|
||||
2025-06-02 14:45:51 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:45:51 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:45:51 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.27ms
|
||||
2025-06-02 14:46:21 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:46:21 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:46:21 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.29ms
|
||||
2025-06-02 14:46:51 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:46:51 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:46:51 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.33ms
|
||||
2025-06-02 14:47:21 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:47:21 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:47:21 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.31ms
|
||||
2025-06-02 14:47:51 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:47:51 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:47:51 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.29ms
|
||||
2025-06-02 14:50:46 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:51:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:51:24 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:51:24 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 9010.43ms
|
||||
2025-06-02 14:51:27 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:51:27 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:51:27 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.68ms
|
||||
2025-06-02 14:51:32 - [printers] printers - [INFO] INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
|
||||
2025-06-02 14:52:02 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
|
||||
2025-06-02 14:52:02 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker
|
||||
2025-06-02 14:52:02 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.66ms
|
||||
|
@ -27601,3 +27601,522 @@
|
||||
2025-06-02 14:34:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000149DF324C00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:34:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:34:31 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:43:36 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-02 14:43:42 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
|
||||
2025-06-02 14:43:42 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
|
||||
2025-06-02 14:43:42 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:43:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0777610>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:43:44 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:43:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C24640>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:43:46 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:43:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0796C40>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:43:48 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:43:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA07F5490>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:43:50 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:43:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ACF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:43:52 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:43:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:43:55 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:43:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717240>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:43:57 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:43:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C78160>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:43:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:43:59 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:44:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C78380>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:44:01 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:44:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C787C0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:44:03 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:44:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C78AF0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:44:05 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:44:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C78E20>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:44:07 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:44:09 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C79150>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:09 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:44:09 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:44:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C79480>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:44:11 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:44:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717240>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:44:13 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:44:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B570>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:44:16 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:44:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A690>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:44:19 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:44:21 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:21 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:44:21 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:44:23 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B460>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:23 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:44:23 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:44:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ACF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:44:25 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:44:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C79590>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:44:27 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:44:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C79260>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:44:29 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:44:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C78D10>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:44:31 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:44:33 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C78C00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:33 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:44:33 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:44:35 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ACF0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:35 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:44:35 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:44:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A8B0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:44:37 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:44:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079BAC0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:44:40 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:44:42 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079AF10>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:42 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:44:42 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:44:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0799BF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:44:44 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:44:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:44:46 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:44:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98270>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:44:48 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:44:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C985A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:44:51 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:44:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C988D0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:44:53 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:44:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98AF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:44:55 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:44:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98F30>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:44:57 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:44:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0799BF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:44:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:44:59 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:45:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079AF10>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:45:01 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:45:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079BAC0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:45:03 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:45:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A8B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:45:05 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:45:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ACF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:45:08 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:45:10 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C99040>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:10 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:45:10 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:45:12 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98D10>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:12 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:45:12 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:45:14 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C989E0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:14 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:45:14 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:45:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C986B0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:45:16 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:45:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98380>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:45:18 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:45:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C99260>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:45:20 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:45:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B020>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:45:22 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:45:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A250>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:45:26 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:45:28 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:28 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:45:28 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:45:30 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079AF10>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:30 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:45:30 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:45:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0799BF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:45:32 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:45:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:45:34 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:45:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C99150>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:45:36 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:45:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98270>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:45:38 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:45:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C985A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:45:41 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:45:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C988D0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:45:43 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:45:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98490>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:45:45 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:45:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C98E20>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:45:47 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:45:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717680>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:45:49 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:45:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079BBD0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:45:51 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:45:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A690>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:45:53 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:45:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0799D00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:45:55 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:45:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B460>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:45:57 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:45:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ABE0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:45:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:46:00 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:46:02 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94160>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:02 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:46:02 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:46:04 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C945A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:04 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:46:04 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:46:06 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C948D0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:06 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:46:06 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:46:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94C00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:46:08 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:46:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94F30>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:46:11 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:46:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C95260>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:46:13 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:46:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B020>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:46:15 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:46:17 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ACF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:17 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:46:17 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:46:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0799D00>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:46:19 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:46:21 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079BAC0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:21 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:46:21 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:46:23 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079AF10>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:23 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:46:23 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:46:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717680>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:46:25 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:46:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94F30>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:46:27 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:46:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94C00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:46:29 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:46:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C948D0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:46:31 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:46:33 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C945A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:33 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:46:34 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:46:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94270>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:46:36 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:46:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:46:38 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:46:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0799BF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:46:40 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:46:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A690>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:46:43 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:46:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079AAD0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:46:45 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:46:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:46:47 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:46:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A250>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:46:49 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:46:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94160>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:46:51 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:46:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94270>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:46:53 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:46:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C946B0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:46:55 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:46:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94AF0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:57 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:46:57 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:46:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94E20>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:46:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:46:59 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:47:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C95480>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:47:01 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:47:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A250>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:47:03 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:47:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ABE0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:47:05 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:47:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B460>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:47:09 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:47:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A690>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:47:11 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:47:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079BBD0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:47:13 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:47:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717240>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:47:15 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:47:17 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C957B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:17 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:47:17 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:47:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C956A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:47:19 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:47:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94F30>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:47:22 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:47:24 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C949E0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:24 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:47:24 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:47:26 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C948D0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:26 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:47:26 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:47:28 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717680>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:28 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:47:28 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:47:30 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B570>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:30 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:47:30 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:47:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079AF10>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:47:32 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:47:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079BAC0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:47:34 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:47:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0799D00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:47:36 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:47:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B020>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:47:38 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:47:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C946B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:47:41 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:47:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94C00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:47:44 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:47:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C947C0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:47:46 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:47:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C95480>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:47:48 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:47:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C959D0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:47:50 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:47:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C958C0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:47:52 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:47:54 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B020>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:54 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:47:54 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:47:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079ACF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:47:56 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:47:58 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B680>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:47:58 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:47:58 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:48:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079AAD0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:48:01 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:48:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079B570>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:48:03 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:48:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0717240>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:48:05 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:48:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94050>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:48:07 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:48:09 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94160>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:09 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:48:09 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:48:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C957B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:48:11 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:48:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C956A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:48:13 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:48:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C94F30>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:15 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:48:15 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:48:17 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA0C949E0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:17 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:48:18 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:48:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001ECA079A8B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:48:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:48:20 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:50:27 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-02 14:50:32 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
|
||||
2025-06-02 14:50:32 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
|
||||
2025-06-02 14:50:32 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:50:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1A27610>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:50:34 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:50:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1EB8510>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:50:37 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:50:39 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1EB9350>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:39 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:50:39 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:50:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1AA6330>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:50:41 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:50:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1A4A7A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:50:43 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:50:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1F5C6B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:50:45 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:50:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1A4B9B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:50:47 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:50:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1A4A690>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:50:49 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:50:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001E8F1F5C050>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:50:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:50:51 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:51:08 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-02 14:51:14 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
|
||||
2025-06-02 14:51:14 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
|
||||
2025-06-02 14:51:14 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:51:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x0000014034447610>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:51:16 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:51:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140344E9A70>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:51:18 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:51:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140344E9940>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:51:20 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:51:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140344C69F0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:51:22 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:51:24 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x0000014034940050>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:24 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:51:24 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:51:26 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349409E0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:26 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:51:26 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:51:28 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x0000014034940AF0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:28 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:51:28 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:51:30 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001403446B240>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:30 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:51:30 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:51:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001403446ABE0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:51:32 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:51:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001403446B9B0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:51:34 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:51:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001403446A9C0>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:51:36 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:51:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140343E7240>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:51:38 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:51:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A4270>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:51:41 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:51:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A4380>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:43 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:51:43 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:51:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A48D0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:45 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:51:45 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
2025-06-02 14:51:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140343E7240>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten
|
||||
2025-06-02 14:51:48 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test
|
||||
2025-06-02 14:51:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001403446BF00>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten
|
||||
2025-06-02 14:51:50 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test
|
||||
2025-06-02 14:51:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x0000014034469E10>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten
|
||||
2025-06-02 14:51:52 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test
|
||||
2025-06-02 14:51:54 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A8380>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:54 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
|
||||
2025-06-02 14:51:54 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test
|
||||
2025-06-02 14:51:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A86B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
|
||||
2025-06-02 14:51:56 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test
|
||||
2025-06-02 14:51:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A89E0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:51:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten
|
||||
2025-06-02 14:51:59 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test
|
||||
2025-06-02 14:52:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A8D10>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten
|
||||
2025-06-02 14:52:01 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test
|
||||
2025-06-02 14:52:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A9040>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten
|
||||
2025-06-02 14:52:03 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test
|
||||
2025-06-02 14:52:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x0000014034469E10>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten
|
||||
2025-06-02 14:52:05 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi
|
||||
2025-06-02 14:52:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001403446B680>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten
|
||||
2025-06-02 14:52:07 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi
|
||||
2025-06-02 14:52:09 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A9480>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:09 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten
|
||||
2025-06-02 14:52:09 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee
|
||||
2025-06-02 14:52:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A9370>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:11 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten
|
||||
2025-06-02 14:52:11 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee
|
||||
2025-06-02 14:52:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A8D10>, 'Connection to 192.168.0.104 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:13 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten
|
||||
2025-06-02 14:52:13 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2
|
||||
2025-06-02 14:52:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A89E0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten
|
||||
2025-06-02 14:52:16 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2
|
||||
2025-06-02 14:52:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A86B0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten
|
||||
2025-06-02 14:52:18 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test
|
||||
2025-06-02 14:52:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140349A85A0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
|
||||
2025-06-02 14:52:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten
|
||||
2025-06-02 14:52:20 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test
|
||||
|
@ -111,3 +111,5 @@
|
||||
2025-06-02 14:30:44 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
|
||||
2025-06-02 14:34:09 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
|
||||
2025-06-02 14:43:37 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
|
||||
2025-06-02 14:50:28 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
|
||||
2025-06-02 14:51:09 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
|
||||
|
@ -197,3 +197,5 @@
|
||||
2025-06-02 14:30:44 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
|
||||
2025-06-02 14:34:09 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
|
||||
2025-06-02 14:43:37 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
|
||||
2025-06-02 14:50:28 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
|
||||
2025-06-02 14:51:09 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
|
||||
|
@ -1007,3 +1007,21 @@
|
||||
2025-06-02 14:43:37 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
|
||||
2025-06-02 14:43:37 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
|
||||
2025-06-02 14:43:37 - [startup] startup - [INFO] INFO - ==================================================
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - ==================================================
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - 🚀 MYP Platform Backend wird gestartet...
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - 🐍 Python Version: 3.13.3 (tags/v3.13.3:6280bb5, Apr 8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32)
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-02T14:50:28.305696
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
|
||||
2025-06-02 14:50:28 - [startup] startup - [INFO] INFO - ==================================================
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - ==================================================
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - 🚀 MYP Platform Backend wird gestartet...
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - 🐍 Python Version: 3.13.3 (tags/v3.13.3:6280bb5, Apr 8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32)
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-02T14:51:09.300938
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
|
||||
2025-06-02 14:51:09 - [startup] startup - [INFO] INFO - ==================================================
|
||||
|
@ -479,3 +479,11 @@
|
||||
2025-06-02 14:43:36 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-02 14:43:36 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-02 14:43:36 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-02 14:50:27 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-02 14:50:27 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-02 14:50:27 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-02 14:50:27 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-02 14:51:08 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-02 14:51:08 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-02 14:51:08 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-02 14:51:08 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
|
2
backend/static/css/tailwind.min.css
vendored
2
backend/static/css/tailwind.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,9 +1,11 @@
|
||||
[Unit]
|
||||
Description=MYP Kiosk Browser Autostart (Chromium HTTPS)
|
||||
Description=MYP Kiosk Browser Autostart (Chromium HTTPS) - Wartungsfreier Produktionsbetrieb
|
||||
Documentation=https://github.com/MYP-Druckerverwaltung
|
||||
After=graphical-session.target myp-https.service
|
||||
Wants=myp-https.service
|
||||
After=graphical-session.target myp-https.service network-online.target
|
||||
Wants=myp-https.service network-online.target
|
||||
Requires=graphical-session.target
|
||||
StartLimitBurst=5
|
||||
StartLimitInterval=600
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
@ -11,45 +13,118 @@ User=kiosk
|
||||
Group=kiosk
|
||||
Environment=DISPLAY=:0
|
||||
Environment=XAUTHORITY=/home/kiosk/.Xauthority
|
||||
Environment=HOME=/home/kiosk
|
||||
Environment=XDG_RUNTIME_DIR=/run/user/1001
|
||||
Environment=WAYLAND_DISPLAY=
|
||||
Environment=GDK_BACKEND=x11
|
||||
WorkingDirectory=/home/kiosk
|
||||
|
||||
# Warte auf HTTPS-Backend und starte dann Chromium
|
||||
ExecStartPre=/bin/bash -c 'echo "Warte auf HTTPS Backend..."; for i in {1..60}; do if curl -k -s https://localhost:443 >/dev/null 2>&1; then echo "HTTPS Backend erreichbar"; break; fi; echo "Warte... ($i/60)"; sleep 2; done'
|
||||
# Robuste Backend-Wartung mit verbesserter Fehlererkennung
|
||||
ExecStartPre=/bin/bash -c '\
|
||||
echo "=== MYP Kiosk-Service startet $(date) ==="; \
|
||||
\
|
||||
# Prüfe ob X11 läuft \
|
||||
for i in {1..30}; do \
|
||||
if DISPLAY=:0 xset q >/dev/null 2>&1; then \
|
||||
echo "✅ X11 Display verfügbar"; \
|
||||
break; \
|
||||
fi; \
|
||||
echo "⏳ Warte auf X11 Display... ($i/30)"; \
|
||||
sleep 2; \
|
||||
done; \
|
||||
\
|
||||
# Warte auf HTTPS-Backend mit verbesserter Erkennung \
|
||||
echo "🔍 Warte auf HTTPS Backend..."; \
|
||||
for i in {1..120}; do \
|
||||
if curl -k -s --connect-timeout 3 --max-time 5 https://localhost:443/api/kiosk/status >/dev/null 2>&1; then \
|
||||
echo "✅ HTTPS Backend erreichbar und API verfügbar"; \
|
||||
break; \
|
||||
elif curl -k -s --connect-timeout 3 --max-time 5 https://localhost:443 >/dev/null 2>&1; then \
|
||||
echo "✅ HTTPS Backend erreichbar"; \
|
||||
break; \
|
||||
fi; \
|
||||
echo "⏳ Warte auf Backend... ($i/120)"; \
|
||||
sleep 3; \
|
||||
done; \
|
||||
\
|
||||
# Räume alte Browser-Prozesse auf \
|
||||
pkill -f "chromium.*kiosk" 2>/dev/null || true; \
|
||||
pkill -f "firefox.*kiosk" 2>/dev/null || true; \
|
||||
sleep 2; \
|
||||
'
|
||||
|
||||
# Robuster Kiosk-Start mit Fehlerresilienz
|
||||
ExecStart=/bin/bash -c '\
|
||||
# Bildschirmauflösung ermitteln \
|
||||
RESOLUTION=$(DISPLAY=:0 xrandr 2>/dev/null | grep "*" | head -1 | awk "{print \$1}" || echo "1920x1080"); \
|
||||
set -e; \
|
||||
\
|
||||
# Logging-Setup \
|
||||
LOG_FILE="/var/log/myp-kiosk.log"; \
|
||||
exec 1> >(tee -a "$LOG_FILE"); \
|
||||
exec 2>&1; \
|
||||
\
|
||||
echo "🚀 Starte Kiosk-Modus $(date)"; \
|
||||
\
|
||||
# Bildschirmauflösung robust ermitteln \
|
||||
RESOLUTION=$(DISPLAY=:0 xrandr 2>/dev/null | grep -E "\*|\+" | head -1 | awk "{print \$1}" || echo "1920x1080"); \
|
||||
WIDTH=$(echo $RESOLUTION | cut -d"x" -f1); \
|
||||
HEIGHT=$(echo $RESOLUTION | cut -d"x" -f2); \
|
||||
echo "Erkannte Auflösung: ${WIDTH}x${HEIGHT}"; \
|
||||
echo "📺 Bildschirmauflösung: ${WIDTH}x${HEIGHT}"; \
|
||||
\
|
||||
# Bildschirmschoner deaktivieren \
|
||||
DISPLAY=:0 xset s off; \
|
||||
DISPLAY=:0 xset s noblank; \
|
||||
DISPLAY=:0 xset -dpms; \
|
||||
# Display-Konfiguration optimieren \
|
||||
DISPLAY=:0 xset s off 2>/dev/null || true; \
|
||||
DISPLAY=:0 xset s noblank 2>/dev/null || true; \
|
||||
DISPLAY=:0 xset -dpms 2>/dev/null || true; \
|
||||
DISPLAY=:0 xset r rate 250 30 2>/dev/null || true; \
|
||||
echo "⚙️ Display-Energieverwaltung deaktiviert"; \
|
||||
\
|
||||
# Mauszeiger verstecken \
|
||||
DISPLAY=:0 unclutter -idle 0.1 -root -noevents & \
|
||||
if command -v unclutter >/dev/null 2>&1; then \
|
||||
DISPLAY=:0 unclutter -idle 0.5 -root -noevents & \
|
||||
echo "🖱️ Mauszeiger-Versteckung aktiviert"; \
|
||||
fi; \
|
||||
\
|
||||
# Browser-Auswahl mit Prioritäten \
|
||||
BROWSER=""; \
|
||||
BROWSER_ARGS=""; \
|
||||
\
|
||||
# Chromium Kiosk-Modus starten \
|
||||
if command -v chromium >/dev/null 2>&1; then \
|
||||
BROWSER="chromium"; \
|
||||
elif command -v chromium-browser >/dev/null 2>&1; then \
|
||||
BROWSER="chromium-browser"; \
|
||||
else \
|
||||
echo "Kein Chromium gefunden - verwende Firefox"; \
|
||||
elif command -v google-chrome >/dev/null 2>&1; then \
|
||||
BROWSER="google-chrome"; \
|
||||
elif command -v firefox-esr >/dev/null 2>&1; then \
|
||||
BROWSER="firefox-esr"; \
|
||||
elif command -v firefox >/dev/null 2>&1; then \
|
||||
BROWSER="firefox"; \
|
||||
else \
|
||||
echo "❌ Kein unterstützter Browser gefunden"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
\
|
||||
echo "Starte $BROWSER im Kiosk-Modus..."; \
|
||||
echo "🌐 Verwende Browser: $BROWSER"; \
|
||||
\
|
||||
if [[ "$BROWSER" == "chromium"* ]]; then \
|
||||
exec $BROWSER \
|
||||
# Browser-spezifische Argumente \
|
||||
if [[ "$BROWSER" == "chromium"* ]] || [[ "$BROWSER" == "google-chrome"* ]]; then \
|
||||
BROWSER_ARGS=" \
|
||||
--kiosk \
|
||||
--no-sandbox \
|
||||
--disable-dev-shm-usage \
|
||||
--disable-gpu-sandbox \
|
||||
--disable-software-rasterizer \
|
||||
--disable-background-timer-throttling \
|
||||
--disable-backgrounding-occluded-windows \
|
||||
--disable-renderer-backgrounding \
|
||||
--disable-field-trial-config \
|
||||
--disable-features=TranslateUI,VizDisplayCompositor,AudioServiceOutOfProcess \
|
||||
--enable-features=OverlayScrollbar,VaapiVideoDecoder \
|
||||
--force-device-scale-factor=1.0 \
|
||||
--window-size=${WIDTH},${HEIGHT} \
|
||||
--window-position=0,0 \
|
||||
--user-data-dir=/home/kiosk/.chromium-kiosk \
|
||||
--disable-infobars \
|
||||
--disable-session-crashed-bubble \
|
||||
--disable-restore-session-state \
|
||||
--disable-features=TranslateUI \
|
||||
--disable-extensions \
|
||||
--disable-plugins \
|
||||
--disable-popup-blocking \
|
||||
@ -59,24 +134,15 @@ ExecStart=/bin/bash -c '\
|
||||
--noerrdialogs \
|
||||
--no-first-run \
|
||||
--no-default-browser-check \
|
||||
--no-crash-upload \
|
||||
--disable-crash-reporter \
|
||||
--disable-logging \
|
||||
--autoplay-policy=no-user-gesture-required \
|
||||
--start-fullscreen \
|
||||
--start-maximized \
|
||||
--window-size=${WIDTH},${HEIGHT} \
|
||||
--window-position=0,0 \
|
||||
--user-data-dir=/home/kiosk/.chromium-kiosk \
|
||||
--disable-background-mode \
|
||||
--force-device-scale-factor=1.0 \
|
||||
--disable-pinch \
|
||||
--overscroll-history-navigation=0 \
|
||||
--disable-dev-shm-usage \
|
||||
--memory-pressure-off \
|
||||
--max_old_space_size=512 \
|
||||
--disable-background-timer-throttling \
|
||||
--disable-backgrounding-occluded-windows \
|
||||
--disable-renderer-backgrounding \
|
||||
--disable-features=VizDisplayCompositor \
|
||||
--enable-features=OverlayScrollbar \
|
||||
--hide-scrollbars \
|
||||
--ignore-certificate-errors \
|
||||
--ignore-ssl-errors \
|
||||
@ -84,24 +150,77 @@ ExecStart=/bin/bash -c '\
|
||||
--disable-web-security \
|
||||
--allow-running-insecure-content \
|
||||
--unsafely-treat-insecure-origin-as-secure=https://localhost:443 \
|
||||
https://localhost:443; \
|
||||
--disable-blink-features=AutomationControlled \
|
||||
--disable-ipc-flooding-protection"; \
|
||||
else \
|
||||
exec firefox-esr \
|
||||
# Firefox-Argumente \
|
||||
BROWSER_ARGS=" \
|
||||
--kiosk \
|
||||
--width=${WIDTH} \
|
||||
--height=${HEIGHT} \
|
||||
https://localhost:443; \
|
||||
fi'
|
||||
--no-remote \
|
||||
--new-instance"; \
|
||||
fi; \
|
||||
\
|
||||
# URL mit Fallback \
|
||||
TARGET_URL="https://localhost:443"; \
|
||||
\
|
||||
# Browser starten mit Fehlerbehandlung \
|
||||
echo "🖥️ Starte $BROWSER im Kiosk-Modus..."; \
|
||||
echo "🔗 URL: $TARGET_URL"; \
|
||||
\
|
||||
# Umgebungsvariablen setzen \
|
||||
export DISPLAY=:0; \
|
||||
export HOME=/home/kiosk; \
|
||||
export XDG_RUNTIME_DIR=/run/user/1001; \
|
||||
export LIBGL_ALWAYS_SOFTWARE=1; \
|
||||
export MOZ_DISABLE_RDD_SANDBOX=1; \
|
||||
export MOZ_DISABLE_CONTENT_SANDBOX=1; \
|
||||
\
|
||||
# Browser-Start mit exec für korrekte Signal-Behandlung \
|
||||
exec $BROWSER $BROWSER_ARGS "$TARGET_URL" 2>&1; \
|
||||
'
|
||||
|
||||
# Robuste Restart-Konfiguration für wartungsfreien Betrieb
|
||||
Restart=always
|
||||
RestartSec=15
|
||||
StartLimitBurst=3
|
||||
StartLimitInterval=300
|
||||
StartLimitBurst=5
|
||||
StartLimitInterval=600
|
||||
TimeoutStartSec=300
|
||||
TimeoutStopSec=30
|
||||
KillMode=mixed
|
||||
KillSignal=SIGTERM
|
||||
|
||||
# Logging
|
||||
# Ressourcen-Management für Stabilität
|
||||
LimitNOFILE=65536
|
||||
LimitNPROC=4096
|
||||
MemoryHigh=1G
|
||||
MemoryMax=1.5G
|
||||
CPUQuota=80%
|
||||
|
||||
# Erweiterte Service-Überwachung
|
||||
WatchdogSec=60
|
||||
NotifyAccess=all
|
||||
|
||||
# Fehlerresilienz-Features
|
||||
PrivateNetwork=false
|
||||
PrivateTmp=true
|
||||
ProtectHome=false
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/home/kiosk /var/log /tmp
|
||||
NoNewPrivileges=false
|
||||
|
||||
# Logging-Konfiguration
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=myp-kiosk
|
||||
LogRateLimitBurst=1000
|
||||
LogRateLimitIntervalSec=30
|
||||
|
||||
# Service-Abhängigkeiten für robuste Startsequenz
|
||||
Requisite=myp-https.service
|
||||
BindsTo=graphical-session.target
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
Also=myp-https.service
|
@ -524,6 +524,36 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white/60 dark:bg-slate-700/60 backdrop-blur-sm rounded-xl border border-slate-200 dark:border-slate-600 p-6 shadow-lg">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">System-Steuerung</h3>
|
||||
<div class="space-y-3">
|
||||
<button id="kiosk-restart-btn" class="w-full px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors text-sm font-medium flex items-center justify-center space-x-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span>Kiosk neustarten</span>
|
||||
</button>
|
||||
<button id="restart-system-btn" class="w-full px-4 py-2 bg-orange-500 text-white rounded-lg hover:bg-orange-600 transition-colors text-sm font-medium flex items-center justify-center space-x-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
<span>System neustarten</span>
|
||||
</button>
|
||||
<button id="shutdown-system-btn" class="w-full px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors text-sm font-medium flex items-center justify-center space-x-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18.364 5.636M5.636 18.364l12.728-12.728"/>
|
||||
</svg>
|
||||
<span>System herunterfahren</span>
|
||||
</button>
|
||||
<button id="system-status-btn" class="w-full px-4 py-2 bg-slate-500 text-white rounded-lg hover:bg-slate-600 transition-colors text-sm font-medium flex items-center justify-center space-x-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
<span>System-Status</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white/60 dark:bg-slate-700/60 backdrop-blur-sm rounded-xl border border-slate-200 dark:border-slate-600 p-6 shadow-lg">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Konfiguration</h3>
|
||||
<div class="space-y-3">
|
||||
@ -533,8 +563,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<button id="update-printers-btn" class="w-full px-4 py-2 bg-indigo-500 text-white rounded-lg hover:bg-indigo-600 transition-colors text-sm font-medium">
|
||||
Drucker aktualisieren
|
||||
</button>
|
||||
<button id="restart-system-btn" class="w-full px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors text-sm font-medium">
|
||||
System neustarten
|
||||
<button id="error-recovery-toggle-btn" class="w-full px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors text-sm font-medium flex items-center justify-center space-x-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span>Fehlerresilienz</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -976,5 +1009,532 @@ function getCsrfToken() {
|
||||
const token = document.querySelector('meta[name="csrf-token"]');
|
||||
return token ? token.getAttribute('content') : '';
|
||||
}
|
||||
|
||||
// ===== ERWEITERTE SYSTEM-CONTROL-FUNKTIONALITÄT =====
|
||||
|
||||
class SystemControlManager {
|
||||
constructor() {
|
||||
this.pendingOperations = new Map();
|
||||
this.lastStatusUpdate = null;
|
||||
this.isInitialized = false;
|
||||
|
||||
this.initializeEventListeners();
|
||||
this.startStatusPolling();
|
||||
this.isInitialized = true;
|
||||
|
||||
console.log('🔧 System-Control-Manager initialisiert');
|
||||
}
|
||||
|
||||
initializeEventListeners() {
|
||||
// Kiosk-Neustart
|
||||
const kioskRestartBtn = document.getElementById('kiosk-restart-btn');
|
||||
if (kioskRestartBtn) {
|
||||
kioskRestartBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleKioskRestart();
|
||||
});
|
||||
}
|
||||
|
||||
// System-Neustart
|
||||
const restartBtn = document.getElementById('restart-system-btn');
|
||||
if (restartBtn) {
|
||||
restartBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleSystemRestart();
|
||||
});
|
||||
}
|
||||
|
||||
// System-Shutdown
|
||||
const shutdownBtn = document.getElementById('shutdown-system-btn');
|
||||
if (shutdownBtn) {
|
||||
shutdownBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleSystemShutdown();
|
||||
});
|
||||
}
|
||||
|
||||
// System-Status
|
||||
const statusBtn = document.getElementById('system-status-btn');
|
||||
if (statusBtn) {
|
||||
statusBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.showSystemStatus();
|
||||
});
|
||||
}
|
||||
|
||||
// Error-Recovery Toggle
|
||||
const errorRecoveryBtn = document.getElementById('error-recovery-toggle-btn');
|
||||
if (errorRecoveryBtn) {
|
||||
errorRecoveryBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.toggleErrorRecovery();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async handleKioskRestart() {
|
||||
const confirmed = await this.confirmOperation(
|
||||
'Kiosk-Neustart',
|
||||
'Möchten Sie das Kiosk-Display neustarten? Dies dauert ca. 10-30 Sekunden.',
|
||||
'Der Kiosk wird neugestartet...'
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/kiosk/restart', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
},
|
||||
body: JSON.stringify({
|
||||
delay_seconds: 10,
|
||||
reason: 'Manueller Kiosk-Neustart über Admin-Panel'
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.success) {
|
||||
showNotification('Kiosk-Neustart geplant', 'success');
|
||||
this.trackOperation(result.operation_id, 'kiosk_restart');
|
||||
} else {
|
||||
showNotification(result.error || 'Fehler beim Kiosk-Neustart', 'error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Kiosk-Neustart Fehler:', error);
|
||||
showNotification('Fehler beim Kiosk-Neustart: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async handleSystemRestart() {
|
||||
const confirmed = await this.confirmOperation(
|
||||
'System-Neustart',
|
||||
'WARNUNG: Das gesamte System wird neu gestartet! Dies dauert ca. 2-5 Minuten.',
|
||||
'Das System wird neu gestartet...',
|
||||
true
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
// Zusätzliche Verzögerungsabfrage
|
||||
const delayInput = prompt('Verzögerung in Sekunden (10-3600):', '60');
|
||||
if (!delayInput) return;
|
||||
|
||||
const delay = Math.max(10, Math.min(3600, parseInt(delayInput) || 60));
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/system/restart', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
},
|
||||
body: JSON.stringify({
|
||||
delay_seconds: delay,
|
||||
reason: 'Manueller System-Neustart über Admin-Panel'
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.success) {
|
||||
showNotification(`System-Neustart in ${delay} Sekunden geplant`, 'success');
|
||||
this.trackOperation(result.operation_id, 'system_restart');
|
||||
this.showCountdown(delay, 'System-Neustart');
|
||||
} else {
|
||||
showNotification(result.error || 'Fehler beim System-Neustart', 'error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('System-Neustart Fehler:', error);
|
||||
showNotification('Fehler beim System-Neustart: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async handleSystemShutdown() {
|
||||
const confirmed = await this.confirmOperation(
|
||||
'System-Shutdown',
|
||||
'KRITISCHE WARNUNG: Das System wird komplett heruntergefahren!',
|
||||
'Das System wird heruntergefahren...',
|
||||
true
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
// Doppelte Bestätigung
|
||||
const doubleConfirm = confirm('LETZTE WARNUNG: System wirklich herunterfahren?\nDas System muss danach manuell neu gestartet werden!');
|
||||
if (!doubleConfirm) return;
|
||||
|
||||
// Verzögerungsabfrage
|
||||
const delayInput = prompt('Verzögerung in Sekunden (10-3600):', '30');
|
||||
if (!delayInput) return;
|
||||
|
||||
const delay = Math.max(10, Math.min(3600, parseInt(delayInput) || 30));
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/system/shutdown', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
},
|
||||
body: JSON.stringify({
|
||||
delay_seconds: delay,
|
||||
reason: 'Manueller System-Shutdown über Admin-Panel'
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.success) {
|
||||
showNotification(`System-Shutdown in ${delay} Sekunden geplant`, 'error');
|
||||
this.trackOperation(result.operation_id, 'system_shutdown');
|
||||
this.showCountdown(delay, 'System-Shutdown');
|
||||
} else {
|
||||
showNotification(result.error || 'Fehler beim System-Shutdown', 'error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('System-Shutdown Fehler:', error);
|
||||
showNotification('Fehler beim System-Shutdown: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async showSystemStatus() {
|
||||
try {
|
||||
showNotification('System-Status wird geladen...', 'info');
|
||||
|
||||
const response = await fetch('/api/admin/system/status', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
}
|
||||
});
|
||||
|
||||
const status = await response.json();
|
||||
|
||||
if (response.ok && status.success) {
|
||||
this.displaySystemStatusModal(status);
|
||||
} else {
|
||||
showNotification('Fehler beim Laden des System-Status', 'error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('System-Status Fehler:', error);
|
||||
showNotification('Fehler beim System-Status: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async toggleErrorRecovery() {
|
||||
try {
|
||||
// Aktuellen Status abrufen
|
||||
const statusResponse = await fetch('/api/admin/error-recovery/status', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
}
|
||||
});
|
||||
|
||||
const statusData = await statusResponse.json();
|
||||
const currentlyActive = statusData?.statistics?.monitoring_active || false;
|
||||
|
||||
// Toggle-Operation
|
||||
const response = await fetch('/api/admin/error-recovery/toggle', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
},
|
||||
body: JSON.stringify({
|
||||
enable: !currentlyActive
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.success) {
|
||||
showNotification(result.message, 'success');
|
||||
this.updateErrorRecoveryButton(result.monitoring_active);
|
||||
} else {
|
||||
showNotification(result.error || 'Fehler beim Error-Recovery Toggle', 'error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error-Recovery Toggle Fehler:', error);
|
||||
showNotification('Fehler beim Error-Recovery Toggle: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
updateErrorRecoveryButton(isActive) {
|
||||
const btn = document.getElementById('error-recovery-toggle-btn');
|
||||
if (btn) {
|
||||
if (isActive) {
|
||||
btn.classList.remove('bg-green-500', 'hover:bg-green-600');
|
||||
btn.classList.add('bg-yellow-500', 'hover:bg-yellow-600');
|
||||
btn.querySelector('span').textContent = 'Fehlerresilienz AN';
|
||||
} else {
|
||||
btn.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
|
||||
btn.classList.add('bg-green-500', 'hover:bg-green-600');
|
||||
btn.querySelector('span').textContent = 'Fehlerresilienz AUS';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async confirmOperation(title, message, processingMessage, isDestructive = false) {
|
||||
const bgColor = isDestructive ? 'bg-red-100 dark:bg-red-900' : 'bg-blue-100 dark:bg-blue-900';
|
||||
const iconColor = isDestructive ? 'text-red-600 dark:text-red-400' : 'text-blue-600 dark:text-blue-400';
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4';
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="bg-white dark:bg-slate-800 rounded-2xl p-6 shadow-2xl max-w-md w-full">
|
||||
<div class="text-center mb-6">
|
||||
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full ${bgColor} mb-4">
|
||||
<svg class="h-6 w-6 ${iconColor}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 15.5c-.77.833.192 2.5 1.732 2.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-slate-900 dark:text-white mb-2">${title}</h3>
|
||||
<p class="text-sm text-slate-500 dark:text-slate-400">${message}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-3">
|
||||
<button id="modal-cancel" class="flex-1 px-4 py-2 bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-xl hover:bg-slate-300 dark:hover:bg-slate-600 transition-colors">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button id="modal-confirm" class="flex-1 px-4 py-2 ${isDestructive ? 'bg-red-500 hover:bg-red-600' : 'bg-blue-500 hover:bg-blue-600'} text-white rounded-xl transition-colors">
|
||||
Bestätigen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
const cancelBtn = modal.querySelector('#modal-cancel');
|
||||
const confirmBtn = modal.querySelector('#modal-confirm');
|
||||
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
document.body.removeChild(modal);
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
confirmBtn.addEventListener('click', () => {
|
||||
document.body.removeChild(modal);
|
||||
resolve(true);
|
||||
});
|
||||
|
||||
// ESC-Key
|
||||
const handleEsc = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
document.body.removeChild(modal);
|
||||
document.removeEventListener('keydown', handleEsc);
|
||||
resolve(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleEsc);
|
||||
});
|
||||
}
|
||||
|
||||
showCountdown(seconds, operationType) {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fixed inset-0 bg-black/75 backdrop-blur-sm z-50 flex items-center justify-center p-4';
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="bg-white dark:bg-slate-800 rounded-2xl p-8 shadow-2xl max-w-sm w-full text-center">
|
||||
<div class="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-orange-100 dark:bg-orange-900 mb-6">
|
||||
<svg class="h-8 w-8 text-orange-600 dark:text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-slate-900 dark:text-white mb-2">${operationType}</h3>
|
||||
<p class="text-slate-600 dark:text-slate-400 mb-6">Wird ausgeführt in:</p>
|
||||
<div id="countdown-timer" class="text-4xl font-bold text-orange-600 dark:text-orange-400 mb-6">${seconds}</div>
|
||||
<button id="countdown-cancel" class="px-6 py-2 bg-red-500 text-white rounded-xl hover:bg-red-600 transition-colors">
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
const timerEl = modal.querySelector('#countdown-timer');
|
||||
const cancelBtn = modal.querySelector('#countdown-cancel');
|
||||
|
||||
let remainingSeconds = seconds;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
remainingSeconds--;
|
||||
timerEl.textContent = remainingSeconds;
|
||||
|
||||
if (remainingSeconds <= 0) {
|
||||
clearInterval(interval);
|
||||
document.body.removeChild(modal);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
cancelBtn.addEventListener('click', async () => {
|
||||
clearInterval(interval);
|
||||
document.body.removeChild(modal);
|
||||
|
||||
// Versuche Operation zu stornieren
|
||||
try {
|
||||
// Hier könnte der API-Call zum Stornieren implementiert werden
|
||||
showNotification('Operation wird storniert...', 'info');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Stornieren:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
displaySystemStatusModal(status) {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4';
|
||||
|
||||
const servicesHtml = Object.entries(status.services || {}).map(([name, state]) => {
|
||||
const isActive = state === 'active';
|
||||
return `
|
||||
<div class="flex items-center justify-between py-2 px-3 rounded-lg ${isActive ? 'bg-green-50 dark:bg-green-900/20' : 'bg-red-50 dark:bg-red-900/20'}">
|
||||
<span class="font-medium">${name}</span>
|
||||
<span class="px-2 py-1 rounded-full text-xs ${isActive ? 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100' : 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100'}">${state}</span>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
const metrics = status.system_metrics || {};
|
||||
const errorRecovery = status.error_recovery || {};
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="bg-white dark:bg-slate-800 rounded-2xl p-6 shadow-2xl max-w-2xl w-full max-h-90vh overflow-y-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h3 class="text-xl font-bold text-slate-900 dark:text-white">System-Status</h3>
|
||||
<button id="close-status-modal" class="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Services -->
|
||||
<div>
|
||||
<h4 class="font-semibold text-slate-900 dark:text-white mb-3">Services</h4>
|
||||
<div class="space-y-2">
|
||||
${servicesHtml}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System-Metriken -->
|
||||
<div>
|
||||
<h4 class="font-semibold text-slate-900 dark:text-white mb-3">System-Metriken</h4>
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between">
|
||||
<span>Speicher:</span>
|
||||
<span class="font-medium ${metrics.memory_percent > 80 ? 'text-red-600' : 'text-green-600'}">${metrics.memory_percent?.toFixed(1) || 0}%</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>Festplatte:</span>
|
||||
<span class="font-medium ${metrics.disk_percent > 90 ? 'text-red-600' : 'text-green-600'}">${metrics.disk_percent?.toFixed(1) || 0}%</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>System-Last:</span>
|
||||
<span class="font-medium">${metrics.load_average?.toFixed(2) || 0}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fehlerresilienz -->
|
||||
<div class="md:col-span-2">
|
||||
<h4 class="font-semibold text-slate-900 dark:text-white mb-3">Fehlerresilienz</h4>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div class="text-center p-3 bg-slate-50 dark:bg-slate-700 rounded-lg">
|
||||
<div class="text-2xl font-bold text-blue-600">${errorRecovery.total_errors || 0}</div>
|
||||
<div class="text-xs text-slate-600 dark:text-slate-400">Gesamt-Fehler</div>
|
||||
</div>
|
||||
<div class="text-center p-3 bg-slate-50 dark:bg-slate-700 rounded-lg">
|
||||
<div class="text-2xl font-bold text-orange-600">${errorRecovery.errors_last_24h || 0}</div>
|
||||
<div class="text-xs text-slate-600 dark:text-slate-400">Letzten 24h</div>
|
||||
</div>
|
||||
<div class="text-center p-3 bg-slate-50 dark:bg-slate-700 rounded-lg">
|
||||
<div class="text-2xl font-bold text-green-600">${errorRecovery.recovery_success_rate || 0}%</div>
|
||||
<div class="text-xs text-slate-600 dark:text-slate-400">Erfolgsrate</div>
|
||||
</div>
|
||||
<div class="text-center p-3 bg-slate-50 dark:bg-slate-700 rounded-lg">
|
||||
<div class="text-2xl font-bold ${status.is_safe ? 'text-green-600' : 'text-red-600'}">${status.is_safe ? 'SICHER' : 'UNSICHER'}</div>
|
||||
<div class="text-xs text-slate-600 dark:text-slate-400">Status</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 pt-4 border-t border-slate-200 dark:border-slate-600">
|
||||
<div class="text-sm text-slate-500 dark:text-slate-400">
|
||||
Letztes Update: ${new Date().toLocaleString('de-DE')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
const closeBtn = modal.querySelector('#close-status-modal');
|
||||
closeBtn.addEventListener('click', () => {
|
||||
document.body.removeChild(modal);
|
||||
});
|
||||
|
||||
// ESC zum Schließen
|
||||
const handleEsc = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
document.body.removeChild(modal);
|
||||
document.removeEventListener('keydown', handleEsc);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleEsc);
|
||||
}
|
||||
|
||||
trackOperation(operationId, type) {
|
||||
this.pendingOperations.set(operationId, {
|
||||
id: operationId,
|
||||
type: type,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
async startStatusPolling() {
|
||||
// Überwache Error-Recovery-Status
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/admin/error-recovery/status', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.updateErrorRecoveryButton(data?.statistics?.monitoring_active || false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.debug('Status-Polling Fehler:', error);
|
||||
}
|
||||
}, 30000); // Alle 30 Sekunden
|
||||
}
|
||||
}
|
||||
|
||||
// Globaler System-Control-Manager
|
||||
let systemControlManager = null;
|
||||
|
||||
// Initialisierung beim DOM-Laden
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (!systemControlManager) {
|
||||
systemControlManager = new SystemControlManager();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -13,207 +13,124 @@
|
||||
|
||||
<style>
|
||||
.calendar-container {
|
||||
@apply bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 p-6 mb-6 transition-all duration-300;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.fc-event {
|
||||
font-size: 11px;
|
||||
border-radius: 6px;
|
||||
padding: 2px 6px;
|
||||
border: none !important;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
.fc-event-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* FullCalendar Dark Mode */
|
||||
.dark .fc-theme-standard td,
|
||||
.dark .fc-theme-standard th {
|
||||
border-color: #374151;
|
||||
}
|
||||
|
||||
.dark .fc-theme-standard .fc-scrollgrid {
|
||||
border-color: #374151;
|
||||
}
|
||||
|
||||
.dark .fc-col-header-cell {
|
||||
background: #1f2937;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.dark .fc-daygrid-day {
|
||||
background: #1f2937;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.dark .fc-day-today {
|
||||
background-color: #065f46 !important;
|
||||
}
|
||||
|
||||
.dark .fc-button-primary {
|
||||
background: #3b82f6;
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.dark .fc-button-primary:hover {
|
||||
background: #2563eb;
|
||||
border-color: #2563eb;
|
||||
}
|
||||
|
||||
.dark .fc-toolbar-title {
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
@apply bg-gradient-to-br from-blue-500 via-blue-600 to-indigo-700 dark:from-blue-600 dark:via-blue-700 dark:to-indigo-800 rounded-xl p-6 text-white shadow-lg border border-blue-200 dark:border-blue-800 transition-all duration-300 hover:shadow-xl hover:scale-105;
|
||||
}
|
||||
|
||||
.stats-card-success {
|
||||
@apply bg-gradient-to-br from-emerald-500 via-emerald-600 to-green-700 dark:from-emerald-600 dark:via-emerald-700 dark:to-green-800;
|
||||
}
|
||||
|
||||
.stats-card-warning {
|
||||
@apply bg-gradient-to-br from-amber-500 via-orange-500 to-orange-600 dark:from-amber-600 dark:via-orange-600 dark:to-orange-700;
|
||||
}
|
||||
|
||||
.stats-card-danger {
|
||||
@apply bg-gradient-to-br from-red-500 via-red-600 to-rose-700 dark:from-red-600 dark:via-red-700 dark:to-rose-800;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
@apply bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 p-6 mb-6 transition-all duration-300;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
@apply flex gap-4 items-center flex-wrap;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-calendar {
|
||||
@apply bg-blue-600 hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-600 text-white border-none px-4 py-2 rounded-lg cursor-pointer transition-all duration-200 font-medium shadow-md hover:shadow-lg;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.btn-calendar:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.btn-calendar.active {
|
||||
@apply bg-blue-800 dark:bg-blue-500 shadow-lg scale-105;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
@apply bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-600 text-white px-4 py-2 rounded-lg transition-all duration-200 font-medium shadow-md hover:shadow-lg;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-blue-600 hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition-all duration-200 font-medium shadow-md hover:shadow-lg;
|
||||
background: #1d4ed8;
|
||||
}
|
||||
|
||||
.legend {
|
||||
@apply flex gap-6 flex-wrap mt-4 pt-4 border-t border-gray-200 dark:border-gray-600;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
@apply flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
@apply w-4 h-4 rounded;
|
||||
}
|
||||
|
||||
.select-custom {
|
||||
@apply border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
@apply bg-gradient-to-r from-blue-50 via-indigo-50 to-purple-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 border-b border-gray-200 dark:border-gray-700;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
@apply fixed inset-0 bg-black bg-opacity-50 dark:bg-opacity-70 z-50 transition-opacity duration-300;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
@apply bg-white dark:bg-gray-800 rounded-xl max-w-2xl w-full p-6 shadow-2xl border border-gray-200 dark:border-gray-700 transition-all duration-300;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
@apply flex justify-between items-center mb-6 pb-4 border-b border-gray-200 dark:border-gray-600;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@apply text-xl font-semibold text-gray-900 dark:text-gray-100;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
@apply text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors duration-200 text-xl;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
@apply grid grid-cols-1 md:grid-cols-2 gap-4 text-sm;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
@apply space-y-1;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
@apply font-medium text-gray-600 dark:text-gray-400;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
@apply text-gray-900 dark:text-gray-100 bg-gray-50 dark:bg-gray-700 px-3 py-2 rounded-lg;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
@apply bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-400 p-3 rounded-lg border border-red-200 dark:border-red-800;
|
||||
}
|
||||
|
||||
.notes-section {
|
||||
@apply bg-blue-50 dark:bg-blue-900/20 text-blue-800 dark:text-blue-300 p-3 rounded-lg border border-blue-200 dark:border-blue-800;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors duration-300">
|
||||
<div class="min-h-screen">
|
||||
<!-- Header mit Breadcrumb -->
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="py-8">
|
||||
<nav class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-6">
|
||||
<div class="py-6">
|
||||
<nav class="text-sm font-medium text-gray-600 mb-4">
|
||||
<ol class="list-none p-0 inline-flex">
|
||||
<li class="flex items-center">
|
||||
<a href="{{ url_for('admin_page') }}" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200">
|
||||
<i class="fas fa-tachometer-alt mr-2"></i>
|
||||
Admin-Dashboard
|
||||
</a>
|
||||
<svg class="fill-current w-4 h-4 mx-3 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
|
||||
<path d="M285.476 272.971c4.686 4.686 4.686 12.284 0 16.97l-133.952 133.954c-4.686 4.686-12.284 4.686-16.97 0l-133.952-133.954c-4.686-4.686-4.686-12.284 0-16.97 4.686-4.686 12.284-4.686 16.97 0l125.462 125.463 125.462-125.463c4.686-4.686 12.284-4.686 16.97 0z"/>
|
||||
<a href="{{ url_for('admin_page') }}" class="hover:text-blue-600">Admin-Dashboard</a>
|
||||
<svg class="fill-current w-3 h-3 mx-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
|
||||
<path d="m285.476 272.971c4.686 4.686 4.686 12.284 0 16.97l-133.952 133.954c-4.686 4.686-12.284 4.686-16.97 0l-133.952-133.954c-4.686-4.686-4.686-12.284 0-16.97 4.686-4.686 12.284-4.686 16.97 0l125.462 125.463 125.462-125.463c4.686-4.686 12.284-4.686 16.97 0z"/>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="text-gray-900 dark:text-gray-100 font-semibold">
|
||||
<i class="fas fa-plug mr-2"></i>
|
||||
<li class="text-gray-900 font-semibold">
|
||||
Steckdosenschaltzeiten
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="flex flex-col lg:flex-row lg:justify-between lg:items-center gap-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-2">
|
||||
<i class="fas fa-plug text-blue-600 dark:text-blue-400 mr-4"></i>
|
||||
<h1 class="text-3xl font-bold text-gray-900">
|
||||
<i class="fas fa-plug text-blue-600 mr-3"></i>
|
||||
Steckdosenschaltzeiten
|
||||
</h1>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-400">
|
||||
<p class="mt-2 text-gray-600">
|
||||
Kalenderübersicht aller Drucker-Steckdosenschaltungen mit detaillierter Analyse
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3">
|
||||
<button id="refreshData" class="btn-primary">
|
||||
<button id="refreshData" class="btn-calendar">
|
||||
<i class="fas fa-sync-alt mr-2"></i>
|
||||
Aktualisieren
|
||||
</button>
|
||||
|
||||
<button id="cleanupLogs" class="btn-danger">
|
||||
<button id="cleanupLogs" class="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700">
|
||||
<i class="fas fa-trash mr-2"></i>
|
||||
Alte Logs löschen
|
||||
</button>
|
||||
@ -224,64 +141,52 @@
|
||||
</div>
|
||||
|
||||
<!-- Haupt-Container -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-8">
|
||||
<!-- Statistik-Karten -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
|
||||
<div class="stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm opacity-90 mb-1">Schaltungen (24h)</p>
|
||||
<p class="text-3xl font-bold" id="totalLogs">{{ stats.total_logs or 0 }}</p>
|
||||
<p class="text-xs opacity-80 mt-1">Gesamt-Events</p>
|
||||
</div>
|
||||
<div class="p-3 bg-white/20 rounded-xl">
|
||||
<i class="fas fa-chart-line text-2xl"></i>
|
||||
<p class="text-sm opacity-80">Schaltungen (24h)</p>
|
||||
<p class="text-2xl font-bold" id="totalLogs">{{ stats.total_logs or 0 }}</p>
|
||||
</div>
|
||||
<i class="fas fa-chart-line text-2xl opacity-60"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-card stats-card-success">
|
||||
<div class="stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm opacity-90 mb-1">Erfolgsrate</p>
|
||||
<p class="text-3xl font-bold" id="successRate">{{ "%.1f"|format(100 - stats.error_rate) }}%</p>
|
||||
<p class="text-xs opacity-80 mt-1">Erfolgreiche Schaltungen</p>
|
||||
</div>
|
||||
<div class="p-3 bg-white/20 rounded-xl">
|
||||
<i class="fas fa-check-circle text-2xl"></i>
|
||||
<p class="text-sm opacity-80">Erfolgsrate</p>
|
||||
<p class="text-2xl font-bold" id="successRate">{{ "%.1f"|format(100 - stats.error_rate) }}%</p>
|
||||
</div>
|
||||
<i class="fas fa-check-circle text-2xl opacity-60"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-card stats-card-warning">
|
||||
<div class="stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm opacity-90 mb-1">Ø Antwortzeit</p>
|
||||
<p class="text-3xl font-bold" id="avgResponseTime">
|
||||
<p class="text-sm opacity-80">Ø Antwortzeit</p>
|
||||
<p class="text-2xl font-bold" id="avgResponseTime">
|
||||
{% if stats.average_response_time_ms %}
|
||||
{{ "%.0f"|format(stats.average_response_time_ms) }}ms
|
||||
{% else %}
|
||||
N/A
|
||||
{% endif %}
|
||||
</p>
|
||||
<p class="text-xs opacity-80 mt-1">Durchschnittlich</p>
|
||||
</div>
|
||||
<div class="p-3 bg-white/20 rounded-xl">
|
||||
<i class="fas fa-stopwatch text-2xl"></i>
|
||||
</div>
|
||||
<i class="fas fa-stopwatch text-2xl opacity-60"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-card stats-card-danger">
|
||||
<div class="stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm opacity-90 mb-1">Fehlerzahl</p>
|
||||
<p class="text-3xl font-bold" id="errorCount">{{ stats.error_count or 0 }}</p>
|
||||
<p class="text-xs opacity-80 mt-1">Gescheiterte Versuche</p>
|
||||
</div>
|
||||
<div class="p-3 bg-white/20 rounded-xl">
|
||||
<i class="fas fa-exclamation-triangle text-2xl"></i>
|
||||
<p class="text-sm opacity-80">Fehlerzahl</p>
|
||||
<p class="text-2xl font-bold" id="errorCount">{{ stats.error_count or 0 }}</p>
|
||||
</div>
|
||||
<i class="fas fa-exclamation-triangle text-2xl opacity-60"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -289,62 +194,39 @@
|
||||
<!-- Filter und Steuerung -->
|
||||
<div class="control-panel">
|
||||
<div class="filter-group">
|
||||
<div class="flex items-center gap-3">
|
||||
<label for="printerFilter" class="font-medium text-gray-700 dark:text-gray-300">
|
||||
<i class="fas fa-filter mr-2"></i>
|
||||
Drucker filtern:
|
||||
</label>
|
||||
<select id="printerFilter" class="select-custom">
|
||||
<option value="">Alle Drucker anzeigen</option>
|
||||
<label for="printerFilter" class="font-medium text-gray-700">Drucker filtern:</label>
|
||||
<select id="printerFilter" class="border border-gray-300 rounded-lg px-3 py-2 bg-white">
|
||||
<option value="">Alle Drucker</option>
|
||||
{% for printer in printers %}
|
||||
<option value="{{ printer.id }}">{{ printer.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 border-l border-gray-300 dark:border-gray-600 pl-6 ml-6">
|
||||
<label class="font-medium text-gray-700 dark:text-gray-300">
|
||||
<i class="fas fa-eye mr-2"></i>
|
||||
Ansicht:
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<button id="monthView" class="btn-calendar active">
|
||||
<i class="fas fa-calendar-alt mr-1"></i>
|
||||
Monat
|
||||
</button>
|
||||
<button id="weekView" class="btn-calendar">
|
||||
<i class="fas fa-calendar-week mr-1"></i>
|
||||
Woche
|
||||
</button>
|
||||
<button id="dayView" class="btn-calendar">
|
||||
<i class="fas fa-calendar-day mr-1"></i>
|
||||
Tag
|
||||
</button>
|
||||
</div>
|
||||
<div class="border-l border-gray-300 pl-4 ml-4">
|
||||
<label class="font-medium text-gray-700 mr-3">Ansicht:</label>
|
||||
<button id="monthView" class="btn-calendar active">Monat</button>
|
||||
<button id="weekView" class="btn-calendar">Woche</button>
|
||||
<button id="dayView" class="btn-calendar">Tag</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Legende -->
|
||||
<div class="legend">
|
||||
<h4 class="font-medium text-gray-700 dark:text-gray-300 w-full mb-2">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
Status-Legende:
|
||||
</h4>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color bg-green-500"></div>
|
||||
<span><i class="fas fa-toggle-on mr-1"></i>Steckdose EIN</span>
|
||||
<div class="legend-color" style="background-color: #10b981;"></div>
|
||||
<span>Steckdose EIN</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color bg-orange-500"></div>
|
||||
<span><i class="fas fa-toggle-off mr-1"></i>Steckdose AUS</span>
|
||||
<div class="legend-color" style="background-color: #f59e0b;"></div>
|
||||
<span>Steckdose AUS</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color bg-blue-500"></div>
|
||||
<span><i class="fas fa-plug mr-1"></i>Verbunden</span>
|
||||
<div class="legend-color" style="background-color: #3b82f6;"></div>
|
||||
<span>Verbunden</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color bg-red-500"></div>
|
||||
<span><i class="fas fa-unlink mr-1"></i>Getrennt</span>
|
||||
<div class="legend-color" style="background-color: #ef4444;"></div>
|
||||
<span>Getrennt</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -355,15 +237,12 @@
|
||||
</div>
|
||||
|
||||
<!-- Detailansicht Modal -->
|
||||
<div id="eventDetailModal" class="modal-overlay hidden">
|
||||
<div id="eventDetailModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
|
||||
<div class="flex items-center justify-center min-h-screen p-4">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">
|
||||
<i class="fas fa-info-circle mr-2 text-blue-600 dark:text-blue-400"></i>
|
||||
Schaltung Details
|
||||
</h3>
|
||||
<button id="closeModal" class="close-button">
|
||||
<div class="bg-white rounded-lg max-w-lg w-full p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold">Schaltung Details</h3>
|
||||
<button id="closeModal" class="text-gray-400 hover:text-gray-600">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -372,9 +251,8 @@
|
||||
<!-- Wird dynamisch gefüllt -->
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end gap-3">
|
||||
<button id="closeModalBtn" class="px-6 py-2 bg-gray-300 dark:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-500 transition-all duration-200 font-medium">
|
||||
<i class="fas fa-times mr-2"></i>
|
||||
<div class="mt-6 flex justify-end">
|
||||
<button id="closeModalBtn" class="px-4 py-2 bg-gray-300 text-gray-700 rounded-lg hover:bg-gray-400">
|
||||
Schließen
|
||||
</button>
|
||||
</div>
|
||||
@ -414,17 +292,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
eventDidMount: function(info) {
|
||||
// Tooltip hinzufügen
|
||||
info.el.title = info.event.title + '\nKlicken für Details';
|
||||
|
||||
// Hover-Effekt
|
||||
info.el.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'scale(1.05)';
|
||||
this.style.zIndex = '10';
|
||||
});
|
||||
|
||||
info.el.addEventListener('mouseleave', function() {
|
||||
this.style.transform = 'scale(1)';
|
||||
this.style.zIndex = '1';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -459,108 +326,78 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const modal = document.getElementById('eventDetailModal');
|
||||
const content = document.getElementById('modalContent');
|
||||
|
||||
const startTime = new Date(event.start).toLocaleString('de-DE', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
const startTime = new Date(event.start).toLocaleString('de-DE');
|
||||
|
||||
let detailsHtml = `
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center p-4 bg-gray-50 dark:bg-gray-700 rounded-xl">
|
||||
<span class="w-6 h-6 rounded-lg mr-4 shadow-md" style="background-color: ${event.backgroundColor}"></span>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center">
|
||||
<span class="w-4 h-4 rounded mr-3" style="background-color: ${event.backgroundColor}"></span>
|
||||
<span class="font-medium">${event.title}</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<h4 class="font-semibold text-lg text-gray-900 dark:text-gray-100">${event.title}</h4>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">${startTime}</p>
|
||||
</div>
|
||||
<span class="font-medium text-gray-600">Zeitpunkt:</span>
|
||||
<p>${startTime}</p>
|
||||
</div>
|
||||
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-printer mr-1"></i>
|
||||
Drucker:
|
||||
</div>
|
||||
<div class="detail-value">${props.printer_name}</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Drucker:</span>
|
||||
<p>${props.printer_name}</p>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Status:
|
||||
</div>
|
||||
<div class="detail-value capitalize">${props.status}</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Status:</span>
|
||||
<p class="capitalize">${props.status}</p>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-source mr-1"></i>
|
||||
Quelle:
|
||||
</div>
|
||||
<div class="detail-value capitalize">${props.source}</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Quelle:</span>
|
||||
<p class="capitalize">${props.source}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (props.user_name) {
|
||||
detailsHtml += `
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-user mr-1"></i>
|
||||
Benutzer:
|
||||
</div>
|
||||
<div class="detail-value">${props.user_name}</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Benutzer:</span>
|
||||
<p>${props.user_name}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (props.response_time_ms) {
|
||||
detailsHtml += `
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-clock mr-1"></i>
|
||||
Antwortzeit:
|
||||
</div>
|
||||
<div class="detail-value">${props.response_time_ms}ms</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Antwortzeit:</span>
|
||||
<p>${props.response_time_ms}ms</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (props.power_consumption) {
|
||||
detailsHtml += `
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-bolt mr-1"></i>
|
||||
Verbrauch:
|
||||
</div>
|
||||
<div class="detail-value">${props.power_consumption}W</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Verbrauch:</span>
|
||||
<p>${props.power_consumption}W</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (props.voltage) {
|
||||
detailsHtml += `
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-tachometer-alt mr-1"></i>
|
||||
Spannung:
|
||||
</div>
|
||||
<div class="detail-value">${props.voltage}V</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Spannung:</span>
|
||||
<p>${props.voltage}V</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (props.current) {
|
||||
detailsHtml += `
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">
|
||||
<i class="fas fa-wave-square mr-1"></i>
|
||||
Strom:
|
||||
</div>
|
||||
<div class="detail-value">${props.current}A</div>
|
||||
<div>
|
||||
<span class="font-medium text-gray-600">Strom:</span>
|
||||
<p>${props.current}A</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -569,78 +406,39 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
if (props.notes) {
|
||||
detailsHtml += `
|
||||
<div class="mt-6">
|
||||
<div class="notes-section">
|
||||
<div class="flex items-start gap-2">
|
||||
<i class="fas fa-sticky-note mt-0.5"></i>
|
||||
<div>
|
||||
<span class="font-medium">Notizen:</span>
|
||||
<p class="mt-1">${props.notes}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<span class="font-medium text-gray-600">Notizen:</span>
|
||||
<p class="text-sm bg-gray-50 p-2 rounded mt-1">${props.notes}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (props.error_message) {
|
||||
detailsHtml += `
|
||||
<div class="mt-6">
|
||||
<div class="error-message">
|
||||
<div class="flex items-start gap-2">
|
||||
<i class="fas fa-exclamation-triangle mt-0.5"></i>
|
||||
<div>
|
||||
<span class="font-medium">Fehlermeldung:</span>
|
||||
<p class="mt-1">${props.error_message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<span class="font-medium text-red-600">Fehlermeldung:</span>
|
||||
<p class="text-sm bg-red-50 text-red-700 p-2 rounded mt-1">${props.error_message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
content.innerHTML = detailsHtml;
|
||||
modal.classList.remove('hidden');
|
||||
|
||||
// Animation
|
||||
setTimeout(() => {
|
||||
modal.style.opacity = '1';
|
||||
modal.querySelector('.modal-content').style.transform = 'scale(1)';
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// Modal schließen
|
||||
function closeModal() {
|
||||
const modal = document.getElementById('eventDetailModal');
|
||||
modal.style.opacity = '0';
|
||||
modal.querySelector('.modal-content').style.transform = 'scale(0.95)';
|
||||
|
||||
setTimeout(() => {
|
||||
modal.classList.add('hidden');
|
||||
}, 300);
|
||||
document.getElementById('eventDetailModal').classList.add('hidden');
|
||||
}
|
||||
|
||||
// Event-Listener
|
||||
document.getElementById('closeModal').addEventListener('click', closeModal);
|
||||
document.getElementById('closeModalBtn').addEventListener('click', closeModal);
|
||||
|
||||
// Modal schließen bei Klick außerhalb
|
||||
document.getElementById('eventDetailModal').addEventListener('click', function(e) {
|
||||
if (e.target === this) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Drucker-Filter
|
||||
document.getElementById('printerFilter').addEventListener('change', function() {
|
||||
currentPrinterFilter = this.value;
|
||||
calendar.refetchEvents();
|
||||
|
||||
// Visual Feedback
|
||||
this.style.transform = 'scale(1.02)';
|
||||
setTimeout(() => {
|
||||
this.style.transform = 'scale(1)';
|
||||
}, 150);
|
||||
});
|
||||
|
||||
// Ansicht-Buttons
|
||||
@ -668,29 +466,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Aktualisieren-Button
|
||||
document.getElementById('refreshData').addEventListener('click', function() {
|
||||
// Loading-Animation
|
||||
const originalContent = this.innerHTML;
|
||||
this.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Lädt...';
|
||||
this.disabled = true;
|
||||
|
||||
calendar.refetchEvents();
|
||||
loadStatistics();
|
||||
|
||||
// Reset nach 2 Sekunden
|
||||
setTimeout(() => {
|
||||
this.innerHTML = originalContent;
|
||||
this.disabled = false;
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
// Cleanup-Button
|
||||
document.getElementById('cleanupLogs').addEventListener('click', function() {
|
||||
if (confirm('Möchten Sie wirklich alte Logs löschen? (älter als 30 Tage)\n\nDieser Vorgang kann nicht rückgängig gemacht werden.')) {
|
||||
// Loading-Animation
|
||||
const originalContent = this.innerHTML;
|
||||
this.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Lösche...';
|
||||
this.disabled = true;
|
||||
|
||||
if (confirm('Möchten Sie wirklich alte Logs löschen? (älter als 30 Tage)')) {
|
||||
fetch('/api/admin/plug-schedules/cleanup', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -702,20 +484,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(`✅ Erfolgreich ${data.deleted_count} alte Einträge gelöscht`);
|
||||
alert(`Erfolgreich ${data.deleted_count} alte Einträge gelöscht`);
|
||||
calendar.refetchEvents();
|
||||
loadStatistics();
|
||||
} else {
|
||||
alert('❌ Fehler beim Löschen: ' + data.error);
|
||||
alert('Fehler beim Löschen: ' + data.error);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Fehler:', error);
|
||||
alert('❌ Fehler beim Löschen der Logs');
|
||||
})
|
||||
.finally(() => {
|
||||
this.innerHTML = originalContent;
|
||||
this.disabled = false;
|
||||
alert('Fehler beim Löschen der Logs');
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -727,15 +505,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const stats = data.statistics;
|
||||
|
||||
// Animierte Zahlen-Updates
|
||||
animateValue('totalLogs', stats.total_logs || 0);
|
||||
animateValue('successRate', (100 - (stats.error_rate || 0)), '%');
|
||||
animateValue('errorCount', stats.error_count || 0);
|
||||
|
||||
const avgTime = stats.average_response_time_ms;
|
||||
document.getElementById('totalLogs').textContent = stats.total_logs || 0;
|
||||
document.getElementById('successRate').textContent = (100 - (stats.error_rate || 0)).toFixed(1) + '%';
|
||||
document.getElementById('avgResponseTime').textContent =
|
||||
avgTime ? Math.round(avgTime) + 'ms' : 'N/A';
|
||||
stats.average_response_time_ms ? Math.round(stats.average_response_time_ms) + 'ms' : 'N/A';
|
||||
document.getElementById('errorCount').textContent = stats.error_count || 0;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@ -743,37 +517,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
// Animierte Werte-Updates
|
||||
function animateValue(elementId, endValue, suffix = '') {
|
||||
const element = document.getElementById(elementId);
|
||||
const startValue = parseInt(element.textContent) || 0;
|
||||
const difference = endValue - startValue;
|
||||
const duration = 1000; // 1 Sekunde
|
||||
const steps = 30;
|
||||
const stepValue = difference / steps;
|
||||
const stepDuration = duration / steps;
|
||||
|
||||
let currentValue = startValue;
|
||||
let currentStep = 0;
|
||||
|
||||
const timer = setInterval(() => {
|
||||
currentStep++;
|
||||
currentValue += stepValue;
|
||||
|
||||
if (currentStep >= steps) {
|
||||
currentValue = endValue;
|
||||
clearInterval(timer);
|
||||
}
|
||||
|
||||
element.textContent = Math.round(currentValue) + suffix;
|
||||
}, stepDuration);
|
||||
}
|
||||
|
||||
// Kalender initialisieren
|
||||
initCalendar();
|
||||
|
||||
// Initiales Laden der Statistiken
|
||||
loadStatistics();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
641
backend/utils/error_recovery.py
Normal file
641
backend/utils/error_recovery.py
Normal file
@ -0,0 +1,641 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Robustes Error-Recovery-System für wartungsfreien Produktionsbetrieb
|
||||
Automatische Fehlererkennung, -behebung und -prävention
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional, Callable, Any
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
import logging
|
||||
import json
|
||||
import subprocess
|
||||
import psutil
|
||||
from contextlib import contextmanager
|
||||
import signal
|
||||
|
||||
# Logging-Setup
|
||||
try:
|
||||
from utils.logging_config import get_logger
|
||||
recovery_logger = get_logger("error_recovery")
|
||||
except ImportError:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
recovery_logger = logging.getLogger("error_recovery")
|
||||
|
||||
|
||||
class ErrorSeverity(Enum):
|
||||
"""Schweregrade von Fehlern"""
|
||||
LOW = "low"
|
||||
MEDIUM = "medium"
|
||||
HIGH = "high"
|
||||
CRITICAL = "critical"
|
||||
|
||||
|
||||
class RecoveryAction(Enum):
|
||||
"""Verfügbare Recovery-Aktionen"""
|
||||
LOG_ONLY = "log_only"
|
||||
RESTART_SERVICE = "restart_service"
|
||||
RESTART_COMPONENT = "restart_component"
|
||||
CLEAR_CACHE = "clear_cache"
|
||||
RESET_DATABASE = "reset_database"
|
||||
RESTART_SYSTEM = "restart_system"
|
||||
EMERGENCY_STOP = "emergency_stop"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ErrorPattern:
|
||||
"""Definiert ein Fehlermuster und zugehörige Recovery-Aktionen"""
|
||||
name: str
|
||||
patterns: List[str] # Regex-Patterns für Fehlererkennung
|
||||
severity: ErrorSeverity
|
||||
actions: List[RecoveryAction]
|
||||
max_occurrences: int = 3 # Maximale Anzahl vor Eskalation
|
||||
time_window: int = 300 # Zeitfenster in Sekunden
|
||||
escalation_actions: List[RecoveryAction] = field(default_factory=list)
|
||||
description: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class ErrorOccurrence:
|
||||
"""Einzelnes Auftreten eines Fehlers"""
|
||||
timestamp: datetime
|
||||
pattern_name: str
|
||||
error_message: str
|
||||
severity: ErrorSeverity
|
||||
context: Dict[str, Any] = field(default_factory=dict)
|
||||
recovery_attempted: List[RecoveryAction] = field(default_factory=list)
|
||||
recovery_successful: bool = False
|
||||
|
||||
|
||||
class ErrorRecoveryManager:
|
||||
"""
|
||||
Zentraler Manager für automatische Fehlererkennung und -behebung.
|
||||
Überwacht kontinuierlich das System und führt automatische Recovery durch.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.is_active = False
|
||||
self.error_patterns: Dict[str, ErrorPattern] = {}
|
||||
self.error_history: List[ErrorOccurrence] = []
|
||||
self.recovery_handlers: Dict[RecoveryAction, Callable] = {}
|
||||
self.monitoring_thread: Optional[threading.Thread] = None
|
||||
self.lock = threading.Lock()
|
||||
|
||||
# Konfiguration
|
||||
self.config = {
|
||||
"check_interval": 30, # Sekunden
|
||||
"max_history_size": 1000,
|
||||
"auto_recovery_enabled": True,
|
||||
"critical_error_threshold": 5,
|
||||
"system_restart_threshold": 10,
|
||||
"log_file_paths": [
|
||||
"logs/app/app.log",
|
||||
"logs/errors/errors.log",
|
||||
"logs/database/database.log"
|
||||
]
|
||||
}
|
||||
|
||||
# Initialisiere Standard-Fehlermuster
|
||||
self._init_default_patterns()
|
||||
|
||||
# Initialisiere Recovery-Handler
|
||||
self._init_recovery_handlers()
|
||||
|
||||
recovery_logger.info("🛡️ Error-Recovery-Manager initialisiert")
|
||||
|
||||
def _init_default_patterns(self):
|
||||
"""Initialisiert Standard-Fehlermuster für häufige Probleme"""
|
||||
patterns = [
|
||||
# Datenbank-Fehler
|
||||
ErrorPattern(
|
||||
name="database_lock",
|
||||
patterns=[
|
||||
r"database is locked",
|
||||
r"SQLite.*locked",
|
||||
r"OperationalError.*locked"
|
||||
],
|
||||
severity=ErrorSeverity.HIGH,
|
||||
actions=[RecoveryAction.RESET_DATABASE],
|
||||
max_occurrences=3,
|
||||
escalation_actions=[RecoveryAction.RESTART_SERVICE],
|
||||
description="Datenbank-Sperrung"
|
||||
),
|
||||
|
||||
# Memory-Fehler
|
||||
ErrorPattern(
|
||||
name="memory_exhausted",
|
||||
patterns=[
|
||||
r"MemoryError",
|
||||
r"Out of memory",
|
||||
r"Cannot allocate memory"
|
||||
],
|
||||
severity=ErrorSeverity.CRITICAL,
|
||||
actions=[RecoveryAction.CLEAR_CACHE, RecoveryAction.RESTART_SERVICE],
|
||||
max_occurrences=2,
|
||||
escalation_actions=[RecoveryAction.RESTART_SYSTEM],
|
||||
description="Speicher erschöpft"
|
||||
),
|
||||
|
||||
# Network-Fehler
|
||||
ErrorPattern(
|
||||
name="connection_error",
|
||||
patterns=[
|
||||
r"ConnectionError",
|
||||
r"Network is unreachable",
|
||||
r"Connection refused"
|
||||
],
|
||||
severity=ErrorSeverity.MEDIUM,
|
||||
actions=[RecoveryAction.RESTART_COMPONENT],
|
||||
max_occurrences=5,
|
||||
escalation_actions=[RecoveryAction.RESTART_SERVICE],
|
||||
description="Netzwerk-Verbindungsfehler"
|
||||
),
|
||||
|
||||
# Kiosk-Fehler
|
||||
ErrorPattern(
|
||||
name="kiosk_crash",
|
||||
patterns=[
|
||||
r"chromium.*crashed",
|
||||
r"firefox.*crashed",
|
||||
r"X11.*error",
|
||||
r"Display.*not found"
|
||||
],
|
||||
severity=ErrorSeverity.HIGH,
|
||||
actions=[RecoveryAction.RESTART_COMPONENT],
|
||||
max_occurrences=3,
|
||||
escalation_actions=[RecoveryAction.RESTART_SYSTEM],
|
||||
description="Kiosk-Display Fehler"
|
||||
),
|
||||
|
||||
# Service-Fehler
|
||||
ErrorPattern(
|
||||
name="service_failure",
|
||||
patterns=[
|
||||
r"systemctl.*failed",
|
||||
r"Service.*not found",
|
||||
r"Failed to start"
|
||||
],
|
||||
severity=ErrorSeverity.HIGH,
|
||||
actions=[RecoveryAction.RESTART_SERVICE],
|
||||
max_occurrences=3,
|
||||
escalation_actions=[RecoveryAction.RESTART_SYSTEM],
|
||||
description="System-Service Fehler"
|
||||
),
|
||||
|
||||
# Disk-Fehler
|
||||
ErrorPattern(
|
||||
name="disk_full",
|
||||
patterns=[
|
||||
r"No space left on device",
|
||||
r"Disk full",
|
||||
r"OSError.*28"
|
||||
],
|
||||
severity=ErrorSeverity.CRITICAL,
|
||||
actions=[RecoveryAction.CLEAR_CACHE],
|
||||
max_occurrences=1,
|
||||
escalation_actions=[RecoveryAction.EMERGENCY_STOP],
|
||||
description="Festplatte voll"
|
||||
),
|
||||
|
||||
# Flask-Fehler
|
||||
ErrorPattern(
|
||||
name="flask_error",
|
||||
patterns=[
|
||||
r"Internal Server Error",
|
||||
r"500 Internal Server Error",
|
||||
r"Application failed to start"
|
||||
],
|
||||
severity=ErrorSeverity.HIGH,
|
||||
actions=[RecoveryAction.RESTART_SERVICE],
|
||||
max_occurrences=3,
|
||||
escalation_actions=[RecoveryAction.RESTART_SYSTEM],
|
||||
description="Flask-Anwendungsfehler"
|
||||
)
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
self.error_patterns[pattern.name] = pattern
|
||||
|
||||
def _init_recovery_handlers(self):
|
||||
"""Initialisiert Handler für Recovery-Aktionen"""
|
||||
self.recovery_handlers = {
|
||||
RecoveryAction.LOG_ONLY: self._handle_log_only,
|
||||
RecoveryAction.RESTART_SERVICE: self._handle_restart_service,
|
||||
RecoveryAction.RESTART_COMPONENT: self._handle_restart_component,
|
||||
RecoveryAction.CLEAR_CACHE: self._handle_clear_cache,
|
||||
RecoveryAction.RESET_DATABASE: self._handle_reset_database,
|
||||
RecoveryAction.RESTART_SYSTEM: self._handle_restart_system,
|
||||
RecoveryAction.EMERGENCY_STOP: self._handle_emergency_stop
|
||||
}
|
||||
|
||||
def start_monitoring(self):
|
||||
"""Startet kontinuierliche Überwachung"""
|
||||
if self.is_active:
|
||||
recovery_logger.warning("Monitoring bereits aktiv")
|
||||
return
|
||||
|
||||
self.is_active = True
|
||||
self.monitoring_thread = threading.Thread(
|
||||
target=self._monitor_loop,
|
||||
daemon=True,
|
||||
name="ErrorRecoveryMonitor"
|
||||
)
|
||||
self.monitoring_thread.start()
|
||||
recovery_logger.info("🔍 Error-Monitoring gestartet")
|
||||
|
||||
def stop_monitoring(self):
|
||||
"""Stoppt Überwachung"""
|
||||
self.is_active = False
|
||||
if self.monitoring_thread and self.monitoring_thread.is_alive():
|
||||
self.monitoring_thread.join(timeout=5)
|
||||
recovery_logger.info("🛑 Error-Monitoring gestoppt")
|
||||
|
||||
def _monitor_loop(self):
|
||||
"""Hauptschleife für kontinuierliche Überwachung"""
|
||||
while self.is_active:
|
||||
try:
|
||||
# Log-Dateien prüfen
|
||||
self._check_log_files()
|
||||
|
||||
# System-Metriken prüfen
|
||||
self._check_system_metrics()
|
||||
|
||||
# Service-Status prüfen
|
||||
self._check_service_status()
|
||||
|
||||
# Alte Einträge bereinigen
|
||||
self._cleanup_old_entries()
|
||||
|
||||
time.sleep(self.config["check_interval"])
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"Fehler in Monitor-Loop: {e}")
|
||||
time.sleep(5) # Kurze Pause bei Fehlern
|
||||
|
||||
def _check_log_files(self):
|
||||
"""Prüft Log-Dateien auf Fehlermuster"""
|
||||
for log_path in self.config["log_file_paths"]:
|
||||
try:
|
||||
if not os.path.exists(log_path):
|
||||
continue
|
||||
|
||||
# Lese nur neue Zeilen (vereinfacht)
|
||||
with open(log_path, 'r', encoding='utf-8') as f:
|
||||
# Gehe zu den letzten 1000 Zeilen
|
||||
lines = f.readlines()
|
||||
recent_lines = lines[-1000:] if len(lines) > 1000 else lines
|
||||
|
||||
for line in recent_lines:
|
||||
self._analyze_log_line(line, log_path)
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.debug(f"Fehler beim Lesen von {log_path}: {e}")
|
||||
|
||||
def _analyze_log_line(self, line: str, source: str):
|
||||
"""Analysiert einzelne Log-Zeile auf Fehlermuster"""
|
||||
import re
|
||||
|
||||
for pattern_name, pattern in self.error_patterns.items():
|
||||
for regex in pattern.patterns:
|
||||
try:
|
||||
if re.search(regex, line, re.IGNORECASE):
|
||||
self._handle_error_detection(
|
||||
pattern_name=pattern_name,
|
||||
error_message=line.strip(),
|
||||
context={"source": source, "pattern": regex}
|
||||
)
|
||||
break
|
||||
except Exception as e:
|
||||
recovery_logger.debug(f"Regex-Fehler für {regex}: {e}")
|
||||
|
||||
def _check_system_metrics(self):
|
||||
"""Prüft System-Metriken auf kritische Werte"""
|
||||
try:
|
||||
# Memory-Check
|
||||
memory = psutil.virtual_memory()
|
||||
if memory.percent > 95:
|
||||
self._handle_error_detection(
|
||||
pattern_name="memory_exhausted",
|
||||
error_message=f"Speicherverbrauch kritisch: {memory.percent:.1f}%",
|
||||
context={"memory_percent": memory.percent}
|
||||
)
|
||||
|
||||
# Disk-Check
|
||||
disk = psutil.disk_usage('/')
|
||||
if disk.percent > 98:
|
||||
self._handle_error_detection(
|
||||
pattern_name="disk_full",
|
||||
error_message=f"Festplatte fast voll: {disk.percent:.1f}%",
|
||||
context={"disk_percent": disk.percent}
|
||||
)
|
||||
|
||||
# Load-Check
|
||||
if hasattr(psutil, 'getloadavg'):
|
||||
load_avg = psutil.getloadavg()[0]
|
||||
if load_avg > 5.0: # Sehr hohe Last
|
||||
self._handle_error_detection(
|
||||
pattern_name="system_overload",
|
||||
error_message=f"System-Last kritisch: {load_avg:.2f}",
|
||||
context={"load_average": load_avg}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.debug(f"System-Metrics-Check fehlgeschlagen: {e}")
|
||||
|
||||
def _check_service_status(self):
|
||||
"""Prüft Status wichtiger Services"""
|
||||
services = ["myp-https.service", "myp-kiosk.service"]
|
||||
|
||||
for service in services:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["sudo", "systemctl", "is-active", service],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
self._handle_error_detection(
|
||||
pattern_name="service_failure",
|
||||
error_message=f"Service {service} nicht aktiv: {result.stdout.strip()}",
|
||||
context={"service": service, "status": result.stdout.strip()}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.debug(f"Service-Check für {service} fehlgeschlagen: {e}")
|
||||
|
||||
def _handle_error_detection(self, pattern_name: str, error_message: str, context: Dict[str, Any] = None):
|
||||
"""Behandelt erkannten Fehler und startet Recovery"""
|
||||
with self.lock:
|
||||
if pattern_name not in self.error_patterns:
|
||||
recovery_logger.warning(f"Unbekanntes Fehlermuster: {pattern_name}")
|
||||
return
|
||||
|
||||
pattern = self.error_patterns[pattern_name]
|
||||
|
||||
# Prüfe ob bereits kürzlich aufgetreten
|
||||
recent_occurrences = self._count_recent_occurrences(pattern_name, pattern.time_window)
|
||||
|
||||
# Erstelle Error-Occurrence
|
||||
occurrence = ErrorOccurrence(
|
||||
timestamp=datetime.now(),
|
||||
pattern_name=pattern_name,
|
||||
error_message=error_message,
|
||||
severity=pattern.severity,
|
||||
context=context or {}
|
||||
)
|
||||
|
||||
self.error_history.append(occurrence)
|
||||
|
||||
recovery_logger.warning(f"🚨 Fehler erkannt: {pattern_name} - {error_message}")
|
||||
|
||||
# Entscheide über Recovery-Aktionen
|
||||
if recent_occurrences >= pattern.max_occurrences:
|
||||
# Eskalation
|
||||
actions = pattern.escalation_actions
|
||||
recovery_logger.error(f"🔥 Eskalation für {pattern_name}: {recent_occurrences} Vorkommen in {pattern.time_window}s")
|
||||
else:
|
||||
# Normale Recovery
|
||||
actions = pattern.actions
|
||||
|
||||
# Führe Recovery-Aktionen aus
|
||||
if self.config["auto_recovery_enabled"]:
|
||||
self._execute_recovery_actions(occurrence, actions)
|
||||
|
||||
def _count_recent_occurrences(self, pattern_name: str, time_window: int) -> int:
|
||||
"""Zählt kürzliche Vorkommen eines Fehlermusters"""
|
||||
cutoff_time = datetime.now() - timedelta(seconds=time_window)
|
||||
return sum(1 for err in self.error_history
|
||||
if err.pattern_name == pattern_name and err.timestamp > cutoff_time)
|
||||
|
||||
def _execute_recovery_actions(self, occurrence: ErrorOccurrence, actions: List[RecoveryAction]):
|
||||
"""Führt Recovery-Aktionen aus"""
|
||||
for action in actions:
|
||||
try:
|
||||
recovery_logger.info(f"🔧 Führe Recovery-Aktion aus: {action.value}")
|
||||
|
||||
handler = self.recovery_handlers.get(action)
|
||||
if handler:
|
||||
success = handler(occurrence)
|
||||
occurrence.recovery_attempted.append(action)
|
||||
|
||||
if success:
|
||||
occurrence.recovery_successful = True
|
||||
recovery_logger.info(f"✅ Recovery erfolgreich: {action.value}")
|
||||
break # Stoppe bei erfolgreicher Recovery
|
||||
else:
|
||||
recovery_logger.warning(f"❌ Recovery fehlgeschlagen: {action.value}")
|
||||
else:
|
||||
recovery_logger.error(f"Kein Handler für Recovery-Aktion: {action.value}")
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"Fehler bei Recovery-Aktion {action.value}: {e}")
|
||||
|
||||
def _handle_log_only(self, occurrence: ErrorOccurrence) -> bool:
|
||||
"""Handler: Nur Logging, keine weitere Aktion"""
|
||||
recovery_logger.info(f"📝 Log-Only für: {occurrence.error_message}")
|
||||
return True
|
||||
|
||||
def _handle_restart_service(self, occurrence: ErrorOccurrence) -> bool:
|
||||
"""Handler: Service-Neustart"""
|
||||
try:
|
||||
from utils.system_control import get_system_control_manager, SystemOperation
|
||||
|
||||
manager = get_system_control_manager()
|
||||
result = manager.schedule_operation(
|
||||
SystemOperation.SERVICE_RESTART,
|
||||
delay_seconds=5,
|
||||
reason=f"Automatische Recovery für: {occurrence.pattern_name}"
|
||||
)
|
||||
|
||||
return result.get("success", False)
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"Service-Neustart fehlgeschlagen: {e}")
|
||||
return False
|
||||
|
||||
def _handle_restart_component(self, occurrence: ErrorOccurrence) -> bool:
|
||||
"""Handler: Komponenten-Neustart (z.B. Kiosk)"""
|
||||
try:
|
||||
from utils.system_control import get_system_control_manager, SystemOperation
|
||||
|
||||
manager = get_system_control_manager()
|
||||
result = manager.schedule_operation(
|
||||
SystemOperation.KIOSK_RESTART,
|
||||
delay_seconds=5,
|
||||
reason=f"Automatische Recovery für: {occurrence.pattern_name}"
|
||||
)
|
||||
|
||||
return result.get("success", False)
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"Komponenten-Neustart fehlgeschlagen: {e}")
|
||||
return False
|
||||
|
||||
def _handle_clear_cache(self, occurrence: ErrorOccurrence) -> bool:
|
||||
"""Handler: Cache leeren"""
|
||||
try:
|
||||
# App-Caches leeren
|
||||
from app import clear_user_cache, clear_printer_status_cache
|
||||
clear_user_cache()
|
||||
clear_printer_status_cache()
|
||||
|
||||
# System-Cache leeren
|
||||
if os.name != 'nt':
|
||||
subprocess.run(["sudo", "sync"], timeout=10)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"Cache-Clearing fehlgeschlagen: {e}")
|
||||
return False
|
||||
|
||||
def _handle_reset_database(self, occurrence: ErrorOccurrence) -> bool:
|
||||
"""Handler: Datenbank-Reset"""
|
||||
try:
|
||||
from utils.database_cleanup import safe_database_cleanup
|
||||
|
||||
result = safe_database_cleanup(force_mode_switch=True)
|
||||
return result.get("success", False)
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"Database-Reset fehlgeschlagen: {e}")
|
||||
return False
|
||||
|
||||
def _handle_restart_system(self, occurrence: ErrorOccurrence) -> bool:
|
||||
"""Handler: System-Neustart"""
|
||||
try:
|
||||
from utils.system_control import schedule_system_restart
|
||||
|
||||
result = schedule_system_restart(
|
||||
delay_seconds=60,
|
||||
reason=f"Automatische Recovery für kritischen Fehler: {occurrence.pattern_name}",
|
||||
force=True
|
||||
)
|
||||
|
||||
return result.get("success", False)
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"System-Neustart fehlgeschlagen: {e}")
|
||||
return False
|
||||
|
||||
def _handle_emergency_stop(self, occurrence: ErrorOccurrence) -> bool:
|
||||
"""Handler: Notfall-Stopp"""
|
||||
try:
|
||||
recovery_logger.critical(f"🚨 NOTFALL-STOPP: {occurrence.error_message}")
|
||||
|
||||
# Führe sofortigen Shutdown durch
|
||||
from utils.shutdown_manager import get_shutdown_manager
|
||||
shutdown_manager = get_shutdown_manager()
|
||||
shutdown_manager.force_shutdown(1)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
recovery_logger.error(f"Notfall-Stopp fehlgeschlagen: {e}")
|
||||
return False
|
||||
|
||||
def _cleanup_old_entries(self):
|
||||
"""Bereinigt alte Error-History-Einträge"""
|
||||
with self.lock:
|
||||
if len(self.error_history) > self.config["max_history_size"]:
|
||||
self.error_history = self.error_history[-self.config["max_history_size"]:]
|
||||
|
||||
def get_error_statistics(self) -> Dict[str, Any]:
|
||||
"""Gibt Fehler-Statistiken zurück"""
|
||||
with self.lock:
|
||||
total_errors = len(self.error_history)
|
||||
|
||||
# Fehler nach Schweregrad
|
||||
by_severity = {}
|
||||
for severity in ErrorSeverity:
|
||||
by_severity[severity.value] = sum(1 for err in self.error_history
|
||||
if err.severity == severity)
|
||||
|
||||
# Fehler nach Pattern
|
||||
by_pattern = {}
|
||||
for pattern_name in self.error_patterns.keys():
|
||||
by_pattern[pattern_name] = sum(1 for err in self.error_history
|
||||
if err.pattern_name == pattern_name)
|
||||
|
||||
# Letzten 24h
|
||||
last_24h = datetime.now() - timedelta(hours=24)
|
||||
recent_errors = sum(1 for err in self.error_history
|
||||
if err.timestamp > last_24h)
|
||||
|
||||
# Recovery-Erfolgsrate
|
||||
attempted_recoveries = sum(1 for err in self.error_history
|
||||
if err.recovery_attempted)
|
||||
successful_recoveries = sum(1 for err in self.error_history
|
||||
if err.recovery_successful)
|
||||
|
||||
success_rate = (successful_recoveries / attempted_recoveries * 100) if attempted_recoveries > 0 else 0
|
||||
|
||||
return {
|
||||
"total_errors": total_errors,
|
||||
"errors_last_24h": recent_errors,
|
||||
"by_severity": by_severity,
|
||||
"by_pattern": by_pattern,
|
||||
"recovery_success_rate": round(success_rate, 1),
|
||||
"monitoring_active": self.is_active,
|
||||
"auto_recovery_enabled": self.config["auto_recovery_enabled"]
|
||||
}
|
||||
|
||||
def get_recent_errors(self, limit: int = 50) -> List[Dict[str, Any]]:
|
||||
"""Gibt kürzliche Fehler zurück"""
|
||||
with self.lock:
|
||||
recent = self.error_history[-limit:] if limit else self.error_history
|
||||
|
||||
return [{
|
||||
"timestamp": err.timestamp.isoformat(),
|
||||
"pattern_name": err.pattern_name,
|
||||
"error_message": err.error_message,
|
||||
"severity": err.severity.value,
|
||||
"context": err.context,
|
||||
"recovery_attempted": [action.value for action in err.recovery_attempted],
|
||||
"recovery_successful": err.recovery_successful
|
||||
} for err in recent]
|
||||
|
||||
|
||||
# Globaler Error-Recovery-Manager
|
||||
_error_recovery_manager: Optional[ErrorRecoveryManager] = None
|
||||
_recovery_lock = threading.Lock()
|
||||
|
||||
|
||||
def get_error_recovery_manager() -> ErrorRecoveryManager:
|
||||
"""
|
||||
Singleton-Pattern für globalen Error-Recovery-Manager.
|
||||
|
||||
Returns:
|
||||
ErrorRecoveryManager: Globaler Error-Recovery-Manager
|
||||
"""
|
||||
global _error_recovery_manager
|
||||
|
||||
with _recovery_lock:
|
||||
if _error_recovery_manager is None:
|
||||
_error_recovery_manager = ErrorRecoveryManager()
|
||||
return _error_recovery_manager
|
||||
|
||||
|
||||
def start_error_monitoring():
|
||||
"""Startet Error-Monitoring"""
|
||||
manager = get_error_recovery_manager()
|
||||
manager.start_monitoring()
|
||||
|
||||
|
||||
def stop_error_monitoring():
|
||||
"""Stoppt Error-Monitoring"""
|
||||
manager = get_error_recovery_manager()
|
||||
manager.stop_monitoring()
|
||||
|
||||
|
||||
def force_error_check(log_message: str = None):
|
||||
"""Erzwingt manuelle Fehlerprüfung"""
|
||||
if log_message:
|
||||
manager = get_error_recovery_manager()
|
||||
manager._analyze_log_line(log_message, "manual_check")
|
658
backend/utils/system_control.py
Normal file
658
backend/utils/system_control.py
Normal file
@ -0,0 +1,658 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Robuste System-Control-Funktionen für wartungsfreien Produktionsbetrieb
|
||||
Bietet sichere Restart-, Shutdown- und Kiosk-Verwaltungsfunktionen
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
import signal
|
||||
import psutil
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional, Tuple, Any
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import json
|
||||
from contextlib import contextmanager
|
||||
from enum import Enum
|
||||
|
||||
# Logging-Setup
|
||||
try:
|
||||
from utils.logging_config import get_logger
|
||||
system_logger = get_logger("system_control")
|
||||
except ImportError:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
system_logger = logging.getLogger("system_control")
|
||||
|
||||
|
||||
class SystemOperation(Enum):
|
||||
"""Verfügbare System-Operationen"""
|
||||
RESTART = "restart"
|
||||
SHUTDOWN = "shutdown"
|
||||
KIOSK_RESTART = "kiosk_restart"
|
||||
KIOSK_ENABLE = "kiosk_enable"
|
||||
KIOSK_DISABLE = "kiosk_disable"
|
||||
SERVICE_RESTART = "service_restart"
|
||||
EMERGENCY_STOP = "emergency_stop"
|
||||
|
||||
|
||||
class SystemControlManager:
|
||||
"""
|
||||
Zentraler Manager für alle System-Control-Operationen.
|
||||
Bietet sichere und robuste Funktionen für wartungsfreien Betrieb.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.is_windows = os.name == 'nt'
|
||||
self.pending_operations: Dict[str, Dict] = {}
|
||||
self.operation_history: List[Dict] = []
|
||||
self.lock = threading.Lock()
|
||||
|
||||
# Konfiguration
|
||||
self.config = {
|
||||
"restart_delay": 60, # Sekunden
|
||||
"shutdown_delay": 30, # Sekunden
|
||||
"kiosk_restart_delay": 10, # Sekunden
|
||||
"max_operation_history": 100,
|
||||
"safety_checks": True,
|
||||
"require_confirmation": True
|
||||
}
|
||||
|
||||
# Service-Namen für verschiedene Plattformen
|
||||
self.services = {
|
||||
"https": "myp-https.service",
|
||||
"kiosk": "myp-kiosk.service",
|
||||
"watchdog": "kiosk-watchdog.service"
|
||||
}
|
||||
|
||||
system_logger.info("🔧 System-Control-Manager initialisiert")
|
||||
|
||||
def is_safe_to_operate(self) -> Tuple[bool, str]:
|
||||
"""
|
||||
Prüft ob System-Operationen sicher ausgeführt werden können.
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (is_safe, reason)
|
||||
"""
|
||||
try:
|
||||
# Prüfe Systemlast
|
||||
load_avg = psutil.getloadavg()[0] if hasattr(psutil, 'getloadavg') else 0
|
||||
if load_avg > 2.0:
|
||||
return False, f"Hohe Systemlast: {load_avg:.2f}"
|
||||
|
||||
# Prüfe verfügbaren Speicher
|
||||
memory = psutil.virtual_memory()
|
||||
if memory.percent > 90:
|
||||
return False, f"Wenig verfügbarer Speicher: {memory.percent:.1f}% belegt"
|
||||
|
||||
# Prüfe aktive Drucker-Jobs
|
||||
try:
|
||||
from models import get_db_session, Job
|
||||
db_session = get_db_session()
|
||||
active_jobs = db_session.query(Job).filter(
|
||||
Job.status.in_(["printing", "queued", "preparing"])
|
||||
).count()
|
||||
db_session.close()
|
||||
|
||||
if active_jobs > 0:
|
||||
return False, f"Aktive Druckjobs: {active_jobs}"
|
||||
except Exception as e:
|
||||
system_logger.warning(f"Job-Prüfung fehlgeschlagen: {e}")
|
||||
|
||||
# Prüfe kritische Prozesse
|
||||
critical_processes = ["chromium", "firefox", "python"]
|
||||
for proc in psutil.process_iter(['pid', 'name', 'cpu_percent']):
|
||||
try:
|
||||
if any(crit in proc.info['name'].lower() for crit in critical_processes):
|
||||
if proc.info['cpu_percent'] > 80:
|
||||
return False, f"Kritischer Prozess unter hoher Last: {proc.info['name']}"
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
|
||||
return True, "System ist sicher für Operationen"
|
||||
|
||||
except Exception as e:
|
||||
system_logger.error(f"Fehler bei Sicherheitsprüfung: {e}")
|
||||
return False, f"Sicherheitsprüfung fehlgeschlagen: {e}"
|
||||
|
||||
def schedule_operation(self,
|
||||
operation: SystemOperation,
|
||||
delay_seconds: int = None,
|
||||
user_id: str = None,
|
||||
reason: str = None,
|
||||
force: bool = False) -> Dict[str, Any]:
|
||||
"""
|
||||
Plant eine System-Operation mit Verzögerung.
|
||||
|
||||
Args:
|
||||
operation: Art der Operation
|
||||
delay_seconds: Verzögerung in Sekunden (None = Standard)
|
||||
user_id: ID des anfragenden Benutzers
|
||||
reason: Grund für die Operation
|
||||
force: Sicherheitsprüfungen überspringen
|
||||
|
||||
Returns:
|
||||
Dict mit Operation-Details
|
||||
"""
|
||||
with self.lock:
|
||||
# Sicherheitsprüfung (außer bei Force)
|
||||
if not force and self.config["safety_checks"]:
|
||||
is_safe, safety_reason = self.is_safe_to_operate()
|
||||
if not is_safe:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Operation abgelehnt: {safety_reason}",
|
||||
"safety_check": False
|
||||
}
|
||||
|
||||
# Standard-Verzögerung setzen
|
||||
if delay_seconds is None:
|
||||
delay_seconds = {
|
||||
SystemOperation.RESTART: self.config["restart_delay"],
|
||||
SystemOperation.SHUTDOWN: self.config["shutdown_delay"],
|
||||
SystemOperation.KIOSK_RESTART: self.config["kiosk_restart_delay"],
|
||||
SystemOperation.KIOSK_ENABLE: 5,
|
||||
SystemOperation.KIOSK_DISABLE: 5,
|
||||
SystemOperation.SERVICE_RESTART: 10,
|
||||
SystemOperation.EMERGENCY_STOP: 0
|
||||
}.get(operation, 30)
|
||||
|
||||
# Operations-ID generieren
|
||||
operation_id = f"{operation.value}_{int(time.time())}"
|
||||
scheduled_time = datetime.now() + timedelta(seconds=delay_seconds)
|
||||
|
||||
# Operation speichern
|
||||
operation_data = {
|
||||
"id": operation_id,
|
||||
"operation": operation.value,
|
||||
"scheduled_time": scheduled_time,
|
||||
"delay_seconds": delay_seconds,
|
||||
"user_id": user_id,
|
||||
"reason": reason or "Keine Begründung angegeben",
|
||||
"force": force,
|
||||
"created_at": datetime.now(),
|
||||
"status": "scheduled"
|
||||
}
|
||||
|
||||
self.pending_operations[operation_id] = operation_data
|
||||
|
||||
# Operation in separatem Thread ausführen
|
||||
thread = threading.Thread(
|
||||
target=self._execute_delayed_operation,
|
||||
args=(operation_id,),
|
||||
daemon=True
|
||||
)
|
||||
thread.start()
|
||||
|
||||
system_logger.info(f"🕐 Operation geplant: {operation.value} in {delay_seconds}s")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"operation_id": operation_id,
|
||||
"scheduled_time": scheduled_time.isoformat(),
|
||||
"delay_seconds": delay_seconds,
|
||||
"message": f"Operation '{operation.value}' geplant für {scheduled_time.strftime('%H:%M:%S')}"
|
||||
}
|
||||
|
||||
def _execute_delayed_operation(self, operation_id: str):
|
||||
"""
|
||||
Führt geplante Operation nach Verzögerung aus.
|
||||
|
||||
Args:
|
||||
operation_id: ID der auszuführenden Operation
|
||||
"""
|
||||
try:
|
||||
operation_data = self.pending_operations.get(operation_id)
|
||||
if not operation_data:
|
||||
return
|
||||
|
||||
# Warten bis zur geplanten Zeit
|
||||
scheduled_time = operation_data["scheduled_time"]
|
||||
wait_time = (scheduled_time - datetime.now()).total_seconds()
|
||||
|
||||
if wait_time > 0:
|
||||
time.sleep(wait_time)
|
||||
|
||||
# Status aktualisieren
|
||||
operation_data["status"] = "executing"
|
||||
operation_data["executed_at"] = datetime.now()
|
||||
|
||||
# Operation ausführen
|
||||
operation = SystemOperation(operation_data["operation"])
|
||||
result = self._execute_operation(operation, operation_data)
|
||||
|
||||
# Ergebnis speichern
|
||||
operation_data["result"] = result
|
||||
operation_data["status"] = "completed" if result.get("success") else "failed"
|
||||
operation_data["completed_at"] = datetime.now()
|
||||
|
||||
# In Historie verschieben
|
||||
self._move_to_history(operation_id)
|
||||
|
||||
except Exception as e:
|
||||
system_logger.error(f"Fehler bei verzögerter Operation {operation_id}: {e}")
|
||||
if operation_id in self.pending_operations:
|
||||
self.pending_operations[operation_id]["status"] = "error"
|
||||
self.pending_operations[operation_id]["error"] = str(e)
|
||||
self._move_to_history(operation_id)
|
||||
|
||||
def _execute_operation(self, operation: SystemOperation, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""
|
||||
Führt die eigentliche System-Operation aus.
|
||||
|
||||
Args:
|
||||
operation: Art der Operation
|
||||
operation_data: Operation-Daten
|
||||
|
||||
Returns:
|
||||
Dict mit Ergebnis
|
||||
"""
|
||||
try:
|
||||
system_logger.info(f"▶️ Führe Operation aus: {operation.value}")
|
||||
|
||||
if operation == SystemOperation.RESTART:
|
||||
return self._restart_system(operation_data)
|
||||
elif operation == SystemOperation.SHUTDOWN:
|
||||
return self._shutdown_system(operation_data)
|
||||
elif operation == SystemOperation.KIOSK_RESTART:
|
||||
return self._restart_kiosk(operation_data)
|
||||
elif operation == SystemOperation.KIOSK_ENABLE:
|
||||
return self._enable_kiosk(operation_data)
|
||||
elif operation == SystemOperation.KIOSK_DISABLE:
|
||||
return self._disable_kiosk(operation_data)
|
||||
elif operation == SystemOperation.SERVICE_RESTART:
|
||||
return self._restart_services(operation_data)
|
||||
elif operation == SystemOperation.EMERGENCY_STOP:
|
||||
return self._emergency_stop(operation_data)
|
||||
else:
|
||||
return {"success": False, "error": f"Unbekannte Operation: {operation.value}"}
|
||||
|
||||
except Exception as e:
|
||||
system_logger.error(f"Fehler bei Operation {operation.value}: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def _restart_system(self, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""Startet das System neu."""
|
||||
try:
|
||||
system_logger.warning("🔄 System-Neustart wird ausgeführt...")
|
||||
|
||||
# Cleanup vor Neustart
|
||||
self._cleanup_before_restart()
|
||||
|
||||
# System-Neustart je nach Plattform
|
||||
if self.is_windows:
|
||||
subprocess.run(["shutdown", "/r", "/t", "0"], check=True)
|
||||
else:
|
||||
subprocess.run(["sudo", "systemctl", "reboot"], check=True)
|
||||
|
||||
return {"success": True, "message": "System-Neustart initiiert"}
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
return {"success": False, "error": f"Neustart fehlgeschlagen: {e}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Unerwarteter Fehler: {e}"}
|
||||
|
||||
def _shutdown_system(self, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""Fährt das System herunter."""
|
||||
try:
|
||||
system_logger.warning("🛑 System-Shutdown wird ausgeführt...")
|
||||
|
||||
# Cleanup vor Shutdown
|
||||
self._cleanup_before_restart()
|
||||
|
||||
# System-Shutdown je nach Plattform
|
||||
if self.is_windows:
|
||||
subprocess.run(["shutdown", "/s", "/t", "0"], check=True)
|
||||
else:
|
||||
subprocess.run(["sudo", "systemctl", "poweroff"], check=True)
|
||||
|
||||
return {"success": True, "message": "System-Shutdown initiiert"}
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
return {"success": False, "error": f"Shutdown fehlgeschlagen: {e}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Unerwarteter Fehler: {e}"}
|
||||
|
||||
def _restart_kiosk(self, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""Startet nur den Kiosk-Modus neu."""
|
||||
try:
|
||||
system_logger.info("🖥️ Kiosk-Neustart wird ausgeführt...")
|
||||
|
||||
success_count = 0
|
||||
errors = []
|
||||
|
||||
# Kiosk-Service neustarten
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "restart", self.services["kiosk"]],
|
||||
check=True, timeout=30)
|
||||
success_count += 1
|
||||
system_logger.info("✅ Kiosk-Service neugestartet")
|
||||
except Exception as e:
|
||||
errors.append(f"Kiosk-Service: {e}")
|
||||
|
||||
# Watchdog-Service neustarten (falls vorhanden)
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "restart", self.services["watchdog"]],
|
||||
check=True, timeout=30)
|
||||
success_count += 1
|
||||
system_logger.info("✅ Watchdog-Service neugestartet")
|
||||
except Exception as e:
|
||||
errors.append(f"Watchdog-Service: {e}")
|
||||
|
||||
# X11-Session neustarten
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "restart", "getty@tty1.service"],
|
||||
check=True, timeout=30)
|
||||
success_count += 1
|
||||
system_logger.info("✅ X11-Session neugestartet")
|
||||
except Exception as e:
|
||||
errors.append(f"X11-Session: {e}")
|
||||
|
||||
if success_count > 0:
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Kiosk neugestartet ({success_count} Services)",
|
||||
"errors": errors if errors else None
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Alle Kiosk-Neustarts fehlgeschlagen",
|
||||
"details": errors
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Kiosk-Neustart fehlgeschlagen: {e}"}
|
||||
|
||||
def _enable_kiosk(self, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""Aktiviert den Kiosk-Modus."""
|
||||
try:
|
||||
system_logger.info("🖥️ Kiosk-Modus wird aktiviert...")
|
||||
|
||||
# Kiosk-Service aktivieren und starten
|
||||
subprocess.run(["sudo", "systemctl", "enable", self.services["kiosk"]],
|
||||
check=True, timeout=30)
|
||||
subprocess.run(["sudo", "systemctl", "start", self.services["kiosk"]],
|
||||
check=True, timeout=30)
|
||||
|
||||
# Watchdog aktivieren
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "enable", self.services["watchdog"]],
|
||||
check=True, timeout=30)
|
||||
subprocess.run(["sudo", "systemctl", "start", self.services["watchdog"]],
|
||||
check=True, timeout=30)
|
||||
except Exception as e:
|
||||
system_logger.warning(f"Watchdog-Aktivierung fehlgeschlagen: {e}")
|
||||
|
||||
return {"success": True, "message": "Kiosk-Modus aktiviert"}
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
return {"success": False, "error": f"Kiosk-Aktivierung fehlgeschlagen: {e}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Unerwarteter Fehler: {e}"}
|
||||
|
||||
def _disable_kiosk(self, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""Deaktiviert den Kiosk-Modus."""
|
||||
try:
|
||||
system_logger.info("🖥️ Kiosk-Modus wird deaktiviert...")
|
||||
|
||||
# Kiosk-Service stoppen und deaktivieren
|
||||
subprocess.run(["sudo", "systemctl", "stop", self.services["kiosk"]],
|
||||
check=True, timeout=30)
|
||||
subprocess.run(["sudo", "systemctl", "disable", self.services["kiosk"]],
|
||||
check=True, timeout=30)
|
||||
|
||||
# Watchdog stoppen
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "stop", self.services["watchdog"]],
|
||||
check=True, timeout=30)
|
||||
subprocess.run(["sudo", "systemctl", "disable", self.services["watchdog"]],
|
||||
check=True, timeout=30)
|
||||
except Exception as e:
|
||||
system_logger.warning(f"Watchdog-Deaktivierung fehlgeschlagen: {e}")
|
||||
|
||||
return {"success": True, "message": "Kiosk-Modus deaktiviert"}
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
return {"success": False, "error": f"Kiosk-Deaktivierung fehlgeschlagen: {e}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Unerwarteter Fehler: {e}"}
|
||||
|
||||
def _restart_services(self, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""Startet wichtige Services neu."""
|
||||
try:
|
||||
system_logger.info("🔄 Services werden neugestartet...")
|
||||
|
||||
success_count = 0
|
||||
errors = []
|
||||
|
||||
# HTTPS-Service neustarten
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "restart", self.services["https"]],
|
||||
check=True, timeout=60)
|
||||
success_count += 1
|
||||
system_logger.info("✅ HTTPS-Service neugestartet")
|
||||
except Exception as e:
|
||||
errors.append(f"HTTPS-Service: {e}")
|
||||
|
||||
# NetworkManager neustarten (falls nötig)
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "restart", "NetworkManager"],
|
||||
check=True, timeout=30)
|
||||
success_count += 1
|
||||
system_logger.info("✅ NetworkManager neugestartet")
|
||||
except Exception as e:
|
||||
errors.append(f"NetworkManager: {e}")
|
||||
|
||||
if success_count > 0:
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Services neugestartet ({success_count})",
|
||||
"errors": errors if errors else None
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Alle Service-Neustarts fehlgeschlagen",
|
||||
"details": errors
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Service-Neustart fehlgeschlagen: {e}"}
|
||||
|
||||
def _emergency_stop(self, operation_data: Dict) -> Dict[str, Any]:
|
||||
"""Notfall-Stopp aller Services."""
|
||||
try:
|
||||
system_logger.warning("🚨 Notfall-Stopp wird ausgeführt...")
|
||||
|
||||
# Flask-App stoppen
|
||||
try:
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
except Exception as e:
|
||||
system_logger.error(f"Flask-Stopp fehlgeschlagen: {e}")
|
||||
|
||||
return {"success": True, "message": "Notfall-Stopp initiiert"}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Notfall-Stopp fehlgeschlagen: {e}"}
|
||||
|
||||
def _cleanup_before_restart(self):
|
||||
"""Führt Cleanup-Operationen vor Neustart/Shutdown aus."""
|
||||
try:
|
||||
system_logger.info("🧹 Cleanup vor Neustart/Shutdown...")
|
||||
|
||||
# Shutdown-Manager verwenden falls verfügbar
|
||||
try:
|
||||
from utils.shutdown_manager import get_shutdown_manager
|
||||
shutdown_manager = get_shutdown_manager()
|
||||
shutdown_manager.shutdown(exit_code=0)
|
||||
except ImportError:
|
||||
system_logger.warning("Shutdown-Manager nicht verfügbar")
|
||||
|
||||
# Datenbank-Cleanup
|
||||
try:
|
||||
from utils.database_cleanup import safe_database_cleanup
|
||||
safe_database_cleanup(force_mode_switch=False)
|
||||
except ImportError:
|
||||
system_logger.warning("Database-Cleanup nicht verfügbar")
|
||||
|
||||
# Cache leeren
|
||||
self._clear_caches()
|
||||
|
||||
except Exception as e:
|
||||
system_logger.error(f"Cleanup fehlgeschlagen: {e}")
|
||||
|
||||
def _clear_caches(self):
|
||||
"""Leert alle Caches."""
|
||||
try:
|
||||
# User-Cache leeren
|
||||
from app import clear_user_cache, clear_printer_status_cache
|
||||
clear_user_cache()
|
||||
clear_printer_status_cache()
|
||||
|
||||
# System-Cache leeren
|
||||
if not self.is_windows:
|
||||
subprocess.run(["sudo", "sync"], timeout=10)
|
||||
subprocess.run(["sudo", "echo", "3", ">", "/proc/sys/vm/drop_caches"],
|
||||
shell=True, timeout=10)
|
||||
|
||||
except Exception as e:
|
||||
system_logger.warning(f"Cache-Clearing fehlgeschlagen: {e}")
|
||||
|
||||
def _move_to_history(self, operation_id: str):
|
||||
"""Verschiebt abgeschlossene Operation in Historie."""
|
||||
with self.lock:
|
||||
if operation_id in self.pending_operations:
|
||||
operation_data = self.pending_operations.pop(operation_id)
|
||||
self.operation_history.append(operation_data)
|
||||
|
||||
# Historie begrenzen
|
||||
if len(self.operation_history) > self.config["max_operation_history"]:
|
||||
self.operation_history = self.operation_history[-self.config["max_operation_history"]:]
|
||||
|
||||
def cancel_operation(self, operation_id: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Bricht geplante Operation ab.
|
||||
|
||||
Args:
|
||||
operation_id: ID der abzubrechenden Operation
|
||||
|
||||
Returns:
|
||||
Dict mit Ergebnis
|
||||
"""
|
||||
with self.lock:
|
||||
if operation_id not in self.pending_operations:
|
||||
return {"success": False, "error": "Operation nicht gefunden"}
|
||||
|
||||
operation_data = self.pending_operations[operation_id]
|
||||
if operation_data["status"] == "executing":
|
||||
return {"success": False, "error": "Operation bereits in Ausführung"}
|
||||
|
||||
operation_data["status"] = "cancelled"
|
||||
operation_data["cancelled_at"] = datetime.now()
|
||||
self._move_to_history(operation_id)
|
||||
|
||||
system_logger.info(f"❌ Operation abgebrochen: {operation_id}")
|
||||
|
||||
return {"success": True, "message": "Operation erfolgreich abgebrochen"}
|
||||
|
||||
def get_pending_operations(self) -> List[Dict]:
|
||||
"""Gibt alle geplanten Operationen zurück."""
|
||||
with self.lock:
|
||||
return list(self.pending_operations.values())
|
||||
|
||||
def get_operation_history(self, limit: int = 20) -> List[Dict]:
|
||||
"""Gibt Operation-Historie zurück."""
|
||||
with self.lock:
|
||||
return self.operation_history[-limit:] if limit else self.operation_history
|
||||
|
||||
def get_system_status(self) -> Dict[str, Any]:
|
||||
"""Gibt aktuellen System-Status zurück."""
|
||||
try:
|
||||
# Service-Status prüfen
|
||||
service_status = {}
|
||||
for name, service in self.services.items():
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["sudo", "systemctl", "is-active", service],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
service_status[name] = result.stdout.strip()
|
||||
except Exception as e:
|
||||
service_status[name] = f"error: {e}"
|
||||
|
||||
# System-Metriken
|
||||
memory = psutil.virtual_memory()
|
||||
disk = psutil.disk_usage('/')
|
||||
|
||||
# Aktive Operations
|
||||
pending_ops = len(self.pending_operations)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"services": service_status,
|
||||
"system_metrics": {
|
||||
"memory_percent": memory.percent,
|
||||
"memory_available_gb": memory.available / (1024**3),
|
||||
"disk_percent": disk.percent,
|
||||
"disk_free_gb": disk.free / (1024**3),
|
||||
"load_average": psutil.getloadavg()[0] if hasattr(psutil, 'getloadavg') else 0
|
||||
},
|
||||
"operations": {
|
||||
"pending": pending_ops,
|
||||
"history_count": len(self.operation_history)
|
||||
},
|
||||
"is_safe": self.is_safe_to_operate()[0]
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
|
||||
# Globaler System-Control-Manager
|
||||
_system_control_manager: Optional[SystemControlManager] = None
|
||||
_control_lock = threading.Lock()
|
||||
|
||||
|
||||
def get_system_control_manager() -> SystemControlManager:
|
||||
"""
|
||||
Singleton-Pattern für globalen System-Control-Manager.
|
||||
|
||||
Returns:
|
||||
SystemControlManager: Globaler System-Control-Manager
|
||||
"""
|
||||
global _system_control_manager
|
||||
|
||||
with _control_lock:
|
||||
if _system_control_manager is None:
|
||||
_system_control_manager = SystemControlManager()
|
||||
return _system_control_manager
|
||||
|
||||
|
||||
# Convenience-Funktionen
|
||||
def schedule_system_restart(delay_seconds: int = 60, user_id: str = None, reason: str = None, force: bool = False) -> Dict[str, Any]:
|
||||
"""Plant System-Neustart."""
|
||||
manager = get_system_control_manager()
|
||||
return manager.schedule_operation(SystemOperation.RESTART, delay_seconds, user_id, reason, force)
|
||||
|
||||
|
||||
def schedule_system_shutdown(delay_seconds: int = 30, user_id: str = None, reason: str = None, force: bool = False) -> Dict[str, Any]:
|
||||
"""Plant System-Shutdown."""
|
||||
manager = get_system_control_manager()
|
||||
return manager.schedule_operation(SystemOperation.SHUTDOWN, delay_seconds, user_id, reason, force)
|
||||
|
||||
|
||||
def restart_kiosk(delay_seconds: int = 10, user_id: str = None, reason: str = None) -> Dict[str, Any]:
|
||||
"""Plant Kiosk-Neustart."""
|
||||
manager = get_system_control_manager()
|
||||
return manager.schedule_operation(SystemOperation.KIOSK_RESTART, delay_seconds, user_id, reason)
|
||||
|
||||
|
||||
def get_system_status() -> Dict[str, Any]:
|
||||
"""Gibt System-Status zurück."""
|
||||
manager = get_system_control_manager()
|
||||
return manager.get_system_status()
|
Loading…
x
Reference in New Issue
Block a user