Shelly und Home Assistant Skripte
- Tobias Volk
- vor 1 Stunde
- 10 Min. Lesezeit
Shelly Skript
Shelly Skript für den Shelly Pro 3EM in Verbindung mit dem TopAC powered by Shelly Ladegerät. Detaillierte Informationen dazu bekommt ihr bei meinem YouTube-Kollegen Florian Behncke in seinem Video: https://youtu.be/y5ucDPxbGCg
Hier meine angepasste Version für einen Shelly Pro 3EM mit einphasigem Betrieb des TopAC:
// ============================================================
// PV-Ueberschussladen mit Shelly Pro 3EM
// Steuerung einer TopAC Wallbox
// ============================================================
// -------------------- KONFIGURATION --------------------------
var TOPAC_IP = "192.168.0.42";
var PHASES = 1;
var MIN_A = 6;
var MAX_A = 16;
// Gewuenschter Netzbezug / Einspeisung
// Positiv = Netzbezug
// Negativ = Einspeisung
var TARGET_GRID_W = -400;
// Start / Stop
var START_W = -1400;
var STOP_W = -1250;
// Schutzgrenzen
var HARD_IMPORT_W = 200;
var PANIC_IMPORT_W = 1000;
// Regelintervalle
var FAST_MS = 10000; // schnelle Schutzregelung: 10 s
var SLOW_MS = 20000; // normale Regelung: 20 s
// Zero-Test
var ZERO_TEST_ENABLED = true;
var ZERO_GRID_LIMIT_W = 150;
var ZERO_TEST_COUNT = 24;
var ZERO_TEST_COOLDOWN = 36;
// -------------------- VARIABLEN ------------------------------
var zeroCounter = 0;
var zeroCooldown = 0;
var lastAmps = 0;
var chargerState = "unknown";
var busyFast = false;
var busySlow = false;
var initialized = false;
// ============================================================
// HTTP-FUNKTION
// Wird weiterhin fuer die TopAC verwendet.
// ============================================================
function httpGet(url, desc, cb) {
"HTTP.GET",
{
url: url,
timeout: 8
},
function (res, err) {
if (err) {
print("[FEHLER] " + desc + " | " + JSON.stringify(err));
if (cb) {
cb(null, err);
}
return;
}
if (!res || res.code < 200 || res.code >= 300) {
print(
"[FEHLER] " +
desc +
" | HTTP " +
(res ? res.code : "leer")
);
if (cb) {
cb(null, "HTTP Fehler");
}
return;
}
if (cb) {
cb(res, null);
}
}
);
}
// ============================================================
// PRO 3EM AUSLESEN
//
// WICHTIG:
// Der Pro 3EM stellt den Energiezaehler als em:0 bereit.
// total_act_power ist die gesamte aktuelle Wirkleistung.
//
// Positiv = Netzbezug
// Negativ = Einspeisung
// ============================================================
function readGrid(cb) {
var em = Shelly.getComponentStatus("em:0");
if (!em) {
print("[FEHLER] Pro 3EM EM:0 nicht verfuegbar");
cb(null);
return;
}
var gridW = Number(em.total_act_power);
if (isNaN(gridW)) {
print(
"[FEHLER] Pro 3EM total_act_power ungueltig: " +
JSON.stringify(em)
);
cb(null);
return;
}
cb(gridW);
}
// ============================================================
// TOPAC STROM AUSLESEN
// ============================================================
function readTopACCurrent(cb) {
httpGet(
"http://" +
TOPAC_IP +
"/rpc/Number.GetStatus?id=200",
"TopAC Strom lesen",
function (res, err) {
if (err || !res || !res.body) {
cb(null);
return;
}
try {
var data = JSON.parse(res.body);
var amps = Number(data.value);
if (isNaN(amps)) {
cb(null);
} else {
cb(amps);
}
} catch (e) {
print(
"[FEHLER] TopAC Strom JSON: " +
JSON.stringify(e)
);
cb(null);
}
}
);
}
// ============================================================
// TOPAC STATUS AUSLESEN
// ============================================================
function readTopACState(cb) {
httpGet(
"http://" +
TOPAC_IP +
"/rpc/Enum.GetStatus?id=200",
"TopAC Status lesen",
function (res, err) {
if (err || !res || !res.body) {
cb(null);
return;
}
try {
var data = JSON.parse(res.body);
cb(String(data.value));
} catch (e) {
print(
"[FEHLER] TopAC Status JSON: " +
JSON.stringify(e)
);
cb(null);
}
}
);
}
// ============================================================
// LADestrom SETZEN
// ============================================================
function setCurrent(amps) {
amps = Math.round(amps);
if (amps < MIN_A) {
amps = MIN_A;
}
if (amps > MAX_A) {
amps = MAX_A;
}
httpGet(
"http://" +
TOPAC_IP +
"/rpc/Number.Set?id=200&value=" +
amps,
"TopAC Strom " + amps + " A"
);
lastAmps = amps;
}
// ============================================================
// TOPAC AUF MINDESTSTROM VORBEREITEN
// ============================================================
function prepareMinCurrent() {
httpGet(
"http://" +
TOPAC_IP +
"/rpc/Number.Set?id=200&value=" +
MIN_A,
"TopAC vorbereitet auf " + MIN_A + " A"
);
}
// ============================================================
// LADUNG STARTEN
// ============================================================
function startCharge() {
httpGet(
"http://" +
TOPAC_IP +
"/rpc/Boolean.Set?id=200&value=true",
"TopAC START"
);
}
// ============================================================
// LADUNG STOPPEN
// ============================================================
function stopCharge(reason) {
print("STOP: " + reason);
httpGet(
"http://" +
TOPAC_IP +
"/rpc/Boolean.Set?id=200&value=false",
"TopAC STOP"
);
lastAmps = 0;
zeroCounter = 0;
prepareMinCurrent();
}
// ============================================================
// VERFUEGBARE LEISTUNG BERECHNEN
// ============================================================
function calcAvailable(gridW) {
var estimatedChargingW =
lastAmps 230 PHASES;
return (
-gridW +
estimatedChargingW -
TARGET_GRID_W
);
}
// ============================================================
// PRUEFEN, OB TOPAC AKTIV IST
// ============================================================
function isChargingState() {
return (
chargerState === "charger_charging" ||
chargerState === "charger_wait" ||
chargerState === "charger_pause" ||
chargerState === "charger_insert"
);
}
// ============================================================
// TOPAC STATUS AKTUALISIEREN
// ============================================================
function refreshTopAC(cb) {
readTopACState(function (state) {
if (state !== null) {
chargerState = state;
}
readTopACCurrent(function (amps) {
if (
amps !== null &&
amps >= MIN_A &&
amps <= MAX_A
) {
if (isChargingState()) {
lastAmps = amps;
}
}
if (cb) {
cb();
}
});
});
}
// ============================================================
// SCHNELLE SCHUTZREGELUNG
// ============================================================
function fastProtect() {
if (busyFast) {
return;
}
busyFast = true;
readGrid(function (gridW) {
busyFast = false;
if (gridW === null) {
print("FAST: keine Messung");
return;
}
// ----------------------------------------------------
// PANIC
// ----------------------------------------------------
if (gridW > PANIC_IMPORT_W) {
stopCharge(
"Panic Netzbezug " +
gridW +
" W"
);
return;
}
// ----------------------------------------------------
// NORMALER NETZBEZUG
// ----------------------------------------------------
if (gridW > HARD_IMPORT_W) {
print(
"FAST: Netzbezug " +
gridW +
" W -> runter"
);
if (lastAmps > MIN_A) {
setCurrent(lastAmps - 1);
} else if (lastAmps > 0) {
stopCharge(
"Netzbezug bei Mindeststrom"
);
}
return;
}
// ----------------------------------------------------
// ZERO-TEST
// ----------------------------------------------------
if (
ZERO_TEST_ENABLED &&
lastAmps > MIN_A
) {
if (zeroCooldown > 0) {
zeroCooldown--;
} else {
if (
gridW > -ZERO_GRID_LIMIT_W &&
gridW < ZERO_GRID_LIMIT_W
) {
zeroCounter++;
print(
"ZERO-TEST: " +
zeroCounter +
"/" +
ZERO_TEST_COUNT
);
} else {
zeroCounter = 0;
}
if (
zeroCounter >=
ZERO_TEST_COUNT
) {
print(
"ZERO-TEST: testweise 1 A runter"
);
setCurrent(lastAmps - 1);
zeroCounter = 0;
zeroCooldown =
ZERO_TEST_COOLDOWN;
}
}
}
});
}
// ============================================================
// NORMALE PV-REGELUNG
// ============================================================
function slowRegulate() {
if (busySlow) {
return;
}
busySlow = true;
refreshTopAC(function () {
readGrid(function (gridW) {
busySlow = false;
if (gridW === null) {
print(
"SLOW: keine Messung"
);
return;
}
var availableW =
calcAvailable(gridW);
print("------------------------------------");
print(
"TopAC Status: " +
chargerState
);
print(
"Netz: " +
gridW +
" W"
);
print(
"Letzte Ampere: " +
lastAmps +
" A"
);
print(
"Verfuegbar: " +
availableW +
" W"
);
// ------------------------------------------------
// PANIC
// ------------------------------------------------
if (gridW > PANIC_IMPORT_W) {
stopCharge(
"Panic slow"
);
return;
}
// ------------------------------------------------
// NETZBEZUG
// ------------------------------------------------
if (gridW > HARD_IMPORT_W) {
print(
"SLOW: Netzbezug -> kein Hochregeln"
);
return;
}
// ------------------------------------------------
// STARTBEDINGUNG
// ------------------------------------------------
if (
lastAmps <= 0 &&
availableW < START_W
) {
print(
"Noch nicht genug Ueberschuss zum Start"
);
stopCharge(
"zu wenig Startueberschuss"
);
return;
}
// ------------------------------------------------
// STOPBEDINGUNG
// ------------------------------------------------
if (
lastAmps > 0 &&
availableW < STOP_W
) {
stopCharge(
"zu wenig Ueberschuss"
);
return;
}
// ------------------------------------------------
// ZIELSTROM BERECHNEN
// ------------------------------------------------
var targetAmps =
Math.floor(
availableW /
(230 * PHASES)
);
if (targetAmps < MIN_A) {
targetAmps = MIN_A;
}
if (targetAmps > MAX_A) {
targetAmps = MAX_A;
}
// ------------------------------------------------
// LANGSAM HOCHREGELN
// ------------------------------------------------
if (targetAmps > lastAmps) {
if (lastAmps <= 0) {
targetAmps = MIN_A;
} else {
targetAmps =
lastAmps + 1;
}
}
print(
"Zielstrom: " +
targetAmps +
" A"
);
if (
targetAmps !== lastAmps
) {
setCurrent(targetAmps);
} else {
print(
"Strom unveraendert"
);
}
startCharge();
});
});
}
// ============================================================
// INITIALISIERUNG
// ============================================================
function init() {
print(
"Initialisiere TopAC..."
);
readTopACState(function (state) {
if (state !== null) {
chargerState = state;
}
readTopACCurrent(function (amps) {
if (
amps !== null &&
amps >= MIN_A &&
amps <= MAX_A &&
isChargingState()
) {
lastAmps = amps;
print(
"Initialer TopAC-Strom uebernommen: " +
lastAmps +
" A"
);
} else {
lastAmps = 0;
prepareMinCurrent();
print(
"TopAC nicht aktiv, auf " +
MIN_A +
" A vorbereitet"
);
}
initialized = true;
// Einmal sofort pruefen
fastProtect();
// Erste normale Regelung nach 5 Sekunden
Timer.set(
5000,
false,
slowRegulate
);
// Schnelle Schutzregelung
Timer.set(
FAST_MS,
true,
fastProtect
);
// Normale PV-Regelung
Timer.set(
SLOW_MS,
true,
slowRegulate
);
});
});
}
// ============================================================
// START
// ============================================================
print(
"PV-Ueberschussladen fuer Shelly Pro 3EM gestartet"
);
init();
Helper-Skript
Helper Skript, um Ladung mit maximaler Stromstärke zu erkennen und das eigentliche Skript außer Kraft zu setzen:
// ============================================================
// Watchdog + manuelle Netzladen-Erkennung
//
// Läuft als eigenes Script auf dem Shelly Pro 3EM
//
// Run on startup = EIN
//
// Aufgabe:
// - PV-Überschuss-Script überwachen
// - manuelles Netzladen erkennen
// - PV-Script während Netzladen stoppen
// - nach Ende des Netzladens wieder starten
// ============================================================
// ------------------------------------------------------------
// KONFIGURATION
// ------------------------------------------------------------
var TARGET_SCRIPT_NAME = "PV Überschuss";
var CHECK_MS = 30000;
// Lastsprung, der als manuelles Laden interpretiert wird
var JUMP_DETECT_W = 4000;
// Mindest-Netzbezug für Erkennung
var HIGH_IMPORT_W = 3000;
// Netzladen gilt als beendet,
// wenn der Netzbezug darunter fällt
var END_IMPORT_W = 2500;
// Alternativ: deutliche Einspeisung
var EXPORT_BACK_W = 4300;
// Anzahl notwendiger Messungen
var END_COUNT_NEEDED = 2;
// ------------------------------------------------------------
// STATUS
// ------------------------------------------------------------
var manualGridMode = false;
var lastGridW = null;
var endCounter = 0;
var exportCounter = 0;
var busy = false;
// ============================================================
// NETZLEISTUNG DES PRO 3EM LESEN
// ============================================================
//
// Positiv:
// Netzbezug
//
// Negativ:
// Einspeisung
//
// Beispiel:
// +6500 W = 6,5 kW Netzbezug
// -4300 W = 4,3 kW Einspeisung
// ============================================================
function readGrid(cb) {
var em = Shelly.getComponentStatus("em:0");
if (!em) {
print("[FEHLER] EM:0 nicht verfuegbar");
cb(null);
return;
}
var gridW = Number(em.total_act_power);
if (isNaN(gridW)) {
print(
"[FEHLER] total_act_power ungueltig"
);
cb(null);
return;
}
cb(gridW);
}
// ============================================================
// PV-SCRIPT SUCHEN
// ============================================================
function findPvScript(cb) {
"Script.List",
{},
function (res, err) {
if (
err ||
!res ||
!res.scripts
) {
print(
"[FEHLER] Script.List"
);
cb(null);
return;
}
for (
var i = 0;
i < res.scripts.length;
i++
) {
if (
res.scripts[i].name ===
TARGET_SCRIPT_NAME
) {
cb(res.scripts[i]);
return;
}
}
cb(null);
}
);
}
// ============================================================
// PV-SCRIPT STARTEN
// ============================================================
function startPvScript() {
findPvScript(function (s) {
if (!s) {
print(
"PV-Script nicht gefunden: " +
TARGET_SCRIPT_NAME
);
return;
}
if (s.running !== true) {
print(
"Starte PV-Script ID " +
);
"Script.Start",
{
id: s.id
}
);
} else {
print(
"PV-Script läuft"
);
}
});
}
// ============================================================
// PV-SCRIPT STOPPEN
// ============================================================
function stopPvScript() {
findPvScript(function (s) {
if (!s) {
print(
"PV-Script nicht gefunden: " +
TARGET_SCRIPT_NAME
);
return;
}
if (s.running === true) {
print(
"Stoppe PV-Script ID " +
);
"Script.Stop",
{
id: s.id
}
);
} else {
print(
"PV-Script ist bereits aus"
);
}
});
}
// ============================================================
// WATCHDOG
// ============================================================
function watchdog() {
if (busy) {
return;
}
busy = true;
readGrid(function (gridW) {
busy = false;
print("------------------------------------");
print(
"Grid: " +
gridW +
" W"
);
print(
"LastGrid: " +
lastGridW +
" W"
);
print(
"ManualGridMode: " +
manualGridMode
);
// ----------------------------------------------------
// KEINE MESSUNG
// ----------------------------------------------------
if (gridW === null) {
print(
"Keine Netzmessung -> PV-Script starten"
);
startPvScript();
return;
}
// ----------------------------------------------------
// LASTSPRUNG BERECHNEN
// ----------------------------------------------------
var jumpW = 0;
if (lastGridW !== null) {
jumpW =
gridW -
lastGridW;
}
print(
"Lastsprung: " +
jumpW +
" W"
);
// ====================================================
// NORMALBETRIEB
// ====================================================
if (!manualGridMode) {
// ------------------------------------------------
// MANUELLES NETZLADEN ERKANNT
// ------------------------------------------------
if (
jumpW > JUMP_DETECT_W &&
gridW > HIGH_IMPORT_W
) {
manualGridMode = true;
endCounter = 0;
exportCounter = 0;
print(
"===================================="
);
print(
"MANUELLES NETZLADEN ERKANNT"
);
print(
"Lastsprung: " +
jumpW +
" W"
);
print(
"Netzbezug: " +
gridW +
" W"
);
print(
"PV-Script wird gestoppt"
);
print(
"===================================="
);
stopPvScript();
lastGridW = gridW;
return;
}
// ------------------------------------------------
// NORMALER PV-BETRIEB
// ------------------------------------------------
startPvScript();
lastGridW = gridW;
return;
}
// ====================================================
// MANUELLES NETZLADEN AKTIV
// ====================================================
print(
"Manuelles Netzladen aktiv"
);
// PV-Script sicherheitshalber
// weiterhin ausgeschaltet halten
stopPvScript();
// ----------------------------------------------------
// FALL 1:
// DEUTLICHE EINSPEISUNG
// ----------------------------------------------------
if (
gridW <= -EXPORT_BACK_W
) {
exportCounter++;
print(
"Echte Einspeisung erkannt " +
exportCounter +
"/" +
END_COUNT_NEEDED
);
} else {
exportCounter = 0;
}
// ----------------------------------------------------
// FALL 2:
// NETZBEZUG IST DEUTLICH GESUNKEN
// ----------------------------------------------------
if (
gridW < END_IMPORT_W
) {
endCounter++;
print(
"Netzladen beendet / Fahrzeug weg " +
endCounter +
"/" +
END_COUNT_NEEDED
);
} else {
endCounter = 0;
}
// ----------------------------------------------------
// NETZLADEN BEENDET
// ----------------------------------------------------
if (
exportCounter >=
END_COUNT_NEEDED ||
endCounter >=
END_COUNT_NEEDED
) {
manualGridMode = false;
endCounter = 0;
exportCounter = 0;
print(
"===================================="
);
print(
"Netzladen beendet"
);
print(
"Zurueck zu PV-Ueberschussbetrieb"
);
print(
"===================================="
);
startPvScript();
lastGridW = gridW;
return;
}
lastGridW = gridW;
});
}
// ============================================================
// START
// ============================================================
print(
"Watchdog mit Lastsprung-Erkennung gestartet"
);
watchdog();
Timer.set(
CHECK_MS,
true,
watchdog
);
Skript für Home Assistant
Das folgende Skript enstand mit ChatGPT. Ziel ist die Überschussladung eines Elektroautos mit dem TopAC powered by Shelly Ladegerät, das einphasig angeschlossen ist. Die aktuelle Einspeiseleistung am Netzanschlusspunkt wird von einem Shelly Pro 3EM ermittelt.
alias: Top AC - Automatisches Überschussladen
description: Automatische Regelung des Top AC anhand des Shelly Pro 3EM
mode: single
triggers:
# Start bei mehr als 1400 W Überschuss für 20 Sekunden
- trigger: numeric_state
entity_id: sensor.shellypro3em_9454c5ba8324_leistung
below: -1400
for:
seconds: 20
id: start
# Regelung alle 20 Sekunden
- trigger: time_pattern
seconds: "/20"
id: regelung
# Sicherheitsabschaltung bei mehr als 1000 W für 30 Sekunden
- trigger: numeric_state
entity_id: sensor.shellypro3em_9454c5ba8324_leistung
above: 1000
for:
seconds: 30
id: sicherheit
actions:
# Aktuelle Shelly-Leistung und Ladeleistung auslesen
- variables:
shelly_power: >
{{ states('sensor.shellypro3em_9454c5ba8324_leistung') | float(0) }}
charging_current: >
{{ states('number.eve01_b08184ef44ac_strombegrenzung') | float(6) }}
- choose:
# ======================================================
# SICHERHEITSABSCHALTUNG
# Shelly > 1000 W für 30 Sekunden
# ======================================================
- conditions:
- condition: trigger
id: sicherheit
sequence:
- action: switch.turn_off
target:
device_id: 8bb6ba44d08b3d896addfbb65cd40ac2
# ======================================================
# TOP AC STARTEN
# Shelly < -1400 W für 20 Sekunden
# ======================================================
- conditions:
- condition: trigger
id: start
sequence:
# Ladestrom zunächst auf 6 A setzen
- action: number.set_value
target:
entity_id: number.eve01_b08184ef44ac_strombegrenzung
data:
value: 6
# Kurz warten
- delay:
seconds: 2
# Top AC einschalten
- action: switch.turn_on
target:
device_id: 8bb6ba44d08b3d896addfbb65cd40ac2
# ======================================================
# NORMALE REGELUNG ALLE 20 SEKUNDEN
# ======================================================
- conditions:
- condition: trigger
id: regelung
sequence:
- choose:
# ------------------------------------------------
# LADESTROM UM 1 A ERHÖHEN
# Shelly < -400 W
# Maximal 16 A
# ------------------------------------------------
- conditions:
- condition: template
value_template: >
{{
shelly_power < -400
and charging_current < 16
}}
sequence:
- action: number.set_value
target:
entity_id: number.eve01_b08184ef44ac_strombegrenzung
data:
value: >
{{ [charging_current + 1, 16] | min }}
# ------------------------------------------------
# LADESTROM UM 1 A VERRINGERN
# Shelly > 100 W
# ------------------------------------------------
- conditions:
- condition: template
value_template: >
{{
shelly_power > 100
and charging_current > 6
}}
sequence:
- action: number.set_value
target:
entity_id: number.eve01_b08184ef44ac_strombegrenzung
data:
value: >
{{ [charging_current - 1, 6] | max }}
# ------------------------------------------------
# BEI 6 A UND >100 W NETZBEZUG
# TOP AC AUSSCHALTEN
# ------------------------------------------------
- conditions:
- condition: template
value_template: >
{{
shelly_power > 100
and charging_current <= 6
}}
sequence:
- action: switch.turn_off
target:
device_id: 8bb6ba44d08b3d896addfbb65cd40ac2



