Die Dateien wurden geändert oder hinzugefügt:
This commit is contained in:
@@ -18,7 +18,7 @@ from typing import Dict, List, Tuple, Any, Optional
|
||||
from models import Printer, User, Job, get_db_session
|
||||
from utils.logging_config import get_logger, measure_execution_time
|
||||
from utils.security_suite import require_permission, Permission, check_permission
|
||||
from utils.hardware_integration import printer_monitor
|
||||
from utils.hardware_integration import printer_monitor, tapo_controller
|
||||
from utils.drag_drop_system import drag_drop_manager
|
||||
|
||||
# Logger initialisieren
|
||||
@@ -1047,4 +1047,527 @@ def get_drag_drop_config():
|
||||
|
||||
# =============================================================================
|
||||
# ENDE DRAG & DROP API
|
||||
# =============================================================================
|
||||
# =============================================================================
|
||||
|
||||
@printers_blueprint.route("/tapo/status-check", methods=["POST"])
|
||||
@login_required
|
||||
@require_permission(Permission.CONTROL_PRINTER)
|
||||
@measure_execution_time(logger=printers_logger, task_name="API-Massenhafte-Tapo-Status-Prüfung")
|
||||
def mass_tapo_status_check():
|
||||
"""
|
||||
Führt eine vollständige Tapo-Status-Überprüfung für alle Drucker durch.
|
||||
|
||||
Returns:
|
||||
JSON mit detailliertem Status aller Tapo-Steckdosen
|
||||
"""
|
||||
printers_logger.info(f"Massenhafte Tapo-Status-Prüfung von Benutzer {current_user.name}")
|
||||
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
|
||||
# Alle Drucker laden
|
||||
all_printers = db_session.query(Printer).all()
|
||||
|
||||
# Tapo-Controller laden
|
||||
try:
|
||||
from utils.hardware_integration import tapo_controller
|
||||
tapo_available = True
|
||||
except Exception as e:
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Tapo-Controller nicht verfügbar: {str(e)}",
|
||||
"tapo_available": False
|
||||
}), 500
|
||||
|
||||
printer_status = []
|
||||
summary = {
|
||||
"total_printers": len(all_printers),
|
||||
"printers_with_tapo": 0,
|
||||
"printers_without_tapo": 0,
|
||||
"tapo_online": 0,
|
||||
"tapo_offline": 0,
|
||||
"tapo_unreachable": 0,
|
||||
"configuration_issues": 0
|
||||
}
|
||||
|
||||
for printer in all_printers:
|
||||
printer_info = {
|
||||
"id": printer.id,
|
||||
"name": printer.name,
|
||||
"model": printer.model,
|
||||
"location": printer.location,
|
||||
"active": printer.active,
|
||||
"has_tapo_config": bool(printer.plug_ip),
|
||||
"plug_ip": printer.plug_ip,
|
||||
"last_checked": datetime.now()
|
||||
}
|
||||
|
||||
if not printer.plug_ip:
|
||||
# Drucker ohne Tapo-Konfiguration
|
||||
summary["printers_without_tapo"] += 1
|
||||
printer_info.update({
|
||||
"tapo_status": "not_configured",
|
||||
"tapo_reachable": False,
|
||||
"power_status": None,
|
||||
"recommendations": ["Tapo-Steckdose konfigurieren für automatische Steuerung"]
|
||||
})
|
||||
else:
|
||||
# Drucker mit Tapo-Konfiguration
|
||||
summary["printers_with_tapo"] += 1
|
||||
|
||||
# Konfigurationsprüfung
|
||||
config_issues = []
|
||||
if not printer.plug_username:
|
||||
config_issues.append("Tapo-Benutzername fehlt")
|
||||
if not printer.plug_password:
|
||||
config_issues.append("Tapo-Passwort fehlt")
|
||||
|
||||
if config_issues:
|
||||
summary["configuration_issues"] += 1
|
||||
printer_info.update({
|
||||
"tapo_status": "configuration_error",
|
||||
"tapo_reachable": False,
|
||||
"power_status": None,
|
||||
"config_issues": config_issues,
|
||||
"recommendations": ["Tapo-Anmeldedaten vervollständigen"]
|
||||
})
|
||||
else:
|
||||
# Vollständige Konfiguration - Status prüfen
|
||||
try:
|
||||
reachable, status = tapo_controller.check_outlet_status(
|
||||
printer.plug_ip,
|
||||
printer_id=printer.id
|
||||
)
|
||||
|
||||
if reachable:
|
||||
if status == "on":
|
||||
summary["tapo_online"] += 1
|
||||
status_type = "online"
|
||||
recommendations = []
|
||||
else:
|
||||
summary["tapo_offline"] += 1
|
||||
status_type = "offline"
|
||||
recommendations = ["Steckdose kann bei Bedarf eingeschaltet werden"]
|
||||
else:
|
||||
summary["tapo_unreachable"] += 1
|
||||
status_type = "unreachable"
|
||||
recommendations = ["Netzwerkverbindung prüfen", "IP-Adresse überprüfen"]
|
||||
|
||||
printer_info.update({
|
||||
"tapo_status": status_type,
|
||||
"tapo_reachable": reachable,
|
||||
"power_status": status,
|
||||
"recommendations": recommendations
|
||||
})
|
||||
|
||||
# Drucker-Status in DB aktualisieren
|
||||
if reachable:
|
||||
printer.last_checked = datetime.now()
|
||||
if status == "on":
|
||||
printer.status = "online"
|
||||
else:
|
||||
printer.status = "offline"
|
||||
else:
|
||||
printer.status = "unreachable"
|
||||
|
||||
except Exception as tapo_error:
|
||||
summary["tapo_unreachable"] += 1
|
||||
printer_info.update({
|
||||
"tapo_status": "error",
|
||||
"tapo_reachable": False,
|
||||
"power_status": None,
|
||||
"error": str(tapo_error),
|
||||
"recommendations": ["Tapo-Verbindung prüfen", "Anmeldedaten überprüfen"]
|
||||
})
|
||||
printers_logger.warning(f"Tapo-Fehler für {printer.name}: {str(tapo_error)}")
|
||||
|
||||
# Aktuelle Jobs für zusätzliche Info
|
||||
active_jobs = db_session.query(Job).filter(
|
||||
Job.printer_id == printer.id,
|
||||
Job.status.in_(["running", "printing", "active", "scheduled"])
|
||||
).count()
|
||||
|
||||
printer_info["active_jobs"] = active_jobs
|
||||
if active_jobs > 0:
|
||||
printer_info.setdefault("recommendations", []).append(
|
||||
f"Vorsicht: {active_jobs} aktive Job(s) bei Steckdosen-Änderungen"
|
||||
)
|
||||
|
||||
printer_status.append(printer_info)
|
||||
|
||||
# Änderungen in DB speichern
|
||||
db_session.commit()
|
||||
db_session.close()
|
||||
|
||||
# Übersicht der Ergebnisse
|
||||
coverage_percentage = (summary["printers_with_tapo"] / summary["total_printers"] * 100) if summary["total_printers"] > 0 else 0
|
||||
health_score = ((summary["tapo_online"] + summary["tapo_offline"]) / summary["printers_with_tapo"] * 100) if summary["printers_with_tapo"] > 0 else 0
|
||||
|
||||
printers_logger.info(f"Tapo-Status-Check abgeschlossen: {summary['printers_with_tapo']} konfiguriert, "
|
||||
f"{summary['tapo_online']} online, {summary['tapo_unreachable']} nicht erreichbar")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"tapo_available": tapo_available,
|
||||
"printers": printer_status,
|
||||
"summary": summary,
|
||||
"metrics": {
|
||||
"coverage_percentage": round(coverage_percentage, 1),
|
||||
"health_score": round(health_score, 1),
|
||||
"needs_attention": summary["configuration_issues"] + summary["tapo_unreachable"]
|
||||
},
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Unerwarteter Fehler bei Massenhafte-Tapo-Status-Prüfung: {str(e)}")
|
||||
if 'db_session' in locals():
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Systemfehler: {str(e)}"
|
||||
}), 500
|
||||
|
||||
@printers_blueprint.route("/tapo/configuration-wizard", methods=["POST"])
|
||||
@login_required
|
||||
@require_permission(Permission.ADMIN)
|
||||
@measure_execution_time(logger=printers_logger, task_name="API-Tapo-Konfigurationsassistent")
|
||||
def tapo_configuration_wizard():
|
||||
"""
|
||||
Automatischer Konfigurationsassistent für Tapo-Steckdosen.
|
||||
Versucht automatisch verfügbare Steckdosen zu erkennen und zu konfigurieren.
|
||||
"""
|
||||
printers_logger.info(f"Tapo-Konfigurationsassistent von Admin {current_user.name}")
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
auto_configure = data.get('auto_configure', True)
|
||||
test_ips = data.get('test_ips', [])
|
||||
|
||||
# Tapo-Controller laden
|
||||
try:
|
||||
from utils.hardware_integration import tapo_controller
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Tapo-Controller nicht verfügbar: {str(e)}"
|
||||
}), 500
|
||||
|
||||
db_session = get_db_session()
|
||||
|
||||
# Standard-IP-Bereich für Mercedes-Benz TBA (normalerweise 192.168.1.201-206)
|
||||
if not test_ips:
|
||||
test_ips = [f"192.168.1.{i}" for i in range(201, 207)] # 6 Standard-Arbeitsplätze
|
||||
|
||||
discovery_results = {
|
||||
"tested_ips": test_ips,
|
||||
"discovered_devices": [],
|
||||
"configured_printers": [],
|
||||
"errors": []
|
||||
}
|
||||
|
||||
printers_logger.info(f"Teste {len(test_ips)} IP-Adressen auf Tapo-Geräte...")
|
||||
|
||||
# Discovery für jede IP-Adresse
|
||||
for ip in test_ips:
|
||||
try:
|
||||
printers_logger.debug(f"Teste IP: {ip}")
|
||||
|
||||
# Ping-Test
|
||||
if not tapo_controller.ping_address(ip, timeout=3):
|
||||
discovery_results["errors"].append(f"{ip}: Nicht erreichbar (Ping fehlgeschlagen)")
|
||||
continue
|
||||
|
||||
# Tapo-Verbindungstest
|
||||
test_result = tapo_controller.test_connection(ip)
|
||||
|
||||
if test_result["success"]:
|
||||
device_info = test_result.get("device_info", {})
|
||||
|
||||
discovered_device = {
|
||||
"ip": ip,
|
||||
"device_info": device_info,
|
||||
"nickname": device_info.get("nickname", f"Tapo Device {ip}"),
|
||||
"model": device_info.get("model", "Unknown"),
|
||||
"device_on": device_info.get("device_on", False)
|
||||
}
|
||||
|
||||
discovery_results["discovered_devices"].append(discovered_device)
|
||||
printers_logger.info(f"✅ Tapo-Gerät gefunden: {ip} - {discovered_device['nickname']}")
|
||||
|
||||
# Auto-Konfiguration wenn gewünscht
|
||||
if auto_configure:
|
||||
# Suche nach Drucker ohne Tapo-Konfiguration
|
||||
unconfigured_printer = db_session.query(Printer).filter(
|
||||
Printer.plug_ip.is_(None),
|
||||
Printer.active == True
|
||||
).first()
|
||||
|
||||
if unconfigured_printer:
|
||||
# Konfiguriere den ersten verfügbaren Drucker
|
||||
unconfigured_printer.plug_ip = ip
|
||||
unconfigured_printer.plug_username = "admin" # Standard für Tapo
|
||||
unconfigured_printer.plug_password = "admin" # Standard für Tapo
|
||||
unconfigured_printer.last_checked = datetime.now()
|
||||
|
||||
configured_info = {
|
||||
"printer_id": unconfigured_printer.id,
|
||||
"printer_name": unconfigured_printer.name,
|
||||
"tapo_ip": ip,
|
||||
"tapo_nickname": discovered_device['nickname']
|
||||
}
|
||||
|
||||
discovery_results["configured_printers"].append(configured_info)
|
||||
printers_logger.info(f"✅ Drucker '{unconfigured_printer.name}' automatisch mit {ip} verknüpft")
|
||||
else:
|
||||
discovery_results["errors"].append(f"{ip}: Tapo-Gerät gefunden, aber kein unkonfigurierter Drucker verfügbar")
|
||||
|
||||
else:
|
||||
discovery_results["errors"].append(f"{ip}: Erreichbar, aber kein Tapo-Gerät oder Authentifizierung fehlgeschlagen")
|
||||
|
||||
except Exception as ip_error:
|
||||
discovery_results["errors"].append(f"{ip}: Fehler beim Test - {str(ip_error)}")
|
||||
printers_logger.warning(f"Fehler beim Testen von {ip}: {str(ip_error)}")
|
||||
|
||||
# Änderungen speichern
|
||||
db_session.commit()
|
||||
db_session.close()
|
||||
|
||||
# Zusammenfassung
|
||||
summary = {
|
||||
"tested_ips": len(test_ips),
|
||||
"discovered_devices": len(discovery_results["discovered_devices"]),
|
||||
"configured_printers": len(discovery_results["configured_printers"]),
|
||||
"errors": len(discovery_results["errors"])
|
||||
}
|
||||
|
||||
printers_logger.info(f"Tapo-Konfigurationsassistent abgeschlossen: {summary}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"Discovery abgeschlossen: {summary['discovered_devices']} Geräte gefunden, "
|
||||
f"{summary['configured_printers']} Drucker konfiguriert",
|
||||
"results": discovery_results,
|
||||
"summary": summary,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler beim Tapo-Konfigurationsassistent: {str(e)}")
|
||||
if 'db_session' in locals():
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Systemfehler: {str(e)}"
|
||||
}), 500
|
||||
|
||||
@printers_blueprint.route("/tapo/validate-configuration/<int:printer_id>", methods=["POST"])
|
||||
@login_required
|
||||
@require_permission(Permission.ADMIN)
|
||||
@measure_execution_time(logger=printers_logger, task_name="API-Tapo-Konfigurationsvalidierung")
|
||||
def validate_tapo_configuration(printer_id):
|
||||
"""
|
||||
Validiert die Tapo-Konfiguration eines spezifischen Druckers.
|
||||
Führt umfassende Tests durch: Ping, Authentifizierung, Funktionalität.
|
||||
"""
|
||||
printers_logger.info(f"Tapo-Konfigurationsvalidierung für Drucker {printer_id} von Admin {current_user.name}")
|
||||
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
|
||||
|
||||
if not printer:
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Drucker nicht gefunden"
|
||||
}), 404
|
||||
|
||||
# Tapo-Controller laden
|
||||
try:
|
||||
from utils.hardware_integration import tapo_controller
|
||||
except Exception as e:
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Tapo-Controller nicht verfügbar: {str(e)}"
|
||||
}), 500
|
||||
|
||||
validation_results = {
|
||||
"printer": {
|
||||
"id": printer.id,
|
||||
"name": printer.name,
|
||||
"model": printer.model,
|
||||
"location": printer.location
|
||||
},
|
||||
"configuration": {
|
||||
"has_ip": bool(printer.plug_ip),
|
||||
"has_username": bool(printer.plug_username),
|
||||
"has_password": bool(printer.plug_password),
|
||||
"ip_address": printer.plug_ip
|
||||
},
|
||||
"tests": {
|
||||
"ping": {"status": "not_run", "message": ""},
|
||||
"authentication": {"status": "not_run", "message": ""},
|
||||
"functionality": {"status": "not_run", "message": ""},
|
||||
"device_info": {"status": "not_run", "message": ""}
|
||||
},
|
||||
"overall_status": "unknown",
|
||||
"recommendations": []
|
||||
}
|
||||
|
||||
# Konfigurationsprüfung
|
||||
if not printer.plug_ip:
|
||||
validation_results["overall_status"] = "not_configured"
|
||||
validation_results["recommendations"].append("IP-Adresse der Tapo-Steckdose eintragen")
|
||||
elif not printer.plug_username or not printer.plug_password:
|
||||
validation_results["overall_status"] = "incomplete_config"
|
||||
validation_results["recommendations"].append("Benutzername und Passwort für Tapo-Steckdose eintragen")
|
||||
else:
|
||||
# Umfassende Tests durchführen
|
||||
all_tests_passed = True
|
||||
|
||||
# Test 1: Ping/Erreichbarkeit
|
||||
try:
|
||||
ping_success = tapo_controller.ping_address(printer.plug_ip, timeout=5)
|
||||
if ping_success:
|
||||
validation_results["tests"]["ping"] = {
|
||||
"status": "passed",
|
||||
"message": "Steckdose ist im Netzwerk erreichbar"
|
||||
}
|
||||
else:
|
||||
validation_results["tests"]["ping"] = {
|
||||
"status": "failed",
|
||||
"message": "Steckdose nicht erreichbar - Netzwerkproblem oder falsche IP"
|
||||
}
|
||||
all_tests_passed = False
|
||||
validation_results["recommendations"].append("IP-Adresse überprüfen")
|
||||
validation_results["recommendations"].append("Netzwerkverbindung der Steckdose prüfen")
|
||||
except Exception as ping_error:
|
||||
validation_results["tests"]["ping"] = {
|
||||
"status": "error",
|
||||
"message": f"Ping-Test fehlgeschlagen: {str(ping_error)}"
|
||||
}
|
||||
all_tests_passed = False
|
||||
|
||||
# Test 2: Authentifizierung (nur wenn Ping erfolgreich)
|
||||
if validation_results["tests"]["ping"]["status"] == "passed":
|
||||
try:
|
||||
auth_result = tapo_controller.test_connection(
|
||||
printer.plug_ip,
|
||||
username=printer.plug_username,
|
||||
password=printer.plug_password
|
||||
)
|
||||
|
||||
if auth_result["success"]:
|
||||
validation_results["tests"]["authentication"] = {
|
||||
"status": "passed",
|
||||
"message": "Authentifizierung erfolgreich"
|
||||
}
|
||||
|
||||
# Geräteinformationen extrahieren
|
||||
device_info = auth_result.get("device_info", {})
|
||||
validation_results["tests"]["device_info"] = {
|
||||
"status": "passed",
|
||||
"message": "Geräteinformationen abgerufen",
|
||||
"data": {
|
||||
"nickname": device_info.get("nickname", "Unbekannt"),
|
||||
"model": device_info.get("model", "Unbekannt"),
|
||||
"device_on": device_info.get("device_on", False),
|
||||
"signal_level": device_info.get("signal_level", 0)
|
||||
}
|
||||
}
|
||||
else:
|
||||
validation_results["tests"]["authentication"] = {
|
||||
"status": "failed",
|
||||
"message": f"Authentifizierung fehlgeschlagen: {auth_result.get('error', 'Unbekannt')}"
|
||||
}
|
||||
all_tests_passed = False
|
||||
validation_results["recommendations"].append("Benutzername und Passwort überprüfen")
|
||||
|
||||
except Exception as auth_error:
|
||||
validation_results["tests"]["authentication"] = {
|
||||
"status": "error",
|
||||
"message": f"Authentifizierungstest fehlgeschlagen: {str(auth_error)}"
|
||||
}
|
||||
all_tests_passed = False
|
||||
|
||||
# Test 3: Funktionalität (nur wenn Authentifizierung erfolgreich)
|
||||
if validation_results["tests"]["authentication"]["status"] == "passed":
|
||||
try:
|
||||
reachable, status = tapo_controller.check_outlet_status(
|
||||
printer.plug_ip,
|
||||
printer_id=printer_id
|
||||
)
|
||||
|
||||
if reachable:
|
||||
validation_results["tests"]["functionality"] = {
|
||||
"status": "passed",
|
||||
"message": f"Status erfolgreich abgerufen: {status}",
|
||||
"current_status": status
|
||||
}
|
||||
|
||||
# Drucker-Status in DB aktualisieren
|
||||
printer.last_checked = datetime.now()
|
||||
printer.status = "online" if status == "on" else "offline"
|
||||
|
||||
else:
|
||||
validation_results["tests"]["functionality"] = {
|
||||
"status": "failed",
|
||||
"message": "Status konnte nicht abgerufen werden"
|
||||
}
|
||||
all_tests_passed = False
|
||||
|
||||
except Exception as func_error:
|
||||
validation_results["tests"]["functionality"] = {
|
||||
"status": "error",
|
||||
"message": f"Funktionalitätstest fehlgeschlagen: {str(func_error)}"
|
||||
}
|
||||
all_tests_passed = False
|
||||
|
||||
# Gesamtstatus bestimmen
|
||||
if all_tests_passed:
|
||||
validation_results["overall_status"] = "fully_functional"
|
||||
validation_results["recommendations"].append("Konfiguration ist vollständig und funktional")
|
||||
else:
|
||||
failed_tests = [test for test, result in validation_results["tests"].items()
|
||||
if result["status"] in ["failed", "error"]]
|
||||
|
||||
if "ping" in failed_tests:
|
||||
validation_results["overall_status"] = "network_issue"
|
||||
elif "authentication" in failed_tests:
|
||||
validation_results["overall_status"] = "auth_issue"
|
||||
elif "functionality" in failed_tests:
|
||||
validation_results["overall_status"] = "functionality_issue"
|
||||
else:
|
||||
validation_results["overall_status"] = "partial_failure"
|
||||
|
||||
# Aktuelle Jobs als Sicherheitshinweis
|
||||
active_jobs = db_session.query(Job).filter(
|
||||
Job.printer_id == printer_id,
|
||||
Job.status.in_(["running", "printing", "active"])
|
||||
).count()
|
||||
|
||||
if active_jobs > 0:
|
||||
validation_results["safety_warning"] = f"{active_jobs} aktive Job(s) - Vorsicht bei Steckdosen-Tests"
|
||||
|
||||
db_session.commit()
|
||||
db_session.close()
|
||||
|
||||
printers_logger.info(f"Tapo-Validierung für {printer.name} abgeschlossen: {validation_results['overall_status']}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"validation": validation_results,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler bei Tapo-Konfigurationsvalidierung: {str(e)}")
|
||||
if 'db_session' in locals():
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Systemfehler: {str(e)}"
|
||||
}), 500
|
Reference in New Issue
Block a user