🛠️ "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:
@@ -13,11 +13,19 @@ from typing import Dict, Tuple, List, Optional
|
||||
from flask import session
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
import os
|
||||
|
||||
from models import get_db_session, Printer
|
||||
from utils.logging_config import get_logger
|
||||
from config.settings import PRINTERS
|
||||
|
||||
# TP-Link Tapo P110 Unterstützung hinzufügen
|
||||
try:
|
||||
from PyP100 import PyP110
|
||||
TAPO_AVAILABLE = True
|
||||
except ImportError:
|
||||
TAPO_AVAILABLE = False
|
||||
|
||||
# Logger initialisieren
|
||||
monitor_logger = get_logger("printer_monitor")
|
||||
|
||||
@@ -110,57 +118,35 @@ class PrinterMonitor:
|
||||
|
||||
def _turn_outlet_off(self, ip_address: str, username: str, password: str, timeout: int = 5) -> bool:
|
||||
"""
|
||||
Schaltet eine Smart-Steckdose aus.
|
||||
Schaltet eine TP-Link Tapo P110-Steckdose aus.
|
||||
|
||||
Args:
|
||||
ip_address: IP-Adresse der Steckdose
|
||||
username: Benutzername für die Steckdose
|
||||
password: Passwort für die Steckdose
|
||||
timeout: Timeout in Sekunden
|
||||
timeout: Timeout in Sekunden (wird ignoriert, da PyP100 eigenes Timeout hat)
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich ausgeschaltet
|
||||
"""
|
||||
if not TAPO_AVAILABLE:
|
||||
monitor_logger.error("⚠️ PyP100-Modul nicht verfügbar - kann Tapo-Steckdose nicht schalten")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Für TP-Link Tapo und ähnliche Smart Plugs
|
||||
auth = (username, password)
|
||||
# TP-Link Tapo P110 Verbindung herstellen
|
||||
p110 = PyP110.P110(ip_address, username, password)
|
||||
p110.handshake() # Authentifizierung
|
||||
p110.login() # Login
|
||||
|
||||
# Verschiedene API-Endpunkte versuchen
|
||||
endpoints = [
|
||||
f"http://{ip_address}/relay/off",
|
||||
f"http://{ip_address}/api/relay/off",
|
||||
f"http://{ip_address}/set?relay=off",
|
||||
f"http://{ip_address}/control?action=off"
|
||||
]
|
||||
# Steckdose ausschalten
|
||||
p110.turnOff()
|
||||
monitor_logger.debug(f"✅ Tapo-Steckdose {ip_address} erfolgreich ausgeschaltet")
|
||||
return True
|
||||
|
||||
for endpoint in endpoints:
|
||||
try:
|
||||
response = requests.post(endpoint, auth=auth, timeout=timeout)
|
||||
if response.status_code in [200, 201, 204]:
|
||||
monitor_logger.debug(f"✅ Steckdose {ip_address} ausgeschaltet via {endpoint}")
|
||||
return True
|
||||
except requests.RequestException:
|
||||
continue
|
||||
|
||||
# Wenn spezifische Endpunkte fehlschlagen, versuche generische JSON-API
|
||||
try:
|
||||
payload = {"system": {"set_relay_state": {"state": 0}}}
|
||||
response = requests.post(
|
||||
f"http://{ip_address}/api",
|
||||
json=payload,
|
||||
auth=auth,
|
||||
timeout=timeout
|
||||
)
|
||||
if response.status_code in [200, 201]:
|
||||
monitor_logger.debug(f"✅ Steckdose {ip_address} ausgeschaltet via JSON-API")
|
||||
return True
|
||||
except requests.RequestException:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
monitor_logger.debug(f"⚠️ Fehler beim Ausschalten der Steckdose {ip_address}: {str(e)}")
|
||||
|
||||
return False
|
||||
monitor_logger.debug(f"⚠️ Fehler beim Ausschalten der Tapo-Steckdose {ip_address}: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_live_printer_status(self, use_session_cache: bool = True) -> Dict[int, Dict]:
|
||||
"""
|
||||
@@ -364,7 +350,6 @@ class PrinterMonitor:
|
||||
ipaddress.ip_address(ip_address.strip())
|
||||
|
||||
# Platform-spezifische Ping-Befehle
|
||||
import os
|
||||
if os.name == 'nt': # Windows
|
||||
cmd = ['ping', '-n', '1', '-w', str(timeout * 1000), ip_address.strip()]
|
||||
else: # Unix/Linux/macOS
|
||||
@@ -386,79 +371,40 @@ class PrinterMonitor:
|
||||
|
||||
def _check_outlet_status(self, ip_address: str, username: str, password: str, timeout: int = 5) -> Tuple[bool, str]:
|
||||
"""
|
||||
Überprüft den Status einer Smart-Steckdose.
|
||||
Überprüft den Status einer TP-Link Tapo P110-Steckdose.
|
||||
|
||||
Args:
|
||||
ip_address: IP-Adresse der Steckdose
|
||||
username: Benutzername für die Steckdose
|
||||
password: Passwort für die Steckdose
|
||||
timeout: Timeout in Sekunden
|
||||
timeout: Timeout in Sekunden (wird ignoriert, da PyP100 eigenes Timeout hat)
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (Erreichbar, Status) - Status: "on", "off", "unknown"
|
||||
"""
|
||||
if not TAPO_AVAILABLE:
|
||||
monitor_logger.debug("⚠️ PyP100-Modul nicht verfügbar - kann Tapo-Steckdosen-Status nicht abfragen")
|
||||
return False, "unknown"
|
||||
|
||||
try:
|
||||
auth = (username, password)
|
||||
# TP-Link Tapo P110 Verbindung herstellen
|
||||
p110 = PyP110.P110(ip_address, username, password)
|
||||
p110.handshake() # Authentifizierung
|
||||
p110.login() # Login
|
||||
|
||||
# Verschiedene API-Endpunkte für Status-Abfrage versuchen
|
||||
status_endpoints = [
|
||||
f"http://{ip_address}/status",
|
||||
f"http://{ip_address}/api/status",
|
||||
f"http://{ip_address}/relay/status",
|
||||
f"http://{ip_address}/state"
|
||||
]
|
||||
# Geräteinformationen abrufen
|
||||
device_info = p110.getDeviceInfo()
|
||||
|
||||
for endpoint in status_endpoints:
|
||||
try:
|
||||
response = requests.get(endpoint, auth=auth, timeout=timeout)
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
data = response.json()
|
||||
|
||||
# Verschiedene JSON-Strukturen handhaben
|
||||
if isinstance(data, dict):
|
||||
# TP-Link Format
|
||||
if 'system' in data and 'get_sysinfo' in data['system']:
|
||||
relay_state = data['system']['get_sysinfo'].get('relay_state')
|
||||
return True, "on" if relay_state == 1 else "off"
|
||||
|
||||
# Direktes Format
|
||||
if 'relay_state' in data:
|
||||
return True, "on" if data['relay_state'] in [1, True, "on"] else "off"
|
||||
|
||||
if 'state' in data:
|
||||
return True, "on" if data['state'] in [1, True, "on"] else "off"
|
||||
|
||||
if 'power' in data:
|
||||
return True, "on" if data['power'] in [1, True, "on"] else "off"
|
||||
|
||||
# Wenn JSON-Parsing funktioniert, aber Format unbekannt
|
||||
return True, "unknown"
|
||||
|
||||
except ValueError:
|
||||
# Nicht-JSON Antwort - versuche Text-Parsing
|
||||
text = response.text.lower()
|
||||
if "on" in text or "1" in text or "true" in text:
|
||||
return True, "on"
|
||||
elif "off" in text or "0" in text or "false" in text:
|
||||
return True, "off"
|
||||
return True, "unknown"
|
||||
|
||||
except requests.RequestException:
|
||||
continue
|
||||
# Status auswerten
|
||||
device_on = device_info.get('device_on', False)
|
||||
status = "on" if device_on else "off"
|
||||
|
||||
monitor_logger.debug(f"✅ Tapo-Steckdose {ip_address}: Status = {status}")
|
||||
return True, status
|
||||
|
||||
# Wenn spezifische Endpunkte fehlschlagen, einfache Konnektivitätsprüfung
|
||||
try:
|
||||
response = requests.get(f"http://{ip_address}", auth=auth, timeout=timeout)
|
||||
if response.status_code in [200, 401, 403]: # Erreichbar, aber möglicherweise Authentifizierungsproblem
|
||||
return True, "unknown"
|
||||
except requests.RequestException:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
monitor_logger.debug(f"⚠️ Fehler bei Steckdosen-Status-Check {ip_address}: {str(e)}")
|
||||
|
||||
return False, "unknown"
|
||||
monitor_logger.debug(f"⚠️ Fehler bei Tapo-Steckdosen-Status-Check {ip_address}: {str(e)}")
|
||||
return False, "unknown"
|
||||
|
||||
def clear_all_caches(self):
|
||||
"""Löscht alle Caches (Session und DB)."""
|
||||
|
Reference in New Issue
Block a user