Solardaten live auf der Webseite – so funktioniert das Dashboard

Wie ich mit einem Cloudflare Worker die SolarManager-API abfrage, die Daten in einem KV-Speicher zwischenspeichere und dann live auf pv-strom.ch anzeige – ohne eigenen Server.

Auf der Startseite von pv-strom.ch läuft ein Live-Dashboard: Aktuelle PV-Produktion, Batteriestand, Netzbezug und Einspeisung – alles in Echtzeit. Wer das sieht, fragt manchmal: Wie funktioniert das technisch? Woher kommen die Daten?

Dieser Artikel beschreibt den Weg – von der API des SolarManagers bis zum fertigen Dashboard im Browser.

Das Prinzip auf einen Blick

🔆 SolarManager
API v3
⚙️ Cloudflare
Worker
🗄️ KV-Speicher
Zwischenlager
🌐 Dashboard
pv-strom.ch

Der SolarManager ist das Energie-Management-System (HEMS) meiner Anlage. Er hat eine REST-API, über die man alle Live-Daten abrufen kann. Ein Cloudflare Worker – ein kleines Stück JavaScript, das in der Cloud läuft – fragt diese API regelmässig ab und legt die Daten in einem KV-Speicher (Key-Value-Store) ab. Das fertige Dashboard liest dann aus diesem Speicher.

⚡ Warum dieser Umweg? Die SolarManager-API ist nicht öffentlich zugänglich und erfordert einen API-Key. Diesen Key darf man nie direkt im Browser-Code einbauen – sonst wäre er für alle sichtbar. Der Worker läuft serverseitig und schützt die Zugangsdaten.

Schritt 1: Die SolarManager-API v3

SolarManager bietet eine REST-API auf api.solarmanager.ch. Die aktuelle Version 3 erfordert Basic Authentication (Benutzername + Passwort, Base64-kodiert). Die wichtigsten Endpunkte für das Dashboard:

Endpunkt Liefert
/v3/sensor/[gateway-id]/current Aktueller Systemstatus: PV, Batterie, Netz, Verbrauch
/v3/sensor/[gateway-id]/consumers Einzelne Verbraucher (Wallbox, Boiler…)
/v3/meter/[device-id] Netzanschlusspunkt: Import/Export kumulativ in Wh

Der Meter-Endpunkt liefert den kumulierten Zählerstand in Wh (eWhTotal) – gemessen vom SolarManager direkt am Netzanschlusspunkt. Für die tägliche Einspeisung brauche ich die Differenz zum Abend-Snapshot – dazu gleich mehr.

Exkurs: WP-Verbrauchsdaten via Shelly Script

Die Wärmepumpen werden mit myStrom Switches gemessen. myStrom-Geräte bieten eine lokale API, können aber keine Daten direkt an externe Dienste wie Cloudflare schicken. Deshalb läuft auf einem Shelly ein Script, das regelmässig die lokale myStrom-API abfragt und die Messwerte per HTTP-Request an den Cloudflare Worker weiterleitet:

🔌 myStrom Switch
misst Watt
📟 Shelly Script
liest lokal aus
⚙️ Cloudflare
Worker
🗄️ KV-Speicher

Shelly-Geräte unterstützen eigene Scripts (JavaScript-ähnlich) und können problemlos HTTP-Requests an externe URLs absetzen – was myStrom-Geräte alleine nicht können.

Schritt 2: Der Cloudflare Worker

Cloudflare Workers sind JavaScript-Funktionen, die auf dem globalen Cloudflare-Netzwerk laufen – ohne eigenen Server. Der Worker hat zwei Aufgaben:

  1. Cron-Job (täglich 21:50 UTC): Einspeisung des Tages im KV festhalten – Details in Schritt 3
  2. HTTP-Endpunkt: Das Dashboard ruft den Worker direkt auf – der holt die Live-Daten aus der SolarManager-API und liefert sie als JSON zurück

Der HTTP-Endpunkt wird bei jedem Dashboard-Aufruf ausgeführt: Er fragt die SolarManager-API direkt ab und liefert die Daten als JSON zurück – vereinfacht:

// HTTP-Endpunkt: bei jedem Dashboard-Aufruf ausgeführt async function handleRequest(env) { const auth = btoa(`${env.SM_USER}:${env.SM_PASS}`); const res = await fetch( `https://api.solarmanager.ch/v3/sensor/${env.GATEWAY_ID}/current`, { headers: { Authorization: `Basic ${auth}` } } ); const data = await res.json(); // Direkt als JSON zurückliefern – kein KV-Write return Response.json({ pv: data.currentPvGeneration, battery: data.currentBatteryLevel, grid: data.currentGridPower, ts: new Date().toISOString() }); }
💡 Zugangsdaten sicher aufbewahren: API-Key und Passwort werden nie direkt im Code geschrieben, sondern als Environment Variables im Cloudflare-Dashboard hinterlegt. Der Worker liest sie über env.SM_USER usw. aus.

Schritt 3: Die tägliche Einspeisung messen

Die SolarManager-API liefert den kumulierten Zählerstand seit Installation (eWhTotal). Um die heutige Einspeisung zu berechnen, brauche ich eine tägliche Referenz – das übernimmt der Cron:

// Cron: täglich 21:50 UTC = 23:50 Schweizer Zeit (Sommerzeit) async function handleMidnight(env) { const meter = await fetchMeterValue(env); await env.SOLAR_KV.put("midnight_export", meter.eWhTotal.toString()); } // Im handleRequest: Tageseinspeisung berechnen const snapshot = parseFloat( await env.SOLAR_KV.get("midnight_export") ?? "0" ); const exportToday = (current.eWhTotal - snapshot) / 1000; // → kWh

Das Schweizer Datum muss dabei korrekt aus der Zeitzone Europe/Zurich bestimmt werden – nicht UTC, sonst stimmt der Tagesschnitt im Sommer (Sommerzeit) nicht.

Schritt 4: Zwei Worker – einer privat, einer öffentlich

Für meine Installation laufen zwei getrennte Worker:

Worker Funktion Besonderheit
Solar-Worker Holt Daten, schreibt KV Privat, hat KV-Schreibrechte + Cron
Einspeisung-Worker Liest KV, liefert JSON ans Dashboard Öffentlich eingebettet auf pv-strom.ch

Der Split entstand wegen dem KV-Write-Limit (1'000/Tag): Jeder Seitenaufruf löste einen Write aus – zu viel. Nebeneffekte: keine API-Zugangsdaten im öffentlichen Worker, und am privaten lässt sich gefahrlos experimentieren.

Schritt 5: Das Dashboard im Browser

Das Dashboard auf pv-strom.ch ist eine HTML-Seite, die alle 60 Sekunden per fetch() den öffentlichen Einspeisung-Worker aufruft und die JSON-Daten in die Anzeige einbaut. Kein Framework, kein Build-System – reines JavaScript.

// Alle 60 Sekunden aktualisieren async function refresh() { const res = await fetch("https://DEIN-WORKER.workers.dev/"); const d = await res.json(); document.getElementById("pv-power").textContent = (d.pv / 1000).toFixed(2) + " kW"; document.getElementById("battery").textContent = d.battery + "%"; // ... weitere Felder } setInterval(refresh, 60000); refresh(); // sofort beim Laden

Cloudflare Free Plan – reicht das?

Für den Anfang ja. Der Engpass ist das KV-Write-Limit (1'000/Tag) – nicht die Requests. Wer den Worker häufig nutzt oder mehrere KV-Schlüssel schreibt, stösst schnell daran.

📊 Kosten in der Übersicht:
Cloudflare Workers Free: CHF 0 – KV-Writes limitiert (1'000/Tag)
SolarManager: CHF 44/Jahr (Basispaket)

Was ich dabei gelernt habe

Ich bin kein Programmierer – und war es nie. Trotzdem habe ich dieses System von Grund auf aufgebaut, mit KI-Assistenz und viel Hartnäckigkeit. Das hat gut 3–4 Wochen iteratives Debugging gebraucht. Der grösste Teil der Arbeit steckte nicht im Konzept, sondern in den Details: die richtige Zeitzone erwischen, den Unterschied zwischen v2 und v3 SolarManager-API verstehen, und herausfinden warum der KV-Speicher sporadisch alte Werte lieferte (Antwort: Cloudflare cached KV-Reads für einige Sekunden).

Mit der Zeit habe ich gelernt was wie zusammenhängt – und konnte Fehler im generierten Code zunehmend selbst erkennen und korrigieren, manchmal schneller als die KI selbst.

Das Resultat ist ein System, das ohne eigenen Server, ohne monatliche Serverkosten und ohne Wartungsaufwand läuft.

Interesse am Script?

Wer eine ähnliche Lösung für die eigenen Solardaten aufbauen möchte, kann sich gerne melden. Der Cloudflare Worker und das Shelly Script für die myStrom-Anbindung lassen sich mit wenigen Anpassungen übertragen – sobald genug Interesse besteht, stelle ich eine anonymisierte Version zur Verfügung.

Kommentare

Beliebte Posts