Zum Inhalt springen

Shelly Datenbank-Integration

    Teil 3: Sensordaten lokal speichern und verwalten

    In den ersten beiden Teilen dieser Serie hast du die Shelly-Plattform kennengelernt und erste Skripte erstellt. Jetzt wird es richtig spannend: Du lernst, wie du alle Sensordaten dauerhaft in einer eigenen MySQL-Datenbank speicherst – vollständig lokal, ohne Cloud, mit voller Kontrolle.

    Nach diesem Artikel kannst du Temperatur, Luftfeuchtigkeit, Leistungsdaten und Schaltereignisse automatisch in einer Datenbank sammeln. Das ist die Grundlage für spätere Visualisierungen, Auswertungen und Langzeit-Analysen.


    1. Warum eine eigene Datenbank?

    Die Shelly-Cloud bietet zwar Diagramme und Verlaufsdaten – aber warum solltest du trotzdem eine eigene Datenbank betreiben?

    Vorteile der lokalen Datenspeicherung

    • Datenschutz: Alle Daten bleiben bei dir – kein Upload in fremde Clouds
    • Unabhängigkeit: Keine Abhängigkeit von Internet oder Cloud-Diensten
    • Unbegrenzte Speicherung: Keine Limits bei Datenmenge oder Zeitraum
    • Flexible Auswertungen: Beliebige SQL-Abfragen für individuelle Analysen
    • Integration: Kombiniere Shelly-Daten mit anderen Datenquellen
    • Geschwindigkeit: Lokale Abfragen sind deutlich schneller als Cloud-APIs

    🎯 Ziel dieses Artikels: Am Ende hast du ein laufendes System, das alle Shelly-Daten automatisch in deine MySQL-Datenbank schreibt – bereit für Visualisierung und Auswertung.

    2. Das Gesamtsystem im Überblick

    Bevor wir in die Details gehen, hier die Architektur unseres Systems:

    [Shelly-Geräte]
          ↓ JavaScript-Skript
          ↓ HTTP POST (JSON)
    [PHP-Empfangsscript] - dein Server (Raspberry, Mini-PC, Server im Web u.v.m.)
          ↓ Daten validieren
          ↓ INSERT Statement
    [MySQL-Datenbank] - - dein Server (Raspberry, Mini-PC, Server im Web u.v.m.)
          ↓ Gespeicherte Daten
    [Auswertung & Dashboard]
    

    2.1 Welche Daten speichern wir?

    In meinem Bungalow-Setup sammle ich folgende Daten:

    • Temperatur und Luftfeuchtigkeit (Shelly H&T WiFi und H&T BLU)
    • Leistungsmessung (Shelly Plug S Gen3/4: Watt, Spannung, Energie)
    • Schaltzustände (Relais Ein/Aus)
    • Geräteinformationen (Geräte-ID, Name, Batteriestatus)

    📋 Wichtig zu wissen: JavaScript-Scripting funktioniert nur auf Shelly Plus- und Gen3/4-Geräten (Plug S Gen3/4, BLU Gateway). Der Shelly H&T WiFi unterstützt kein Scripting – hier nutzen wir URL-Aktionen. Der H&T BLU sendet nur Bluetooth-Pakete, die vom Gateway ausgewertet werden.


    3. MySQL-Datenbank einrichten

    Ich gehe davon aus, dass du bereits einen MySQL/MariaDB-Server laufen hast (z.B. im Proxmox-Container, Raspberry oder einen PC, auf dem Datenbankdienste verfügbar sind).

    Bitte schaue dir dazu auch meine Beiträge zur Installation von Apache2 und MySQL an. Dort ist bereits beschrieben, wie du eine Datenbank anlegst und wie die Daten zum Beispiel von einem ESP32 gespeichert werden.

    https://diytechadventures.de/sensordaten-an-pc-senden/
    https://diytechadventures.de/daten-an-webserver-senden/

    Nachfolgende Installation betrifft Debian mit in Proxmox laufenden Datenbank-Container.

    Falls noch nicht, installiere MariaDB:

    sudo apt install mariadb-server -y

    3.1 Datenbank und Benutzer erstellen

    Verbinde dich mit deinem MySQL-Server:

    mysql -u root -p, oder falls DB und mysql woanders liegt
    mysql -h 192.168.2.xxx -u dein User -p

    Führe folgende SQL-Befehle aus:

    — Datenbank erstellen
    CREATE DATABASE shelly_data CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

    — Benutzer erstellen
    CREATE USER ’shelly_user’@’%‘ IDENTIFIED BY ‚dein_sicheres_passwort‘;

    — Rechte vergeben
    GRANT ALL PRIVILEGES ON shelly_data.* TO ’shelly_user’@’%‘;
    FLUSH PRIVILEGES;

    ⚠️ Sicherheit: Ersetze ‚dein_sicheres_passwort‘ mit einem starken Passwort! Das ‚%‘ erlaubt Zugriff von allen IPs – für Produktion solltest du das auf spezifische IPs einschränken.

    3.2 Tabellen anlegen

    Jetzt erstellen wir die Tabellen für unsere verschiedenen Datentypen:

    3.2.1 Tabelle für Temperatur und Luftfeuchtigkeit

    USE shelly_data; // diese Datenbank unter mysql nutzen

    CREATE TABLE sensor_data (
    id INT AUTO_INCREMENT PRIMARY KEY,
    device_id VARCHAR(50) NOT NULL,
    device_name VARCHAR(100),
    temperature DECIMAL(5,2),
    humidity DECIMAL(5,2),
    battery INT,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_device_time (device_id, timestamp),
    INDEX idx_timestamp (timestamp)
    ) ENGINE=InnoDB;

    3.2.2 Tabelle für Energiemessung

    CREATE TABLE power_data (
    id INT AUTO_INCREMENT PRIMARY KEY,
    device_id VARCHAR(50) NOT NULL,
    device_name VARCHAR(100),
    power DECIMAL(8,2),
    voltage DECIMAL(6,2),
    current DECIMAL(6,2),
    energy_total DECIMAL(10,3),
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_device_time (device_id, timestamp),
    INDEX idx_timestamp (timestamp)
    ) ENGINE=InnoDB;

    3.2.3 Tabelle für Schaltereignisse

    CREATE TABLE switch_events (
    id INT AUTO_INCREMENT PRIMARY KEY,
    device_id VARCHAR(50) NOT NULL,
    device_name VARCHAR(100),
    switch_state BOOLEAN,
    event_type VARCHAR(20),
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_device_time (device_id, timestamp),
    INDEX idx_timestamp (timestamp)
    ) ENGINE=InnoDB;

    💡 Indizes: Die INDEX-Anweisungen beschleunigen spätere Abfragen erheblich. Der kombinierte Index idx_device_time ist besonders wichtig für Zeitraumabfragen pro Gerät.

    3.3 Tabellen prüfen

    Prüfe ob alle Tabellen korrekt angelegt wurden:

    SHOW TABLES;

    DESCRIBE sensor_data;
    DESCRIBE power_data;
    DESCRIBE switch_events;


    4. PHP-Empfangsscript erstellen

    Das PHP-Script empfängt die HTTP-POST-Requests von den Shelly-Geräten und schreibt die Daten in die Datenbank.

    4.1 Basis-Script für Sensordaten

    Erstelle die Datei /var/www/html/shelly_receive.php oder falls du mit XAMPP ein Datenbanksystem aufgebaut hat, dann den entsprechenden Pfad:

    💡 Hinweis: Dieses Script unterstützt sowohl POST-Requests (von Shelly-Skripten) als auch GET-Requests (von H&T WiFi URL-Aktionen).

    <?php
    date_default_timezone_set('Europe/Berlin');
    // Fehler-Logging aktivieren
    error_reporting(E_ALL);
    ini_set('display_errors', 0);
    ini_set('log_errors', 1);
    ini_set('error_log', '/var/log/apache2/shelly_errors.log');
    
    // Datenbank-Konfiguration
    define('DB_HOST', '192.168.2.xxx'); // bitte Url anpassen
    define('DB_USER', 'shelly_user');
    define('DB_PASS', 'xxxxxxx');  // ANPASSEN!
    define('DB_NAME', 'shelly_data');
    
    
    function map_event_type($raw)
    {
        if ($raw === null) {
            return 'Unbekannt';
        }
    
        switch (strtolower($raw)) {
            case 'ws_in':
            case 'ws':
                return 'App / Weboberfläche';
            case 'btn':
            case 'button':
                return 'Physischer Taster';
            case 'http':
                return 'HTTP-Befehl';
            case 'mqtt':
                return 'MQTT-Befehl';
            case 'timer':
                return 'Timer / Auto-Off';
            case 'schedule':
                return 'Zeitplan';
            case 'cloud':
                return 'Shelly Cloud';
            case 'boot':
                return 'Systemstart';
            default:
                return ucfirst($raw);   // Fallback: Original sauber anzeigen
        }
    }
    
    // ---- BTHome v2 Decoder ----
    function bthome_decode_ht($b64) {
      $buf = base64_decode($b64, true);
      if ($buf === false || strlen($buf) < 2) {
        return ['ok' => false, 'err' => 'bad_base64_or_short'];
      }
      
      $info = ord($buf[0]);
      $enc  = ($info & 0x01) === 1;
      $ver  = $info >> 5;
      
      if ($ver !== 2) {
        return ['ok' => false, 'err' => 'unsupported_version'];
      }
      if ($enc) {
        return ['ok' => false, 'err' => 'encrypted_not_supported'];
      }
      
      $i = 1;
      $n = strlen($buf);
      $out = ['ok' => true];
      
      $get = function(int $bytes) use (&$i, $n, $buf) {
        if ($i + $bytes > $n) return null;
        $v = substr($buf, $i, $bytes);
        $i += $bytes;
        return $v;
      };
      
      while ($i < $n) {
        $id = ord($buf[$i++]);
        
        switch ($id) {
          case 0x00: { // PID uint8
            $v = $get(1);
            if ($v === null) return ['ok' => false, 'err' => 'trunc_pid'];
            $out['pid'] = ord($v);
            break;
          }
          case 0x01: { // battery uint8 %
            $v = $get(1);
            if ($v === null) return ['ok' => false, 'err' => 'trunc_bat'];
            $out['battery'] = ord($v);
            break;
          }
          case 0x02: { // temperature int16 *0.01 °C
            $v = $get(2);
            if ($v === null) return ['ok' => false, 'err' => 'trunc_t01'];
            $u = unpack('v', $v)[1];
            if ($u & 0x8000) $u -= 65536;
            $out['temperature'] = $u * 0.01;
            break;
          }
          case 0x03: { // humidity uint16 *0.01 %
            $v = $get(2);
            if ($v === null) return ['ok' => false, 'err' => 'trunc_h01'];
            $u = unpack('v', $v)[1];
            $out['humidity'] = $u * 0.01;
            break;
          }
          case 0x2E: { // humidity uint8 %
            $v = $get(1);
            if ($v === null) return ['ok' => false, 'err' => 'trunc_h08'];
            $out['humidity'] = ord($v) * 1.0;
            break;
          }
          case 0x45: { // temperature int16 *0.1 °C
            $v = $get(2);
            if ($v === null) return ['ok' => false, 'err' => 'trunc_t10'];
            $u = unpack('v', $v)[1];
            if ($u & 0x8000) $u -= 65536;
            $out['temperature'] = $u * 0.1;
            break;
          }
          default:
            // Unbekannt → beenden (haben aber bereits Werte)
            $i = $n;
        }
      }
      
      return $out;
    }
    
    // ---- Request verarbeiten ----
    
    // GET-Parameter (für H&T WiFi URL-Aktionen)
    if ($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET)) {
        $data = [
            'device_id'    => $_GET['device_id']    ?? 'unknown',
            'device_name'  => $_GET['device_name']  ?? null,
            'temperature'  => $_GET['temperature']  ?? null,
            'humidity'     => $_GET['humidity']     ?? null,
            'battery'      => $_GET['battery']      ?? null,
            'power'        => $_GET['power']        ?? null,
            'voltage'      => $_GET['voltage']      ?? null,
            'current'      => $_GET['current']      ?? null,
            'energy_total' => $_GET['energy_total'] ?? null,
            'switch_state' => $_GET['switch_state'] ?? null,
            // NEU: event_type aus source übernehmen
            'event_type'   => $_GET['source'] ?? ($_GET['event_type'] ?? null),
        ];
    
        
    // POST-Requests
    } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $raw = file_get_contents('php://input');
        $json_data = json_decode($raw, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            http_response_code(400);
            exit('Invalid JSON');
        }
        
        // BLU Gateway Daten (mit fcd2 Base64)
        if (isset($json_data['fcd2']) && isset($json_data['mac'])) {
            error_log("BLU Gateway: MAC=" . $json_data['mac']);
            
            $decoded = bthome_decode_ht($json_data['fcd2']);
            
            if (!$decoded['ok']) {
                error_log("BTHome decode error: " . $decoded['err']);
                http_response_code(400);
                exit('BTHome decode failed');
            }
            
            error_log("BTHome decoded: " . json_encode($decoded));
            
            $data = [
                'device_id' => $json_data['mac'],
                'device_name' => 'BLU_' . substr($json_data['mac'], -5),
                'temperature' => $decoded['temperature'] ?? null,
                'humidity' => $decoded['humidity'] ?? null,
                'battery' => $decoded['battery'] ?? null
            ];
        }
        // Normale JSON-Daten (von Plug S Scripten)
        else {
            $data = $json_data;
        }
        
    } else {
        http_response_code(405);
        exit('Method Not Allowed');
    }
    
    // ---- Datenbankverbindung ----
    try {
        $pdo = new PDO(
            'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4',
            DB_USER,
            DB_PASS,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    } catch (PDOException $e) {
        error_log('DB Connection Error: ' . $e->getMessage());
        http_response_code(500);
        exit('Database Connection Failed');
    }
    
    // ---- Daten speichern ----
    
    if (isset($data['temperature']) || isset($data['humidity'])) {
        // Sensordaten
        $stmt = $pdo->prepare(
            'INSERT INTO sensor_data (device_id, device_name, temperature, humidity, battery) '
            . 'VALUES (?, ?, ?, ?, ?)'
        );
        
        $success = $stmt->execute([
            $data['device_id'] ?? 'unknown',
            $data['device_name'] ?? null,
            $data['temperature'] ?? null,
            $data['humidity'] ?? null,
            $data['battery'] ?? null
        ]);
        
        if ($success) {
            error_log("Sensor-Daten gespeichert: " . ($data['device_id'] ?? 'unknown'));
        }
        
    } elseif (isset($data['power']) || isset($data['voltage'])) {
        // Leistungsdaten
        $stmt = $pdo->prepare(
            'INSERT INTO power_data (device_id, device_name, power, voltage, current, energy_total) '
            . 'VALUES (?, ?, ?, ?, ?, ?)'
        );
        
        $success = $stmt->execute([
            $data['device_id'] ?? 'unknown',
            $data['device_name'] ?? null,
            $data['power'] ?? null,
            $data['voltage'] ?? null,
            $data['current'] ?? null,
            $data['energy_total'] ?? null
        ]);
        
        if ($success) {
            error_log("Power-Daten gespeichert: " . ($data['device_id'] ?? 'unknown'));
        }
        
    } elseif (isset($data['switch_state'])) {
    
        // 0/1 normalisieren
        $switchState = ($data['switch_state'] === 'on') ? 1 : 0;
    
        // Event-Typ auf Klartext abbilden
        $humanEvent = map_event_type($data['event_type'] ?? null);
    
        $stmt = $pdo->prepare(
            'INSERT INTO switch_events (device_id, device_name, switch_state, event_type)
             VALUES (?, ?, ?, ?)'
        );
    
        $success = $stmt->execute([
            $data['device_id']   ?? 'unknown',
            $data['device_name'] ?? null,
            $switchState,
            $humanEvent,   // <-- hier landet der Klartext
        ]);
    
        if ($success) {
            error_log("Switch-Event gespeichert: " . ($data['device_id'] ?? 'unknown'));
        }
    
        
    } else {
        http_response_code(400);
        exit('Unknown data type');
    }
    
    // Erfolg
    http_response_code(200);
    echo json_encode(['status' => 'success', 'message' => 'Data saved']);
    ?>
    

    🔒 Sicherheit: Das Script nutzt Prepared Statements zur Verhinderung von SQL-Injection. Das ist essenziell für sichere Datenbank-Operationen!

    4.2 Script-Berechtigungen setzen

    sudo chown www-data:www-data /var/www/html/shelly_receive.php
    sudo chmod 644 /var/www/html/shelly_receive.php


    5. Shelly-Skripte für Datenübertragung

    Jetzt erstellen wir die Skripte auf den Shelly-Geräten, die die Daten an unser PHP-Script senden.

    5.1 Shelly Plug S Gen3/4 – Leistungsdaten senden

    Dieses Skript misst alle 5 Minuten Leistung, Spannung und Gesamtenergie:

    // Shelly-Skript Leitungsdaten senden
    // Konfiguration
    let SERVER_URL = "http://192.168.2.xxx/shelly_receive.php"; //hier deine URL angeben
    let DEVICE_NAME = "dein_device_name"; // Device Namen festlegen
    let INTERVAL = 300 * 1000; // 5  Minuten
    
    // Funktion zum Senden der Daten
    function sendPowerData() {
      let status = Shelly.getComponentStatus("switch", 0);
      
      let data = {
        device_id: Shelly.getDeviceInfo().id,
        device_name: DEVICE_NAME,
        power: status.apower,
        voltage: status.voltage,
        current: status.current,
        energy_total: status.aenergy.total
      };
      
      Shelly.call("HTTP.POST", {
        url: SERVER_URL,
        body: JSON.stringify(data),
        headers: {"Content-Type": "application/json"},
        timeout: 5
      }, function(res, err) {
        if (err) {
          print("HTTP Error:", err);
        } else {
          print("Data sent:", res.code);
        }
      });
    }
    
    // Timer starten
    Timer.set(INTERVAL, true, sendPowerData);
    
    // Sofort beim Start ausführen
    sendPowerData();
    

    💡 Anpassung: Ersetze SERVER_URL mit der IP deines Webservers und DEVICE_NAME mit einem aussagekräftigen Namen für das Gerät.

    5.2 Schalter Status senden

    // Senden der Switch-Events
    // =======================
    // Konfiguration
    // =======================
    let CONFIG = {
      // URL zu deiner PHP-Datei (bitte anpassen)
      phpUrl: "http://192.168.2.xxx/shelly_receive.php"
    };
    let DEVICE_NAME = "Shelly1PM_mini";
    
    
    // =======================
    // Status senden
    // =======================
    function sendStatus(state, source) {
      let info = Shelly.getDeviceInfo();
      let devName = DEVICE_NAME;  // fester Name, da info.name null ist
      // Parameter bestehen nur aus sicheren Zeichen → kein Encoding nötig
      let url = CONFIG.phpUrl +
                "?device_id="   + info.id +
                "&device_name=" + devName +
                "&switch_state="       + state +
                "&source="      + source;
    
      Shelly.call(
        "HTTP.GET",
        { url: url },
        function (res, code, msg) {
          if (code !== 0) {
            print("HTTP-Fehler:", code, msg);
          } else {
            print("Status gesendet:", url);
          }
        }
      );
    }
    
    // =======================
    // Event-Handler für Schaltvorgänge
    // =======================
    Shelly.addStatusHandler(function (ev) {
      if (ev.component === "switch:0" &&
          ev.delta && typeof ev.delta.output !== "undefined") {
    
        let state  = ev.delta.output ? "on" : "off";
        let source = ev.delta.source ? ev.delta.source : "unknown";
    
        sendStatus(state, source);
      }
    });
    
    // Optional: Status beim Start senden
    Timer.set(3000, false, function () {
      let st = Shelly.getComponentStatus("switch:0");
      let state = st.output ? "on" : "off";
      sendStatus(state, "boot");
    });

    5.3 Shelly H&T WiFi – URL-Aktionen einrichten

    Wichtig: Der Shelly H&T WiFi unterstützt kein Scripting. Stattdessen nutzt er URL-Aktionen, die in der Weboberfläche oder App konfiguriert werden.

    Aufwach-Intervalle:

    • Batteriebetrieb: Alle 2 Stunden (um Batterie zu schonen)
    • USB-Betrieb: Alle 6 Minuten (häufigere Updates)

    URL-Aktion in der Weboberfläche einrichten

    Damit du ohne Cloud auf den Shelly H&T WiFi zugreifen kannst, muss die rückseitige Reset-Taste einmal gedrückt werden. Bei den Plus- und Gen3-Modellen bleibt das Gerät nach dem Button-Druck für 3 Minuten aktiv und ist in dieser Zeit über IP, MQTT, BLE, WiFi, Cloud und WebSocket zum Programmieren erreichbar.

    Die Dokumentation von Shelly zu diesem Gerät kannst du dir hier ansehen:
    https://kb.shelly.cloud/knowledge-base/shelly-plus-h-t-web-interface-guide
    https://shelly-api-docs.shelly.cloud/gen2/Devices/Gen3/ShellyHTG3

    1. Shelly H&T Webinterface öffnen (z.B. http://192.168.x.x)
    2. Menü ‚Actions‘ aufrufen
    3. ‚Crate Action‘ klicken
    4. Bei ‚Trigger‘ wählen: ‚Report‘ (wird bei jedem Aufwachen ausgelöst)
    5. Bei ‚Action‘ → ‚URLs‘ Tab auswählen
      URL eingeben (siehe unten)
    6. URL mit GET-Parametern:
      http://192.168.2.xxx/shelly_receive.php?device_id=“12345″&device_name=Wohnzimmer&temperature=${temp}

      http://192.168.2.xxx/shelly_receive.php?device_id=“12345″&device_name=Wohnzimmer&humidity=${hum}
      (im H&T kann jeweils nur ein Parameter pro Aktion abgefragt werden. Deshalb 2 mal dies Aktion)

    5.4 BLU Gateway – Bluetooth-Sensoren auswerten

    Das BLU Gateway empfängt Daten von Bluetooth-Sensoren und leitet sie weiter:
    https://kb.shelly.cloud/knowledge-base/kbsa-smart-home-automation-connecting-smart-blueto

    //Shelly Skript zum Scannen der in der Nähe befindlichen BLU Sensoren
    let SERVER_URL = "http://192.168.2.xxx/shelly_receive.php"; // deine URL bitte hier eintragen
    
    // BLE-Scanner starten
    BLE.Scanner.Start({
      duration_ms: 30000,
      active: true
    });
    
    // Ereignis-Handler für BLE-Daten
    BLE.Scanner.Subscribe(function(event, result) {
      if (event !== "result") return;
      
      // Prüfe ob es ein Shelly H&T BLU ist
      if (!result.service_data || !result.service_data["fcd2"]) return;
      
      let svcData = result.service_data["fcd2"];
      
      let data = {
        device_id: result.addr,
        device_name: "BLU_" + result.addr.slice(-5),
        temperature: svcData.t,
        humidity: svcData.rh,
        battery: svcData.battery
      };
      
      Shelly.call("HTTP.POST", {
        url: SERVER_URL,
        body: JSON.stringify(data),
        headers: {"Content-Type": "application/json"}
      }, function(res, err) {
        if (!err) {
          print("BLU data sent");
        }
      });
    });
    

    ⚠️ Service UUID: Die UUID fcd2 ist spezifisch für Shelly H&T BLU. Andere BLE-Geräte haben andere UUIDs.

    6. System testen

    Jetzt prüfen wir, ob Daten in der Datenbank ankommen:

    6.1 Shelly-Skripte starten

    1. Öffne das Shelly Webinterface
    2. Gehe zu Scripts
    3. Füge das entsprechende Skript ein
    4. Aktiviere ‚Enable on Boot‘
    5. Klicke ‚Start‘
    6. Beobachte die Logs für Fehler

    6.2 Datenbank prüfen

    Warte 1-2 Minuten und prüfe dann die Datenbank:

    mysql -u shelly_user -p shelly_data

    — Sensordaten prüfen
    SELECT * FROM sensor_data ORDER BY timestamp DESC LIMIT 10;

    Screenshot Datenbankabfrage sensor_data

    — Leistungsdaten prüfen
    SELECT * FROM power_data ORDER BY timestamp DESC LIMIT 10;

    Screenshot DB Abfrage power_data

    — Anzahl der Einträge
    SELECT COUNT(*) as total FROM sensor_data; SELECT COUNT() as total FROM power_data;

    6.3 Troubleshooting

    Keine Daten in der Datenbank?

    1. Shelly-Logs prüfen: Sind dort HTTP-Fehler zu sehen?
    2. PHP-Logs prüfen:

    sudo tail -f /var/log/apache2/error.log
    sudo tail -f /var/log/apache2/shelly_errors.log

    • Netzwerk prüfen: Kann Shelly den Webserver erreichen?

    # Vom Shelly aus (im Script)
    print(„Testing connection to“, SERVER_URL);

    • Manueller Test mit curl:

    curl -X POST http://192.168.2.100/shelly_receive.php \
    -H „Content-Type: application/json“ \
    -d ‚{„device_id“:“test“,“temperature“:22.5,“humidity“:60}‘

    7. Einfache Datenabfragen

    Jetzt wo Daten fließen, hier ein paar nützliche SQL-Abfragen zum Testen:

    7.1 Aktuelle Werte aller Geräte

    SELECT
    device_name,
    temperature,
    humidity,
    battery,
    timestamp
    FROM sensor_data
    WHERE timestamp > NOW() – INTERVAL 5 MINUTE
    ORDER BY timestamp DESC;

    7.2 Durchschnittswerte der letzten Stunde

    SELECT
    device_name,
    ROUND(AVG(temperature), 2) as avg_temp,
    ROUND(AVG(humidity), 2) as avg_humidity
    FROM sensor_data
    WHERE timestamp > NOW() – INTERVAL 1 HOUR
    GROUP BY device_name;

    Datenbankabfrage Durchschnitt

    7.3 Energieverbrauch pro Tag

    SELECT
    DATE(timestamp) as day,
    device_name,
    MAX(energy_total) – MIN(energy_total) as daily_kwh
    FROM power_data
    WHERE timestamp > NOW() – INTERVAL 7 DAY
    GROUP BY DATE(timestamp), device_name
    ORDER BY day DESC;

    Datenbankabfrage täglicher Verbrauch

    7.4 Min/Max-Werte

    SELECT
    device_name,
    MIN(temperature) as min_temp,
    MAX(temperature) as max_temp,
    MIN(humidity) as min_humidity,
    MAX(humidity) as max_humidity
    FROM sensor_data
    WHERE timestamp > NOW() – INTERVAL 24 HOUR
    GROUP BY device_name;

    Datenbankabfrage min/max

    ✅ Erfolg: Wenn diese Abfragen Daten liefern, läuft dein System perfekt! Im nächsten Artikel erstellen wir daraus schöne Dashboards mit Grafiken.


    8. Wartung und Optimierung

    Damit dein System langfristig gut läuft:

    8.1 Alte Daten automatisch löschen

    Erstelle einen Cronjob zum Löschen alter Daten:

    #!/bin/bash
    
    mysql -u shelly_user -p'dein_passwort' shelly_data <<EOF
    DELETE FROM sensor_data WHERE timestamp < NOW() - INTERVAL 90 DAY;
    DELETE FROM power_data WHERE timestamp < NOW() - INTERVAL 90 DAY;
    DELETE FROM switch_events WHERE timestamp < NOW() - INTERVAL 30 DAY;
    OPTIMIZE TABLE sensor_data;
    OPTIMIZE TABLE power_data;
    OPTIMIZE TABLE switch_events;
    EOF

    Ausführbar machen und Cronjob erstellen:
    sudo chmod +x /usr/local/bin/cleanup_shelly_data.sh

    Cronjob: Jeden Sonntag um 3 Uhr
    sudo crontab -e

    Folgende Zeile hinzufügen:
    0 3 * * 0 /usr/local/bin/cleanup_shelly_data.sh


    Was kommt als Nächstes?

    In den kommenden Artikeln dieser Serie auf diytechadventures.de/projekte zeige ich dir:

    • Webinterface: Dashboards für Visualisierung und Steuerung
    • BLU Gateway Scripting: Bluetooth-Sensoren auswerten und weiterleiten
    • Erweiterte Automationen: Komplexe Szenarien aus meinem Bungalow-Projekt
    • ThingSpeak-Integration: Cloud-Visualisierung für Fernzugriff

    Das war ein Hammer DIYTechAdventure. Schreibe mir, ob du alles nachvollziehen konntest. Anregungen nehme ich ebenfalls gerne entgegen. Freue dich weiter auf den nächsten Beitrag, der sich um die grafische Ausgabe der Sensordaten kümmern wird.

    0 Kommentare
    Inline-Feedbacks
    Alle Kommentare anzeigen