genmad / shellyAPIGen2AndAboveScripts

Scripts for the shelly API Gen2+
4 stars 0 forks source link

Paralleles Steuern von mehreren odob's anstatt Kaskade #4

Open genmad opened 4 months ago

genmad commented 4 months ago

Wenn man mehrere WR's /odobs parallel stört anstatt zu kaskadieren, kann das Vorteile haben.

Diese Vorteile erkaufen wir dann wahrscheinlich aber mit einem unruhigeren Regelverhalten der odob's ???? Nur eine Vermutung.

Dieses Skript ist nicht auf eine gemeinsame DC Seite beschränkt. Mehrere verschiedene Solarfelder inclusive. Batterien könnten wahrscheinlich kombiniert werden. Wenn eine odob ihren Anteil n nicht liefern kann, dann Regeln die anderen ihre Leistung in den nächsten Zyklen nach. Das Läuft dann asymptotisch gegen die benötigte Leistung.

@Manos1966 Bin mal gespannt wie sich das bei dir macht. https://github.com/genmad/shellyAPIGen2AndAboveScripts/blob/HTTP/src/openDTUonBattery/virtualPowerMeters_oDoBParallel.js

BKW1800 commented 3 months ago

oh stimmt. sry das hab ich überlesen im Skript. hätte ich selber auch drauf kommen müssen.

BKW1800 commented 3 months ago

Hallo leider bin ich mit der Programmierung von Shelly Skripten nicht so bewandert. Ich habe noch das Problem, dass ich gerne beide Codes in einem Skript laufen lassen würde, ohne das man zwischen Sommer und Winter manuell umschalten muss. Wenn das aktuelle Datum von z.b 01.11 bis 30.03 ist, dann <Code 1> ausführen, ansonsten <Code 2.>

Code 1 und Code 2 habe ich ja bereits. Eigentlich bräuchte ich nur die Bedingungsstruktur.

Kann mir hierzu vllt. helfen? Wird vermutlich simpel sein?

genmad commented 3 months ago

@BKW1800 In https://github.com/ALLTERCO/shelly-script-examples/blob/main/cover-scheduled-event-handlers.js ab Zeile 83 sollte dir helfen zu realisieren wonach du gefragt hast.

bytesinmotion commented 2 months ago

Darf ich hier kurz reingrätschen. Hab ich das Script richtig verstanden, dass dieses Script jetzt alles autonom regelt und verteilt und man in onBattery nicht mehr den dynamischen Limiter einschalten soll/muss?

genmad commented 2 months ago

Das Skript verteilt nur die Eingangs Leistung von deinem powermeter, neu auf mehrere Wechselrichter oder besser gesagt, mehrere odob Controller. Jeder odob Controller jedoch muss, oder sollte, weiter wie bisher betrieben werden mit dem dynamischen Power Limiter.

Das Skript verteilt weiterhin die Leistung so auf mehrere odob Controller, so dass jeder odon Controller immer eine minimale Leistung bekommt, so dass der Wechselrichter auch laufen kann und ansonsten halt entweder auf Minimum fährt in Max Anschlag oder irgendwo dazwischen halt regelt je nachdem wie gerade der Leistungsbedarf ist.

gitisgreat2023 commented 2 months ago

@genmad mein board ist immer noch nicht da... aber kommt hoffentlich in 2-3 Wochen und dann kann ich auch testen! :-) Eine Frage noch: was machen die min powers? Wird immer versucht beide WRs auf den min powers zu halten? zB zwei WRs, jeweils 15W min power. Jetzt ist 40W gefragt, WR1 macht dann 25W und WR2 15W? (damit WR2 schnell verfügbar ist wenn die gefragte Leistung über WR1 max geht). Oder ist auch bei 0W gefragte power immer WR1 und 2 an mit jeweils 15W, also 30W? Eigentlich streng genommen kann ich bei beide WRs auch die min power auf 0 setzen, oder? Weil beide ODoB sich ja kümmern um den min power...

Manos1966 commented 2 months ago

Kein Min Power Limit kleiner als (siehe Rechte Spalte) am Skript UND am jeweiligen Controller (wuerde ich Euch empfehlen) 😉

Minimum Watt Limit Calculation

https://github.com/helgeerbe/OpenDTU-OnBattery/wiki/Dynamic-Power-Limiter#minimum-power-limit---minimales-leistungslimit

bytesinmotion commented 2 months ago

Das Skript verteilt nur die Eingangs Leistung von deinem powermeter, neu auf mehrere Wechselrichter oder besser gesagt, mehrere odob Controller. Jeder odob Controller jedoch muss, oder sollte, weiter wie bisher betrieben werden mit dem dynamischen Power Limiter.

Ok jetzt glaub ich hab ich das Skript verstanden. Ich dachte du setzt das dynamische Limit via dem Skript aber wenn ich richtig verstanden habe holst du dir nur die Info vom ersten Wechselrichter wieviel er aktuell leistet und schickst dann an den zweiten Wechselrichter nur mehr den "Rest" als benötigt. Hab ich das richtig durchgeblickt?

Könnte man das Skript auch so erweitern um den Smart Micro Solar nachzubauen indem man via diesem Skript direkt das Power an Wechsler 1 hochfährt und Wechler 2 von onBattery geregelt wird. Dadurch bräuchte es dann theoretisch nur einen odob controller?

genmad commented 2 months ago

Ich kenne den smart micro solar nicht. Daher kann ich dazu keine Aussage treffen.

Aber man kann schon einiges machen, aber die Platform hat ein paar Limitierungen, solange man nicht an diese stößt, ist vieles möglich.

bytesinmotion commented 2 months ago

Der sms macht dynamische Steuerung für mehrere Hoys über einem odob. Theoretisch könnte man direkt vom Shelly das Limit eben von einem (ohne Akku z.b.) direkt hochsetzen nicht nur readings liefern. So mein Gedankengang...

genmad commented 2 months ago

@genmad mein board ist immer noch nicht da... aber kommt hoffentlich in 2-3 Wochen und dann kann ich auch testen! :-) Eine Frage noch: was machen die min powers? Wird immer versucht beide WRs auf den min powers zu halten? zB zwei WRs, jeweils 15W min power. Jetzt ist 40W gefragt, WR1 macht dann 25W und WR2 15W? (damit WR2 schnell verfügbar ist wenn die gefragte Leistung über WR1 max geht). Oder ist auch bei 0W gefragte power immer WR1 und 2 an mit jeweils 15W, also 30W? Eigentlich streng genommen kann ich bei beide WRs auch die min power auf 0 setzen, oder? Weil beide ODoB sich ja kümmern um den min power...

Es wird immer mindestens min Power von jedem WR verlangt, egal wie wenig du aktuell brauchst. Das ist deswegen so, dass der Wr immer sehr schnell auf Leistungsänderungen reagieren kann. Die min Power kann man je odob Controller frei einstellen. Es wurde schon von anderen berichtet, dass die Wr‘s teilweise sehr lange brauchen, wenn sie unterhalb einer gewissen Grenze betrieben werden, bevor diese aufwachen und die angeforderte Leistung liefern ( im Minuten ereich).

gitisgreat2023 commented 1 month ago

@genmad @Manos1966 Board ist da! Allerdings läuft es überhaupt noch nicht... :-( Wie schon mal geschrieben, setup ist Pylontech US5000 mit HMT-2250.

Ich habe sowohl im shelly script 45W eingestellt und auch noch in ODoB als min power 45. Alles ging gut, regelte schön. Status wechselte zwischen blau und gelb, so wie es sein soll. Aber nach einige Minuten geht er auf rot, also verliert die Verbindung. Habe einen Antenne am CTM2300 Modul, Signalstärke ist -1 bis 0 dBm.

Habe jetzt mal 70W als minimum im Skript eingestellt und in ODoB 3% (67.5W, also 3% von 2250 gerundet nach oben). Dann springt er nach 15 Minuten mal von rot auf gelbt.... (direkt Signalstärke angeschaut: immer noch -1 bis 0 dBm) bleibt 1-2 Minuten Gelb und Status wird dann wieder rot.

Was sind eure Ideeen? Hat der WR eine Macke? Oder die PCB? Mir fehlen im Moment die Ideeen zu troubleshooten... Schon andere USB supply probiert, ändert alles nichts. (5 Watt Apple sollte dicke reichen, oder?) Weil das Ding eventuell amok läuft habe ich es vom Netzt getrennt, aber trotzdem, weil es noch an der Batterie hängt, sollte er erreichbar (Status Gelb) sein, oder?

Manos1966 commented 1 month ago

Also als erstes teste deine Empfangsleistung und stelle alles richtig. Danach kannst du anfangen den Skript zu testen.

Ich haben den Eindruck, deine Probleme haben nichts mit dem Skript zu tun. Signalstärke a -1 bis 0 dBm, also sehr sehr nah oder???

Abstand erhoehen. Eine Signalstaerke von -40dBm ist gut. In der Regel wird eine Signalstärke von -50 dBm oder höher als ausgezeichnet angesehen, während Werte unter -100 dBm auf eine schlechte Verbindung hinweisen.

Mit -1 ist es zu "Laut" und der Wechselrichter empfaengt die Signale nicht richtig.

genmad commented 1 month ago

Ich würde das minimum in odob viel nidrieger einstellen, oder im script höher. Die werte die du jetzt gleich gesetzt hast, sind konzeptionell nicht gleich. Vielleicht kann das auch ein Problem sein.

BKW1800 commented 1 month ago

ein frage zum Zielwert auf den geregelt werden soll. Dieser kann ja im Skript vorgegeben werden.

Angenommen man will auf -5 Watt regeln und hat 2 WR mit je einer DTU.

Mein WR hat Buchstaben in Seriennummer (1164r00b5fk8): daher wird die Seriennummer ab dem Buchstaben nicht mehr blau angezeigt. was muss hierzu im Code umdeklariert werden, damit das geht? Bei der OpenDTU gab es das Problem auch mal und wurde in einem Update dann angepasst.

genmad commented 1 month ago

ein frage zum Zielwert auf den geregelt werden soll. Dieser kann ja im Skript vorgegeben werden.

Angenommen man will auf -5 Watt regeln und hat 2 WR mit je einer DTU.

  • Wird dieser Wert von -5 Watt dann an alle OpenDtus einzeln gesendet?
  • Wie verhält es sich wenn dann beide ihre virtuellen Powermeter auf -5 Watt regelen. Dann wären das ja eigentlich in Summe am richtigen Stromzähler -10 Watt (2x -5 Watt) oder?

siehe Doku: bei Verwendung der targetconsumption sollen die odob Controller auf 0 Watt stehen.

  • Man ist ka damit für die Regelung der WR an die Shelly Cloud gebunden. Wenn diese ausfällt, was machen dann die WR? Wird dann der in OpenDTU eingetragene Grundlastwert von den WR wieder ausgeregelt?

Wenn das im lokalen Netz betrieben wird, dann ist man nicht auf die shelly cloud angewiesen. Ich habe bei mir nicht die shelly cloud aktiviert. Einfach IP des shellys + Pfad zum Skript angeben und schon sollte es gehen.

  • Welche Gefahr geht für das Skript und Updates/Anpassung durch Shelly in der Cloud aus? Kann es vorkommen, dass das Skript dann in 10 Jahren nicht mehr läuft weil shelly was ändert? Oder kann man dann das Skript einfach an die Anpassung anpassen - oder kann das garnicht vorkommen?

Alles was du dir vostellen kannst oder auch nichts davon. Ich kann nicht in die Zukunft sehen. Das Skript kann man jederzeit anpassen.

  • Der Strombedarf wird ja auf die Anzahl der virtuellen Powermeter gleichmäßig aufgeteilt wird, was ist dann wenn ein WR gar keinen Strom liefert, weil z.b Panele noch im Schatten sind? Dann kann der Strombedarf ja nicht gedeckt werden. Wäre es nicht besser, daher nur den Strombedarf auf die wirklich einspeisenden WR und damit auch die entsprechenden virtuellen Powermeter aufzuteilen? Kann man das im Skript noch anpassen?

Das Skript ist für mehrere Wrs an der selben Batterie geschrieben. Da ich deinen Usecase und Aufbau nicht habe, kann ich da auch nichts entwickeln/testen.

  • Auszug Code: "inverterSerialNumber: 116182803975}"

Mein WR hat Buchstaben in Seriennummer (1164r00b5fk8): daher wird die Seriennummer ab dem Buchstaben nicht mehr blau angezeigt. was muss hierzu im Code umdeklariert werden, damit das geht? Bei der OpenDTU gab es das Problem auch mal und wurde in einem Update dann angepasst.

Versuch mal deinen Nr in einfache Hochkommata zu verpacken. (Vorne und hinten, in dem Beispiel unten konnte ich vorne keine hin machen, da ich das hier über handy schreibe)

"inverterSerialNumber: ‚1164r00b5fk8‘ }"

BKW1800 commented 1 month ago

zu: targetconsumption: //set this value to the target grid consumption as defined in odob controller instead of spreadig up the values across all your odob Controllers // set in each of the odob controller the target grid consumption then to 0 // negative values feed power to th net positive values recieve power from the net let targetGridConsumption_Watt

d.h. konkret, dass die beiden WR dann den eingetragenen Netzverbrauchszielwert vom Code ausregeln und nicht jeder WR dann einzeln den Wert vom virtuellen Wechselrichter. verstehe ich das richtig?

zu: WR SN in Anführungsstriche: Die SN vom WR wir dann "Grün" angezeigt? hat das eine gewisse Bedeutung oder müsste die SN "Blau" sein. Hab Leider aktuell keinen Zugriff auf meinen WR um das zu testen. Glaub beim OpenDTU ware da irgendwie ein Thema mit Hexadezimaler deklarierung!?

zu: meiner Frage wegen der Thematik, wenn ein WR dann nicht versorgt ist: Übernehmen dann die anderen WR die Leistung zusammen, so dass der Netzbezugszielwert erreicht wird. "Wenn eine odob ihren Anteil n nicht liefern kann, dann Regeln die anderen ihre Leistung in den nächsten Zyklen nach. Das Läuft dann asymptotisch gegen die benötigte Leistung" Glaub mit deiner Beschreibung hat sich das dann geklärt und die übrigen WR übernehmen dann die Leistung

noch ein Thema Für jeden WR braucht man ja eine eigene OpenDTU.

// configure http access ( path to the power reading) let httpConfig = { address: "http://192.168.178.67/api/livedata/status?inv=116182803975" // the address of the http powerreading required , jsonPath: "inverters/0/AC/0/Power/v" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // e.g.: inverters[0].name needs to be represented as: inverters.0.name };

Wie ergänzt man dann hier den Code richtig. weil da bräuchte man ja dann noch die IP der zweiten DTU ?

genmad commented 1 month ago

zu: WR SN in Anführungsstriche: Die SN vom WR wir dann "Grün" angezeigt? hat das eine gewisse Bedeutung oder müsste die SN "Blau" sein. Hab Leider aktuell keinen Zugriff auf meinen WR um das zu testen. Glaub beim OpenDTU ware da irgendwie ein Thema mit Hexadezimaler deklarierung!?

Unterscheide bitte zwischen odob und dem skript. Dieses skript ist ein ganz einfacher Algorithmus der einfach virtuelle Powermeter simuliert. Die Seriennummer wurde als Zahl im Beispiel hinterlegt muss aber ein String sein. Das haben wir mit den Anführungszeichen wahrscheinlich erreicht. Ich habe es nicht ausprobiert, sondern nur mal so angeschaut und das könnte passen. Falls es nicht passt wirst du das herausfinden und melden. 😉 Dann schauen wir weiter.

noch ein Thema Für jeden WR braucht man ja eine eigene OpenDTU.

// configure http access ( path to the power reading) let httpConfig = { address: "http://192.168.178.67/api/livedata/status?inv=116182803975" // the address of the http powerreading required , jsonPath: "inverters/0/AC/0/Power/v" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // e.g.: inverters[0].name needs to be represented as: inverters.0.name };

Wie ergänzt man dann hier den Code richtig. weil da bräuchte man ja dann noch die IP der zweiten DTU ?

Das was da konfiguriert ist war nur mein Testcode und der wird in deinem Fall einfach anders sein. Zur Erklärung ich hatte nur einen shellypro3em und habe das script auf diesem entwickelt und den http access dann über odob realisiert. ( ein komplizierter workaround) Du musst an dieser stelle nur einmal den Powermeter konfigurieren, oder ignorieren wenn du es auf einem pro3em laufen lässt. Dann in der config local eintragen, ansonsten den wert aus der odob für den verwendeten Powermeter eintragen.

BKW1800 commented 1 month ago

ok. dann irgnorieren. weil ich das Skript auf dem Shellypro3em realisiere. d.h. ich lösch den Teil: // configure http access ( path to the power reading) let httpConfig = { address: "http://192.168.178.67/api/livedata/status?inv=116182803975" // the address of the http powerreading required , jsonPath: "inverters/0/AC/0/Power/v" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // e.g.: inverters[0].name needs to be represented as: inverters.0.name }; Einfach ganz aus dem Code raus.

Problem wir dann nur sein was im weiteren Code dann noch alles zu löschen ist... ich bin kein Programmierer wie du... oder wie trage ich den powermeter dann hier ein? nur die IP adresse und was ist mit dem JsonPath? Kannst du mir das vllt beispielhat hier mal antworten, wie das aussehen muss?

genmad commented 1 month ago

Du musst nur let netpowerconfig= “local“ eintragen und den rest lässt du einfach stehen.

BKW1800 commented 1 month ago

Hallo, habe ein Problem:

Hab im Shelly pro 3Em das Skript angelegt und gestart. Zuvor in der OpenDTU den Strommeter befüllt.

Nach dem Starten des Skcriptes kommt:

^ 18:12:03 Uncaught SyntaxError: Got ']' expected EOF 18:13:33 at ]; 18:13:33 ^

Skript in Shelly: `// set unique script ID here !!! be aware no other script on your shelly can have the same id !!! ( It does not matter whether they are running or not) let scriptId=1;

// configuration for each controller, add as many as you have controller ( there is an upper limit off 5 controllers defined by the // shelly, because you can only be registered up to 5 HTTP endpoints (at the time of writing this code) // the order in which the controller are added reflects the cascade order, first controller added is the first // in the cascade, the later are following then // nominalPower_Watt : the maximum power the controller can provide // minRequiredPower_Watt: the minimum Power required to run the controller/inverter, // controllerIp: the ip of the odob controller ( inverter needs to be the first/only one registered (e.g.: position 0) at the given controller) // inverterSerialNumber: the serial number of the inverter to control as shown in oDoB ( settings -> inverter -> serial number) let configs= { nominalPower_Watt: 800, minRequiredPower_Watt: 40, controllerIp: '192.168.178.26', inverterSerialNumber: '1164a00b5fe8' } // , { nominalPower_Watt: 800, minRequiredPower_Watt: 80, controllerIp: '192.168.178.68', inverterSerialNumber: 000} ];

//power measuring device // choose between "local" e.g. this script is running on a gen2 device which can measure net power ( nothing else needs to be configured for the powerreading) // or "http" e.g. pulls the power readings by http requests ( configure http settings in httpConfig underneath) let netPowerConfig = "local" // choose one: "local" or "http"

// configure http access ( path to the power reading) let httpConfig = { address: "http://192.168.178.28/script/1/pwr1" // the address of the http powerreading required , jsonPath: "'PWR'" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // e.g.: inverters[0].name needs to be represented as: inverters.0.name };

// Configuration of your oDoB contorller i ( on position i) in the configs variable (see above) (i=1..n) // // define in Powermeter the mode https + Json and use http:///script//pwr // e.g. assume the ip of the shelly on which this script runs on to be: 1.2.3.4, // the script id of this script to be 7 and you want to configure the 2nd odob controller // then use: http://192.168.178.28/script/1/pwr1 // the Json path is always 'PWR'

// set this value to the target grid consumption as defined in odob controller instead of spreadig up the values across all your odob Controllers // set in each of the odob controller the target grid consumption then to 0 // negative values feed power to th net positive values recieve power from the net let targetGridConsumption_Watt = -5;`

anbei ein Screenshot vom Stromzähler in der OpenDTU.OpenDTU_Stromzähler Siehst du einen Fehler?

genmad commented 1 month ago

Du hast das skript nicht korrekt kopiert oder modifiziert. Irgendwo, evtl am ende ist ein ] zu viel. Schau dir mal an wie manos im anderen Thread den code geteilt hat, mach das mal auch so, da in den Kommentaren man das nicht gut lesen kann und auch keine Zeilennummern dran sind.

genmad commented 1 month ago

In dem was du gepostet hast ist bei let config am ende ein ] zu viel. Lösch das mal. Und wenn du code postest, dann bitte als Code formatieren.

BKW1800 commented 1 month ago

oh sry, du hast recht da fehlt eine Klammer. Hab in der DTU den Code eintragen. hab bei PWR die ' noch weggelassen. nun läuft es soweit.

eine grundsätzliche Frage noch. Warum wird im Code der Zielverbrauchswert eingetragen? In der DTU bei WR gibt man den ja sowieso nochmals an und nach dem Regler der WR ja dann den dazugehörigen Strommesser oder? Brauchst du den nur um etwas auszurechnen oder hat er auch eine Auswirkung auf die einzelnen Regler selbst oder in Summe?

genmad commented 1 month ago

Der Zielverbrauchswert macht an dieser stelle für mehrere Wrs meiner Meinung nach Sinn, da ich ihn als skalar verstehe, der im Ergebnis die summe des zu beziehenden Stroms oder einzuspeisenden Stroms angibt. In odob ist das auf die einzelnen Phasen bezogen. Man kann es als kleine Vereinfachung sehen. Der Zielverbrauchswert wird auf die gesamt einspeisende Energie aufsummiert und somit die Regelung der Wrs angepasst. Nichts großes ich habs gemacht weil ich die konnte und für passend empfunden habe.

Manos1966 commented 1 month ago

und wir sind Dankbar, dass du es mit uns geteilt hast, @genmad 💯

BKW1800 commented 1 month ago

Der Zielverbrauchswert macht an dieser stelle für mehrere Wrs meiner Meinung nach Sinn, da ich ihn als skalar verstehe, der im Ergebnis die summe des zu beziehenden Stroms oder einzuspeisenden Stroms angibt. In odob ist das auf die einzelnen Phasen bezogen. Man kann es als kleine Vereinfachung sehen. Der Zielverbrauchswert wird auf die gesamt einspeisende Energie aufsummiert und somit die Regelung der Wrs angepasst. Nichts großes ich habs gemacht weil ich die konnte und für passend empfunden habe.

Heisst das dann quasi, dass damit Regelung bei mehreren z.b 5 x WR besser wird? Weil wenn man nun bei allen fünf WR z.b. +5 Watt in der OpenDTU für den jeweiligen virtuellen Stromzähler einstellt. Würde das ja bedeuten, dass in Summe dann der Zielverbrauchswert real am Stromzähler dann 5x5Wtt = 25 Watt sein müsste.? Das hebelst du mit dem Code dann quasi aus und der Zielverbrauchswert am Stromzähler bleibt dennoch bei +5 Watt?

genmad commented 1 month ago

Heisst das dann quasi, dass damit Regelung bei mehreren z.b 5 x WR besser wird? Weil wenn man nun bei allen fünf WR z.b. +5 Watt in der OpenDTU für den jeweiligen virtuellen Stromzähler einstellt. Würde das ja bedeuten, dass in Summe dann der Zielverbrauchswert real am Stromzähler dann 5x5Wtt = 25 Watt sein müsste.? Das hebelst du mit dem Code dann quasi aus und der Zielverbrauchswert am Stromzähler bleibt dennoch bei +5 Watt?

Besser ? Ich würde es als „anders“ formulieren. odob Controller sind auf sich allein gestellt. Erst mit dem script kann man odob controller parallel betreiben und dabei auf die gesamt Leistung steuern, so dass sich die Controller unterstützen können. Ohne das script fangen mehrere odob Controller entweder an zu schwingen, wenn sie auf die gesamtleistung steuern, oder sie können nur versuchen auf ihrer Phase die angefragte Leistung bereit zu stellen ohne dass andere odobs auf anderen Phasen bei der Leistung helfen können.

BKW1800 commented 1 month ago

du schreibst;

Wenn das im lokalen Netz betrieben wird, dann ist man nicht auf die shelly cloud angewiesen. Ich habe bei mir nicht die shelly cloud aktiviert. Einfach IP des shellys + Pfad zum Skript angeben und schon sollte es gehen.

-> Wo aber wäre dann das Skript abgelegt? Auf dem Speicher von Router und wo muss der Pfad im Skript dann eingetragen werden?

let httpConfig = { address: "http://192.168.178.28/script/1/pwr1" // the address of the http powerreading required , jsonPath: "'PWR'" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // e.g.: inverters[0].name needs to be represented as: inverters.0.name };

Das was da konfiguriert ist war nur mein Testcode und der wird in deinem Fall einfach anders sein. Zur Erklärung ich hatte nur einen shellypro3em und habe das script auf diesem entwickelt und den http access dann über odob realisiert. ( ein komplizierter workaround) Du musst an dieser stelle nur einmal den Powermeter konfigurieren, oder ignorieren wenn du es auf einem pro3em laufen lässt. Dann in der config local eintragen, ansonsten den wert aus der odob für den verwendeten Powermeter eintragen.

Wenn das Skript nicht auf dem Shelly pro3Em dann laufen sollen, sondern nur im Netzwerk, wie muss man aber den obigen Code dann ergänzen?. bei Adresse nur den Pfad des Skriptes auf dem Speicher vom Router angeben?

genmad commented 1 month ago

Das skript wird auf einem shelly, idealerweise dem pro3em ausgeführt.

Bitte lies die Kommentare im script, und wenn dir etwas unklar ist, dann lass es uns gemeinsam verbessern.

Wie kann ich diesen Text:

//power measuring device // choose between "local" e.g. this script is running on a gen2 device which can measure net power ( nothing else needs to be configured for the powerreading) // or "http" e.g. pulls the power readings by http requests ( configure http settings in httpConfig underneath) let netPowerConfig = "http" // choose one: "local" or "http"

verbessern? Meiner Meinung nach steht da alles drin was man machen muss.

Du musst nur let netpowerconfig= “local“ eintragen und den rest lässt du einfach stehen.

Ich meinte auch hier deine Frage schon beantwortet zu haben. Mit dem Kommentar zusammen sollte klar werden, dass wenn man local einträgt httpConfig nicht benutzt wird.

BKW1800 commented 1 month ago

ok ich belasse es dabei. eine grundsätzliche abschließende Frage:

Du schreibst oben "Paralleles Steuern von mehreren odob's anstatt Kaskade"

Spricht was dagegen zwei WR der Serie HM von Hoymilies an den werksseitige Steckern zusammenzuschließen um dann mit nur einem AC-Stecker an eine Schukosteckdose zu gehen und dabei dein Skript zu verwenden?

jeder WR wäre dann selber dem virtuellen Powermeter zugeordnet und regelt sich selbst dann

genmad commented 1 month ago

Bezogen auf das skript sehe ich da im Moment keine Probleme. Könnte aber trotzdem zu welchen führen, man weiß ja nie ganz sicher was der Versuch ergibt. Zu allen anderen rechtlichen und technischen Konsequenzen treffe ich keine Aussage.

Ich weiß nur so viel, dass Schuko Steckdosen nicht mit mehr als 10A Dauerhaft betrieben werden sollen/dürfen. Daher sollte die Leistungssumme der Wr 2300W nicht übersteigen.

Also könnte deine Idee unter umständen nicht optimal sein.

BKW1800 commented 1 month ago

ok. das Skript läuft mit einem WR seit ei paar Tagen sehr stabil. Top. Hab gestern den zweiten WR mit der Batterie eingebunden. (provisorisch in Reichweite des Wlans) Ging da leider nicht, weil der zweite WR mit der Batterie (an seinem finalen Platz) dann zu weit vom Wlan entfernt war und dadurch er den nicht fand. Aber ein Phänomen trat dann auf. Ich habe den zweiten WR und die OpenDTU mit seiner IP in das Skript eingetragen. Das Skript beendete sich aber dann nach einer gewissen Zeit immer automatisch. Ich musste es dann immer händisch wieder starten. Ich schätze mal das, er der den zweiten WR und die OpenDTU sucht und dann aber nicht im Netzwerk findet und das Skript abstürzt.

Kann man das hier nicht so lösen, dass nicht das Skript komplett abstürzt, sondern nur eine Fehlermeldung in das Aktivitätsprotokoll von Shelly geschrieben wird? Wie müsste das Skript dann wo angepasst werden?

Was ich nächstes Wochenende auch noch dann prüfen will. Was ist wenn der zweiten WR keinen Spannung von der zweiten Batterie bekommt, weil die leer ist aber er die Opendtu und den zweiten WR im Netzwerk findet.

Stürzt dann auch das Skript ab, weil er zweiten WR dann eben aus ist? Oder kannst du, dass ohne meinen Test aufgrund des Skriptbaufbaus beantworten?

Manos1966 commented 1 month ago

Ja, das waere keine schlechte Idee @genmad 👍

genmad commented 1 month ago

ok. das Skript läuft mit einem WR seit ei paar Tagen sehr stabil. Top. Hab gestern den zweiten WR mit der Batterie eingebunden. (provisorisch in Reichweite des Wlans) Ging da leider nicht, weil der zweite WR mit der Batterie (an seinem finalen Platz) dann zu weit vom Wlan entfernt war und dadurch er den nicht fand. Aber ein Phänomen trat dann auf. Ich habe den zweiten WR und die OpenDTU mit seiner IP in das Skript eingetragen. Das Skript beendete sich aber dann nach einer gewissen Zeit immer automatisch. Ich musste es dann immer händisch wieder starten. Ich schätze mal das, er der den zweiten WR und die OpenDTU sucht und dann aber nicht im Netzwerk findet und das Skript abstürzt.

Dass, wenn die odobs nicht gefunden werden, das Skript sich beendet ist mit Absicht so. Nur eine voll funktionsfähige Kommunikation kann die Funktionalität des Skripts sicher stellen. Ändern kann man das schon, aber was bringt es einem dann? Wenn ihr wisst das die Kommunikation für euren Anwendungsfall nicht ausreichend ist, dann sollte ihr da das Wlan verbessern.

Kann man das hier nicht so lösen, dass nicht das Skript komplett abstürzt, sondern nur eine Fehlermeldung in das Aktivitätsprotokoll von Shelly geschrieben wird? Wie müsste das Skript dann wo angepasst werden?

Kann man schon, sehe aber dann den Sinn des Skripts nicht mehr. Und ob dass dann noch alles so funktioniert wie es designed wurde ist dann auch eine kandere Frage. Prinzipiel müsste man nur TimerHandles[dict.index] = Timer.set(TIMEOUT_NETWORK * ONE_MINUTE, false, kill, dict.index); überall löschen/auskommentieren.

Was ich nächstes Wochenende auch noch dann prüfen will. Was ist wenn der zweiten WR keinen Spannung von der zweiten Batterie bekommt, weil die leer ist aber er die Opendtu und den zweiten WR im Netzwerk findet.

Stürzt dann auch das Skript ab, weil er zweiten WR dann eben aus ist? Oder kannst du, dass ohne meinen Test aufgrund des Skriptbaufbaus beantworten?

Habe ich nie ausprobiert. Kann ich keine Aussage zu treffen. Wenn odob Werte liefert sollte es kein Problem geben, ausser das die Leistung nicht erreicht wird. Falls odob nichts liefert, dann keine Ahnung.

BKW1800 commented 1 month ago

Hallo,

naja es geht ja weniger darum dass das WLAn zu schwach ist. Ist mir nur aufgefallen, weil mein Wlan da zu schwach war. Könnte ja sein, dass versehentlich eine OpenDTU ausgesteckt wird und dann später wieder ein und man vergisst ( oder andere Personen aus Unwissenheit) aber dann händisch das Skript wieder einzuschalten. Das war mehr so der Gedanke dahinter.

Eine Batterie war leer und der WR schaltete dann ab. Das Skript flog aber nicht raus. Vermutlich weil die OpenDTU bzw. der WR dennoch gefunden wurden. Nur das ausstecken der DTU das verkraftet das Skript nicht. Das Skript stürzt dann ab. Das Abstürzen des Skriptes kann ja auch weiter passieren, wenn es für die Gesamtfunktion konzipiert wurde. Aber kann man nicht noch eine Meldung ins Aktivitätsprotokoll für den Fall erstellen lassen, das man schneller sieht, dass das Skript Offline ist? Wo und was müsste man dann im Skript als Ergänzung dazu einfügen?

BKW1800 commented 1 month ago

@genmad und @Manos1966 Ein Wechselrichter alleine funktioniert. Bei zwei Wechselrichter macht der im Code als zweiter erwähnte Probleme. Er macht nur die angegebene minRequiredPower_Watt.

let configs=[ { nominalPower_Watt: 1200, minRequiredPower_Watt: 24, controllerIp: '192.168.178.26', inverterSerialNumber: '1164a00b5fe8' } , { nominalPower_Watt: 1200, minRequiredPower_Watt: 24, controllerIp: '192.168.178.48', inverterSerialNumber: '116184606027'} ];

//power measuring device // choose between "local" e.g. this script is running on a gen2 device which can measure net power ( nothing else needs to be configured for the powerreading) // or "http" e.g. pulls the power readings by http requests ( configure http settings in httpConfig underneath) let netPowerConfig = "local" // choose one: "local" or "http" // configure http access ( path to the power reading) let httpConfig = { address: "http://192.168.178.28" // the address of the http powerreading required , jsonPath: "'PWR'" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // e.g.: inverters[0].name needs to be represented as: inverters.0.name };

in der Opendtu wird beim ersten WR der virtuelle Powermeter gefunden (auch beim Drücken auf den Testbutton)

http://192.168.178.28/script/1/pwr1

bei der zweiten openDtu mit dem zweiten WR fand er anfangs den virtuelle Powermeter nicht.

http://192.168.178.28/script/1/pwr2 hab dann auf pwr1 gewechselt. Test war erfolgreich und danach bin ich wieder auf PWR2 zurück. Test war erfolgreich. Ob das dann wirklich der richtige virtuelle Powermeter nun ist, weis ich aber nicht.

Der zweite WR hat die Limitvorgabe 24 Watt und der WR regelt 29 Watt aus.

Der 1.WR versucht dann den den Zielverbrauchswert alleine inkl. der Minimalleistung vom WR 2 zu erreichen

Hab das skript verwendet

https://github.com/genmad/shellyAPIGen2AndAboveScripts/blob/master/src/openDTUonBattery/virtualPowerMeters_oDoB.j

Was ist dem dem andren Skript das es noch gibt? virtualPowerMeters_oDoBParallel.js

Funktioniert das mit zwei WR und jeder gibt dann die 1/2 Leistung ab? Was ist hier das Problem, weil ob die Version 0.1.3 empfohlen wurde, um unnötige Probleme mit zu ersparen!? "Wenn eine odob ihren Anteil n nicht liefern kann, dann Regeln die anderen ihre Leistung in den nächsten Zyklen nach. " Geht das mit dem Skript Version 0.1.0 dann auch?

@Manos1966 am 14.Mai hast du geschrieben: "The (old) script is working 36hrs now " Was war das alte Skript für eins und das ging ja dann mit zwei WR oder gab es hier ein anderes Problem?

Ich würde ja gerne zwei WR parrallel mit Nulleinspeisung laufen lassen wollen. Bestenfalls dann jeder 1/2 Leistung oder wenn einer die Leistung nur zu 25% bringt dann soll der andere 75 % machen.

gitisgreat2023 commented 1 month ago

@Manos1966 @genmad ich bin zurück aus dem Urlaub... Es läuft! Vielen Dank an euch beide fürs ermöglichen!! 🎉

Ich konnte das Problem mit der Verbindung mit dem HMT lösen indem ich die Frequenz geändert habe auf 686.25MHz. Allerdings gibts eine komisch Baustelle... Nutze das Kaskadeskript, der HMT-2550 erreicht seinen limits nicht. Total inverter power ist etwa 100-200W unter dem limit der gesetzt wird, über den ganzen range von etwa 200W bis 2250W. Inbalance in den 6 Eingänge sind klein. Grid profile bei beiden ist identisch (wegen voltage ramp). Batterie limitiert auch nicht, das delta tritt auch über den ganzen power range auf (200-2250W), also daran kann es auch nicht liegen. Hat der WR ein issue? Vom HM1500 kenne ich das gar nicht, der liefert sofort den power die er als limit bekommt.

zB hier war der delta stabil rund 103W

(Mist eine line fehlt wo der Limit auf 26% steht, 585W, vergessen zu kopieren) 20:05:16.800 > [DPL::calcPowerLimit] power meter value: 112 W, power meter valid: yes, inverter output: 475 W, solar power (AC): 0 W 20:05:16.850 > [DPL::calcPowerLimit] match household consumption with limit of 587 W 20:05:16.900 > [DPL::setNewPowerLimit] input limit: 587 W, min limit: 45 W, max limit: 2250 W, hysteresis: 0 W 20:05:16.951 > [DPL::setNewPowerLimit] inverter max: 2250 W, inverter is producing, requesting: 587 W, reported: 585 W, diff: 2 W 20:05:17.002 > [DPL::updateInverter] sending limit of 26.1 % (587 W respectively), max output is 2250 W

Über lange Zeit bleibt dieses Delta, es wird nicht weggeregelt. Habe noch nicht getestet ob es uber Stunden bleibt, aber bleibt uber ein Viertelstunde so.

Habe es schon gegoogled aber bin noch nicht schlau geworden. Woran kann das liegen? Liegt es an dem WR? War auch schon komisch dass die standard Frequenz nicht ging, hat eine Weile gedauert bevor ich das zum laufen bekommen habe und finde ich auch verdächtig...

BKW1800 commented 1 month ago

@Manos1966 @genmad ich bin zurück aus dem Urlaub... Es läuft! Vielen Dank an euch beide fürs ermöglichen!! 🎉

Ich konnte das Problem mit der Verbindung mit dem HMT lösen indem ich die Frequenz geändert habe auf 686.25MHz. Allerdings gibts eine komisch Baustelle... der HMT-2550 erreicht seinen limits nicht. Total inverter power ist etwa 100-200W unter dem limit der gesetzt wird, über den ganzen range von etwa 200W bis 2250W. Inbalance in den 6 Eingänge sind klein. Grid profile bei beiden ist identisch (wegen voltage ramp). Batterie limitiert auch nicht, das delta tritt auch über den ganzen power range auf (200-2250W), also daran kann es auch nicht liegen. Hat der WR ein issue? Vom HM1500 kenne ich das gar nicht, der liefert sofort den power die er als limit bekommt.

zB hier war der delta stabil rund 103W 20:05:16.800 > [DPL::calcPowerLimit] power meter value: 112 W, power meter valid: yes, inverter output: 475 W, solar power (AC): 0 W 20:05:16.850 > [DPL::calcPowerLimit] match household consumption with limit of 587 W 20:05:16.900 > [DPL::setNewPowerLimit] input limit: 587 W, min limit: 45 W, max limit: 2250 W, hysteresis: 0 W 20:05:16.951 > [DPL::setNewPowerLimit] inverter max: 2250 W, inverter is producing, requesting: 587 W, reported: 585 W, diff: 2 W 20:05:17.002 > [DPL::updateInverter] sending limit of 26.1 % (587 W respectively), max output is 2250 W

Über lange Zeit bleibt dieses Delta, es wird nicht weggeregelt. Habe noch nicht getestet ob es uber Stunden bleibt, aber bleibt uber ein Viertelstunde so.

Habe es schon gegoogled aber bin noch nicht schlau geworden. Woran kann das liegen? Liegt es an dem WR? War auch schon komisch dass die standard Frequenz nicht ging, hat eine Weile gedauert bevor ich das zum laufen bekommen habe und finde ich auch verdächtig...

Welche Skript verwendest du? laufen bei dir zwei WR parrallel und teilen sich die Einspeiseleistung oder läuft wie bei mir einer nur Auf der min-Wechselrichterleistung?

gitisgreat2023 commented 1 month ago

Kaskade, erst wenn der HM1500 max. ausgelastet wird, wird der HMT dazu geschaltet.

gitisgreat2023 commented 1 month ago

@Manos1966 Ich habe Alufolie um Antenna vom Board gewickelt, jetzt-42dBM statt -1dBm. (Folienantenne ist unterwegs, jetzt ist es ein Stabantenne). Das testen ist hier sehr mühsam da ja ständig keine Verbindung da ist... müsste es ohne Antenne auch funktionieren?

Leider immer noch nicht stabil, nach 5-50 Minuten ist die Verbindung wieder weg. Ich hatte die Sendeleistung auf -3dBm, da scheint es am besten (50 Minuten). Mir gehen die Ideeen aus… Die Frequenz ist auf 865MHz, könnte da eine leichte Tuning was verbessern? Teste gerade mit -6 dBm Sendeleistung, geht ja über die Antenne ja auch raus, vielleicht "clippt" der den HMT?

Also als erstes teste deine Empfangsleistung und stelle alles richtig. Danach kannst du anfangen den Skript zu testen. Ich haben den Eindruck, deine Probleme haben nichts mit dem Skript zu tun. Signalstärke a -1 bis 0 dBm, also sehr sehr nah oder??? Abstand erhoehen. Eine Signalstaerke von -40dBm ist gut. In der Regel wird eine Signalstärke von -50 dBm oder höher als ausgezeichnet angesehen, während Werte unter -100 dBm auf eine schlechte Verbindung hinweisen. Mit -1 ist es zu "Laut" und der Wechselrichter empfaengt die Signale nicht richtig.

Ich konnte das Problem mit der Verbindung mit dem HMT lösen indem ich die Frequenz geändert habe auf 686.25MHz.

Manos1966 commented 1 month ago

Danke, dass du es gepostet hast. Ich werde es mir merken!!!

gitisgreat2023 commented 1 month ago

@genmad Ich dachte ich mache mal einen schnellen software fix bis ich das Problem gelöst habe...

function calculateVirtualPowerReadings( index){ var powerOffAllOtherInverters = generatedPower - previousPower[index]; // print([generatedPower, previousPower[index]]) var virtualPowerMeter = netPower - targetGridConsumption_Watt + powerOffAllOtherInverters - startPower[index]; // limiting the power so that at least each controller recieves its minimum power configured // var limitedPowerMeter = Math.max( configs[index].minRequiredPower_Watt - previousPower[index], virtualPowerMeter); var limitedPowerMeter = Math.max( configs[index].minRequiredPower_Watt - previousPower[index], virtualPowerMeter);

*_if (index == 1){ if (limitedPowerMeter > 50){ limitedPowerMeter = limitedPowerMeter+limitedPowerMeter/1400225; } }_**

// limitedPowerMeter values tell odob //. when negative: reduce the power of the inverter by that ammount, // when positive: increase the power of the inverter by that ammount

// print("Index: " + index); // print("Netpower: " + netPower); // print("VirtualPowerMeter: " + virtualPowerMeter) // print("limitedPowerMeter: " + limitedPowerMeter) return limitedPowerMeter; }

Die Idee ist den Output für den HMT2250 leicht zu skalieren (hier mit 225W bei 1400W). Leider scheint das gar nicht zu gehen, die Änderungen werden glaube ich jeden loop größer, der HM-1500 geht dann auf 0. Stimmt, oder? Wie kann ich solch eine einfache Skalierung implementieren?

BKW1800 commented 1 month ago

@Manos1966 am 14.Mai hast du geschrieben: "The (old) script is working 36hrs now " Was war das alte Skript für eins und das ging ja dann mit mehreren WR oder gab es hier ein anderes Problem?

Ich würde ja gerne zwei WR parrallel mit Nulleinspeisung laufen lassen wollen. Bestenfalls dann jeder 1/2 Leistung oder wenn einer die Leistung nur zu 25% bringt dann soll der andere 75 % machen.

Welche Skript verwendest du? Liefen bei dir mehrere WR parrallel und teilen sich die Einspeiseleistung? siehe deine Screenshots hierzu vom 22.05.24

Manos1966 commented 1 month ago

Ich habe es damals mit vier Wechselrichter getestet, zwei davon an der Batterie angeschlossen.

Problem war die rein Solar Wechselrichter. Nachts haben sie sich ausgeschaltet (Erwartungsgemaess), daraufhin kam eine Fehlermeldung.

// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.

/*

  • Virtual powermeter for open DTU on Battery (oDoB)
  • This scripts runs on any shelly with api 2 or greater.
  • This script reads in the current power comsuption and
  • divides it up between n virtual powermeter. The powermeter
  • are then used to feed an controller of oDoB.
  • The multiple controller will be cascaded so that at each
  • point in time only one controller is working and the others
  • are independently either at their configured minimum power delivery
  • or at their maximum power delivery.
  • Minimum required Firmware version of oDoB controller is 24.02.12
  • With this all said, it is noteworthy, that all controllers
  • are connected to the same DC power supply system
  • ( solarpanels and batteries).
  • **/

// version 0.1.3

// set unique script ID here !!! be aware no other script on your shelly can have the same id !!! ( It does not matter whether they are running or not) let scriptId=3;

// configuration for each controller, add as many as you have controller ( there is an upper limit off 5 controllers defined by the // shelly, because you can only be registered up to 5 HTTP endpoints (at the time of writing this code) // the order in which the controller are added reflects the cascade order, first controller added is the first // in the cascade, the later are following then // nominalPower_Watt : the maximum power the controller can provide // minRequiredPower_Watt: the minimum Power required to run the controller/inverter, // controllerIp: the ip of the odob controller ( inverter needs to be the first/only one registered (e.g.: position 0) at the given controller) // inverterSerialNumber: the serial number of the inverter to control as shown in oDoB ( settings -> inverter -> serial number) let configs=[ { nominalPower_Watt: 1500, minRequiredPower_Watt: 52, controllerIp: '192.168.178.65', inverterSerialNumber: xxxxxxxxx} , { nominalPower_Watt: 800, minRequiredPower_Watt: 200, controllerIp: '192.168.178.93', inverterSerialNumber: xxxxxxxxxxx} //, { nominalPower_Watt: 800, minRequiredPower_Watt: 43, controllerIp: '192.168.178.103', inverterSerialNumber: xxxxxxxxxxx} //, { nominalPower_Watt: 800, minRequiredPower_Watt: 44, controllerIp: '192.168.178.105', inverterSerialNumber: xxxxxxxxx} ];

//power measuring device // choose between "local" e.g. this script is running on a gen2 device which can measure net power ( nothing else needs to be configured for the powerreading) // or "http" e.g. pulls the power readings by http requests ( configure http settings in httpConfig underneath) let netPowerConfig = "http" // choose one off "local" or "http"

// configure http access ( path to the power reading) let httpConfig = { address: "http://192.168.178.75/cm?cmnd=status%208" // the address of the http powerreading required , jsonPath: "StatusSNS/Wohnung/aktuelle_Wirkleistung" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // address: "http://192.168.178.67/api/livedata/status?inv=116182803975" // the address of the http powerreading required // , jsonPath: "inverters/0/AC/0/Power/v" // jsonPath for parsing the message for the powerreading, seperate every field by a '/' // e.g.: inverters[0].name needs to be represented as: inverters.0.name };

// Configuration of your oDoB contorller i ( on position i) in the configs variable (see above) (i=1..n) // // define in Powermeter the mode https + Json and use http:///script//pwr // e.g. assume the ip of the shelly on which this script runs on to be: 1.2.3.4, // the script id of this script to be 7 and you want to configure the 2nd odob controller // then use: http://1.2.3.4/script/7/pwr2 // the Json path is always 'PWR' //http://192.168.178.92/script/3/pwr1 // JSON Pfad: PWR // http://192.168.178.92/script/3/pwr2 // JSON Pfad: PWR // http://192.168.178.92/script/3/pwr3 // JSON Pfad: PWR // http://192.168.178.92/script/3/pwr4 // JSON Pfad: PWR // otherwise use the Stromyaehler // http://192.168.178.75/cm?cmnd=status%208 // JSON Pfad: StatusSNS/Wohnung/aktuelle_Wirkleistung

// -------------------------------------------------- configure above this line, don't touch anything underneath this line !!! -------------------------------------------

// factor to influence the waiting till the next call for NetPower and InverterPower let DELAY = 15;

// THreshold for successively not repsnding devices ( threshold for each individually) let TIMEOUT_THRESHOLD = 30;

// the last power reading off each controller let previousPower = [];

// the defined start power of each controller let startPower = [];

// power brought to you by the grid let netPower = 0;

//power generated by all the controllers/inverters let generatedPower = 0;

let ONE_SECOND = 1000;

let TimeOutCounter = [];

// initialize the previous step power for each controller, // register to mqtt topics // create http endpoints // and configure power readings function initialize(){
TimeOutCounter[0] =0; var cumulatedPower =0; // configure virtualPowerMeter readings for (var i = 0; i < configs.length; i++) { TimeOutCounter[i+1] =0; startPower[i] = cumulatedPower; cumulatedPower = cumulatedPower + configs[i].nominalPower_Watt; previousPower[i] =0; // call twice during a normal hoymiles cycle (5 seconds) dict = {url: "http://" + configs[i].controllerIp + "/api/livedata/status?inv=" + configs[i].inverterSerialNumber, index: i} controllerCall( dict); HTTPServer.registerEndpoint( "pwr" + (i+1) , VirtualPowerMeterReadings, i) } // configure netPower readings switch (netPowerConfig.toLowerCase()){ case 'local': Shelly.addStatusHandler( function(event, userdata){ // Runs when a new Power reading is comming in if (typeof event.delta.total_act_power !== "undefined") { netPower = event.delta.total_act_power; } } , null); break; case 'http': // calls repeatedly, the httpTimer function powerMeterCall(); httpConfig.jsonPath = httpConfig.jsonPath.split("/"); break; } }

// cyclicly update the net power when http is configured function powerMeterCall( userdata){ Shelly.call("HTTP.GET", {url: httpConfig.address}, processHttpResponseForNetPower); }

// cyclicly update the inverter power when http is configured function controllerCall( dict){ Shelly.call("HTTP.GET", {url: dict.url}, processHttpResponseForInverterPower, dict); }

// process the http Response of the power Meter configured with http polling function processHttpResponseForNetPower( result, error_code, error) { if (TimeOutCounter[0] > TIMEOUT_THRESHOLD){ throw {name : "NetPowerNotResponding", message : "Your net power provider did not answer for a while. This script will be terminated now."}; } if (error_code != 0) { netPower = 0; // something went wrong ... what shall we do?? ( with a drunken sailor?) print("function processHttpResponseForNetPower HttpRequest to powerMeter Failed with error code: "); print(error_code); print("\nand Error: ") print(error); TimeOutCounter[0]++; } else { // no try catch here! let them crash on start immediately and if ever somethig changes let them also crash!!! So the user will faster know something is wrong body = JSON.parse(result.body); // dynamically parse the unknown body from a split up string for(i=0; i < httpConfig.jsonPath.length; i++){ body = body[httpConfig.jsonPath[i]]; } netPower = body; TimeOutCounter[0] = 0; // print("NetPower: " + netPower); } Timer.set( DELAY * ONE_SECOND, false, powerMeterCall); }

// process the http Response of the power Meter configured with http polling function processHttpResponseForInverterPower( result, error_code, error, dict) { if (TimeOutCounter[dict.index + 1 ] > TIMEOUT_THRESHOLD){ throw {name : "ControllerNotResponding", message : "Your Controller: " + dict.index + " did not answer for a while. This script will be terminated now."}; }

if (error_code != 0) {
    // something went wrong ... what shall we do?? ( with a drunken sailor?)
    print("function processHttpResponseForInverterPower: HttpRequest to controller '" + configs[dict.index].controllerIp + "'' Failed with error code: ");
    print(error_code);
    print("\nand Error: ")
    print(error);
    TimeOutCounter[dict.index+1]++;
} else {
    // no try catch here! let them crash on start immediately and if ever somethig changes let them also crash!!! So the user will faster know something is wrong
    body = JSON.parse(result.body);
    newPower = body.inverters[0].AC[0].Power.v;
    TimeOutCounter[dict.index+1] =0;
    UpdateControllerPower( newPower, dict.index);
}
Timer.set( DELAY * ONE_SECOND, false, controllerCall, dict);

}

// send the virtual power meter reading to the requester function VirtualPowerMeterReadings( request, response, index){ // print("function VirtualPowerMeterReadings " + index); var virtualPowerMeter = calculateVirtualPowerReadings( index); // print("VPM: " + virtualPowerMeter); response.body = JSON.stringify( { PWR : virtualPowerMeter } ); response.code = 200; response.send(); }

// recieve new controller Power values function UpdateControllerPower( newPower, index){ generatedPower = generatedPower - previousPower[index] + newPower; previousPower[index] = newPower; // print("GeneratedPower: " + generatedPower + " NewPower: " + newPower); }

// calculate the power which is seen by the virtual power meter of index // virtualPowerMeter = measured net-power - required StartPower + over all produced power off all other contorllers function calculateVirtualPowerReadings( index){ var powerOffAllOtherInverters = generatedPower - previousPower[index]; // print([generatedPower, previousPower[index]]) var virtualPowerMeter = netPower + powerOffAllOtherInverters - startPower[index]; // limiting the power so that at least each controller recieves its minimum power configured var limitedPowerMeter = Math.max( configs[index].minRequiredPower_Watt - previousPower[index], virtualPowerMeter); // print("Index: " + index); // print("Netpower: " + netpower); // print("VirtualPowerMeter: " + virtualPowerMeter) // print("limitedPowerMeter: " + limitedPowerMeter) return limitedPowerMeter; }

initialize();

gitisgreat2023 commented 1 month ago

@genmad Ich dachte ich mache mal einen schnellen software fix bis ich das Problem gelöst habe...

function calculateVirtualPowerReadings( index){ var powerOffAllOtherInverters = generatedPower - previousPower[index]; // print([generatedPower, previousPower[index]]) var virtualPowerMeter = netPower - targetGridConsumption_Watt + powerOffAllOtherInverters - startPower[index]; // limiting the power so that at least each controller recieves its minimum power configured // var limitedPowerMeter = Math.max( configs[index].minRequiredPower_Watt - previousPower[index], virtualPowerMeter); var limitedPowerMeter = Math.max( configs[index].minRequiredPower_Watt - previousPower[index], virtualPowerMeter); **_if (index == 1){ if (limitedPowerMeter > 50){ limitedPowerMeter = limitedPowerMeter+limitedPowerMeter/1400*225; } }_** // limitedPowerMeter values tell odob //. when negative: reduce the power of the inverter by that ammount, // when positive: increase the power of the inverter by that ammount // print("Index: " + index); // print("Netpower: " + netPower); // print("VirtualPowerMeter: " + virtualPowerMeter) // print("limitedPowerMeter: " + limitedPowerMeter) return limitedPowerMeter; }

Die Idee ist den Output für den HMT2250 leicht zu skalieren (hier mit 225W bei 1400W). Leider scheint das gar nicht zu gehen, die Änderungen werden glaube ich jeden loop größer, der HM-1500 geht dann auf 0. Stimmt, oder? Wie kann ich solch eine einfache Skalierung implementieren?

Okay.... das hier scheint zu funktionieren...

// calculate the power which is seen by the virtual power meter of index
// virtualPowerMeter = measured net-power - required StartPower + over all produced power off all other contorllers
function calculateVirtualPowerReadings( index){
    var powerOffAllOtherInverters = generatedPower - previousPower[index];
//  print([generatedPower, previousPower[index]])
    var virtualPowerMeter = netPower - targetGridConsumption_Watt + powerOffAllOtherInverters - startPower[index];
//  limiting the power so that at least each controller recieves its minimum power configured
//  var limitedPowerMeter = Math.max( configs[index].minRequiredPower_Watt - previousPower[index], virtualPowerMeter);
    var limitedPowerMeter = Math.max( configs[index].minRequiredPower_Watt - previousPower[index], virtualPowerMeter);
    if (index == 1 && generatedPower > 1650){
      limitedPowerMeter = limitedPowerMeter + Math.max(0, generatedPower/3000*220);
    }
//  limitedPowerMeter values tell odob 
//. when negative: reduce the power of the inverter by that ammount, 
//  when positive: increase the power of the inverter by that ammount

    //print("Index: " + index);
    //print("Netpower: " + netPower);
    //print("VirtualPowerMeter: " + virtualPowerMeter)
    //print("limitedPowerMeter: " + limitedPowerMeter)

    return limitedPowerMeter;
}
gitisgreat2023 commented 1 month ago

@genmad Im Moment nutze ich den Kaskadescript (erst wenn WR1 ausgelastet ist, geht WR2 an). Ich würde gerne mal die parallele Version ausprobieren. Es war mal eine Version virtualPowerMeters_oDoBParallel.js , die finde ich aber nicht mehr, hast du die gelöscht?

Es wäre super wenn man selber die Gewichte setzen kann, zB 30% WR1, 70% WR2 und mit einem threshold für ein WR, zB unter 250W keine Verteilung sondern nur auf WR1. Damit wäre dann abgedeckt dass bei kleine Lasten der kleinere Wechselrichter es präzise regeln kann, und bei höhere Lasten die Lebensdauer/Benutzung über zwei WR verteilt wird.

BKW1800 commented 1 month ago

@genmad Im Moment nutze ich den Kaskadescript (erst wenn WR1 ausgelastet ist, geht WR2 an). Ich würde gerne mal die parallele Version ausprobieren. Es war mal eine Version virtualPowerMeters_oDoBParallel.js , die finde ich aber nicht mehr, hast du die gelöscht?

Es wäre super wenn man selber die Gewichte setzen kann, zB 30% WR1, 70% WR2 und mit einem threshold für ein WR, zB unter 250W keine Verteilung sondern nur auf WR1. Damit wäre dann abgedeckt dass bei kleine Lasten der kleinere Wechselrichter es präzise regeln kann, und bei höhere Lasten die Lebensdauer/Benutzung über zwei WR verteilt wird.

kuck mal hier

https://github.com/genmad/shellyAPIGen2AndAboveScripts/pull/5/commits/5c271b86fd2d0e28afda6ea6e2ebcdf9c8d413f5

werde das Skript auch testen mit der Regelung ob es die Leistungen sauber prozentual verteilt. Hab nur leider erst am Wochenende Zugriff auf zwei WR. Aber vllt kannst du schon mal teste ob ein Fehler auftritt...

gitisgreat2023 commented 1 month ago

@genmad Im Moment nutze ich den Kaskadescript (erst wenn WR1 ausgelastet ist, geht WR2 an). Ich würde gerne mal die parallele Version ausprobieren. Es war mal eine Version virtualPowerMeters_oDoBParallel.js , die finde ich aber nicht mehr, hast du die gelöscht? Es wäre super wenn man selber die Gewichte setzen kann, zB 30% WR1, 70% WR2 und mit einem threshold für ein WR, zB unter 250W keine Verteilung sondern nur auf WR1. Damit wäre dann abgedeckt dass bei kleine Lasten der kleinere Wechselrichter es präzise regeln kann, und bei höhere Lasten die Lebensdauer/Benutzung über zwei WR verteilt wird.

kuck mal hier

5c271b8

werde das Skript auch testen mit der Regelung ob es die Leistungen sauber prozentual verteilt. Hab nur leider erst am Wochenende Zugriff auf zwei WR. Aber vllt kannst du schon mal teste ob ein Fehler auftritt...

Perfekt, danke!! Ich muss erst noch mal die Verbindungen vom 2. WR durchgehen, die verursachen vermutlich die inbalance. Mittels den offset habe ich im Moment einen fix für die Kaskadeversion. Sobald der 2. WR sauber läuft, werde ich die parallele Version ausprobieren!

genmad commented 1 month ago

@genmad Im Moment nutze ich den Kaskadescript (erst wenn WR1 ausgelastet ist, geht WR2 an). Ich würde gerne mal die parallele Version ausprobieren. Es war mal eine Version virtualPowerMeters_oDoBParallel.js , die finde ich aber nicht mehr, hast du die gelöscht? Es wäre super wenn man selber die Gewichte setzen kann, zB 30% WR1, 70% WR2 und mit einem threshold für ein WR, zB unter 250W keine Verteilung sondern nur auf WR1. Damit wäre dann abgedeckt dass bei kleine Lasten der kleinere Wechselrichter es präzise regeln kann, und bei höhere Lasten die Lebensdauer/Benutzung über zwei WR verteilt wird.

kuck mal hier

5c271b8

werde das Skript auch testen mit der Regelung ob es die Leistungen sauber prozentual verteilt. Hab nur leider erst am Wochenende Zugriff auf zwei WR. Aber vllt kannst du schon mal teste ob ein Fehler auftritt...

Das Skript mit der parallelen Regelung von Wechselrichtern funktioniert nicht richtig. Das hatte jedenfalls @Manos1966 zurückgemeldet. Ich habe es noch nicht repariert. Ich habe leider auch keinen zweiten WR mit dem ich das testen könnte. Von daher habe ich das erst einmal liegen gelassen.

Wenn ich wieder mal Zeit habe um das zu machen ( im Herbst oder WInter), dann wäre meine Idee aktuell es so zu implementieren, dass man Phasenrichtig auf eine Nulleinspeisung kommt. D.h. jeweils 1 WR, an Phase 1 und Phase 2. Den Bedarf der Phasen soll der WR der Phase so lange decken wie er es kann. Erst falls er es nicht mehr schafft, dann hilft der andere WR aus um eine summierende Nulleinspeisung zu machen. Die Leistung die auf der 3. Phase benötigt wird wird dann auf die anderen WR's verteilt. Wie genau, ob jeder die Hälfte oder im Verhältnis der Leistungsfähigkeit der WR's ist noch zu diskutieren.

Was sich auf jeden als schlechte Idee herausgestellt hat, ist das Timerbasierte Überwachen der OdoB's im parallel Skript. Man verbraucht zu schnell die Timer von denen man nur 5 hat und 5 sind auch nicht ausreichend.