-
-
Save Kinggrass/be1dc4b9cefd4eb0b4988e6492484083 to your computer and use it in GitHub Desktop.
Shelly Pro 3EM: Saldierende Energiemessung (Net Metering) mit Home Assistant Auto-Discovery
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Shelly Pro 3EM - Net Metering (Saldierung) & Home Assistant Auto-Discovery | |
| * | |
| * This script calculates the total active power across all 3 phases (A+B+C). | |
| * It simulates a "net metering" utility meter: | |
| * - If Total Power > 0: Increases "Import" counter | |
| * - If Total Power < 0: Increases "Export" counter | |
| * | |
| * FEATURES: | |
| * - Precise integration (runs every 0.5s) | |
| * - Persists counters to KVS (flash memory) to survive reboots | |
| * - MQTT Auto-Discovery: Automatically creates sensors in Home Assistant | |
| * | |
| * SETUP: | |
| * 1. Enable MQTT in Shelly Settings | |
| * 2. Add this script in the "Scripts" section | |
| * 3. Enable "Start on boot" | |
| */ | |
| let CONFIG = { | |
| updateInterval: 500, // Calculation cycle in ms | |
| saveInterval: 300, // Save to KVS every 300 cycles (~2.5 min) | |
| mqttPrefix: "homeassistant", // Standard HA discovery prefix | |
| }; | |
| let SHELLY_ID = undefined; | |
| let energyReturnedWs = 0.0; | |
| let energyConsumedWs = 0.0; | |
| let energyReturnedKWh = 0.0; | |
| let energyConsumedKWh = 0.0; | |
| let saveCounter = 0; | |
| let lastPublishedConsumed = ""; | |
| // 1. Get Device ID and start | |
| Shelly.call("Mqtt.GetConfig", "", function (res, err_code, err_msg, ud) { | |
| SHELLY_ID = res["topic_prefix"]; | |
| if (!SHELLY_ID) { | |
| print("ERROR: MQTT not configured or no topic_prefix found!"); | |
| } else { | |
| print("Shelly ID found: " + SHELLY_ID); | |
| LoadCounters(); | |
| // Announce device to Home Assistant after a short delay | |
| Timer.set(2000, false, AnnounceHA); | |
| } | |
| }); | |
| // 2. Home Assistant Auto-Discovery | |
| function AnnounceHA() { | |
| let haTopic = CONFIG.mqttPrefix + "/sensor/shellypro3em-" + SHELLY_ID; | |
| let deviceData = { | |
| "ids": [SHELLY_ID], | |
| "name": "Shelly Pro 3EM", | |
| "mf": "Shelly", | |
| "mdl": "Pro 3EM", | |
| "sw": "Script-Saldierung" | |
| }; | |
| // Sensor 1: Import (Grid Consumption) | |
| let payloadImport = 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", | |
| "dev": deviceData | |
| }); | |
| MQTT.publish(haTopic + "-import/config", payloadImport, 0, true); | |
| // Sensor 2: Export (Return to Grid) | |
| let payloadExport = 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", | |
| "dev": deviceData | |
| }); | |
| MQTT.publish(haTopic + "-export/config", payloadExport, 0, true); | |
| print("Home Assistant Auto-Discovery sent."); | |
| } | |
| // 3. Load/Save Persistence (KVS) | |
| function LoadCounters() { | |
| Shelly.call("KVS.Get", { "key": "EnergyConsumedKWh" }, function (res) { | |
| if (res && res.value) energyConsumedKWh = Number(res.value); | |
| }); | |
| Shelly.call("KVS.Get", { "key": "EnergyReturnedKWh" }, function (res) { | |
| if (res && res.value) energyReturnedKWh = Number(res.value); | |
| }); | |
| } | |
| function SaveCounters() { | |
| Shelly.call("KVS.Set", { "key": "EnergyConsumedKWh", "value": energyConsumedKWh }); | |
| Shelly.call("KVS.Set", { "key": "EnergyReturnedKWh", "value": energyReturnedKWh }); | |
| print("Counters saved to KVS."); | |
| } | |
| // 4. Main Calculation Loop | |
| Timer.set(CONFIG.updateInterval, true, function () { | |
| if (!SHELLY_ID) return; | |
| let em = Shelly.getComponentStatus("em", 0); | |
| if (typeof em.total_act_power !== 'undefined') { | |
| let power = em.total_act_power; | |
| let energyStep = power * (CONFIG.updateInterval / 1000.0); // Energy in Watt-Seconds | |
| // Logic: Net Metering | |
| if (power >= 0) { | |
| energyConsumedWs += energyStep; | |
| } else { | |
| energyReturnedWs -= energyStep; // power is negative, make positive for counter | |
| } | |
| // Convert Ws to kWh (1 kWh = 3,600,000 Ws) | |
| // We use a small buffer (3600 Ws = 1 Wh) to reduce float errors | |
| if (energyConsumedWs >= 3600) { | |
| let chunk = Math.floor(energyConsumedWs / 3600); | |
| energyConsumedKWh += chunk / 1000.0; | |
| energyConsumedWs -= chunk * 3600; | |
| } | |
| if (energyReturnedWs >= 3600) { | |
| let chunk = Math.floor(energyReturnedWs / 3600); | |
| energyReturnedKWh += chunk / 1000.0; | |
| energyReturnedWs -= chunk * 3600; | |
| } | |
| // Publish to MQTT if value changed | |
| let valC = energyConsumedKWh.toFixed(3); | |
| let valR = energyReturnedKWh.toFixed(3); | |
| if (valC !== lastPublishedConsumed) { | |
| MQTT.publish(SHELLY_ID + "/energy_counter/consumed", valC, 0, false); | |
| MQTT.publish(SHELLY_ID + "/energy_counter/returned", valR, 0, false); | |
| lastPublishedConsumed = valC; | |
| } | |
| // Periodic Save | |
| saveCounter++; | |
| if (saveCounter >= CONFIG.saveInterval) { | |
| saveCounter = 0; | |
| SaveCounters(); | |
| } | |
| } | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment