init
This commit is contained in:
parent
715f90ca9b
commit
fd2d3cee8b
245
README.md
245
README.md
@ -1,2 +1,245 @@
|
|||||||
# Smart-Dashboard
|
# Normalisierte Datenbankstruktur für Somfy Tahoma
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Die Datenbank wurde von einer denormalisierten Struktur (mit JSON in `parameters`)
|
||||||
|
in eine vollständig normalisierte relationale Struktur überführt.
|
||||||
|
|
||||||
|
## Datenbankschema
|
||||||
|
|
||||||
|
### Haupttabellen
|
||||||
|
|
||||||
|
#### `actors`
|
||||||
|
Speichert alle Aktoren (Geräte mit Steuerungsfunktion)
|
||||||
|
|
||||||
|
| Spalte | Typ | Beschreibung |
|
||||||
|
|--------|-----|--------------|
|
||||||
|
| id | INT (PK, AUTO_INCREMENT) | Eindeutige ID |
|
||||||
|
| type | VARCHAR(50) | Gerätetyp (z.B. RollerShutter) |
|
||||||
|
| name | VARCHAR(70) | Name des Geräts |
|
||||||
|
| parameters | TEXT (nullable) | Optionale Meta-Informationen |
|
||||||
|
| url | VARCHAR(100) UNIQUE | Tahoma Device URL |
|
||||||
|
|
||||||
|
#### `sensors`
|
||||||
|
Speichert alle Sensoren (Geräte die Werte melden)
|
||||||
|
|
||||||
|
| Spalte | Typ | Beschreibung |
|
||||||
|
|--------|-----|--------------|
|
||||||
|
| id | INT (PK, AUTO_INCREMENT) | Eindeutige ID |
|
||||||
|
| type | VARCHAR(50) | Sensortyp (z.B. TemperatureSensor) |
|
||||||
|
| name | VARCHAR(70) | Name des Sensors |
|
||||||
|
| parameters | TEXT (nullable) | Optionale Meta-Informationen |
|
||||||
|
| url | VARCHAR(100) UNIQUE | Tahoma Device URL |
|
||||||
|
|
||||||
|
### Aktor-Detailtabellen
|
||||||
|
|
||||||
|
#### `actor_commands`
|
||||||
|
Speichert alle verfügbaren Commands für jeden Aktor
|
||||||
|
|
||||||
|
| Spalte | Typ | Beschreibung |
|
||||||
|
|--------|-----|--------------|
|
||||||
|
| id | INT (PK, AUTO_INCREMENT) | Eindeutige ID |
|
||||||
|
| actor_id | INT (FK → actors.id) | Referenz zum Aktor |
|
||||||
|
| command_name | VARCHAR(100) | Name des Commands (z.B. setPosition, open) |
|
||||||
|
|
||||||
|
**Beispieldaten:**
|
||||||
|
```
|
||||||
|
actor_id | command_name
|
||||||
|
---------|-------------
|
||||||
|
1 | open
|
||||||
|
1 | close
|
||||||
|
1 | setPosition
|
||||||
|
2 | on
|
||||||
|
2 | off
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `command_parameters`
|
||||||
|
Speichert die Parameter für jeden Command
|
||||||
|
|
||||||
|
| Spalte | Typ | Beschreibung |
|
||||||
|
|--------|-----|--------------|
|
||||||
|
| id | INT (PK, AUTO_INCREMENT) | Eindeutige ID |
|
||||||
|
| command_id | INT (FK → actor_commands.id) | Referenz zum Command |
|
||||||
|
| parameter_name | VARCHAR(100) | Name des Parameters (z.B. position) |
|
||||||
|
| parameter_type | VARCHAR(50) | Datentyp (z.B. integer, string) |
|
||||||
|
| min_value | DECIMAL(10,2) | Minimaler Wert (nullable) |
|
||||||
|
| max_value | DECIMAL(10,2) | Maximaler Wert (nullable) |
|
||||||
|
| possible_values | TEXT | JSON Array mit möglichen Werten (nullable) |
|
||||||
|
|
||||||
|
**Beispieldaten:**
|
||||||
|
```
|
||||||
|
command_id | parameter_name | parameter_type | min_value | max_value
|
||||||
|
-----------|----------------|----------------|-----------|----------
|
||||||
|
3 | position | integer | 0 | 100
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `actor_states`
|
||||||
|
Speichert die aktuellen States von Aktoren
|
||||||
|
|
||||||
|
| Spalte | Typ | Beschreibung |
|
||||||
|
|--------|-----|--------------|
|
||||||
|
| id | INT (PK, AUTO_INCREMENT) | Eindeutige ID |
|
||||||
|
| actor_id | INT (FK → actors.id) | Referenz zum Aktor |
|
||||||
|
| state_name | VARCHAR(100) | Name des State (z.B. core:ClosureState) |
|
||||||
|
| state_type | INT | State-Typ Code aus Tahoma API |
|
||||||
|
| current_value | VARCHAR(255) | Aktueller Wert |
|
||||||
|
| unit | VARCHAR(20) | Einheit (nullable) |
|
||||||
|
| last_updated | TIMESTAMP | Zeitpunkt der letzten Aktualisierung |
|
||||||
|
|
||||||
|
### Sensor-Detailtabellen
|
||||||
|
|
||||||
|
#### `sensor_states`
|
||||||
|
Speichert alle verfügbaren States für jeden Sensor
|
||||||
|
|
||||||
|
| Spalte | Typ | Beschreibung |
|
||||||
|
|--------|-----|--------------|
|
||||||
|
| id | INT (PK, AUTO_INCREMENT) | Eindeutige ID |
|
||||||
|
| sensor_id | INT (FK → sensors.id) | Referenz zum Sensor |
|
||||||
|
| state_name | VARCHAR(100) | Name des State (z.B. core:TemperatureState) |
|
||||||
|
| state_type | INT | State-Typ Code aus Tahoma API |
|
||||||
|
| current_value | VARCHAR(255) | Aktueller Wert |
|
||||||
|
| unit | VARCHAR(20) | Einheit (z.B. °C, %) (nullable) |
|
||||||
|
| last_updated | TIMESTAMP | Zeitpunkt der letzten Aktualisierung |
|
||||||
|
|
||||||
|
**Beispieldaten:**
|
||||||
|
```
|
||||||
|
sensor_id | state_name | state_type | current_value | unit
|
||||||
|
----------|-------------------------|------------|---------------|------
|
||||||
|
1 | core:TemperatureState | 1 | 21.5 | °C
|
||||||
|
2 | core:LuminanceState | 1 | 350 | lux
|
||||||
|
```
|
||||||
|
|
||||||
|
## Beziehungen (Foreign Keys)
|
||||||
|
|
||||||
|
```
|
||||||
|
actors (1) ──< (N) actor_commands
|
||||||
|
└──< (N) command_parameters
|
||||||
|
|
||||||
|
actors (1) ──< (N) actor_states
|
||||||
|
|
||||||
|
sensors (1) ──< (N) sensor_states
|
||||||
|
```
|
||||||
|
|
||||||
|
Alle Foreign Keys mit `ON DELETE CASCADE` → Wenn ein Aktor/Sensor gelöscht wird,
|
||||||
|
werden automatisch alle zugehörigen Commands, Parameter und States gelöscht.
|
||||||
|
|
||||||
|
## Hilfreiche Views
|
||||||
|
|
||||||
|
### `view_actors_with_commands`
|
||||||
|
Zeigt alle Aktoren mit ihren Commands und Parametern in einer flachen Ansicht
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM view_actors_with_commands WHERE actor_name = 'Wohnzimmer Rollo';
|
||||||
|
```
|
||||||
|
|
||||||
|
### `view_sensors_with_states`
|
||||||
|
Zeigt alle Sensoren mit ihren aktuellen States
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM view_sensors_with_states WHERE sensor_type = 'TemperatureSensor';
|
||||||
|
```
|
||||||
|
|
||||||
|
### `view_all_devices`
|
||||||
|
Zeigt eine Übersicht aller Geräte (Aktoren und Sensoren)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM view_all_devices ORDER BY name;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Beispiel-Queries
|
||||||
|
|
||||||
|
### Alle Commands eines bestimmten Aktors anzeigen
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
a.name as aktor_name,
|
||||||
|
ac.command_name,
|
||||||
|
cp.parameter_name,
|
||||||
|
cp.min_value,
|
||||||
|
cp.max_value
|
||||||
|
FROM actors a
|
||||||
|
JOIN actor_commands ac ON a.id = ac.actor_id
|
||||||
|
LEFT JOIN command_parameters cp ON ac.id = cp.command_id
|
||||||
|
WHERE a.name = 'Wohnzimmer Rollo';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alle Temperatursensoren mit aktuellem Wert
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
s.name as sensor_name,
|
||||||
|
ss.current_value as temperatur,
|
||||||
|
ss.unit,
|
||||||
|
ss.last_updated
|
||||||
|
FROM sensors s
|
||||||
|
JOIN sensor_states ss ON s.id = ss.sensor_id
|
||||||
|
WHERE s.type = 'TemperatureSensor'
|
||||||
|
AND ss.state_name LIKE '%Temperature%';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alle Aktoren eines bestimmten Typs
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
COUNT(DISTINCT ac.id) as anzahl_commands
|
||||||
|
FROM actors a
|
||||||
|
LEFT JOIN actor_commands ac ON a.id = ac.actor_id
|
||||||
|
WHERE a.type = 'RollerShutter'
|
||||||
|
GROUP BY a.id, a.name, a.type;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commands ohne Parameter finden
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
a.name as aktor,
|
||||||
|
ac.command_name
|
||||||
|
FROM actors a
|
||||||
|
JOIN actor_commands ac ON a.id = ac.actor_id
|
||||||
|
LEFT JOIN command_parameters cp ON ac.id = cp.command_id
|
||||||
|
WHERE cp.id IS NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Vorteile der normalisierten Struktur
|
||||||
|
|
||||||
|
1. **Keine Datenduplizierung**: Jeder Command und Parameter wird nur einmal gespeichert
|
||||||
|
2. **Einfache Queries**: SQL-Joins statt JSON-Parsing
|
||||||
|
3. **Flexible Erweiterung**: Neue Spalten können einfach hinzugefügt werden
|
||||||
|
4. **Referentielle Integrität**: Foreign Keys garantieren Konsistenz
|
||||||
|
5. **Performance**: Indizes auf relevanten Spalten für schnelle Suchen
|
||||||
|
6. **Typsicherheit**: Min/Max als DECIMAL statt String
|
||||||
|
|
||||||
|
## Migration von alter zu neuer Struktur
|
||||||
|
|
||||||
|
Falls Sie bereits Daten in der alten Struktur haben:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Backup erstellen
|
||||||
|
CREATE TABLE actors_old AS SELECT * FROM actors;
|
||||||
|
CREATE TABLE sensors_old AS SELECT * FROM sensors;
|
||||||
|
|
||||||
|
-- Alte Tabellen löschen
|
||||||
|
DROP TABLE actors;
|
||||||
|
DROP TABLE sensors;
|
||||||
|
|
||||||
|
-- Neue Struktur erstellen (database_schema.sql ausführen)
|
||||||
|
SOURCE database_schema.sql;
|
||||||
|
|
||||||
|
-- Python-Script ausführen um Daten neu zu importieren
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wartung
|
||||||
|
|
||||||
|
### Regelmäßige Aktualisierung der States
|
||||||
|
Das Script kann regelmäßig ausgeführt werden. Bei `CLEAR_TABLES = True` werden
|
||||||
|
alle Daten neu importiert. Bei `CLEAR_TABLES = False` können Updates implementiert werden.
|
||||||
|
|
||||||
|
### Veraltete Geräte entfernen
|
||||||
|
```sql
|
||||||
|
-- Geräte finden die nicht mehr in der Tahoma Box vorhanden sind
|
||||||
|
-- (nach erneutem Import)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Index-Optimierung prüfen
|
||||||
|
```sql
|
||||||
|
SHOW INDEX FROM actors;
|
||||||
|
SHOW INDEX FROM actor_commands;
|
||||||
|
```
|
||||||
|
|||||||
181
database_schema.sql
Normal file
181
database_schema.sql
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- Somfy Tahoma Datenbank Schema
|
||||||
|
-- Normalisierte Struktur für Aktoren, Sensoren und ihre Parameter
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Datenbank erstellen (falls noch nicht vorhanden)
|
||||||
|
-- CREATE DATABASE IF NOT EXISTS EnergyFlow CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
|
||||||
|
-- USE EnergyFlow;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- HAUPTTABELLEN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabelle: actors
|
||||||
|
-- Speichert alle Aktoren (Geräte mit Steuerungsfunktion)
|
||||||
|
CREATE TABLE IF NOT EXISTS `actors` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`type` varchar(50) NOT NULL COMMENT 'Gerätetyp z.B. RollerShutter',
|
||||||
|
`name` varchar(70) NOT NULL COMMENT 'Name des Geräts',
|
||||||
|
`parameters` text DEFAULT NULL COMMENT 'Zusätzliche Meta-Informationen als JSON',
|
||||||
|
`url` varchar(100) NOT NULL COMMENT 'Tahoma Device URL',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `url` (`url`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
-- Tabelle: sensors
|
||||||
|
-- Speichert alle Sensoren (Geräte die Werte melden)
|
||||||
|
CREATE TABLE IF NOT EXISTS `sensors` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`type` varchar(50) NOT NULL COMMENT 'Sensortyp z.B. TemperatureSensor',
|
||||||
|
`name` varchar(70) NOT NULL COMMENT 'Name des Sensors',
|
||||||
|
`parameters` text DEFAULT NULL COMMENT 'Zusätzliche Meta-Informationen als JSON',
|
||||||
|
`url` varchar(100) NOT NULL COMMENT 'Tahoma Device URL',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `url` (`url`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- AKTOR-BEZOGENE TABELLEN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabelle: actor_commands
|
||||||
|
-- Speichert alle verfügbaren Commands für jeden Aktor
|
||||||
|
CREATE TABLE IF NOT EXISTS `actor_commands` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`actor_id` int(11) NOT NULL COMMENT 'Referenz zum Aktor',
|
||||||
|
`command_name` varchar(100) NOT NULL COMMENT 'Name des Commands z.B. setPosition, open, close',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `actor_id` (`actor_id`),
|
||||||
|
CONSTRAINT `fk_actor_commands_actor`
|
||||||
|
FOREIGN KEY (`actor_id`) REFERENCES `actors`(`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
-- Tabelle: command_parameters
|
||||||
|
-- Speichert die Parameter für jeden Command
|
||||||
|
CREATE TABLE IF NOT EXISTS `command_parameters` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`command_id` int(11) NOT NULL COMMENT 'Referenz zum Command',
|
||||||
|
`parameter_name` varchar(100) NOT NULL COMMENT 'Name des Parameters z.B. position',
|
||||||
|
`parameter_type` varchar(50) DEFAULT NULL COMMENT 'Datentyp z.B. integer, string',
|
||||||
|
`min_value` decimal(10,2) DEFAULT NULL COMMENT 'Minimaler Wert (falls numerisch)',
|
||||||
|
`max_value` decimal(10,2) DEFAULT NULL COMMENT 'Maximaler Wert (falls numerisch)',
|
||||||
|
`possible_values` text DEFAULT NULL COMMENT 'JSON Array mit möglichen Werten (für Enums)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `command_id` (`command_id`),
|
||||||
|
CONSTRAINT `fk_command_parameters_command`
|
||||||
|
FOREIGN KEY (`command_id`) REFERENCES `actor_commands`(`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- SENSOR-BEZOGENE TABELLEN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabelle: sensor_states
|
||||||
|
-- Speichert alle verfügbaren States für jeden Sensor
|
||||||
|
CREATE TABLE IF NOT EXISTS `sensor_states` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`sensor_id` int(11) NOT NULL COMMENT 'Referenz zum Sensor',
|
||||||
|
`state_name` varchar(100) NOT NULL COMMENT 'Name des State z.B. core:TemperatureState',
|
||||||
|
`state_type` int(11) DEFAULT NULL COMMENT 'State-Typ Code aus Tahoma API',
|
||||||
|
`current_value` varchar(255) DEFAULT NULL COMMENT 'Aktueller Wert des State',
|
||||||
|
`unit` varchar(20) DEFAULT NULL COMMENT 'Einheit z.B. °C, %, lux',
|
||||||
|
`last_updated` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `sensor_id` (`sensor_id`),
|
||||||
|
CONSTRAINT `fk_sensor_states_sensor`
|
||||||
|
FOREIGN KEY (`sensor_id`) REFERENCES `sensors`(`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- GEMEINSAME TABELLE FÜR ACTOR STATES (optional)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabelle: actor_states
|
||||||
|
-- Speichert die aktuellen States von Aktoren (z.B. aktuelle Position)
|
||||||
|
CREATE TABLE IF NOT EXISTS `actor_states` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`actor_id` int(11) NOT NULL COMMENT 'Referenz zum Aktor',
|
||||||
|
`state_name` varchar(100) NOT NULL COMMENT 'Name des State z.B. core:ClosureState',
|
||||||
|
`state_type` int(11) DEFAULT NULL COMMENT 'State-Typ Code aus Tahoma API',
|
||||||
|
`current_value` varchar(255) DEFAULT NULL COMMENT 'Aktueller Wert des State',
|
||||||
|
`unit` varchar(20) DEFAULT NULL COMMENT 'Einheit falls vorhanden',
|
||||||
|
`last_updated` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `actor_id` (`actor_id`),
|
||||||
|
CONSTRAINT `fk_actor_states_actor`
|
||||||
|
FOREIGN KEY (`actor_id`) REFERENCES `actors`(`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDIZES FÜR PERFORMANCE
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Zusätzliche Indizes für häufige Queries
|
||||||
|
CREATE INDEX idx_actors_type ON actors(type);
|
||||||
|
CREATE INDEX idx_sensors_type ON sensors(type);
|
||||||
|
CREATE INDEX idx_actor_commands_name ON actor_commands(command_name);
|
||||||
|
CREATE INDEX idx_sensor_states_name ON sensor_states(state_name);
|
||||||
|
CREATE INDEX idx_actor_states_name ON actor_states(state_name);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- VIEWS (optional - für einfachere Queries)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- View: Alle Aktoren mit ihren Commands
|
||||||
|
CREATE OR REPLACE VIEW view_actors_with_commands AS
|
||||||
|
SELECT
|
||||||
|
a.id as actor_id,
|
||||||
|
a.name as actor_name,
|
||||||
|
a.type as actor_type,
|
||||||
|
a.url as actor_url,
|
||||||
|
ac.id as command_id,
|
||||||
|
ac.command_name,
|
||||||
|
cp.parameter_name,
|
||||||
|
cp.parameter_type,
|
||||||
|
cp.min_value,
|
||||||
|
cp.max_value,
|
||||||
|
cp.possible_values
|
||||||
|
FROM actors a
|
||||||
|
LEFT JOIN actor_commands ac ON a.id = ac.actor_id
|
||||||
|
LEFT JOIN command_parameters cp ON ac.id = cp.command_id
|
||||||
|
ORDER BY a.id, ac.id, cp.id;
|
||||||
|
|
||||||
|
-- View: Alle Sensoren mit ihren States
|
||||||
|
CREATE OR REPLACE VIEW view_sensors_with_states AS
|
||||||
|
SELECT
|
||||||
|
s.id as sensor_id,
|
||||||
|
s.name as sensor_name,
|
||||||
|
s.type as sensor_type,
|
||||||
|
s.url as sensor_url,
|
||||||
|
ss.state_name,
|
||||||
|
ss.state_type,
|
||||||
|
ss.current_value,
|
||||||
|
ss.unit,
|
||||||
|
ss.last_updated
|
||||||
|
FROM sensors s
|
||||||
|
LEFT JOIN sensor_states ss ON s.id = ss.sensor_id
|
||||||
|
ORDER BY s.id, ss.id;
|
||||||
|
|
||||||
|
-- View: Übersicht aller Geräte
|
||||||
|
CREATE OR REPLACE VIEW view_all_devices AS
|
||||||
|
SELECT
|
||||||
|
'actor' as device_category,
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
url
|
||||||
|
FROM actors
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
'sensor' as device_category,
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
url
|
||||||
|
FROM sensors
|
||||||
|
ORDER BY device_category, name;
|
||||||
598
tahoma_to_mysql.py
Normal file
598
tahoma_to_mysql.py
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Somfy Tahoma Local API to MySQL Database Script
|
||||||
|
Liest alle Aktoren und Sensoren aus der Tahoma Box und speichert sie in MySQL
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import pymysql
|
||||||
|
from pymysql import Error
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from typing import List, Dict, Optional
|
||||||
|
import urllib3
|
||||||
|
|
||||||
|
# SSL-Warnungen deaktivieren (Tahoma verwendet selbst-signierte Zertifikate)
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
# Logging konfigurieren
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TahomaAPI:
|
||||||
|
"""Klasse für die Kommunikation mit der Tahoma Local API"""
|
||||||
|
|
||||||
|
def __init__(self, gateway_ip: str, api_token: str):
|
||||||
|
"""
|
||||||
|
Initialisiert die Tahoma API Verbindung
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gateway_ip: IP-Adresse der Tahoma Box
|
||||||
|
api_token: API Token (Bearer Token)
|
||||||
|
"""
|
||||||
|
self.base_url = f"https://{gateway_ip}:8443/enduser-mobile-web/1/enduserAPI"
|
||||||
|
self.headers = {
|
||||||
|
"Authorization": f"Bearer {api_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_setup(self) -> Optional[Dict]:
|
||||||
|
"""
|
||||||
|
Ruft die komplette Setup-Konfiguration ab
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary mit allen Geräten oder None bei Fehler
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
url = f"{self.base_url}/setup"
|
||||||
|
response = requests.get(url, headers=self.headers, verify=False, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"Fehler beim Abrufen der Setup-Daten: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_devices(self) -> List[Dict]:
|
||||||
|
"""
|
||||||
|
Extrahiert alle Geräte aus dem Setup
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Liste aller Geräte
|
||||||
|
"""
|
||||||
|
setup = self.get_setup()
|
||||||
|
if not setup:
|
||||||
|
return []
|
||||||
|
|
||||||
|
devices = setup.get('devices', [])
|
||||||
|
logger.info(f"{len(devices)} Geräte gefunden")
|
||||||
|
return devices
|
||||||
|
|
||||||
|
def get_device_definition(self, device_url: str) -> Optional[Dict]:
|
||||||
|
"""
|
||||||
|
Ruft die detaillierte Definition eines Geräts ab
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device_url: URL des Geräts
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary mit Gerätedefinition oder None bei Fehler
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Device URL encodieren
|
||||||
|
from urllib.parse import quote
|
||||||
|
encoded_url = quote(device_url, safe='')
|
||||||
|
url = f"{self.base_url}/setup/devices/{encoded_url}"
|
||||||
|
response = requests.get(url, headers=self.headers, verify=False, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.debug(f"Fehler beim Abrufen der Device-Definition für {device_url}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_device_states(self, device_url: str) -> List[Dict]:
|
||||||
|
"""
|
||||||
|
Ruft die aktuellen States eines Geräts ab
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device_url: URL des Geräts
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Liste der States
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from urllib.parse import quote
|
||||||
|
encoded_url = quote(device_url, safe='')
|
||||||
|
url = f"{self.base_url}/setup/devices/{encoded_url}/states"
|
||||||
|
response = requests.get(url, headers=self.headers, verify=False, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.debug(f"Fehler beim Abrufen der Device-States für {device_url}: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseManager:
|
||||||
|
"""Klasse für die MySQL-Datenbankoperationen"""
|
||||||
|
|
||||||
|
def __init__(self, host: str, database: str, user: str, password: str, port: int = 3306):
|
||||||
|
"""
|
||||||
|
Initialisiert die Datenbankverbindung
|
||||||
|
|
||||||
|
Args:
|
||||||
|
host: MySQL Host
|
||||||
|
database: Datenbankname
|
||||||
|
user: Benutzername
|
||||||
|
password: Passwort
|
||||||
|
port: Port (Standard: 3306)
|
||||||
|
"""
|
||||||
|
self.host = host
|
||||||
|
self.database = database
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
self.port = port
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
def connect(self) -> bool:
|
||||||
|
"""
|
||||||
|
Stellt Verbindung zur Datenbank her
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True bei Erfolg, False bei Fehler
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.connection = pymysql.connect(
|
||||||
|
host=self.host,
|
||||||
|
database=self.database,
|
||||||
|
user=self.user,
|
||||||
|
password=self.password,
|
||||||
|
port=self.port,
|
||||||
|
charset='utf8mb4'
|
||||||
|
)
|
||||||
|
logger.info("Erfolgreich mit MariaDB/MySQL-Datenbank verbunden")
|
||||||
|
return True
|
||||||
|
except Error as e:
|
||||||
|
logger.error(f"Fehler bei der Datenbankverbindung: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
"""Schließt die Datenbankverbindung"""
|
||||||
|
if self.connection:
|
||||||
|
self.connection.close()
|
||||||
|
logger.info("Datenbankverbindung geschlossen")
|
||||||
|
|
||||||
|
def clear_tables(self):
|
||||||
|
"""Löscht alle Einträge aus allen Tabellen"""
|
||||||
|
try:
|
||||||
|
cursor = self.connection.cursor()
|
||||||
|
# Foreign Key Constraints temporär deaktivieren
|
||||||
|
cursor.execute("SET FOREIGN_KEY_CHECKS=0")
|
||||||
|
|
||||||
|
# Alle Tabellen leeren
|
||||||
|
cursor.execute("DELETE FROM command_parameters")
|
||||||
|
cursor.execute("DELETE FROM actor_commands")
|
||||||
|
cursor.execute("DELETE FROM actor_states")
|
||||||
|
cursor.execute("DELETE FROM actors")
|
||||||
|
|
||||||
|
cursor.execute("DELETE FROM sensor_states")
|
||||||
|
cursor.execute("DELETE FROM sensors")
|
||||||
|
|
||||||
|
# Foreign Key Constraints wieder aktivieren
|
||||||
|
cursor.execute("SET FOREIGN_KEY_CHECKS=1")
|
||||||
|
|
||||||
|
self.connection.commit()
|
||||||
|
logger.info("Alle Tabellen geleert")
|
||||||
|
cursor.close()
|
||||||
|
except Error as e:
|
||||||
|
logger.error(f"Fehler beim Leeren der Tabellen: {e}")
|
||||||
|
self.connection.rollback()
|
||||||
|
|
||||||
|
def insert_actor(self, device_type: str, name: str, url: str,
|
||||||
|
commands: list, states: list) -> bool:
|
||||||
|
"""
|
||||||
|
Fügt einen Aktor mit Commands und States in die Datenbank ein
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device_type: Gerätetyp (z.B. RollerShutter)
|
||||||
|
name: Gerätename
|
||||||
|
url: URL zum Gerät
|
||||||
|
commands: Liste der Commands mit Parametern
|
||||||
|
states: Liste der States
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True bei Erfolg, False bei Fehler
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor = self.connection.cursor()
|
||||||
|
|
||||||
|
# 1. Aktor einfügen
|
||||||
|
query = """
|
||||||
|
INSERT INTO actors (type, name, parameters, url)
|
||||||
|
VALUES (%s, %s, NULL, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(query, (device_type, name, url))
|
||||||
|
actor_id = cursor.lastrowid
|
||||||
|
|
||||||
|
# 2. Commands einfügen
|
||||||
|
for cmd in commands:
|
||||||
|
command_name = cmd.get('command', '')
|
||||||
|
|
||||||
|
# Command einfügen
|
||||||
|
cmd_query = """
|
||||||
|
INSERT INTO actor_commands (actor_id, command_name)
|
||||||
|
VALUES (%s, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(cmd_query, (actor_id, command_name))
|
||||||
|
command_id = cursor.lastrowid
|
||||||
|
|
||||||
|
# Parameter des Commands einfügen
|
||||||
|
cmd_params = cmd.get('parameters', [])
|
||||||
|
for param in cmd_params:
|
||||||
|
param_query = """
|
||||||
|
INSERT INTO command_parameters
|
||||||
|
(command_id, parameter_name, parameter_type, min_value, max_value, possible_values)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
param_name = param.get('name', '')
|
||||||
|
param_type = param.get('type', '')
|
||||||
|
min_val = param.get('min')
|
||||||
|
max_val = param.get('max')
|
||||||
|
possible_vals = json.dumps(param.get('values')) if 'values' in param else None
|
||||||
|
|
||||||
|
cursor.execute(param_query,
|
||||||
|
(command_id, param_name, param_type, min_val, max_val, possible_vals))
|
||||||
|
|
||||||
|
# 3. States einfügen
|
||||||
|
for state in states:
|
||||||
|
state_query = """
|
||||||
|
INSERT INTO actor_states
|
||||||
|
(actor_id, state_name, state_type, current_value)
|
||||||
|
VALUES (%s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
state_name = state.get('name', '')
|
||||||
|
state_type = state.get('type', 0)
|
||||||
|
current_value = str(state.get('current_value', '')) if 'current_value' in state else None
|
||||||
|
|
||||||
|
cursor.execute(state_query, (actor_id, state_name, state_type, current_value))
|
||||||
|
|
||||||
|
self.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Error as e:
|
||||||
|
logger.error(f"Fehler beim Einfügen des Aktors {name}: {e}")
|
||||||
|
self.connection.rollback()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def insert_sensor(self, device_type: str, name: str, url: str,
|
||||||
|
states: list) -> bool:
|
||||||
|
"""
|
||||||
|
Fügt einen Sensor mit States in die Datenbank ein
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device_type: Gerätetyp (z.B. TemperatureSensor)
|
||||||
|
name: Gerätename
|
||||||
|
url: URL zum Gerät
|
||||||
|
states: Liste der States
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True bei Erfolg, False bei Fehler
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor = self.connection.cursor()
|
||||||
|
|
||||||
|
# 1. Sensor einfügen
|
||||||
|
query = """
|
||||||
|
INSERT INTO sensors (type, name, parameters, url)
|
||||||
|
VALUES (%s, %s, NULL, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(query, (device_type, name, url))
|
||||||
|
sensor_id = cursor.lastrowid
|
||||||
|
|
||||||
|
# 2. States einfügen
|
||||||
|
for state in states:
|
||||||
|
state_query = """
|
||||||
|
INSERT INTO sensor_states
|
||||||
|
(sensor_id, state_name, state_type, current_value)
|
||||||
|
VALUES (%s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
state_name = state.get('name', '')
|
||||||
|
state_type = state.get('type', 0)
|
||||||
|
current_value = str(state.get('current_value', '')) if 'current_value' in state else None
|
||||||
|
|
||||||
|
cursor.execute(state_query, (sensor_id, state_name, state_type, current_value))
|
||||||
|
|
||||||
|
self.connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Error as e:
|
||||||
|
logger.error(f"Fehler beim Einfügen des Sensors {name}: {e}")
|
||||||
|
self.connection.rollback()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceClassifier:
|
||||||
|
"""Klassifiziert Geräte als Aktoren oder Sensoren"""
|
||||||
|
|
||||||
|
# Bekannte Aktor-Typen (können erweitert werden)
|
||||||
|
ACTOR_TYPES = {
|
||||||
|
'RollerShutter', 'ExteriorScreen', 'Awning', 'Blind',
|
||||||
|
'GarageDoor', 'Window', 'Light', 'OnOff', 'DimmableLight',
|
||||||
|
'HeatingSystem', 'Valve', 'Switch', 'Door', 'Curtain',
|
||||||
|
'VenetianBlind', 'PergolaScreen'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bekannte Sensor-Typen (können erweitert werden)
|
||||||
|
SENSOR_TYPES = {
|
||||||
|
'TemperatureSensor', 'LightSensor', 'HumiditySensor',
|
||||||
|
'ContactSensor', 'OccupancySensor', 'SmokeSensor',
|
||||||
|
'WaterDetectionSensor', 'WindowHandle', 'MotionSensor',
|
||||||
|
'SunSensor', 'WindSensor', 'RainSensor', 'ConsumptionSensor'
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_actor(cls, device: Dict) -> bool:
|
||||||
|
"""
|
||||||
|
Prüft, ob ein Gerät ein Aktor ist
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device: Geräte-Dictionary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True wenn Aktor, False sonst
|
||||||
|
"""
|
||||||
|
device_type = device.get('uiClass', '')
|
||||||
|
|
||||||
|
# Prüfung nach bekannten Typen
|
||||||
|
if device_type in cls.ACTOR_TYPES:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Prüfung nach Commandos (Aktoren haben typischerweise Commands)
|
||||||
|
commands = device.get('definition', {}).get('commands', [])
|
||||||
|
if commands and len(commands) > 0:
|
||||||
|
# Wenn Commands wie open, close, on, off existieren
|
||||||
|
command_names = [cmd.get('commandName', '') for cmd in commands]
|
||||||
|
actor_commands = {'open', 'close', 'on', 'off', 'up', 'down', 'setPosition', 'dim'}
|
||||||
|
if any(cmd in actor_commands for cmd in command_names):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_sensor(cls, device: Dict) -> bool:
|
||||||
|
"""
|
||||||
|
Prüft, ob ein Gerät ein Sensor ist
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device: Geräte-Dictionary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True wenn Sensor, False sonst
|
||||||
|
"""
|
||||||
|
device_type = device.get('uiClass', '')
|
||||||
|
|
||||||
|
# Prüfung nach bekannten Typen
|
||||||
|
if device_type in cls.SENSOR_TYPES:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Prüfung nach States (Sensoren haben typischerweise nur States, keine Commands)
|
||||||
|
states = device.get('states', [])
|
||||||
|
commands = device.get('definition', {}).get('commands', [])
|
||||||
|
|
||||||
|
# Sensor hat States aber keine oder nur wenige Commands
|
||||||
|
if states and len(states) > 0 and len(commands) <= 1:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def extract_actor_data(device: Dict) -> tuple:
|
||||||
|
"""
|
||||||
|
Extrahiert Commands und States aus einem Aktor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device: Geräte-Dictionary von der Tahoma API
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple (commands_list, states_list)
|
||||||
|
"""
|
||||||
|
commands = []
|
||||||
|
states = []
|
||||||
|
|
||||||
|
# Commands aus der Definition extrahieren
|
||||||
|
cmd_definitions = device.get('definition', {}).get('commands', [])
|
||||||
|
|
||||||
|
for cmd in cmd_definitions:
|
||||||
|
command_name = cmd.get('commandName', '')
|
||||||
|
|
||||||
|
command_entry = {
|
||||||
|
'command': command_name,
|
||||||
|
'parameters': []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Alle Parameter des Commands durchgehen
|
||||||
|
cmd_params = cmd.get('parameters', [])
|
||||||
|
for cmd_param in cmd_params:
|
||||||
|
param_detail = {
|
||||||
|
'name': cmd_param.get('name', 'value')
|
||||||
|
}
|
||||||
|
|
||||||
|
# Datentyp
|
||||||
|
param_type = cmd_param.get('type')
|
||||||
|
if param_type:
|
||||||
|
param_detail['type'] = param_type
|
||||||
|
|
||||||
|
# Min/Max Werte für numerische Parameter
|
||||||
|
if 'min' in cmd_param:
|
||||||
|
param_detail['min'] = cmd_param['min']
|
||||||
|
if 'max' in cmd_param:
|
||||||
|
param_detail['max'] = cmd_param['max']
|
||||||
|
|
||||||
|
# Mögliche Werte (enum)
|
||||||
|
if 'values' in cmd_param:
|
||||||
|
param_detail['values'] = cmd_param['values']
|
||||||
|
|
||||||
|
command_entry['parameters'].append(param_detail)
|
||||||
|
|
||||||
|
commands.append(command_entry)
|
||||||
|
|
||||||
|
# States extrahieren
|
||||||
|
state_definitions = device.get('states', [])
|
||||||
|
for state in state_definitions:
|
||||||
|
state_name = state.get('name', '')
|
||||||
|
if state_name:
|
||||||
|
state_entry = {
|
||||||
|
'name': state_name,
|
||||||
|
'type': state.get('type', 0)
|
||||||
|
}
|
||||||
|
if 'value' in state:
|
||||||
|
state_entry['current_value'] = state['value']
|
||||||
|
states.append(state_entry)
|
||||||
|
|
||||||
|
return commands, states
|
||||||
|
|
||||||
|
|
||||||
|
def extract_sensor_data(device: Dict) -> list:
|
||||||
|
"""
|
||||||
|
Extrahiert States aus einem Sensor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
device: Geräte-Dictionary von der Tahoma API
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Liste der States
|
||||||
|
"""
|
||||||
|
states = []
|
||||||
|
|
||||||
|
# States extrahieren
|
||||||
|
state_definitions = device.get('states', [])
|
||||||
|
for state in state_definitions:
|
||||||
|
state_name = state.get('name', '')
|
||||||
|
if state_name:
|
||||||
|
state_entry = {
|
||||||
|
'name': state_name,
|
||||||
|
'type': state.get('type', 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Aktueller Wert falls vorhanden
|
||||||
|
if 'value' in state:
|
||||||
|
state_entry['current_value'] = state['value']
|
||||||
|
|
||||||
|
states.append(state_entry)
|
||||||
|
|
||||||
|
return states
|
||||||
|
|
||||||
|
|
||||||
|
def process_devices(tahoma: TahomaAPI, db: DatabaseManager, clear_before_insert: bool = True):
|
||||||
|
"""
|
||||||
|
Verarbeitet alle Geräte und speichert sie in der Datenbank
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tahoma: TahomaAPI Instanz
|
||||||
|
db: DatabaseManager Instanz
|
||||||
|
clear_before_insert: Tabellen vor dem Einfügen leeren (Standard: True)
|
||||||
|
"""
|
||||||
|
# Geräte von der API abrufen
|
||||||
|
devices = tahoma.get_devices()
|
||||||
|
|
||||||
|
if not devices:
|
||||||
|
logger.warning("Keine Geräte gefunden")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Optional: Tabellen leeren
|
||||||
|
if clear_before_insert:
|
||||||
|
db.clear_tables()
|
||||||
|
|
||||||
|
actor_count = 0
|
||||||
|
sensor_count = 0
|
||||||
|
unknown_count = 0
|
||||||
|
|
||||||
|
for device in devices:
|
||||||
|
device_url = device.get('deviceURL', '')
|
||||||
|
device_name = device.get('label', 'Unbekannt')
|
||||||
|
device_type = device.get('uiClass', 'Unknown')
|
||||||
|
|
||||||
|
# Gerät klassifizieren
|
||||||
|
is_actor = DeviceClassifier.is_actor(device)
|
||||||
|
is_sensor = DeviceClassifier.is_sensor(device)
|
||||||
|
|
||||||
|
if is_actor:
|
||||||
|
# Daten extrahieren
|
||||||
|
commands, states = extract_actor_data(device)
|
||||||
|
|
||||||
|
# In Datenbank speichern
|
||||||
|
if db.insert_actor(device_type, device_name, device_url, commands, states):
|
||||||
|
actor_count += 1
|
||||||
|
logger.info(f"Aktor hinzugefügt: {device_name} ({device_type}) - "
|
||||||
|
f"{len(commands)} Commands, {len(states)} States")
|
||||||
|
|
||||||
|
elif is_sensor:
|
||||||
|
# Daten extrahieren
|
||||||
|
states = extract_sensor_data(device)
|
||||||
|
|
||||||
|
# In Datenbank speichern
|
||||||
|
if db.insert_sensor(device_type, device_name, device_url, states):
|
||||||
|
sensor_count += 1
|
||||||
|
logger.info(f"Sensor hinzugefügt: {device_name} ({device_type}) - "
|
||||||
|
f"{len(states)} States")
|
||||||
|
|
||||||
|
else:
|
||||||
|
unknown_count += 1
|
||||||
|
logger.warning(f"Unbekanntes Gerät: {device_name} ({device_type})")
|
||||||
|
|
||||||
|
logger.info(f"\nZusammenfassung:")
|
||||||
|
logger.info(f"Aktoren gespeichert: {actor_count}")
|
||||||
|
logger.info(f"Sensoren gespeichert: {sensor_count}")
|
||||||
|
logger.info(f"Unbekannte Geräte: {unknown_count}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Hauptfunktion"""
|
||||||
|
|
||||||
|
# ===== KONFIGURATION =====
|
||||||
|
# Tahoma Box Einstellungen
|
||||||
|
TAHOMA_IP = "192.168.1.XXX" # IP-Adresse Ihrer Tahoma Box
|
||||||
|
TAHOMA_TOKEN = "YOUR_API_TOKEN_HERE" # Ihr API Token
|
||||||
|
|
||||||
|
# MySQL Datenbank Einstellungen
|
||||||
|
DB_HOST = "localhost"
|
||||||
|
DB_NAME = "EnergyFlow"
|
||||||
|
DB_USER = "your_username"
|
||||||
|
DB_PASSWORD = "your_password"
|
||||||
|
DB_PORT = 3306
|
||||||
|
|
||||||
|
# Optionen
|
||||||
|
CLEAR_TABLES = True # Tabellen vor dem Import leeren
|
||||||
|
# =========================
|
||||||
|
|
||||||
|
# Tahoma API initialisieren
|
||||||
|
logger.info("Verbinde mit Tahoma Box...")
|
||||||
|
tahoma = TahomaAPI(TAHOMA_IP, TAHOMA_TOKEN)
|
||||||
|
|
||||||
|
# Datenbank initialisieren
|
||||||
|
logger.info("Verbinde mit MySQL-Datenbank...")
|
||||||
|
db = DatabaseManager(DB_HOST, DB_NAME, DB_USER, DB_PASSWORD, DB_PORT)
|
||||||
|
|
||||||
|
if not db.connect():
|
||||||
|
logger.error("Datenbankverbindung fehlgeschlagen. Abbruch.")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Geräte verarbeiten und in Datenbank speichern
|
||||||
|
process_devices(tahoma, db, clear_before_insert=CLEAR_TABLES)
|
||||||
|
logger.info("Import erfolgreich abgeschlossen!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler während der Verarbeitung: {e}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Datenbankverbindung schließen
|
||||||
|
db.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
x
Reference in New Issue
Block a user