ablesio Smart Home
Zur App →
Der Growatt-Wechselrichter zählt den Gesamtertrag ohnehin mit. Ein kleines Skript schickt ihn jeden Sonntagabend an ablesio – dort siehst du Wochenertrag, Eigenverbrauch und Ersparnis, ganz ohne Ablesen. Beispiel: das Balkonkraftwerk an der Pergola.
In ablesio unter Profil → 🔌 Smart-Home-Integration einen Token erstellen, z.B. mit der Bezeichnung „ioBroker", und in der Instanz ablesio.0 eintragen.


Im ioBroker-Admin unter Objekte:
growatt.0.642452.devices.FMH1B0307N.totalData.mTotal (in kWh). Anlage- und Seriennummer sind bei dir andere – gesucht ist totalData.mTotal.FMH1B0307N. Im Objektbaum liegt er unter ablesio.0.meters.mtr_… (öffentliche Zähler-ID), der Datenpunkt meterNumber zeigt die Nummer.
Im ioBroker-Admin unter Skripte ein neues JavaScript anlegen (z.B. „Balkonkraftwerk-Wochen-Sync"), den Inhalt unten einfügen und oben unter ANLAGEN Quelle und Zählernummer eintragen. Mehrere Balkonkraftwerke? Einfach je Anlage eine Zeile ergänzen.
/**
* ablesio – Balkonkraftwerk einmal pro Woche an ablesio übermitteln (Beispiel: Pergola)
* ====================================================================================
* Für den JavaScript-Adapter von ioBroker. Liest den Gesamtertrag der Growatt-Wechselrichter
* und schreibt ihn in den ablesio-Adapter (ablesio.0.meters.<id>.submitReading). Der Adapter
* prüft den Wert (unverändert → übersprungen, kleiner als zuletzt → nicht gesendet) und
* übermittelt ihn an ablesio.
*
* Voraussetzungen: ablesio-Adapter ≥ 0.3.0 (mit Token aus ablesio Ultimate), Growatt-Adapter.
* Anleitung: https://ablesio.de/smart-home
*/
// ---------- Einstellungen ----------
/**
* Welche Anlage wohin: Growatt-Datenpunkt (Gesamtertrag in kWh) → ablesio-Zähler.
* Der ablesio-Zähler wird über seine ZÄHLERNUMMER gefunden (wie in ablesio eingetragen, z.B. die
* Seriennummer des Wechselrichters). Alternativ: ablesioZaehler: '<ID aus Objekte → ablesio.0.meters>'.
*/
const ANLAGEN = [
{ name: 'Pergola', quelle: 'growatt.0.642452.devices.FMH1B0307N.totalData.mTotal', zaehlernummer: 'FMH1B0307N' },
// Weitere Anlagen einfach als eigene Zeile ergänzen, z.B.:
// { name: 'Zweite Anlage', quelle: 'growatt.0.<Anlage>.devices.<Seriennummer>.totalData.mTotal', zaehlernummer: '<Seriennummer>' },
];
/** Wann synchronisiert wird (Cron): sonntags um 20:00 Uhr, wenn der Tagesertrag drin ist */
const ZEITPLAN = '0 20 * * 0';
/** Werte, die länger nicht aktualisiert wurden, nicht übermitteln (Wechselrichter offline?) */
const MAX_ALTER_TAGE = 3;
/** Zum Testen auf true setzen: synchronisiert einmal direkt beim Speichern/Starten des Skripts */
const BEIM_START_AUSFUEHREN = false;
/** Instanz des ablesio-Adapters */
const ADAPTER = 'ablesio.0';
// ---------- ab hier nichts ändern ----------
/** ID des ablesio-Zählers: über die Zählernummer suchen (unabhängig vom ID-Schema) oder direkt angegeben */
function zaehlerIdFuer(anlage) {
if (!anlage.zaehlernummer) {
return anlage.ablesioZaehler != null ? String(anlage.ablesioZaehler) : null;
}
let gefunden = null;
$(`state[id=${ADAPTER}.meters.*.meterNumber]`).each((id) => {
const s = getState(id);
if (!gefunden && s && String(s.val).trim().toUpperCase() === String(anlage.zaehlernummer).trim().toUpperCase()) {
gefunden = id.split('.')[3];
}
});
return gefunden;
}
/** Liste der Zähler, die der ablesio-Adapter kennt – als Hilfe bei falscher Zähler-ID */
function vorhandeneZaehler() {
const liste = [];
$(`state[id=${ADAPTER}.meters.*.label]`).each((id) => {
const nr = id.split('.')[3];
const s = getState(id);
const nummer = getState(`${ADAPTER}.meters.${nr}.meterNumber`);
liste.push(`${nr} = ${s && s.val ? s.val : (nummer && nummer.val ? nummer.val : '(ohne Bezeichnung)')}`);
});
return liste.length ? liste.join(', ') : `keine – läuft der ablesio-Adapter (${ADAPTER}) mit gültigem Token?`;
}
async function synchronisieren() {
log(`ablesio: Wochen-Sync für ${ANLAGEN.length} Anlage(n) startet.`);
// Erst prüfen, ob der ablesio-Adapter überhaupt mit ablesio verbunden ist (Token gültig?)
const verbunden = await getStateAsync(`${ADAPTER}.info.connection`);
if (!verbunden || !verbunden.val) {
const fehler = await getStateAsync(`${ADAPTER}.info.lastError`);
log(`ablesio: Sync abgebrochen – der ablesio-Adapter ist nicht mit ablesio verbunden. ` +
(fehler && fehler.val ? `Ursache: ${fehler.val}` : `Läuft ${ADAPTER}? Token prüfen: Instanz-Einstellungen → „Token prüfen".`), 'error');
return;
}
const gesendet = [];
for (const anlage of ANLAGEN) {
const zaehlerId = zaehlerIdFuer(anlage);
const ziel = `${ADAPTER}.meters.${zaehlerId}.submitReading`;
// Objekt prüfen, nicht den Wert: submitReading hat erst nach der ersten Übermittlung einen Wert
if (!zaehlerId || !(await getObjectAsync(ziel))) {
const was = anlage.zaehlernummer ? `mit der Zählernummer ${anlage.zaehlernummer}` : String(anlage.ablesioZaehler);
log(`ablesio: ${anlage.name} – Zähler ${was} gibt es im ablesio-Adapter nicht. Vorhanden: ${vorhandeneZaehler()}`, 'warn');
continue;
}
anlage._id = zaehlerId;
const q = await getStateAsync(anlage.quelle);
if (!q || q.val === null || q.val === undefined) {
log(`ablesio: ${anlage.name} – Datenpunkt ${anlage.quelle} nicht gefunden oder leer – übersprungen.`, 'warn');
continue;
}
const wert = Math.round(Number(q.val) * 100) / 100;
if (!Number.isFinite(wert) || wert <= 0) {
log(`ablesio: ${anlage.name} – „${q.val}" ist kein gültiger Zählerstand – übersprungen.`, 'warn');
continue;
}
// Platzhalter statt Messwert (z.B. SMA per Modbus, wenn der Wechselrichter nachts schläft: 4294967295 u.ä.)
if (wert >= 4294967295 || wert > 10000000) {
log(`ablesio: ${anlage.name} – „${q.val}" ist ein Platzhalter des Wechselrichters (schläft er gerade?) – übersprungen.`, 'warn');
continue;
}
const alterTage = (Date.now() - q.ts) / 86400000;
if (alterTage > MAX_ALTER_TAGE) {
log(`ablesio: ${anlage.name} – Wert ist ${Math.floor(alterTage)} Tage alt (Wechselrichter offline?) – übersprungen.`, 'warn');
continue;
}
await setStateAsync(ziel, wert, false); // ack = false → der ablesio-Adapter übermittelt
gesendet.push(anlage);
log(`ablesio: ${anlage.name} – ${wert} kWh an den ablesio-Adapter übergeben.`);
}
// Ergebnis nach kurzer Zeit ins Log schreiben (der Adapter arbeitet nacheinander ab)
if (gesendet.length) {
setTimeout(() => {
for (const anlage of gesendet) {
const s = getState(`${ADAPTER}.meters.${anlage._id}.lastSubmitStatus`);
log(`ablesio: ${anlage.name} – Ergebnis: ${s && s.val ? s.val : 'noch offen'}`);
}
}, 20000);
}
}
schedule(ZEITPLAN, synchronisieren);
if (BEIM_START_AUSFUEHREN) {
synchronisieren();
}
Zum Ausprobieren BEIM_START_AUSFUEHREN = true setzen und das Skript speichern – es läuft sofort einmal. Im Log siehst du nach etwa 20 Sekunden das Ergebnis. Danach wieder auf false stellen: ab jetzt läuft der Sync jeden Sonntag um 20:00 Uhr.

Beim Zähler „Pergola" erscheint jede Woche ein neuer Stand – mit Wochenertrag und der Ersparnis nach deinem Strompreis.


Meldung in lastSubmitStatus / im Log | Bedeutung |
|---|---|
Sync abgebrochen – … nicht mit ablesio verbunden | Die Ursache steht dahinter, meist API-Token abgelehnt: im ablesio-Profil einen neuen Token erstellen und in der Instanz eintragen – mit „Token prüfen" in den Instanz-Einstellungen gleich testen. |
ok: … übermittelt | alles gut – der Stand ist in ablesio |
übersprungen: unverändert | seit dem letzten Mal kein neuer Ertrag – nichts zu tun |
Fehler: kleiner als der letzte Stand | Stimmt die Quelle (totalData.mTotal der richtigen Anlage) und die Zähler-ID? Sonst: Wechselrichter getauscht oder zurückgesetzt? Dann in ablesio einen Zählerwechsel anlegen |
Fehler: Für diesen Zähler gibt es am … schon eine Ablesung | ablesio nimmt einen Stand pro Kalendertag und Zähler an – der wöchentliche Sonntags-Termin passt immer, auch neben Ablesungen per Hand unter der Woche. Zweimal am selben Tag geht nicht; eine falsche Ablesung korrigierst du im Bericht mit ✏️. |
Wert ist … Tage alt | der Wechselrichter meldet sich nicht (WLAN, Growatt-Cloud) – der Stand wird erst gesendet, wenn er wieder aktuell ist |
Fragen? Über das Kontaktformular – oder Ablo in der App fragen.