iinterne shelly-Daten auslesen und in eigener Struktur speichern
irgendeine beliebige Umrechnung oder Datentransformation durchführen
http-websocket aufmachen und neue Struktur für andere Geräte bereitstellen
mit anderem Gerät diese Struktur einlesen und verarbeiten
Ich hab erstmal nur einen shelly Daten-smoother gemacht: die ewig umherspringenden Leistungswerte waren nicht gut zu gebrauchen für die Nulleinspeisung mit openDTU oB.
Jeder Aufruf des websocket von aussen startet die Funktion. Dann werden die shelly-Daten lokal zwischengespeichert, der “smoother” (mitteln über die letzten 4 Messwerte) läuft durch, und die modifizierte Struktur wird als JSON rausgeschickt. Auf meinem shelly ist der websocket script/2/dtu, neben script/1/spotelly epex-Verarbeitung.
ps-doc.zip (890 Bytes) und diese paar Zeilen haben mich 3 Tage gekostet. Es schwirren nämlich viele Anleitungen herum, deren Methoden alle nicht mehr funktionieren
So. Nach gefühlten 48 Sunden am Rechner an einem einzigen Tag bin ich bei Version 0.2 angekommen. js is ja neu für mich. Aktueller Stand: smoother optimiert auf meine Systeme. Paar Schnitzer rausoperiert. Ein epex Signal ausm spotelly auswerten (hab nur ein Relais). Funktionen:
spotelly schaltet output → (bei mir Boiler + Lader ein) + WR aus
f<49,9 → volle Leistung
f>50,1 → WR aus
U<210 → volle Leistung
U>250 → WR aus
ansonsten über die letzten 3 Werte der Leistungsmessung gemittelte Werte
// shelly script power smooch for openDTU oB. extension for spotelly as script1
var a,b,sp; // 2 pointers, send power
var ip[]; // array for smoothing
var s0[]; // array for switch 0 = min price
var s1[]; // array for switch 1 = max price
var ev[]; // array for response
const ip_sz=3; // size of array
const lp=33.0; // lowest pwr inverter
if ((a===undefined)) // init once
{a=0;
b=0;
sp=0.0;
ip=[];
ev=[];
for (b=0;b<ip_sz;b++){ip.push(lp)}}; // preset array of size ip_sz
function ps(req,res){ // THIS IS WEBSOCKET ENDPOINT
Shelly.call("EM1.GetStatus",{id:0},function(result){ // get shelly power ch 0
ev=result; // store result in local array
b++; // b is pointer into smoothing array
if(b>=ip_sz||b<0){b=0}; // b is pointer into smoothing array
ip[b]=ev.act_power; // store act_power in array
//s1=(Shelly.getComponentStatus("switch",1)); // get max price switch(spotelly)
//if(s1){if (s1.output===true){ip[b]=1000}}; // if max price true: inverter max
s0=(Shelly.getComponentStatus("switch",0)); // get min price switch(spotelly)
if(s0){if (s0.output===true){ip[b]=-1000}};// if min price true: inverter off
if(ev.freq<49.9){ip[b]=1000}; // freq < min: inverter max
if(ev.freq>50.1){ip[b]=-1000}; // freq > max: inverter off
if(ev.voltage<210){ip[b]=1000}; // local voltage < min: inverter max
if(ev.voltage>250){ip[b]=-1000}; // local voltage > max: inverter off
for (a=0;a<(ip_sz-1);a++){sp+=ip[a]}; // add all power array elements
sp=(sp/(ip_sz)); // resize send power to openDTU oB
});
ev.act_power=sp; // prepare outgoing data
ev.id=2;
res.code=200;
res.body=JSON.stringify(ev);
res.headers=[["Content-Type","application/json"],["X-Device-Id",Shelly.getDeviceInfo().id]];
res.send()};
HTTPServer.registerEndpoint("dtu",ps)
läuft stabil, Überfrequenz hatte ich schon kurz und Relais schaltet WR aus. Läuft so erstmal mit. drawback: DTU kriegt stabil nur alle 3s neuen Wert. Mein code ist noch zu langsam. drawback 2: shelly liefert freq nur mit 1 Nachkommastelle.
Einen Tag gekämpft mit bool und output und buttons. Dem pro EM hab ichs geschafft das Betriebssystem zu crashen, neu BS-Version drauf und Reset und läuft wieder. Leider kann spotelly bislang nur auf das Relais unten zugreifen, der boolean button für max. Leistung ausm WR funktioniert aber schon, wenn ich den anklicke.
Noch paar Kleinigkeiten angepasst, Version 0.22 läuft stabil:
// shelly script power smooch for openDTU oB. extension for spotelly as script1
var a,b,sp; // 2 pointers, send power
var ip[]; // array for smoothing
var s0[]; // array for switch 0 = min price
var s1[]; // array for bool 200 = max price
var ev[]; // array for response
const ip_sz=3; // size of array
const lp=33.0; // lowest pwr inverter
if ((a===undefined)) // init once
{a=0;
b=0;
sp=0.0;
ip=[];
ev=[];
for (b=0;b<ip_sz;b++){ip.push(lp)}}; // preset array of size ip_sz
function ps(req,res){ // THIS IS WEBSOCKET ENDPOINT
Shelly.call("EM1.GetStatus",{id:0},function(result){ // get shelly power ch 0
ev=result; // store result in local array
b++; // b is pointer into smoothing array
if(b>=ip_sz||b<0){b=0}; // b is pointer into smoothing array
ip[b]=ev.act_power; // store act_power in array
s1=(Shelly.getComponentStatus("boolean",200)); // max price switch (spotelly)
if(s1){if (s1.value===true){ip[b]=1000}}; // if max price true: inverter max
s0=(Shelly.getComponentStatus("switch",0)); // get min price switch(spotelly)
if(s0){if (s0.output===true){ip[b]=-1000}};// if min price true: inverter off
if(ev.freq<49.9){ip[b]=1000}; // freq < min: inverter max
if(ev.freq>50.1){ip[b]=-1000}; // freq > max: inverter off
if(ev.voltage<210){ip[b]=50}; // local voltage < min: inverter ramp up
if(ev.voltage>250){ip[b]=-50}; // local voltage > max: inverter ramp down
for (a=0;a<(ip_sz-1);a++){sp+=ip[a]}; // add all power array elements
sp=(sp/(ip_sz)); // resize send power to openDTU oB
});
ev.act_power=sp; // prepare outgoing data
ev.id=2;
res.code=200;
res.body=JSON.stringify(ev);
res.headers=[["Content-Type","application/json"],["X-Device-Id",Shelly.getDeviceInfo().id]];
res.send()};
HTTPServer.registerEndpoint("dtu",ps)
Hab mich den halben tag rumgeärgert, weil eine shelly-Messung (nicht etwa der vom shelly angezeigte Wert!) ungefähr 5-6 sekunden brauchte bis in den Inverter. Ursache ist, daß ich im script eine out-of-sync Anweisung verwendet hatte. Steht natürlich nirgends, daß man sich damit ein Ei legt. Rumprobiert und eine andere schnellere inline-Funktion gefunden. Paar Zeilen code weniger, schachteltiefe von 3 auf 2, neuer Wert kommt jetzt in 1-2 sekunden am Inverter an.
Ursache und Lösung:
// old, slow
Shelly.call("EM1.GetStatus",{id:0},function(result){// get shelly power ch 0
ev=result; // store result in local array
// calculations
});
// new, fast
ev=Shelly.getComponentStatus("EM1", 0)
// calculations
Version 0.23 stable läuft mit.
Der pro EM50 hat ein eingebautes Relais, lustigerweise heißt der “switch 0”. An der Seite hat der eine Reihe winziger Löcher, da kann man ein “switch add-on” anstecken, noch ein Relais. Man kann aber auch einfach eine blaue LED in Reihe mit einem 1k Widerstand reinstecken in 2 der Löcher, nämlich “+3,3V” und “out 0” (Vorsicht: mach das im stomlosen Zustand, dann gut isolieren, da kann Netzspannung anliegen) und im shelly “Addo-on switch” aktivieren. Das ist aber nicht etwa switch 1, sondern switch 100 … was haben die geraucht?
das alles muss ich in den shelly laden? Passt doch niemals. Zeig mir die Stelle, an der du die eingebauten Messfehler rausrechnest vielleicht kann ich ein js draus machen
hmm, nee ich lasse das nicht auf den Shellys laufen, läuft aber auf jedem System, Linux, Windows, MacOS. Jeder Raspi geht, bei mir läuft es in einer Virtuellen Maschine im Synology NAS. Messfehler, naja ich denke mit denen muss man leben, aber die Shellys sind schon ziemlich genau, vor allem auch im niedrigen Watt Bereich.
Wie genau brauchst du die Daten? Jeder Zähler im Zählerschrank ist nicht unbedingt genauer.
Wenn ich die Daten vom EVU Zähler mit den Werten der Shellys vergleiche ist der gemessene Unterschied im unteren einstelligen kwh Bereich jetzt nach ca. 9 Monaten.
Grosses umprogrammieren: Messwerterfassung jetzt in einstellbarer Zeitschleife, bei mir alle 0,5s neuer Wert. Die Wertübermittlung shelly bis WR ist noch mal einiges schneller geworden.
Version 0.4 läuft mit:
// shelly meter script power smooch for openDTU oB.
// smoothes power signal, modifies via signals from epex script, line status
// openDTU oB setup: http+json, interval 1s, http://< addr >/script/2/dtu
var a=0; // pointer
var b=0; // pointer
var sp=0.0; // send power
var s0=Shelly.getComponentStatus("switch",0);//array for switch 0 = min price
//var s1=Shelly.getComponentStatus("switch",1);//array for switch 1 = max pri
var s1=Shelly.getComponentStatus("switch",100);//array for switch 100 = max p
//var s1=Shelly.getComponentStatus("boolean",200);//array for switch 200 = ma
var ev=Shelly.getComponentStatus("EM1",0); // array for response
var ip=[]; // array for act_power
var lp=33.0; // act_power preset
var up=600.0; // max. act_power increase into array
var dp=-160.0; // max. act_power decrease into array
const ip_sz=5; // size of array
const tick_sz=500; // timer tick (ms)
var TickHandle;
for (b=0;b<ip_sz;b++){ip.push(lp)}; // preset array of size ip_sz
function Tick(){
ev=Shelly.getComponentStatus("EM1",0); // get shelly power ch 0
b++; // b is pointer into smoothing array
if(b>=ip_sz||b<0){b=0;} // ip_sz ticks passed
ip[b]=ev.act_power; // store act_power in array
// s1=Shelly.getComponentStatus("switch",1); // max price switch
s1=Shelly.getComponentStatus("switch",100); // max price switch
// s1=Shelly.getComponentStatus("boolean",200); // max price switch
if(s1){if (s1.output===true){ip[b]=100}}; // if max price true: inverter max
// if(s1){if (s1.value===true){ip[b]=100}};// if max price true: inverter max
s0=Shelly.getComponentStatus("switch",0); // get min price switch(spotelly)
if(s0){if (s0.output===true){ip[b]=-100}};// if min price true: inverter off
if(ev.freq<49.9){ip[b]=150}; // freq < min: inverter max to smoothing array
if(ev.freq>50.1){ip[b]=-150}; // freq > max: inverter off to smoothing array
if(ev.voltage<212){ip[b]+=33}; // local voltage low: inverter ramp up
if(ev.voltage>248){ip[b]-=33}; // local voltage high: inverter ramp down
if(ip[b]>up){ip[b]=up}; // rise/fall limits
if(ip[b]<dp){ip[b]=dp}; // rise/fall limits
sp=0; // clear send power
for (a=0;a<(ip_sz);a++){sp+=ip[a]}; // add all power array elements
sp=((sp/(ip_sz))); // resize send power
if(ev.freq<49.9){sp=2000}; // freq < min: inverter max immediately
if(ev.freq>50.1){sp[=-2000}; // freq > max: inverter off immediately
//console.log(ip,"=",sp); // diag: input power, send power
Shelly.call("Number.Set",{"id":200,"value":sp})};
function startTimer(){TickHandle=Timer.set(tick_sz,true,Tick)};
startTimer();
function ps(req,res){ // THIS IS WEBSOCKET ENDPOINT
ev.act_power=sp; // prepare outgoing data
ev.id=2;
res.code=200;
res.body=JSON.stringify(ev);
res.headers=[["Content-Type","application/json"],["X-Device-Id",Shelly.getDeviceInfo().id]];
res.send()};
HTTPServer.registerEndpoint("dtu",ps)