🛠️ "Implementiere TP-Link Tapo P110 Unterstützung für Druckerüberwachung und -steuerung"
- Aktualisiere die `check_printer_status()` Funktion zur Verwendung des PyP100-Moduls für die Tapo-Steckdosen. - Füge neue API-Endpunkte hinzu: `test-tapo` für die Verbindungstests einzelner Drucker und `test-all-tapo` für Massentests. - Verbessere die Fehlerbehandlung und Logging für Tapo-Verbindungen. - Aktualisiere die Benutzeroberfläche, um den Datei-Upload als optional zu kennzeichnen. - Implementiere umfassende Tests für die Tapo-Verbindungen in `debug_drucker_erkennung.py` und verbessere die Validierung der Konfiguration in `job_scheduler.py`.
This commit is contained in:
@@ -1281,7 +1281,7 @@ def kiosk_restart_system():
|
||||
@measure_execution_time(logger=printers_logger, task_name="Drucker-Status-Prüfung")
|
||||
def check_printer_status(ip_address: str, timeout: int = 7) -> Tuple[str, bool]:
|
||||
"""
|
||||
Überprüft den Status eines Druckers über Steckdosenabfrage mit Timeout.
|
||||
Überprüft den Status eines Druckers über TP-Link Tapo P110-Steckdosenabfrage.
|
||||
|
||||
Args:
|
||||
ip_address: IP-Adresse der Drucker-Steckdose
|
||||
@@ -1303,7 +1303,7 @@ def check_printer_status(ip_address: str, timeout: int = 7) -> Tuple[str, bool]:
|
||||
printers_logger.warning(f"Ungültige IP-Adresse: {ip_address}")
|
||||
return "offline", False
|
||||
|
||||
# Zuerst prüfen, ob die Steckdose erreichbar ist
|
||||
# Zuerst prüfen, ob die Steckdose erreichbar ist (Ping)
|
||||
if os.name == 'nt': # Windows
|
||||
cmd = ['ping', '-n', '1', '-w', str(timeout * 1000), ip_address.strip()]
|
||||
else: # Unix/Linux/macOS
|
||||
@@ -1326,7 +1326,7 @@ def check_printer_status(ip_address: str, timeout: int = 7) -> Tuple[str, bool]:
|
||||
printers_logger.debug(f"Ping fehlgeschlagen für {ip_address} (Return Code: {result.returncode})")
|
||||
return "offline", False
|
||||
|
||||
# Jetzt den tatsächlichen Steckdosenstatus abfragen
|
||||
# Drucker-Daten aus Datenbank holen für Anmeldedaten
|
||||
db_session = get_db_session()
|
||||
printer = db_session.query(Printer).filter(Printer.plug_ip == ip_address).first()
|
||||
|
||||
@@ -1335,46 +1335,36 @@ def check_printer_status(ip_address: str, timeout: int = 7) -> Tuple[str, bool]:
|
||||
db_session.close()
|
||||
return "offline", False
|
||||
|
||||
# Smart Plug Status prüfen
|
||||
import requests
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
# Standardwerte aus der Datenbank verwenden
|
||||
username = printer.plug_username
|
||||
password = printer.plug_password
|
||||
|
||||
# TP-Link Tapo P110 Status mit PyP100 prüfen
|
||||
try:
|
||||
# Für TP-Link Smart Plugs oder kompatible Steckdosen
|
||||
auth = (username, password)
|
||||
response = requests.get(f"http://{ip_address}/status", auth=auth, timeout=timeout)
|
||||
from PyP100 import PyP110
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
status_data = response.json()
|
||||
# Überprüfen ob die Steckdose eingeschaltet ist
|
||||
if 'system' in status_data and 'get_sysinfo' in status_data['system']:
|
||||
if status_data['system']['get_sysinfo'].get('relay_state') == 1:
|
||||
printers_logger.debug(f"Steckdose {ip_address} ist eingeschaltet")
|
||||
db_session.close()
|
||||
return "online", True
|
||||
except (ValueError, KeyError) as e:
|
||||
printers_logger.debug(f"Fehler beim Parsen der Steckdosen-Antwort: {str(e)}")
|
||||
# Tapo-Steckdose verbinden
|
||||
p110 = PyP110.P110(ip_address, printer.plug_username, printer.plug_password)
|
||||
p110.handshake() # Authentifizierung
|
||||
p110.login() # Login
|
||||
|
||||
# Zweiter Versuch mit einfacher GET-Anfrage
|
||||
response = requests.get(f"http://{ip_address}", auth=auth, timeout=timeout)
|
||||
if response.status_code == 200:
|
||||
printers_logger.debug(f"Steckdose {ip_address} antwortet auf HTTP-Anfrage")
|
||||
# Wenn wir hier ankommen, ist die Steckdose online, aber wir wissen nicht sicher, ob sie eingeschaltet ist
|
||||
# Da wir nur die Verfügbarkeit prüfen, nehmen wir an, dass sie aktiv ist, wenn sie antwortet
|
||||
db_session.close()
|
||||
# Geräteinformationen abrufen
|
||||
device_info = p110.getDeviceInfo()
|
||||
device_on = device_info.get('device_on', False)
|
||||
|
||||
db_session.close()
|
||||
|
||||
if device_on:
|
||||
printers_logger.debug(f"Tapo-Steckdose {ip_address} ist eingeschaltet - Drucker online")
|
||||
return "online", True
|
||||
else:
|
||||
printers_logger.debug(f"Tapo-Steckdose {ip_address} ist ausgeschaltet - Drucker offline")
|
||||
return "offline", False
|
||||
|
||||
except RequestException as e:
|
||||
printers_logger.debug(f"Fehler bei HTTP-Anfrage an Steckdose {ip_address}: {str(e)}")
|
||||
|
||||
# Wenn beide API-Anfragen fehlschlagen, können wir annehmen, dass die Steckdose nicht eingeschaltet ist
|
||||
db_session.close()
|
||||
return "offline", False
|
||||
except ImportError:
|
||||
printers_logger.error("PyP100-Modul nicht verfügbar - kann Tapo-Steckdose nicht abfragen")
|
||||
db_session.close()
|
||||
return "offline", False
|
||||
except Exception as e:
|
||||
printers_logger.debug(f"Fehler bei Tapo-Steckdosen-Abfrage {ip_address}: {str(e)}")
|
||||
db_session.close()
|
||||
return "offline", False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
printers_logger.warning(f"Ping-Timeout für Drucker {ip_address} nach {timeout} Sekunden")
|
||||
@@ -3501,14 +3491,45 @@ def create_user_api():
|
||||
@app.route("/api/admin/printers/<int:printer_id>/toggle", methods=["POST"])
|
||||
@login_required
|
||||
def toggle_printer_power(printer_id):
|
||||
"""Schaltet einen Drucker ein oder aus (nur für Admins)."""
|
||||
"""
|
||||
Schaltet einen Drucker über die zugehörige Steckdose ein/aus.
|
||||
"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Nur Administratoren können Drucker steuern"}), 403
|
||||
return jsonify({"error": "Administratorrechte erforderlich"}), 403
|
||||
|
||||
try:
|
||||
data = request.json
|
||||
power_on = data.get("power_on", True)
|
||||
data = request.get_json()
|
||||
state = data.get("state", True) # Standard: einschalten
|
||||
|
||||
# Steckdose schalten
|
||||
from utils.job_scheduler import toggle_plug
|
||||
success = toggle_plug(printer_id, state)
|
||||
|
||||
if success:
|
||||
action = "eingeschaltet" if state else "ausgeschaltet"
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"Drucker erfolgreich {action}",
|
||||
"printer_id": printer_id,
|
||||
"state": state
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
"error": "Fehler beim Schalten der Steckdose"
|
||||
}), 500
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler beim Schalten von Drucker {printer_id}: {str(e)}")
|
||||
return jsonify({"error": "Interner Serverfehler"}), 500
|
||||
|
||||
@app.route("/api/admin/printers/<int:printer_id>/test-tapo", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def test_printer_tapo_connection(printer_id):
|
||||
"""
|
||||
Testet die Tapo-Steckdosen-Verbindung für einen Drucker.
|
||||
"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
printer = db_session.query(Printer).get(printer_id)
|
||||
|
||||
@@ -3516,33 +3537,114 @@ def toggle_printer_power(printer_id):
|
||||
db_session.close()
|
||||
return jsonify({"error": "Drucker nicht gefunden"}), 404
|
||||
|
||||
# Steckdose schalten
|
||||
from utils.job_scheduler import toggle_plug
|
||||
success = toggle_plug(printer_id, power_on)
|
||||
|
||||
if success:
|
||||
# Status in der Datenbank aktualisieren
|
||||
printer.status = "available" if power_on else "offline"
|
||||
printer.active = power_on
|
||||
db_session.commit()
|
||||
|
||||
action = "eingeschaltet" if power_on else "ausgeschaltet"
|
||||
printers_logger.info(f"Drucker {printer.name} {action} von Admin {current_user.id}")
|
||||
|
||||
if not printer.plug_ip or not printer.plug_username or not printer.plug_password:
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"Drucker erfolgreich {action}",
|
||||
"status": printer.status
|
||||
})
|
||||
else:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Fehler beim Schalten der Steckdose"}), 500
|
||||
|
||||
"error": "Unvollständige Tapo-Konfiguration",
|
||||
"missing": [
|
||||
key for key, value in {
|
||||
"plug_ip": printer.plug_ip,
|
||||
"plug_username": printer.plug_username,
|
||||
"plug_password": printer.plug_password
|
||||
}.items() if not value
|
||||
]
|
||||
}), 400
|
||||
|
||||
db_session.close()
|
||||
|
||||
# Tapo-Verbindung testen
|
||||
from utils.job_scheduler import test_tapo_connection
|
||||
test_result = test_tapo_connection(
|
||||
printer.plug_ip,
|
||||
printer.plug_username,
|
||||
printer.plug_password
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
"printer_id": printer_id,
|
||||
"printer_name": printer.name,
|
||||
"tapo_test": test_result
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler beim Schalten des Druckers {printer_id}: {str(e)}")
|
||||
return jsonify({"error": "Interner Serverfehler"}), 500
|
||||
|
||||
printers_logger.error(f"Fehler beim Testen der Tapo-Verbindung für Drucker {printer_id}: {str(e)}")
|
||||
return jsonify({"error": "Interner Serverfehler beim Verbindungstest"}), 500
|
||||
|
||||
@app.route("/api/admin/printers/test-all-tapo", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def test_all_printers_tapo_connection():
|
||||
"""
|
||||
Testet die Tapo-Steckdosen-Verbindung für alle Drucker.
|
||||
Nützlich für Diagnose und Setup-Validierung.
|
||||
"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
printers = db_session.query(Printer).filter(Printer.active == True).all()
|
||||
db_session.close()
|
||||
|
||||
if not printers:
|
||||
return jsonify({
|
||||
"message": "Keine aktiven Drucker gefunden",
|
||||
"results": []
|
||||
})
|
||||
|
||||
# Alle Drucker testen
|
||||
from utils.job_scheduler import test_tapo_connection
|
||||
results = []
|
||||
|
||||
for printer in printers:
|
||||
result = {
|
||||
"printer_id": printer.id,
|
||||
"printer_name": printer.name,
|
||||
"plug_ip": printer.plug_ip,
|
||||
"has_config": bool(printer.plug_ip and printer.plug_username and printer.plug_password)
|
||||
}
|
||||
|
||||
if result["has_config"]:
|
||||
# Tapo-Verbindung testen
|
||||
test_result = test_tapo_connection(
|
||||
printer.plug_ip,
|
||||
printer.plug_username,
|
||||
printer.plug_password
|
||||
)
|
||||
result["tapo_test"] = test_result
|
||||
else:
|
||||
result["tapo_test"] = {
|
||||
"success": False,
|
||||
"error": "Unvollständige Tapo-Konfiguration",
|
||||
"device_info": None,
|
||||
"status": "unconfigured"
|
||||
}
|
||||
result["missing_config"] = [
|
||||
key for key, value in {
|
||||
"plug_ip": printer.plug_ip,
|
||||
"plug_username": printer.plug_username,
|
||||
"plug_password": printer.plug_password
|
||||
}.items() if not value
|
||||
]
|
||||
|
||||
results.append(result)
|
||||
|
||||
# Zusammenfassung erstellen
|
||||
total_printers = len(results)
|
||||
successful_connections = sum(1 for r in results if r["tapo_test"]["success"])
|
||||
configured_printers = sum(1 for r in results if r["has_config"])
|
||||
|
||||
return jsonify({
|
||||
"summary": {
|
||||
"total_printers": total_printers,
|
||||
"configured_printers": configured_printers,
|
||||
"successful_connections": successful_connections,
|
||||
"success_rate": round(successful_connections / total_printers * 100, 1) if total_printers > 0 else 0
|
||||
},
|
||||
"results": results
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler beim Testen aller Tapo-Verbindungen: {str(e)}")
|
||||
return jsonify({"error": "Interner Serverfehler beim Massentest"}), 500
|
||||
|
||||
# ===== ADMIN FORM ENDPOINTS =====
|
||||
|
||||
@app.route("/admin/users/create", methods=["POST"])
|
||||
|
Reference in New Issue
Block a user