Homeassistant/ha-overview.py
Kenearos 3a941b36fa
Add Home Assistant overview tool
This script connects to Home Assistant and generates a comprehensive overview report of integrations, entities, and services, including options to save the report in JSON, TXT, and HTML formats.
2025-11-17 21:03:23 +01:00

488 lines
17 KiB
Python

#!/usr/bin/env python3
"""
Home Assistant Complete Overview Tool
Erstellt eine vollständige Dokumentation aller Integrationen, Entitäten und Dienste
"""
import requests
import json
from datetime import datetime
from collections import defaultdict
class HomeAssistantOverview:
def __init__(self, url, token):
"""
Initialize Home Assistant connection
Args:
url: Home Assistant URL (z.B. http://homeassistant.local:8123)
token: Long-Lived Access Token
"""
self.url = url.rstrip('/')
self.headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
def test_connection(self):
"""Teste die Verbindung zu Home Assistant"""
try:
response = requests.get(f"{self.url}/api/", headers=self.headers, timeout=10)
if response.status_code == 200:
print("✓ Verbindung erfolgreich!")
return True
else:
print(f"✗ Fehler: Status Code {response.status_code}")
return False
except Exception as e:
print(f"✗ Verbindungsfehler: {e}")
return False
def get_config(self):
"""Hole die Konfiguration"""
response = requests.get(f"{self.url}/api/config", headers=self.headers)
return response.json()
def get_components(self):
"""Hole alle installierten Komponenten/Integrationen"""
response = requests.get(f"{self.url}/api/config/core", headers=self.headers)
data = response.json()
return data.get('components', [])
def get_states(self):
"""Hole alle Entitäten mit ihren Zuständen"""
response = requests.get(f"{self.url}/api/states", headers=self.headers)
return response.json()
def get_services(self):
"""Hole alle verfügbaren Services"""
response = requests.get(f"{self.url}/api/services", headers=self.headers)
return response.json()
def get_events(self):
"""Hole alle verfügbaren Events"""
response = requests.get(f"{self.url}/api/events", headers=self.headers)
return response.json()
def analyze_entities(self, states):
"""Analysiere Entitäten nach Domains"""
by_domain = defaultdict(list)
for entity in states:
domain = entity['entity_id'].split('.')[0]
by_domain[domain].append(entity)
return dict(by_domain)
def generate_report(self):
"""Erstelle einen vollständigen Bericht"""
print("\n" + "="*80)
print("HOME ASSISTANT - VOLLSTÄNDIGER ÜBERBLICK")
print("="*80)
print(f"Generiert am: {datetime.now().strftime('%d.%m.%Y um %H:%M:%S')}")
print("="*80 + "\n")
# Test connection
if not self.test_connection():
return None
print("\n📊 Sammle Daten...\n")
# Sammle alle Daten
config = self.get_config()
components = self.get_components()
states = self.get_states()
services = self.get_services()
events = self.get_events()
entities_by_domain = self.analyze_entities(states)
# Erstelle Report-Struktur
report = {
"timestamp": datetime.now().isoformat(),
"system_info": {
"version": config.get('version'),
"location_name": config.get('location_name'),
"timezone": config.get('time_zone'),
"unit_system": config.get('unit_system'),
"latitude": config.get('latitude'),
"longitude": config.get('longitude')
},
"statistics": {
"total_components": len(components),
"total_entities": len(states),
"total_services": sum(len(domain['services']) for domain in services),
"total_domains": len(entities_by_domain),
"total_events": len(events)
},
"components": sorted(components),
"entities_by_domain": {
domain: len(entities) for domain, entities in sorted(entities_by_domain.items())
},
"detailed_entities": entities_by_domain,
"services": services,
"events": events,
"all_states": states
}
return report
def print_summary(self, report):
"""Drucke eine Zusammenfassung auf die Konsole"""
if not report:
return
print("\n" + "="*80)
print("SYSTEM INFORMATIONEN")
print("="*80)
info = report['system_info']
print(f"Version: {info['version']}")
print(f"Standort: {info['location_name']}")
print(f"Zeitzone: {info['timezone']}")
print(f"Einheitensystem: {info['unit_system']}")
print("\n" + "="*80)
print("STATISTIKEN")
print("="*80)
stats = report['statistics']
print(f"Komponenten: {stats['total_components']}")
print(f"Entitäten: {stats['total_entities']}")
print(f"Services: {stats['total_services']}")
print(f"Domains: {stats['total_domains']}")
print(f"Events: {stats['total_events']}")
print("\n" + "="*80)
print("ENTITÄTEN NACH DOMAIN")
print("="*80)
for domain, count in sorted(report['entities_by_domain'].items(), key=lambda x: x[1], reverse=True):
print(f"{domain:.<30} {count:>4}")
print("\n" + "="*80)
print("TOP 20 KOMPONENTEN")
print("="*80)
for i, component in enumerate(report['components'][:20], 1):
print(f"{i:2}. {component}")
if len(report['components']) > 20:
print(f"... und {len(report['components']) - 20} weitere")
def save_report(self, report, format='json'):
"""Speichere den Report in verschiedenen Formaten"""
if not report:
return None
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
if format == 'json':
filename = f"/mnt/user-data/outputs/ha_overview_{timestamp}.json"
with open(filename, 'w', encoding='utf-8') as f:
json.dump(report, f, indent=2, ensure_ascii=False)
return filename
elif format == 'txt':
filename = f"/mnt/user-data/outputs/ha_overview_{timestamp}.txt"
with open(filename, 'w', encoding='utf-8') as f:
f.write("="*80 + "\n")
f.write("HOME ASSISTANT - VOLLSTÄNDIGER ÜBERBLICK\n")
f.write("="*80 + "\n")
f.write(f"Generiert am: {datetime.now().strftime('%d.%m.%Y um %H:%M:%S')}\n")
f.write("="*80 + "\n\n")
# System Info
f.write("SYSTEM INFORMATIONEN\n")
f.write("-"*80 + "\n")
info = report['system_info']
for key, value in info.items():
f.write(f"{key:20}: {value}\n")
# Statistiken
f.write("\n" + "="*80 + "\n")
f.write("STATISTIKEN\n")
f.write("-"*80 + "\n")
stats = report['statistics']
for key, value in stats.items():
f.write(f"{key:20}: {value}\n")
# Komponenten
f.write("\n" + "="*80 + "\n")
f.write(f"ALLE KOMPONENTEN ({len(report['components'])})\n")
f.write("-"*80 + "\n")
for component in sorted(report['components']):
f.write(f"{component}\n")
# Entitäten nach Domain
f.write("\n" + "="*80 + "\n")
f.write("ENTITÄTEN NACH DOMAIN\n")
f.write("-"*80 + "\n")
for domain, count in sorted(report['entities_by_domain'].items(), key=lambda x: x[1], reverse=True):
f.write(f"{domain:.<30} {count:>4}\n")
# Detaillierte Entitäten
f.write("\n" + "="*80 + "\n")
f.write("ALLE ENTITÄTEN (DETAILLIERT)\n")
f.write("-"*80 + "\n")
for domain, entities in sorted(report['detailed_entities'].items()):
f.write(f"\n### {domain.upper()} ({len(entities)} Entitäten) ###\n")
for entity in entities:
f.write(f"\n Entity ID: {entity['entity_id']}\n")
f.write(f" Name: {entity['attributes'].get('friendly_name', 'N/A')}\n")
f.write(f" Zustand: {entity['state']}\n")
if entity['attributes'].get('device_class'):
f.write(f" Klasse: {entity['attributes']['device_class']}\n")
f.write(f" Letzte Änderung: {entity['last_changed']}\n")
# Services
f.write("\n" + "="*80 + "\n")
f.write("VERFÜGBARE SERVICES\n")
f.write("-"*80 + "\n")
for domain_services in report['services']:
domain = domain_services['domain']
f.write(f"\n### {domain.upper()} ###\n")
for service_name, service_data in domain_services['services'].items():
f.write(f"{domain}.{service_name}\n")
if service_data.get('description'):
f.write(f" {service_data['description']}\n")
# Events
f.write("\n" + "="*80 + "\n")
f.write("VERFÜGBARE EVENTS\n")
f.write("-"*80 + "\n")
for event in report['events']:
f.write(f"{event['event']}\n")
return filename
elif format == 'html':
filename = f"/mnt/user-data/outputs/ha_overview_{timestamp}.html"
with open(filename, 'w', encoding='utf-8') as f:
f.write("""<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home Assistant Überblick</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
h1, h2, h3 {
color: #03a9f4;
}
.header {
background: linear-gradient(135deg, #03a9f4 0%, #0288d1 100%);
color: white;
padding: 30px;
border-radius: 10px;
margin-bottom: 20px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-align: center;
}
.stat-card h3 {
margin: 0 0 10px 0;
font-size: 14px;
color: #666;
}
.stat-card .number {
font-size: 32px;
font-weight: bold;
color: #03a9f4;
}
.section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.entity-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 10px;
}
.entity-card {
border: 1px solid #e0e0e0;
padding: 15px;
border-radius: 5px;
background: #fafafa;
}
.entity-card .id {
font-weight: bold;
color: #0288d1;
margin-bottom: 5px;
}
.entity-card .state {
color: #4caf50;
font-weight: bold;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
th {
background-color: #03a9f4;
color: white;
}
.component-tag {
display: inline-block;
background: #e3f2fd;
color: #0288d1;
padding: 5px 10px;
margin: 5px;
border-radius: 15px;
font-size: 12px;
}
</style>
</head>
<body>
""")
f.write(f"""
<div class="header">
<h1>🏠 Home Assistant Überblick</h1>
<p>Generiert am: {datetime.now().strftime('%d.%m.%Y um %H:%M:%S')}</p>
<p>Version: {report['system_info']['version']} | Standort: {report['system_info']['location_name']}</p>
</div>
<div class="stats-grid">
<div class="stat-card">
<h3>Komponenten</h3>
<div class="number">{report['statistics']['total_components']}</div>
</div>
<div class="stat-card">
<h3>Entitäten</h3>
<div class="number">{report['statistics']['total_entities']}</div>
</div>
<div class="stat-card">
<h3>Services</h3>
<div class="number">{report['statistics']['total_services']}</div>
</div>
<div class="stat-card">
<h3>Domains</h3>
<div class="number">{report['statistics']['total_domains']}</div>
</div>
<div class="stat-card">
<h3>Events</h3>
<div class="number">{report['statistics']['total_events']}</div>
</div>
</div>
<div class="section">
<h2>📦 Installierte Komponenten</h2>
""")
for component in sorted(report['components']):
f.write(f' <span class="component-tag">{component}</span>\n')
f.write("""
</div>
<div class="section">
<h2>📊 Entitäten nach Domain</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Anzahl</th>
</tr>
</thead>
<tbody>
""")
for domain, count in sorted(report['entities_by_domain'].items(), key=lambda x: x[1], reverse=True):
f.write(f"""
<tr>
<td><strong>{domain}</strong></td>
<td>{count}</td>
</tr>
""")
f.write("""
</tbody>
</table>
</div>
<div class="section">
<h2>🔧 Alle Entitäten</h2>
""")
for domain, entities in sorted(report['detailed_entities'].items()):
f.write(f"""
<h3>{domain.upper()} ({len(entities)} Entitäten)</h3>
<div class="entity-list">
""")
for entity in entities:
name = entity['attributes'].get('friendly_name', entity['entity_id'])
f.write(f"""
<div class="entity-card">
<div class="id">{entity['entity_id']}</div>
<div>{name}</div>
<div class="state">Zustand: {entity['state']}</div>
</div>
""")
f.write(" </div>\n")
f.write("""
</div>
</body>
</html>
""")
return filename
def main():
"""Hauptfunktion"""
print("\n" + "="*80)
print("HOME ASSISTANT OVERVIEW TOOL")
print("="*80 + "\n")
# Eingaben
url = input("Home Assistant URL (z.B. http://homeassistant.local:8123): ").strip()
token = input("Long-Lived Access Token: ").strip()
# Erstelle Overview-Objekt
ha = HomeAssistantOverview(url, token)
# Generiere Report
report = ha.generate_report()
if report:
# Zeige Zusammenfassung
ha.print_summary(report)
# Speichere in allen Formaten
print("\n" + "="*80)
print("SPEICHERE BERICHTE...")
print("="*80)
json_file = ha.save_report(report, 'json')
txt_file = ha.save_report(report, 'txt')
html_file = ha.save_report(report, 'html')
print(f"\n✓ JSON-Bericht: {json_file}")
print(f"✓ Text-Bericht: {txt_file}")
print(f"✓ HTML-Bericht: {html_file}")
print("\n" + "="*80)
print("FERTIG!")
print("="*80 + "\n")
if __name__ == "__main__":
main()