281 lines
8.9 KiB
Python
281 lines
8.9 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Tapo Steckdosen Diagnose-Tool
|
||
============================
|
||
|
||
Dieses Script hilft bei der Diagnose von Verbindungsproblemen mit Tapo-Steckdosen.
|
||
|
||
Autor: Till Tomczak
|
||
Datum: 2025-06-20
|
||
"""
|
||
|
||
import sys
|
||
import socket
|
||
import subprocess
|
||
import time
|
||
import os
|
||
from datetime import datetime
|
||
|
||
# Farben für Terminal-Ausgabe
|
||
class Colors:
|
||
GREEN = '\033[92m'
|
||
YELLOW = '\033[93m'
|
||
RED = '\033[91m'
|
||
BLUE = '\033[94m'
|
||
ENDC = '\033[0m'
|
||
BOLD = '\033[1m'
|
||
|
||
def print_header(text):
|
||
"""Druckt eine formatierte Überschrift"""
|
||
print(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.ENDC}")
|
||
print(f"{Colors.BOLD}{Colors.BLUE}{text:^60}{Colors.ENDC}")
|
||
print(f"{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.ENDC}\n")
|
||
|
||
def print_success(text):
|
||
"""Druckt eine Erfolgsmeldung"""
|
||
print(f"{Colors.GREEN}✅ {text}{Colors.ENDC}")
|
||
|
||
def print_warning(text):
|
||
"""Druckt eine Warnung"""
|
||
print(f"{Colors.YELLOW}⚠️ {text}{Colors.ENDC}")
|
||
|
||
def print_error(text):
|
||
"""Druckt eine Fehlermeldung"""
|
||
print(f"{Colors.RED}❌ {text}{Colors.ENDC}")
|
||
|
||
def print_info(text):
|
||
"""Druckt eine Info-Meldung"""
|
||
print(f"{Colors.BLUE}ℹ️ {text}{Colors.ENDC}")
|
||
|
||
def test_port_connection(ip, port, timeout=3):
|
||
"""
|
||
Testet die Verbindung zu einem bestimmten Port.
|
||
|
||
Args:
|
||
ip: IP-Adresse
|
||
port: Port-Nummer
|
||
timeout: Timeout in Sekunden
|
||
|
||
Returns:
|
||
bool: True wenn Verbindung möglich
|
||
"""
|
||
try:
|
||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
sock.settimeout(timeout)
|
||
result = sock.connect_ex((ip, port))
|
||
sock.close()
|
||
return result == 0
|
||
except Exception as e:
|
||
print_error(f"Socket-Fehler: {e}")
|
||
return False
|
||
|
||
def test_icmp_ping(ip):
|
||
"""
|
||
Führt einen ICMP Ping durch.
|
||
|
||
Args:
|
||
ip: IP-Adresse
|
||
|
||
Returns:
|
||
bool: True wenn Ping erfolgreich
|
||
"""
|
||
try:
|
||
# Windows und Linux kompatibel
|
||
param = '-n' if os.name == 'nt' else '-c'
|
||
command = ['ping', param, '1', '-w', '2000', ip]
|
||
result = subprocess.run(command, capture_output=True, text=True, timeout=5)
|
||
return result.returncode == 0
|
||
except Exception as e:
|
||
print_error(f"Ping-Fehler: {e}")
|
||
return False
|
||
|
||
def test_dns_resolution(hostname):
|
||
"""
|
||
Testet die DNS-Auflösung eines Hostnamens.
|
||
|
||
Args:
|
||
hostname: Hostname zum Auflösen
|
||
|
||
Returns:
|
||
str: Aufgelöste IP oder None
|
||
"""
|
||
try:
|
||
ip = socket.gethostbyname(hostname)
|
||
return ip
|
||
except Exception as e:
|
||
print_error(f"DNS-Auflösung fehlgeschlagen: {e}")
|
||
return None
|
||
|
||
def test_tapo_connection(ip, username="admin", password="admin"):
|
||
"""
|
||
Testet die Tapo-Verbindung.
|
||
|
||
Args:
|
||
ip: IP-Adresse der Steckdose
|
||
username: Tapo-Benutzername
|
||
password: Tapo-Passwort
|
||
|
||
Returns:
|
||
dict: Ergebnis der Tests
|
||
"""
|
||
results = {
|
||
"timestamp": datetime.now().isoformat(),
|
||
"ip": ip,
|
||
"tests": {}
|
||
}
|
||
|
||
# Test 1: ICMP Ping
|
||
print_info(f"Teste ICMP Ping zu {ip}...")
|
||
ping_result = test_icmp_ping(ip)
|
||
results["tests"]["icmp_ping"] = ping_result
|
||
if ping_result:
|
||
print_success(f"ICMP Ping zu {ip} erfolgreich")
|
||
else:
|
||
print_warning(f"ICMP Ping zu {ip} fehlgeschlagen (kann durch Firewall blockiert sein)")
|
||
|
||
# Test 2: Port 80 (HTTP)
|
||
print_info(f"Teste Port 80 (HTTP) auf {ip}...")
|
||
port80_result = test_port_connection(ip, 80)
|
||
results["tests"]["port_80"] = port80_result
|
||
if port80_result:
|
||
print_success(f"Port 80 auf {ip} ist erreichbar")
|
||
else:
|
||
print_error(f"Port 80 auf {ip} ist NICHT erreichbar")
|
||
|
||
# Test 3: Port 9999 (Tapo-spezifisch)
|
||
print_info(f"Teste Port 9999 (Tapo) auf {ip}...")
|
||
port9999_result = test_port_connection(ip, 9999)
|
||
results["tests"]["port_9999"] = port9999_result
|
||
if port9999_result:
|
||
print_success(f"Port 9999 auf {ip} ist erreichbar")
|
||
else:
|
||
print_warning(f"Port 9999 auf {ip} ist nicht erreichbar")
|
||
|
||
# Test 4: PyP100 Verbindung
|
||
try:
|
||
print_info("Teste PyP100 Bibliothek...")
|
||
from PyP100.PyP100 import P100
|
||
|
||
print_info(f"Versuche Tapo-Handshake mit {ip}...")
|
||
p100 = P100(ip, username, password)
|
||
|
||
# Handshake
|
||
p100.handshake()
|
||
print_success("Tapo-Handshake erfolgreich")
|
||
results["tests"]["tapo_handshake"] = True
|
||
|
||
# Login
|
||
p100.login()
|
||
print_success("Tapo-Login erfolgreich")
|
||
results["tests"]["tapo_login"] = True
|
||
|
||
# Device Info
|
||
device_info = p100.getDeviceInfo()
|
||
if device_info and 'error_code' in device_info and device_info['error_code'] == 0:
|
||
print_success("Tapo-Geräteinformationen erfolgreich abgerufen")
|
||
results["tests"]["tapo_device_info"] = True
|
||
results["device_info"] = device_info.get('result', {})
|
||
|
||
# Status anzeigen
|
||
device_on = device_info.get('result', {}).get('device_on', False)
|
||
print_info(f"Steckdosen-Status: {'EIN' if device_on else 'AUS'}")
|
||
else:
|
||
print_error("Konnte Geräteinformationen nicht abrufen")
|
||
results["tests"]["tapo_device_info"] = False
|
||
|
||
except ImportError:
|
||
print_error("PyP100 Bibliothek nicht installiert!")
|
||
print_info("Installiere mit: pip install PyP100")
|
||
results["tests"]["pyp100_available"] = False
|
||
except Exception as e:
|
||
print_error(f"Tapo-Verbindung fehlgeschlagen: {e}")
|
||
results["tests"]["tapo_connection"] = False
|
||
results["error"] = str(e)
|
||
|
||
return results
|
||
|
||
def diagnose_network():
|
||
"""Führt eine allgemeine Netzwerk-Diagnose durch"""
|
||
print_header("Netzwerk-Diagnose")
|
||
|
||
# Lokale IP-Adresse ermitteln
|
||
try:
|
||
hostname = socket.gethostname()
|
||
local_ip = socket.gethostbyname(hostname)
|
||
print_info(f"Lokaler Hostname: {hostname}")
|
||
print_info(f"Lokale IP-Adresse: {local_ip}")
|
||
except Exception as e:
|
||
print_error(f"Konnte lokale IP nicht ermitteln: {e}")
|
||
|
||
# Gateway ermitteln (nur Linux/Unix)
|
||
if os.name != 'nt':
|
||
try:
|
||
result = subprocess.run(['ip', 'route', 'show'], capture_output=True, text=True)
|
||
if result.returncode == 0:
|
||
lines = result.stdout.strip().split('\n')
|
||
for line in lines:
|
||
if 'default' in line:
|
||
gateway = line.split()[2]
|
||
print_info(f"Standard-Gateway: {gateway}")
|
||
break
|
||
except:
|
||
pass
|
||
|
||
# DNS-Test
|
||
print_info("Teste DNS-Auflösung...")
|
||
google_ip = test_dns_resolution("google.com")
|
||
if google_ip:
|
||
print_success(f"DNS funktioniert (google.com -> {google_ip})")
|
||
else:
|
||
print_error("DNS-Auflösung fehlgeschlagen")
|
||
|
||
def main():
|
||
"""Hauptfunktion"""
|
||
print_header("Tapo Steckdosen Diagnose-Tool")
|
||
print_info(f"Start: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
# Argumente prüfen
|
||
if len(sys.argv) < 2:
|
||
print_error("Verwendung: python diagnose_tapo.py <IP-Adresse> [username] [password]")
|
||
print_info("Beispiel: python diagnose_tapo.py 192.168.1.100")
|
||
print_info("Beispiel: python diagnose_tapo.py 192.168.1.100 admin admin")
|
||
sys.exit(1)
|
||
|
||
ip_address = sys.argv[1]
|
||
username = sys.argv[2] if len(sys.argv) > 2 else "admin"
|
||
password = sys.argv[3] if len(sys.argv) > 3 else "admin"
|
||
|
||
# Netzwerk-Diagnose
|
||
diagnose_network()
|
||
|
||
# Tapo-Diagnose
|
||
print_header(f"Teste Tapo-Steckdose: {ip_address}")
|
||
results = test_tapo_connection(ip_address, username, password)
|
||
|
||
# Zusammenfassung
|
||
print_header("Diagnose-Zusammenfassung")
|
||
|
||
successful_tests = sum(1 for test, result in results["tests"].items() if result is True)
|
||
total_tests = len(results["tests"])
|
||
|
||
print_info(f"Erfolgreiche Tests: {successful_tests}/{total_tests}")
|
||
|
||
# Empfehlungen
|
||
if not results["tests"].get("port_80", False):
|
||
print_warning("\nEmpfehlungen:")
|
||
print_warning("1. Stelle sicher, dass die Steckdose eingeschaltet ist")
|
||
print_warning("2. Prüfe, ob die IP-Adresse korrekt ist")
|
||
print_warning("3. Stelle sicher, dass sich die Steckdose im gleichen Netzwerk befindet")
|
||
print_warning("4. Prüfe Firewall-Einstellungen")
|
||
print_warning("5. Versuche die Steckdose zurückzusetzen (Reset-Knopf)")
|
||
|
||
if results["tests"].get("port_80", False) and not results["tests"].get("tapo_login", False):
|
||
print_warning("\nAuthentifizierungsproblem:")
|
||
print_warning("1. Prüfe Benutzername und Passwort")
|
||
print_warning("2. Standard-Credentials sind meist 'admin'/'admin'")
|
||
print_warning("3. Wurden die Credentials in der Tapo-App geändert?")
|
||
|
||
print_info(f"\nEnde: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
if __name__ == "__main__":
|
||
main() |