NEWS
Erste Schritte beim Scripten - Einsteigerfrage
-
@doppellhelix sagte in Erste Schritte beim Scripten - Einsteigerfrage:
Ahhh, das erklärt es natürlich.
Das ist aber dann ziemlich unelegant. Dann dürfte man ja nie am Script arbeiten, wenn man nicht bis zum nächsten Sonnenaufgang warten will.Ich bin davon ausgegangen, dass so ein Script wie ein SPS Programm arbeitet und das Script in einer Schleife stetig durchlaufen wird.
Wie bekommt man das denn besser hin?
Den Edit wirst du nicht gelesen habe 2 Möglichkeiten
- initialisiere die Variable mit getState()
- streiche die Variable und verwende überall getState() und setState()
-
@doppellhelix sagte in Erste Schritte beim Scripten - Einsteigerfrage:
Versteh nicht, wo ich die reducedCharging auf falsch setze?
Zum Ablauf:
Bei Sonnenaufgang wird gecheckt, ob 20000 erreicht werden.
Dem ist heute so, als wird reducedCharging auf wahr gestellt.
Zeile 11
Dann passiert bis 11 Uhr nix.
Hier wird in Zeile 27 gecheckt, ob reucedCharging ansteht.
Das sollte es ja eigentlich noch. ISt ja nix anderes dazwischen passiert oder überseh ich was.
Hie nochmal der komplette code, damit wir wieder alle gleich sindDa ist dein Denkfehler:
- du setzt reducedCharging hier auf
falsch
:
var reducedCharging = false;
- wenn das Skript zum Sonnenaufgang läuft wird zum Sonnenaufgang der Wert von reducedCharging verändert. Das wäre heute passiert wenn Du das Skript nicht danach neu gestartet hättest. Ergo ist reducedCharging immer noch
falsch
Vielleicht generell mal als Denkhilfe
Variablen sind:
- nur in dem Kontext bekannt in dem sie definiert sind. Der Kontext kann dabei das Skript sein, kann aber auch nur ein Teil des Skriptes sein:
const scriptglobal = true; if (23<11) { const scriptlocal = true; console.info('A global: ' + scriptglobal + ' local: ' + scriptlocal) } console.info('B global: ' + scriptglobal + ' local: ' + scriptlocal)
liefert:
A global: true local: true
B global: true local: undefined- variablen belegen wenig Speicherplatz und sind nur aktiv wenn das Skript läuft
- auf variablen kann schnell und einfach zugegriffen werden (lesen und schreiben)
Datenpunkte sind:
- persistent - ihre Werte bleiben selbst über den Neustart des ioBroker erhalten
- global - sie sind nicht an einzelne Skripte gebunden
- zugriff erfordert mehr Code, da die Werte aus der Datenpunkt-Datenbank geholt / dahin geschrieben werden müssen. Das macht Datenpunkte weniger 'einfach' zu nutzen
- Zugriff kostet ggf. Zeit, so das der Code:
let value = Math.random(); setState('0_userdata.myState', value); if (value == getState('0_userdata.myState').val console.info('gleich') else console.info('ungleich')
nicht bei jedem Durchlauf die gleiche Meldung ins Log schreibt.
In deinem Code solltest Du die Variable also mit dem Wert des Datenpunktes vorbelegen, damit sie Synchron zum Datenpunkt ist.
var reducedCharging = getState('0_userdata.0.reducedCharging'/*reducedCharging*/).val;
A.
- du setzt reducedCharging hier auf
-
Puuuhhh, und ich dachte nimm dir mal schnell dieses kleine Script und pass es an
Das war wohl sehr naiv.Aber mitlerweile habe ich schon einiges gelernt.
Ich bin mir noch nicht sicher, ob die Variable in den Datenpunkt geschrieben wird..
Habe jetzt alles auf Datenpunkte umgeschrieben.
Hoffe ich zumíndest.//var reducedCharging = false; var reducedCharging = getState('0_userdata.0.reducedCharging'/*reducedCharging*/).val; console.info('reduced charging is '+reducedCharging); schedule({astro: "sunrise"}, function () { var msgText = "Akkustand bei Sonnenaufgang: " + getState('modbus.0.inputRegisters.13022_Battery_level_'/*Batteriekapazität*/).val + "%"; var forecast = getState('pvforecast.0.summary.energy.today'/*Geschätzte Energie (heute)*/).val; if (forecast > 20000) { setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 100); //setState('evcc.0.control.prioritySoc'/*prioritySoc*/, 0); setState('telegram.0.communicate.response', forecast + "Wh Ertrag erwartet. Reduziere Ladeleistung auf 100W bis 11 Uhr"); //reducedCharging = true; setState('0_userdata.0.reducedCharging'/*reducedCharging*/, true); } else { setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 10600); //setState('evcc.0.control.prioritySoc'/*prioritySoc*/, 30); setState('telegram.0.communicate.response', forecast + "Wh Ertrag erwartet. Lade Akku sofort"); setState('0_userdata.0.reducedCharging'/*reducedCharging*/, false); } log(msgText); setState('telegram.0.communicate.response', msgText); }); schedule({hour: 12, minute: 0}, function () { if (reducedCharging) { setState('telegram.0.communicate.response', "Starte laden des Akkus mit 2 kW"); log("Set max_charge_power to 2kW"); setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 2000); //setState('evcc.0.control.prioritySoc'/*prioritySoc*/, 30); } });
EDIT:
hatte das Script ja auf 12 Uhr abgeändert und das hat funktioniert -
@doppellhelix sagte in Erste Schritte beim Scripten - Einsteigerfrage:
Puuuhhh, und ich dachte nimm dir mal schnell dieses kleine Script und pass es an
Das war wohl sehr naiv.Ja, war es. gerade wenn du fremde Skripte übernimmst solltest Du dir vorher die Doku anschauen, damit du verstehst was die einzelnen Befehle wirklich tun.
Habe jetzt alles auf Datenpunkte umgeschrieben.
Hoffe ich zumíndest.Das sieht erst einmal soweit gut aus.
Ob es das macht was du willst lässt sich so nicht sagen - dazu müssten wir wissen was das Ziel ist.
A.
-
@doppellhelix sagte: Script wie ein SPS Programm arbeitet und das Script in einer Schleife stetig durchlaufen wird.
Nein, Javascript wird nicht in einer Schleife durchlaufen, sondern arbeitet Ereignis gesteuert. Ereignisse sind u.a. DP-Trigger, Zeitpläne, Timer.
Das Skript etwas übersichtlicher und mit weniger setState():
const idMaxPower = 'modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/; const idForecast = 'pvforecast.0.summary.energy.today'/*Geschätzte Energie (heute)*/; const idBatt = 'modbus.0.inputRegisters.13022_Battery_level_'/*Batteriekapazität*/; const idTelegram = 'telegram.0.communicate.response'; const idReduced = '0_userdata.0.reducedCharging'/*reducedCharging*/; var reducedCharging = getState(idReduced).val; log('reduced charging is ' + reducedCharging); schedule({astro: "sunrise"}, function () { let msgText = "Akkustand bei Sonnenaufgang: " + getState(idBatt).val + " %\n"; const forecast = getState(idForecast).val; let maxPower = 10600; reducedCharging = forecast > 20000; if (reducedCharging) { maxPower = 100; msgText += forecast + ' Wh Ertrag erwartet. Reduziere Ladeleistung auf 100W bis 11 Uhr'; } else { msgText += forecast + ' Wh Ertrag erwartet. Lade Akku sofort'; } log(msgText); setState(idMaxPower, maxPower); setState(idTelegram, msgText); setState(idReduced, reducedCharging, true); }); schedule('0 11 * * *', function () { if (reducedCharging) { setState(idTelegram, "Starte laden des Akkus mit 2 kW"); log("Set max_charge_power to 2kW"); setState(idMaxPower, 2000); } });
-
Im Grunde genommen soll das Script dafür sorgen, dass die PV Anlage nicht in die 70% Abregelung kommt.
Wenn gutes Wetter ist (pvForecast > 20000 W), dann wird das wahrscheinlich passieren.
Im normalfall wird mit den ersten Sonnenstrahlen der Speicher geladen. Dieser ist um 10 Uhr voll und ab dann wird voll eingespeist.
Indem das Speicherladen verhindert und erst ab 11 Uhr freigegeben wird, umgeht man die 70% Abregelung. Alles über 70% geht dann in den Speicher und nicht ins Netz.@paul53
Klasse. Danke dir.
Das nehme ich gerne mit, sobald ich weiß was da genau passiert.@alle anderen.
Danke für eure Einsatz. -
Nicht das es immer stimmt, aber chatgpt kann man für das erklären von Code eigentlich ganz gut gebrauchen und dann mit den gewonnen Fachbegriffen suchen, ob es nicht mal wieder nur viel Worte gemacht hat.
-
Hallo,
das Script hat leider noch einen kleinen Fehler drin.
Nochmal eine kurze Funktionsbeschreibung:
Bei Sonnenaufgang wird gecheckt, wieviel Sonnenertrag die PV Anlage erwartet.
Liegt der Ertrag über 20000, wird die Ladeleistung für den Battereispeicher zu laden auf 10 gedrosselt, und die Variable reducedCharging auf true gesetzt.
Um 11 Uhr wird die Ladeleistung auf 2000 angehoben.
Wenn keine 20000 zu erwarten sind, bleibts beim Standartwert 10600.Das Problem ist, dass scheinbar immer um 11 Uhr auf 2000 gestellt wird. Und das, obwohl reducedCharging false ist.
Als Beispiel gestern und heute.
Ich bekam eine Telegram Nachricht, dass die 20000 nicht zu erwarten sind.
Um 11Uhr bekam ich dann die Nachricht, dass mit 2000 geladen wird.
Die Ladeleistung steht auch tatsächlich auf 2000 aber die Variable reducedCharging steht auf false.Hat jemand eine Idee, was da falsch läuft?
var reducedCharging = getState('0_userdata.0.reducedCharging'/*reducedCharging*/).val; console.info('reduced charging is '+reducedCharging); schedule({astro: "sunrise"}, function () { var msgText = "Akkustand bei Sonnenaufgang: " + getState('modbus.0.inputRegisters.13022_Battery_level_'/*Batteriekapazität*/).val + "%"; var forecast = getState('pvforecast.0.summary.energy.today'/*Geschätzte Energie (heute)*/).val; if (forecast > 20000) { setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 10); //setState('mqtt.0.evcc.site.prioritySoc'/*evcc/site/prioritySoc*/, 0); setState('telegram.0.communicate.response', forecast + "Wh Ertrag erwartet. Reduziere Ladeleistung auf 100W bis 11 Uhr"); setState('0_userdata.0.reducedCharging'/*reducedCharging*/, true); } else { setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 10600); //setState('mqtt.0.evcc.site.prioritySoc'/*evcc/site/prioritySoc*/, 30); setState('telegram.0.communicate.response', forecast + "Wh Ertrag erwartet. Lade Akku sofort mit voller Leistung"); setState('0_userdata.0.reducedCharging'/*reducedCharging*/, false); } log(msgText); setState('telegram.0.communicate.response', msgText); }); schedule({hour: 11, minute: 0}, function () { if (reducedCharging) { setState('telegram.0.communicate.response', "Starte laden des Akkus mit 2 kW"); log("Set max_charge_power to 2kW"); setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 2000); //setState('mqtt.0.evcc.site.prioritySoc'/*evcc/site/prioritySoc*/, 30); } });
-
@doppellhelix sagte: Hat jemand eine Idee, was da falsch läuft?
Du schreibst zwar zum Sonnenaufgang den Datenpunkt "0_userdata.0.reducedCharging", setzt aber nicht die Variable
reducedCharging
, die um 11:00 Uhr geprüft wird. Siehe Zeile 14 in meinem Vorschlag. -
@paul53 said in Erste Schritte beim Scripten - Einsteigerfrage:
@doppellhelix sagte: Hat jemand eine Idee, was da falsch läuft?
Du schreibst zwar zum Sonnenaufgang den Datenpunkt "0_userdata.0.reducedCharging", setzt aber nicht die Variable
reducedCharging
, die um 11:00 Uhr geprüft wird.Wird das nicht in Zeile 11 gemacht?
setState('0_userdata.0.reducedCharging'/*reducedCharging*/, true);
-
@doppellhelix sagte: Wird das nicht in Zeile 11 gemacht?
Nein, es wird nur der DP geschrieben, aber nicht die Variable gesetzt.
-
Wie schon erwähnt - Variable setzen Variable schreiben - nicht Variable setzen Wert schreiben.
-
@paul53 said in Erste Schritte beim Scripten - Einsteigerfrage:
@doppellhelix sagte: Wird das nicht in Zeile 11 gemacht?
Nein, es wird nur der DP geschrieben, aber nicht die Variable gesetzt.
Boah, ich werde kirre.....
Ist das nicht das Selbe?z.b. in Zeile 8, wird mit setState eine Variable auf den Wert 10 gesetzt.
Das funktioniert.
Warum funktioniert das nicht in der Zeile 11?Bitte erklärs mir.
-
@doppellhelix sagte in Erste Schritte beim Scripten - Einsteigerfrage:
Ist das nicht das Selbe?
nicht bei ioBroker
Datenpunkte enthalten "dauerhaft" gespeicherte Werte, die dem ganzen ioBroker Universum zur Verfügung stehen.
Variablen existieten nur in dem Skript, und das auch nur bis zum Neustart
-
Ok, das ist ein Hinweis, den ich mir aufschreiben sollte.
Ich denke Zeile 11 ist der Knackpunkt. Aber wie muss es dann richtig sein?
Ich habe u.a. hier eine Anleitung für den Syntax benutzt, aber da stehts so drin, wie ich es gemacht habe.
https://www.kreyenborg.koeln/wissensdatenbank/wert-in-datenpunkt-schreiben/ -
@doppellhelix sagte in Erste Schritte beim Scripten - Einsteigerfrage:
Boah, ich werde kirre.....
Ist das nicht das Selbe?z.b. in Zeile 8, wird mit setState eine Variable auf den Wert 10 gesetzt.
Das funktioniert.
Warum funktioniert das nicht in der Zeile 11?Bitte erklärs mir.
Du vermischt 2 unterschiedliche Dinge:
- Variablen (definiert via
let
odervar
im Skript (Z.Bsp. Zeile 1) sind Platzhalter die ausschliesslich innerhalb des Skriptes definiert sind. Sie haben einen (einfachen) Namen nur dann einen Wert wenn dieser nach Start des Skriptes auch 'gesetzt' wird. Diesen kann man einfach direkt einen Wert zuweisen, bzw. den Wert direkt abfragen (Bsp: Zeile 1 für eine Zuweisung, Zeile 10 für eine 'abfrage' - Datenpunkte (oder auch States) - Definiert als Objekte im Objektbaum. Um mit diesen zu Arbeiten musst du einen Aufruf an die Objekt-Datenbank machen (
getState
) Sie haben verschiedene Eigenschaften:
-- die ID, die beigetState
,setState
oderon
genutzt wird
-- einen Wert (val) - Siehe Zeile 1, 5 oder 6.
-- ...
Das was du in Zeile 1 (oder 5,6) machst, ist den Wert eines Datenpunktes in einer Variablen zu speichern. Wohlgemerkt nur den Wert des Datenpunktes zu diesem Zeitpunkt. Es gibt keine verbindung zwischen Variable und Datenpunkt.
Was du in Zeile 11 machst ist einen Datenpunkt mit einem neuen Wert zu beschreiben. Dadurch bleibt aber die Variable reducedCharging unverändert. Willst Du diese auch verändern musst du das explizit tun - idealerweise bevor du den Datenpunkt aktualisierst.if (forecast > 20000) { setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 10); //setState('mqtt.0.evcc.site.prioritySoc'/*evcc/site/prioritySoc*/, 0); setState('telegram.0.communicate.response', forecast + "Wh Ertrag erwartet. Reduziere Ladeleistung auf 100W bis 11 Uhr"); reducedCharging = true; } else { setState('modbus.0.holdingRegisters.33046_Max_Charging_Power'/*Max Ladeleistung*/, 10600); //setState('mqtt.0.evcc.site.prioritySoc'/*evcc/site/prioritySoc*/, 30); setState('telegram.0.communicate.response', forecast + "Wh Ertrag erwartet. Lade Akku sofort mit voller Leistung"); reducedCharging = false } setState('0_userdata.0.reducedCharging'/*reducedCharging*/, reducedCharging);
Zum Thema was nutz ich wann:
- Variablen:
- Variablen verbrauchen etwas Speicher, aber beim Zugriff fast keine Laufzeit.
- Variablen werden bei jedem Start es Skripts neu initialisiert. Wenn das Skript dieses nicht tut, dann mit dem Wert
undefined
- Der code
let Testvar = 0; for (let I=1, I<100,i++) { const oldVal = Testvar Testvar = Math.random(); console.warn('values: ' + Testvar + ' ' + oldVal) }
liefert 100 Zeilen mit jeweils 2 Zahlenpaaren - diese sind immer unterschiedlich.
- Datenpunkte
- Datenpunkte liegen in der Objekt-DB. Sie verbrauchen im Skript erst einmal keinen eigenen Speicher - der ist schon in der Objekt-DB enthalten
- Der Wert eines Datenpunktes b leibt über Skript- und ioBroker-Neustarts erhalten
- der Code
let rndVal = 0; for (let I=1; I<100;i++) { rndVal = Math.random(); setState('0_userdata.0.floatTest', rndVal ) console.warn('values: ' + getState('0_userdata.0.floatTest').val + ' ' + rndVal) }
(Zum Probieren musst du einen Datenpunkt 0_userdata.0.floatTest als typ Numerisch anlegen.)
liefert 100 Zahlenpaare, wobei die Zahlen manchmal gleich und manchmal unterschiedlich sind! (Abhängig von Rechenleistung, Größe der Objektdatenbank, etc...)
A.
- Variablen (definiert via
-
Deshalb war einer meiner ersten Vorschläge dass du die Variablen raus wirfst - das verwirrt nur am Anfang. Für die 4 mal am Tag oder so, wo du die brauchst, kannst du es auch direkt auf dem State lesen.
State sind hier vergleichbar mit Dateisystem und Variablen sind eben ungesicherte Daten.
Deshalb
// anlegen var daten = true; // oder das ist dann so als wenn du die Datei einmalig einlesen würdest. Danach können andere mit dem state // machen was sie wollen, du musst ihn erneut lesen wenn du wissen willst was passiert ist. var daten = getState('meineId.daten', daten); // beschreiben daten = false; // speichern setState('meineId.daten', daten);
-
@ticaki
Ja hattest du, aber zu dem Zeitpunkt war ich ja 0 im Thema drinnen und wusste nicht, was du meinstest@Asgothian
Danke für die ausführliche Erklärung.
Warum unterscheidet man des denn so?
Aus der Historie, weil Speicher früher kostbar war? Oder weil Variablen einfach schneller "im Ablauf" sind? -
@doppellhelix sagte in Erste Schritte beim Scripten - Einsteigerfrage:
Warum unterscheidet man des denn so?
Aus der Historie, weil Speicher früher kostbar war? Oder weil Variablen einfach schneller "im Ablauf" sind?Es geht nicht um Schneller oder langsamer, sondern um das richtige Werkzeug für die richtige Anwendung.
Mit Variablen können viele Dinge getan werden die mit Datenpunkten unhandlich sind und/oder Zeit und Ressourcen kosten.
Was ich in dem Post oben nicht klar erwähnt habe:
Variablen brauchen primär Speicher als Ressource. Zugriff ist billig. Insbesondere wenn es darum geht Werte aus Datenpunkten in Skripten zu nutzen erhöht die Variable den Speicherbedarf, senkt aber im Gegenzug den Ressourcenverbrauch wenn sie geändert wird.Stell dir das ganze so vor:
- Die Variable ist eine Kreidetafel. Willst du den Wert wissen schaust du drauf. Willst du den Ändern wischt du sie aus und schreibst den neuen Wert drauf
- Datenpunkte sind Karteikarten in einem Schober. Willst du den Wert wissen schickst Du jemanden mit der ID los die Karte zu holen. Willst du den Wert anpassen musst du jemanden mit einer neuen Karte losschicken um die Karteikarte im Schober auszutauschen
- Alles was du dauerhaft erhalten willst muss auf einer Karteikarte im Schober stehen. Nur dann bleibt es erhalten.
In deinem Skript ist die Verwendung der Variablen durchaus optional. Der Vorschlag von @ticaki ist nicht schlecht, da der 'gewinn' in deinem Beispiel sehr gering ist.
In einem anderen Beispiel sieht das aber dann anders aus:
- stell dir vor du hast einen Datenpunkt Energielimit. Damit steuerst du ab wie viel Energieüberschuss du das Auto laden willst.
- Jetzt machst du einen Trigger: - jedes mal wenn sich der Energieüberschuss ändert willst du wissen ob er über dem Limit liegt. Wenn du da das Limit jedes mal aus dem Datenpunkt holst ist das schon signifikant mehr Ressourcenverbrauch als das ganze einmal zu holen und in einer Variable abzulegen, mit die du den geänderten Energieüberschuss vergleichen kannst.
A.
(Ja, das Bild vereinfacht, hilft aber) -
@asgothian sagte in Erste Schritte beim Scripten - Einsteigerfrage:
Es geht nicht um Schneller oder langsamer,
naja, je nach Anwendungsfall schon.
@Doppellhelix
JS Scripte arbeiten asynchron, dh. sie warten nicht unbedingt auf die Abarbeitung des gerade ausgeführten Befehls, sondern führen direkt den nächsten aus.Wenn jetzt der Bote mit der letzten Karteikarte noch unterwegs ist, hat der, der den Wert nachsehen will, u.U. noch den alten Wert.
Hier ist die Kreidetafel die bessere Methode, weil schneller.