✅ Der Shelly Pro 3EM ist ein beliebter Energiezähler für Home Assistant – aber er hat ein bekanntes Problem: Er saldiert nicht so, wie es dein Stromzähler tut. Das führt zu verfälschten Werten im Energiedashboard, vor allem wenn du eine PV-Anlage oder ein Balkonkraftwerk betreibst, dass nicht auf allen 3 Phasen verteilt einspeist.
In diesem Beitrag erkläre ich dir das Problem und zeige dir zwei gleichwertige Lösungswege: ein Skript direkt auf dem Shelly, oder Template-Sensoren in Home Assistant. Beide funktionieren gut – sie haben nur unterschiedliche Voraussetzungen und Eigenschaften.
Inhaltsverzeichnis
- ⚡ Das Problem: Was bedeutet „saldieren“ überhaupt?
- 📜 Lösung: Skript direkt auf dem Shelly
- 🎁 Bonus: Historische Statistiken übertragen
- 🔔 Bonus 2: Benachrichtigung bei Sensor-Ausfall
- 📊 Fazit
⚡ Das Problem: Was bedeutet „saldieren“ überhaupt?
Dein Haushalt wird über drei Phasen mit Strom versorgt. Jeder Verbraucher – und auch deine PV-Anlage oder dein Balkonkraftwerk – hängt an einer dieser drei Phasen. Wenn dein Balkonkraftwerk auf Phase A einspeist und gleichzeitig auf Phase B und C Verbraucher laufen, verrechnet dein Stromzähler das automatisch: Er saldiert. Sprich: Er zieht die Einspeisung von Phase A vom Bezug der anderen Phasen ab, bevor er den Gesamtverbrauch anzeigt.
Der Shelly Pro 3EM macht das nicht – zumindest nicht bei der Energiezählung (kWh). Ein konkretes Beispiel:
- Phase A: Balkonkraftwerk speist −600 W ein
- Phase B: 50 W Verbrauch
- Phase C: 550 W Verbrauch
Dein Stromzähler saldiert: −600 + 50 + 550 = 0 W → kein Nettobezug, keine Einspeisung.
Der Shelly Pro 3EM hingegen zählt: 600 Wh als Einspeisung auf Phase A und gleichzeitig 600 Wh als Verbrauch auf Phase B+C – obwohl du real nichts eingespeist und nichts bezogen hast.
Für die Momentanleistung (Watt) macht der Shelly das übrigens korrekt – da wird korrekt über alle Phasen saldiert. Das Problem liegt ausschließlich bei der kumulierten Energiezählung (kWh).
📖 Wie du den Shelly Pro 3EM generell in Home Assistant einbindest und fürs Energiedashboard nutzt, erkläre ich ausführlich hier: Strom, Wasser & Gas überwachen mit Home Assistant
📜 Lösung: Skript direkt auf dem Shelly
Ja, es gibt Skripte die direkt auf dem Shelly laufen und die Saldierung dort erledigen – alles auf einem Gerät, ohne dass Home Assistant daran beteiligt ist. Die bekanntesten sind:
- Davc0m (empfohlen, MQTT + HA Auto-Discovery): gist.github.com/Davc0m
- sicanins (alternativ): github.com/sicanins/shelly-pro3EM-energycounter
Diese Ansätze funktionieren gut, ich nutze das Skript von Davc0m – es gibt aber ein paar Dinge die du vorher wissen solltest:
⚠️ Shelly-App-Graphen bleiben falsch: In der Shelly App selbst werden weiterhin die unsaldierten Werte angezeigt. Die Skripte können das nicht korrigieren.
⚠️ Flash-Verschleiß (konfigurierbar): Skripte die ihre Zählerstände im KVS (Key-Value-Storage, also Flash-Speicher) des Shellys ablegen, schreiben teils alle 60–150 Sekunden in den Flash. Das sind hunderte bis tausende Schreibvorgänge pro Tag – Flash-Speicher hat aber begrenzte Schreibzyklen. Neuere Skriptversionen haben dieses Intervall entschärft oder bieten eine Option, die Persistenz komplett zu deaktivieren. Wer Home Assistant nutzt, kann das bedenkenlos ausschalten – HA trackt den Zählerstand ohnehin weiter.
⚠️ MQTT-Abhängigkeit: Das Davc0m-Skript (die ausgefeilteste Lösung) arbeitet über MQTT mit HA Auto-Discovery – wer ausschließlich die native Shelly-Integration nutzt und kein MQTT betreibt, hat hier Mehraufwand.
📹 Mehr zu MQTT im Video auf YouTube: https://youtu.be/6dakzuffues
Das Davc0m-Skript ist die ausgefeilteste Variante und läuft bei mir selbst produktiv – mit deaktivierter Persistenz (RAM-only) und einem großzügigen Save-Intervall. Wer kein MQTT betreiben möchte, findet in Lösung 2 eine gleichwertige Alternative direkt in Home Assistant.
Das 👆 ist die Lösung, die ich auch im Video erklärt habe
🧑💻 Alternative Lösung: Template-Sensoren in Home Assistant
Wer kein MQTT betreiben möchte oder alles lieber in Home Assistant verwaltet, kann die Saldierung komplett in HA abbilden – ohne Script auf dem Shelly, ohne zusätzliche Dienste. Der Trick: Die Momentanleistung (W) saldiert der Shelly bereits korrekt. Wir nehmen also die drei Phasen-Sensoren, summieren sie in HA zu einem Gesamtwert, filtern positiv (Bezug) und negativ (Einspeisung) auf – und lassen Home Assistant daraus saubere kWh integrieren.
Schritt 1: Template-Sensoren anlegen
Gehe in Home Assistant zu Einstellungen → Geräte & Dienste → Helfer → Helfer hinzufügen → Vorlage → Sensor.
Lege zwei Template-Sensoren an:
Sensor 1 – Netz Leistung Bezug (W):
{% set p = states('sensor.shellypro3em_DEINE_ID_phase_a_active_power') | float(0)
+ states('sensor.shellypro3em_DEINE_ID_phase_b_active_power') | float(0)
+ states('sensor.shellypro3em_DEINE_ID_phase_c_active_power') | float(0) %}
{{ [p, 0] | max | round(1) }}
Einstellungen: Einheit W, Geräteklasse power, Zustandsklasse measurement
Sensor 2 – Netz Leistung Einspeisung (W):
{% set p = states('sensor.shellypro3em_DEINE_ID_phase_a_active_power') | float(0)
+ states('sensor.shellypro3em_DEINE_ID_phase_b_active_power') | float(0)
+ states('sensor.shellypro3em_DEINE_ID_phase_c_active_power') | float(0) %}
{{ [p * -1, 0] | max | round(1) }}
Einstellungen: Einheit W, Geräteklasse power, Zustandsklasse measurement
Die Entity-IDs deiner Phasen-Sensoren findest du unter Einstellungen → Geräte & Dienste → Shelly → dein Gerät → Entitäten – such nach „Leistung“ oder „active power“.
Schritt 2: Integrations-Helfer (kWh) anlegen
Gehe zu Einstellungen → Geräte & Dienste → Helfer → Helfer hinzufügen → Integrieren.
Lege zwei Integrationshelfer an:
- Netzbezug kWh saldiert → Quellsensor:
sensor.netz_leistung_bezug, Präfix:k, Methode:Trapez - Netzeinspeisung kWh saldiert → Quellsensor:
sensor.netz_leistung_einspeisung, Präfix:k, Methode:Trapez
Die Methode Trapez (trapezoidal) ist am genauesten – sie mittelt den Leistungswert am Anfang und Ende jedes Intervalls.
Schritt 3: Energiedashboard umstellen
Gehe zu Einstellungen → Dashboards → Energie und tausche die bisherigen Shelly-Sensoren gegen die neuen aus:
- Netzstrom (Bezug):
sensor.netzbezug_kwh_saldiert - Rückgabe ans Netz:
sensor.netzeinspeisung_kwh_saldiert
🎁 Bonus: Historische Statistiken übertragen
Wenn du die alten Shelly-Sensoren bereits im Energiedashboard hattest, stecken dort Monate an Verbrauchshistorie drin. Die kannst du per SQLite-Direktzugriff zu den neuen Sensoren kopieren – so entsteht kein Datenverlust und das Dashboard zeigt weiterhin eine lückenlose Historie.
⚠️ Backup ist Pflicht! Bevor du irgendwas an der SQLite-Datenbank änderst, erstelle ein vollständiges HA-Backup.
Du brauchst dafür das SQLite Web Add-on (verfügbar im HA Add-on Store). Dort kannst du SQL-Statements direkt ausführen – aber jeweils nur eines auf einmal.
🗼 Netzbezug migrieren
Ersetze die Entitäts-IDs natürlich durch deine eigenen Entitäts-IDs:
- Quell-Sensor, von dem die Daten kopiert werden:
sensor.shellypro3em_DEINE_ID_total_active_energy - Ziel-Sensor, zu dem die Statistik kopiert wird:
sensor.netzbezug_kwh_saldiert
Hinweis: Falls du wie im Video empfohlen Verbrauchszähler angelegt hast, gibts du natürlich diese als Ziel-Entität an.
1. Metadaten anlegen (falls noch nicht vorhanden):
INSERT OR IGNORE INTO statistics_meta (statistic_id, source, unit_of_measurement, has_mean, has_sum, name) VALUES ('sensor.netzbezug_kwh_saldiert', 'recorder', 'kWh', 0, 1, 'Netzbezug kWh saldiert')
2. Statistiken kopieren:
INSERT OR IGNORE INTO statistics (created, created_ts, start, start_ts, mean, min, max, last_reset, last_reset_ts, state, sum, metadata_id) SELECT s.created, s.created_ts, s.start, s.start_ts, s.mean, s.min, s.max, s.last_reset, s.last_reset_ts, s.state, s.sum, (SELECT id FROM statistics_meta WHERE statistic_id = 'sensor.netzbezug_kwh_saldiert') FROM statistics s JOIN statistics_meta m ON s.metadata_id = m.id WHERE m.statistic_id = 'sensor.shellypro3em_DEINE_ID_total_active_energy'
3. Kontrolle:
SELECT COUNT(s.id) as eintraege, datetime(MIN(s.start_ts), 'unixepoch') as erster, datetime(MAX(s.start_ts), 'unixepoch') as letzter, MAX(s.sum) as sum_kwh FROM statistics s JOIN statistics_meta m ON s.metadata_id = m.id WHERE m.statistic_id = 'sensor.netzbezug_kwh_saldiert'
☀️ Netzeinspeisung migrieren
Ersetze die Entitäts-IDs natürlich durch deine eigenen Entitäts-IDs:
- Quell-Sensor, von dem die Daten kopiert werden:
sensor.shellypro3em_DEINE_ID_total_active_returned_energy - Ziel-Sensor, zu dem die Statistik kopiert wird:
sensor.netzeinspeisung_kwh_saldiert
Hinweis: Falls du wie im Video empfohlen Verbrauchszähler angelegt hast, gibts du natürlich diese als Ziel-Entität an.
1. Metadaten anlegen:
INSERT OR IGNORE INTO statistics_meta (statistic_id, source, unit_of_measurement, has_mean, has_sum, name) VALUES ('sensor.netzeinspeisung_kwh_saldiert', 'recorder', 'kWh', 0, 1, 'Netzeinspeisung kWh saldiert')
2. Statistiken kopieren:
INSERT OR IGNORE INTO statistics (created, created_ts, start, start_ts, mean, min, max, last_reset, last_reset_ts, state, sum, metadata_id) SELECT s.created, s.created_ts, s.start, s.start_ts, s.mean, s.min, s.max, s.last_reset, s.last_reset_ts, s.state, s.sum, (SELECT id FROM statistics_meta WHERE statistic_id = 'sensor.netzeinspeisung_kwh_saldiert') FROM statistics s JOIN statistics_meta m ON s.metadata_id = m.id WHERE m.statistic_id = 'sensor.shellypro3em_DEINE_ID_total_active_returned_energy'
3. Kontrolle:
SELECT COUNT(s.id) as eintraege, datetime(MIN(s.start_ts), 'unixepoch') as erster, datetime(MAX(s.start_ts), 'unixepoch') as letzter, MAX(s.sum) as sum_kwh FROM statistics s JOIN statistics_meta m ON s.metadata_id = m.id WHERE m.statistic_id = 'sensor.netzeinspeisung_kwh_saldiert'
Die alten Sensoren bleiben dabei erhalten – sie werden nur nicht mehr im Energiedashboard verwendet. Du kannst sie jederzeit wieder reaktivieren.
📖 Mehr zur Korrektur von Statistiken in Home Assistant: Statistiken in Home Assistant korrigieren (Energie Dashboard)
🔔 Bonus 2: Benachrichtigung bei Sensor-Ausfall
Da beide Lösungen auf dem kontinuierlichen Watt-Signal des Shellys basieren (entweder direkt auf dem Gerät oder als HA-Integration), lohnt es sich, eine Automation einzurichten die dich benachrichtigt wenn der Shelly nicht mehr erreichbar ist oder sein Sensor sich ungewöhnlich lange nicht mehr ändert. So weißt du sofort, wenn Lücken in der Energiezählung entstehen.
Die Automation überwacht sensor.shellypro3em_DEINE_ID_phase_a_active_power und feuert in drei Fällen nach jeweils 5 Minuten:
- unavailable – Shelly ist nicht erreichbar
- unknown – Sensor liefert unbekannte Werte
- keine Änderung – Wert bleibt 5 Minuten konstant (Gerät möglicherweise eingefroren)
Gehe zu Einstellungen → Automationen → Automation erstellen → YAML-Editor und füge folgenden Code ein. Ersetze dabei DEINE_ID durch deine tatsächliche Shelly-ID und passe den Notify-Service auf dein Gerät an:
alias: Shelly Pro 3EM – Sensor Offline Benachrichtigung
description: >
Benachrichtigt wenn der Shelly Pro 3EM Watt-Sensor für mehr als 5 Minuten
unavailable/unknown ist oder sich nicht mehr ändert.
mode: single
trigger:
- platform: state
entity_id: sensor.shellypro3em_DEINE_ID_phase_a_active_power
to: unavailable
for: "00:05:00"
id: unavailable
- platform: state
entity_id: sensor.shellypro3em_DEINE_ID_phase_a_active_power
to: unknown
for: "00:05:00"
id: unknown
- platform: state
entity_id: sensor.shellypro3em_DEINE_ID_phase_a_active_power
for: "00:05:00"
id: no_change
action:
- choose:
- conditions:
- condition: trigger
id: unavailable
sequence:
- action: notify.mobile_app_DEIN_GERAET
data:
title: ⚠️ Shelly Pro 3EM offline
message: >
Der Shelly Pro 3EM ist seit mehr als 5 Minuten nicht erreichbar
(unavailable). Die saldierten Energiewerte werden in dieser Zeit
nicht gezählt.
- conditions:
- condition: trigger
id: unknown
sequence:
- action: notify.mobile_app_DEIN_GERAET
data:
title: ⚠️ Shelly Pro 3EM – Sensor unbekannt
message: >
Der Shelly Pro 3EM Sensor meldet seit mehr als 5 Minuten
'unknown'. Bitte Gerät prüfen.
- conditions:
- condition: trigger
id: no_change
sequence:
- action: notify.mobile_app_DEIN_GERAET
data:
title: ⚠️ Shelly Pro 3EM – Keine Aktualisierung
message: >
Der Shelly Pro 3EM Watt-Sensor hat sich seit mehr als 5 Minuten
nicht geändert. Möglicherweise ist das Gerät eingefroren oder
die Verbindung unterbrochen.
Den notify.mobile_app_DEIN_GERAET-Service findest du in HA unter Entwicklerwerkzeuge → Dienste – such nach „notify“ und wähle dein Smartphone aus.
📊 Fazit
Der Shelly Pro 3EM ist ein sehr gutes Gerät – das Saldierungsproblem existiert nicht in der Shelly app, wenn man weiß wo man umstellen kann, aber sehr wohl in Home Assistant.
Es ist lösbar, und zwar auf zwei gleichwertige Arten: Entweder mit einem Skript direkt auf dem Shelly (robust, alles auf einem Gerät, MQTT nötig) oder mit Template-Sensoren in Home Assistant (kein MQTT, keine zusätzliche Konfiguration auf dem Gerät, etwas fummeliger).
Beide Wege liefern korrekte Werte im Energiedashboard. Die historischen Daten lassen sich dank SQLite nahtlos übertragen, sodass dein Dashboard sofort wieder eine lückenlose Historie zeigt.
Hast du Fragen oder läuft bei dir etwas anders? Schreib’s in die Kommentare oder komm in die simon42 Community!



Habe auf die oben beschriebene Weise PV-Produktionsdaten zweier Sensoren zusammengeführt. Das oben beschriebene Problem mit dem hohen negativen Werten am Übergangsdatum vom einen auf den anderen Sensor entsteht, weil der neue Sensor nicht bei der Summe vom alten Sensor weitermacht, sondern bei 0 anfängt. Die letzte Summe muss zu „sum“ und „state“ addiert werden:
update statistics
set sum = state + 558.22 ,
state = state + 558.22
where metadata_id = 246 and datetime( start_ts , ‚unixepoch‘ ) >= ‚2025-03-23 14:00:00‘
Mein 1. Sensor hatte zuletzt sum = 558.22 hier euren Wert nehmen
Mein 2. Sensor hat die metadata_id = 246 ditto
Der 2. Sensor hat am 23.3.2025 um 14 Uhr den 1. Statistikeintrag ditto
Leider muss man die Summe in neuen Sensor auch korrigieren, das passiert nicht automatisch. Habe den Fehler nur in die Gegenwart verschoben. Melde mich, wenn eine Lösung funktioniert.
Moin.
Ich habe auch gerade die version 2 mit den Templates umgesetzt.
Die Leistungssensoren sind Helfer vom Typ Template und dann Sensor.
Aber ich bekomme am Ende den Netzeinspeisungssensor saldiert nicht ins Energiedashboard. Der wird dort nicht angezeigt.
Was mache ich falsch?
Hi, mein Problem ist vielleicht ähnlich zu Michaels. Im Abschnitt: “ Alternative Lösung: Template-Sensoren in Home Assistant – Schritt 1: Template-Sensoren anlegen“ schreibst du „Gehe in Home Assistant zu Einstellungen → Geräte & Dienste → Helfer → Helfer hinzufügen → Vorlage → Sensor.“
Ich finde nur „Helfer -> Helfer erstellen -> und dann weder Vorlage noch Sensor“. Ich sehe Ableitungs-, Integral-, Kombination-, Schwellenwert-, und Tageszeitsensor. Habe ich ne falsche Version oder bin ich irgendwo falsch abgebogen?
Ich hab mir auch nochmal ein paar Gedanken zu dem Skript auf dem Shelly gemacht, da ich auch mehr als einen benutze und das mit dem Namen nicht gut gelöst war
/**
* Shelly Pro 3EM – Net Metering (Saldierung) & Home Assistant Auto-Discovery
*/
let CONFIG = {
updateInterval: 1000, // Berechnungszyklus in ms (entspricht EM-Refresh-Takt)
mqttPrefix: „homeassistant“ // Standard HA Discovery Prefix
};
let VERSION = „1.3.4“;
let SHELLY_ID = null;
let DEVICE_NAME = null;
let energyReturnedWh = 0.0;
let energyConsumedWh = 0.0;
let lastPublishedConsumed = „“;
let lastPublishedReturned = „“;
let announced = false;
function TryAnnounceAndPublish() {
if (!SHELLY_ID || !DEVICE_NAME || !MQTT.isConnected()) return;
if (!announced) {
AnnounceHA();
announced = true;
}
PublishCounters(true);
}
function PublishCounters(force) {
if (!SHELLY_ID) return;
// Intern Wh, MQTT-Ausgabe in kWh
let valC = (energyConsumedWh / 1000).toFixed(3);
let valR = (energyReturnedWh / 1000).toFixed(3);
if (!force && valC === lastPublishedConsumed && valR === lastPublishedReturned) return;
let okC = MQTT.publish(SHELLY_ID + „/energy_counter/consumed“, valC, 0, true);
let okR = MQTT.publish(SHELLY_ID + „/energy_counter/returned“, valR, 0, true);
if (okC) lastPublishedConsumed = valC;
if (okR) lastPublishedReturned = valR;
}
MQTT.setConnectHandler(function () {
print(„MQTT connected.“);
announced = false;
TryAnnounceAndPublish();
});
MQTT.setDisconnectHandler(function () {
print(„MQTT disconnected.“);
});
Shelly.call(„Mqtt.GetConfig“, {}, function (res, err_code, err_msg) {
if (!res) {
print(„ERROR: Mqtt.GetConfig returned null! Code: “ + err_code + “ | “ + err_msg);
return;
}
SHELLY_ID = res.topic_prefix ? res.topic_prefix : null;
if (!SHELLY_ID) {
print(„ERROR: No MQTT topic_prefix set. Please check MQTT configuration.“);
return;
}
print(„Shelly ID: “ + SHELLY_ID + “ | Script v“ + VERSION);
Shelly.call(„Sys.GetConfig“, {}, function (res, err_code, err_msg) {
if (res && res.device && res.device.name) {
DEVICE_NAME = res.device.name;
} else {
DEVICE_NAME = SHELLY_ID;
print(„WARNING: Could not read device name, falling back to Shelly ID.“);
}
print(„Device name: “ + DEVICE_NAME);
TryAnnounceAndPublish();
});
});
function AnnounceHA() {
if (!SHELLY_ID) return;
let haTopic = CONFIG.mqttPrefix + „/sensor/“ + SHELLY_ID;
let avtyTopic = SHELLY_ID + „/online“;
let dev = {
„ids“: [SHELLY_ID],
„name“: DEVICE_NAME,
„mf“: „Shelly“,
„mdl“: „Pro 3EM“,
„sw“: „Saldierung v“ + VERSION
};
let okImport = MQTT.publish(
haTopic + „-import/config“,
JSON.stringify({
„name“: „Saldierend Netzbezug“,
„uniq_id“: SHELLY_ID + „_sald_import“,
„stat_t“: SHELLY_ID + „/energy_counter/consumed“,
„unit_of_meas“: „kWh“,
„dev_cla“: „energy“,
„stat_cla“: „total_increasing“,
„avty_t“: avtyTopic,
„pl_avail“: „true“,
„pl_not_avail“: „false“,
„dev“: dev
}),
0, true
);
let okExport = MQTT.publish(
haTopic + „-export/config“,
JSON.stringify({
„name“: „Saldierend Netzeinspeisung“,
„uniq_id“: SHELLY_ID + „_sald_export“,
„stat_t“: SHELLY_ID + „/energy_counter/returned“,
„unit_of_meas“: „kWh“,
„dev_cla“: „energy“,
„stat_cla“: „total_increasing“,
„avty_t“: avtyTopic,
„pl_avail“: „true“,
„pl_not_avail“: „false“,
„dev“: dev
}),
0, true
);
if (okImport && okExport) {
print(„HA Auto-Discovery sent.“);
} else {
print(„WARNING: HA Discovery publish failed (MQTT not ready?).“);
}
}
Timer.set(CONFIG.updateInterval, true, function () {
if (!SHELLY_ID) return;
let em = Shelly.getComponentStatus(„em“, 0);
if (!em || typeof em.total_act_power !== „number“ || isNaN(em.total_act_power)) return;
let power = em.total_act_power;
// power === 0: kein Energiefluss, kein Beitrag zu den Zählern
if (power === 0) return;
// W × ms / 3.600.000 = Wh
let deltaWh = Math.abs(power) * (CONFIG.updateInterval / 3600000.0);
if (power > 0) {
energyConsumedWh += deltaWh;
} else {
energyReturnedWh += deltaWh;
}
PublishCounters(false);
});
Hey zusammen,
habe das nun auch bei mir eingerichtet, wie im Video erklärt,
bei mir bleibt der Saldierend Export immer 0kwh und ich verstehe nicht warum 🙁 ! Evlt. jemand eine Erklärung?
Viele Grüße
Christian
let dev = {
„ids“: [SHELLY_ID],
„name“: „Shelly Pro 3EM“,
„mf“: „Shelly“,
„mdl“: „Pro 3EM“,
„sw“: „Saldierung v“ + VERSION
};
Super nervig, wenn man mehr als 7 Geräte hat und diese nun unter „Geräte & Dienste“ => MQTT => nun 7 mal „Pro 3EM“ lauten.
hab ich umgebaut auf:
/**
* Shelly Pro 3EM – Net Metering (Saldierung) & Home Assistant Auto-Discovery
* Version: 1.1.9
*
* DISCLAIMER:
* Use this script entirely at your own risk! I assume absolutely no liability
* for any direct, indirect, or consequential damages. This includes, but is
* not limited to, damage to the Shelly device, any connected electrical
* equipment, other devices in your network, data loss, or system malfunctions.
* By using this script, you acknowledge that you alone are responsible for
* your hardware and setup.
*
* CHANGELOG (v1.1.8 → v1.1.9):
* – Added dynamic device name via Sys.GetConfig()
* – Discovery now waits until MQTT topic_prefix and device name are initialized
* – Fallback to „Shelly Pro 3EM“ if no custom device name is set
*/
let CONFIG = {
updateInterval: 500, // Calculation cycle in ms
enablePersistence: false, // true = Save counter states to flash memory
saveInterval: 1800, // Save to KVS every 1800 cycles (~15 min.)
mqttPrefix: „homeassistant“ // Standard HA Discovery Prefix
};
let VERSION = „1.1.9“;
let SHELLY_ID = null;
let DEVICE_NAME = „Shelly Pro 3EM“;
let mqttConfigLoaded = false;
let sysConfigLoaded = false;
let energyReturnedWs = 0.0;
let energyConsumedWs = 0.0;
let energyReturnedKWh = 0.0;
let energyConsumedKWh = 0.0;
let saveCounter = 0;
let lastPublishedConsumed = „“;
let lastPublishedReturned = „“;
let countersLoaded = false;
// ─────────────────────────────────────────────
// 1. Helper Functions
// ─────────────────────────────────────────────
function TryAnnounceAndPublish() {
if (!SHELLY_ID) return;
if (!mqttConfigLoaded) return;
if (!sysConfigLoaded) return;
if (!MQTT.isConnected()) return;
AnnounceHA();
if (countersLoaded) {
PublishCounters(true);
}
}
function PublishCounters(force) {
if (!SHELLY_ID || !countersLoaded) return;
let valC = energyConsumedKWh.toFixed(3);
let valR = energyReturnedKWh.toFixed(3);
if (!force && valC === lastPublishedConsumed && valR === lastPublishedReturned) return;
let okC = MQTT.publish(SHELLY_ID + „/energy_counter/consumed“, valC, 0, true);
let okR = MQTT.publish(SHELLY_ID + „/energy_counter/returned“, valR, 0, true);
if (okC) lastPublishedConsumed = valC;
if (okR) lastPublishedReturned = valR;
}
// ─────────────────────────────────────────────
// 2. MQTT Event Handlers
// ─────────────────────────────────────────────
MQTT.setConnectHandler(function () {
print(„MQTT connected.“);
TryAnnounceAndPublish();
});
MQTT.setDisconnectHandler(function () {
// Cannot publish here – connection is already gone.
// HA uses native Shelly LWT on /online for offline detection.
print(„MQTT disconnected.“);
});
// ─────────────────────────────────────────────
// 3. Get Device ID / Name and Initialize
// ─────────────────────────────────────────────
Shelly.call(„Mqtt.GetConfig“, {}, function (res, err_code, err_msg) {
if (!res) {
print(„ERROR: Mqtt.GetConfig returned null! Code: “ + err_code + “ | “ + err_msg);
return;
}
SHELLY_ID = res.topic_prefix ? res.topic_prefix : null;
if (!SHELLY_ID) {
print(„ERROR: No MQTT topic_prefix set. Please check MQTT configuration.“);
return;
}
mqttConfigLoaded = true;
print(„Shelly ID: “ + SHELLY_ID + “ | Script v“ + VERSION);
if (CONFIG.enablePersistence) {
LoadCounters();
} else {
countersLoaded = true;
print(„Persistence disabled. Counters start at 0.“);
}
// Handles script restarts when MQTT is already connected
TryAnnounceAndPublish();
});
Shelly.call(„Sys.GetConfig“, {}, function (res, err_code, err_msg) {
if (!res) {
print(„ERROR: Sys.GetConfig returned null! Code: “ + err_code + “ | “ + err_msg);
print(„INFO: Using fallback device name: “ + DEVICE_NAME);
sysConfigLoaded = true;
TryAnnounceAndPublish();
return;
}
if (res.device && res.device.name) {
DEVICE_NAME = res.device.name;
print(„Device name loaded: “ + DEVICE_NAME);
} else {
print(„INFO: No custom device name set. Using fallback: “ + DEVICE_NAME);
}
sysConfigLoaded = true;
TryAnnounceAndPublish();
});
// ─────────────────────────────────────────────
// 4. Home Assistant Auto-Discovery
// ─────────────────────────────────────────────
function AnnounceHA() {
if (!SHELLY_ID) return;
let haTopic = CONFIG.mqttPrefix + „/sensor/“ + SHELLY_ID;
let avtyTopic = SHELLY_ID + „/online“;
let dev = {
„ids“: [SHELLY_ID],
„name“: DEVICE_NAME,
„mf“: „Shelly“,
„mdl“: „Pro 3EM“,
„sw“: „Saldierung v“ + VERSION
};
let okImport = MQTT.publish(
haTopic + „-import/config“,
JSON.stringify({
„name“: „Saldierend Import“,
„uniq_id“: SHELLY_ID + „_sald_import“,
„stat_t“: SHELLY_ID + „/energy_counter/consumed“,
„unit_of_meas“: „kWh“,
„dev_cla“: „energy“,
„stat_cla“: „total_increasing“,
„avty_t“: avtyTopic,
„pl_avail“: „true“,
„pl_not_avail“: „false“,
„dev“: dev
}),
0, true
);
let okExport = MQTT.publish(
haTopic + „-export/config“,
JSON.stringify({
„name“: „Saldierend Export“,
„uniq_id“: SHELLY_ID + „_sald_export“,
„stat_t“: SHELLY_ID + „/energy_counter/returned“,
„unit_of_meas“: „kWh“,
„dev_cla“: „energy“,
„stat_cla“: „total_increasing“,
„avty_t“: avtyTopic,
„pl_avail“: „true“,
„pl_not_avail“: „false“,
„dev“: dev
}),
0, true
);
if (okImport && okExport) {
print(„HA Auto-Discovery sent. Device name: “ + DEVICE_NAME);
} else {
print(„WARNING: HA Discovery publish failed (MQTT not ready?).“);
}
}
// ─────────────────────────────────────────────
// 5. Load / Save Persistence (KVS)
// ─────────────────────────────────────────────
function LoadCounters() {
let loadedCount = 0;
function checkDone() {
loadedCount++;
if (loadedCount === 2) {
countersLoaded = true;
lastPublishedConsumed = „“;
lastPublishedReturned = „“;
print(„Counters ready. Consumed: “ + energyConsumedKWh + “ kWh | Returned: “ + energyReturnedKWh + “ kWh“);
if (MQTT.isConnected()) PublishCounters(true);
}
}
Shelly.call(„KVS.Get“, { „key“: „EnergyConsumedKWh“ }, function (res, err_code) {
if (res && res.value !== undefined && res.value !== null) {
energyConsumedKWh = Number(res.value);
if (isNaN(energyConsumedKWh)) energyConsumedKWh = 0.0;
print(„Loaded EnergyConsumedKWh: “ + energyConsumedKWh);
} else if (err_code !== 0) {
print(„INFO: EnergyConsumedKWh not in KVS yet (first run?).“);
}
checkDone();
});
Shelly.call(„KVS.Get“, { „key“: „EnergyReturnedKWh“ }, function (res, err_code) {
if (res && res.value !== undefined && res.value !== null) {
energyReturnedKWh = Number(res.value);
if (isNaN(energyReturnedKWh)) energyReturnedKWh = 0.0;
print(„Loaded EnergyReturnedKWh: “ + energyReturnedKWh);
} else if (err_code !== 0) {
print(„INFO: EnergyReturnedKWh not in KVS yet (first run?).“);
}
checkDone();
});
}
function SaveCounters() {
Shelly.call(„KVS.Set“, { „key“: „EnergyConsumedKWh“, „value“: energyConsumedKWh.toFixed(3) });
Shelly.call(„KVS.Set“, { „key“: „EnergyReturnedKWh“, „value“: energyReturnedKWh.toFixed(3) });
print(„Counters saved to KVS.“);
}
// ─────────────────────────────────────────────
// 6. Main Calculation Loop
// ─────────────────────────────────────────────
Timer.set(CONFIG.updateInterval, true, function () {
if (!SHELLY_ID || !countersLoaded) return;
let em = Shelly.getComponentStatus(„em“, 0);
if (!em || typeof em.total_act_power !== „number“) return;
let power = em.total_act_power;
let energyStep = power * (CONFIG.updateInterval / 1000.0);
if (power >= 0) {
energyConsumedWs += energyStep;
} else {
energyReturnedWs += Math.abs(energyStep);
}
if (energyConsumedWs >= 3600) {
let chunkC = Math.floor(energyConsumedWs / 3600);
energyConsumedKWh += chunkC / 1000.0;
energyConsumedWs -= chunkC * 3600;
}
if (energyReturnedWs >= 3600) {
let chunkR = Math.floor(energyReturnedWs / 3600);
energyReturnedKWh += chunkR / 1000.0;
energyReturnedWs -= chunkR * 3600;
}
PublishCounters(false);
if (CONFIG.enablePersistence) {
saveCounter++;
if (saveCounter >= CONFIG.saveInterval) {
saveCounter = 0;
SaveCounters();
}
}
});
Ausgehend von meiner Solaranlage wo die Werte recht schnell hochschnellen heißt es nun:
* Pro3EM-Solaranlage
* Pro3EM-Solaranlage Saldierend Export
Besonders letzteres. Es hieß bei allen 7 immer Pro3EM-Saldierend Export
Unnütz bei mehr als 2 Geräten.
Aktuell habe ich mehrere Sensor Helfer, mit denen ich mein BKW herausrechne und die Import/Export Werte vom Shelly bereinige. Das würde ich gern mit dem Script nun ersetzen.
Das Script im 3EM Plus läuft, MQTT Sensoren sind da: sensor.shelly_pro_3em_saldierend_export und sensor.shelly_pro_3em_saldierend_import.
Ich habe jetzt probiert die neuen Sensoren zu migrieren.
Im Energy Dashboard hab ich grad die Sensoren (Helfer): sensor.energy_vom_netz_kwh und sensor.energy_ins_netz_kwh
Die SQL Scripte hab ich ausgeführt, von den Helfern zu den neuen MQTT Sensoren. Danach die beiden neuen MQTT Sensoren im Energy Dashboard hinterlegt. Jetzt waren die vergangenen Daten scheinbar okay, nur am „heutigen“ Tag wurden rund -4.100 kWh angezeigt. Das kann ja auch nicht stimmen und ich hab das Backup wiederhergestellt.
Die Kosten waren auch auf 0€, aber da fehlte vermutlich noch die Kostenmigration.
Jemand ne Ahnung?
Das problem habe ich auch das am heutigen tag der gesamte stromverbrauch als -kWh wert angezeigt wird. Ich würde gerne wissen ob es bei den anderen auch so ist. Wegen den kosten habe ich in den youtube kommentaren einen gesehen der das bemägelt hat und selbst gefixt hat leider keine anleitung dazu.
Hallo,
ich habe über Entwicklerwerkzeuge > Statistik im entsprechenden Verbrauchszähler nach Ausreißern gesucht.
Zum Zeitpunkt der Migration habe ich dort einen negativen Wert in genau der Höhe der bisherigen Summe gefunden. Den hab ich einfach auf 0 geändert und schon hat alles wieder gepasst.
Einspeisung habe ich (noch) nicht. Aber vermutlich wird’s da genau so funktionieren, wenn ein Ausreißer drin steht.
Zum ändern der Statistik gibts schon ein Video:
https://www.simon42.com/statistiken-home-assistant-korrigieren/
Viele Grüße
J
gleiches Problem hier….
Würde die Daten in der Datenbank ja auch selber editieren aber keine Ahnung wo…
Gilt das alles auch für die anderen 3-phasigen Shellys, etwa 3EM-63W/T oder 3EM (ohne Pro)?
Hi Andreas, ja da brauchst du die Alternative über die Templates. Steht auch oben beschrieben. Wobei die Übersetzung hier und da nicht ganz stimmt. Aber wenn du schon mal Helfer angelegt hast – kein großes Problem.
Danke für die Anleitung aber wie nutzt man SQLite Web Add-on wo muss man die Skripte eintagen?
Mfg,
Christoph
Wie stoppe ich die Datenbankoperationen?
Hallo,
wenn ich das Template anlege und die Einstellungen für Maßeinheit setze zu W, erhalte ich folgende Meldung: ‚W‘ is not a valid unit for device class ‚energy‘; expected one of ‚cal‘, ‚Gcal‘, ‚GJ‘, ‚GWh‘, ‚J‘, ‚kcal‘, ‚kJ‘, ‚kWh‘, ‚Mcal‘, ‚MJ‘, ‚MWh‘, ‚mWh‘, ‚TWh‘, ‚Wh‘
gleiches auch bei Zustandsklasse
‚measurement‘ is not a valid state class for device class ‚energy‘; expected one of ‚total‘, ‚total_increasing‘
Was kann ich tun?
Die Zustandsklasse ist Leistung