NEWS
Script Formulierung
-
am Monatsende wird Gasverbrauch in "Gas_letzten_Monat" geschrieben.
Die letzten 13 Monatswerte sollen unter "javascript.0.Ressource.Gas.json" abgelegt werden.Das Script funktioniert so nun so weit - habe es gemäß trail und error erstellt.
Ich würde gerne wissen, ob man das eine oder andere besser / einfacher machen kann.
let id = "javascript.0.Ressource.Gas.json"; let mId = "javascript.0.Ressource.Gas.Gas_letzten_Monat"; var jsontab = []; if ( !existsState(id )) { createState(id, JSON.stringify(jsontab), {type: 'string', name: 'Verbrauch' }, function() {}); }; on({id: mId, change: "ne"}, async function (obj) { var value = obj.state.val; const jsonObj = JSON.parse(getState(id).val); var date = new Date(); const monate = ["JAN", "FEB", "MAR","APR", "MAI", "JUN", "JUL", "AUG", "SEP", "OKT", "NOV", "DEZ"]; var Zeitraum = monate[date.getMonth()-1] + "-" + date.getFullYear(); // MMM-JJJJ jsonObj.push({Monat:Zeitraum, Wert:value}); while (Object.keys(jsonObj).length >13) { jsonObj.splice(0, 1); } setState(id,JSON.stringify(jsonObj)); });
-
@senior1418
ist doch ganz gut. Da das Json die Eigenschaft length hat könnte man das weg lassen, das async müsste auch unnötig sein.while ( jsonObj.length > 13) { jsonObj.splice(a,1); }
createState(id, JSON.stringify([])...
-
@ticaki sagte in Script Formulierung:
@senior1418
ist doch ganz gut. Da das Json die Eigenschaft length hat könnte man das weg lassen, das async müsste auch unnötig sein.while ( jsonObj.length > 13) { jsonObj.splice(a,1); }
createState(id, JSON.stringify([])...
OK, verstehe - habe in der Hierarchie zu hoch angesetzt ..
Aber wie wird das "a" abgeleitet ? ```jsonObj.splice(a,1);
-
@senior1418
Ups... das a ist 0. Ich hatte vorher ne for schleife, aber die war überflüssig groß, hab das a vergessen zu ändern. -
@ticaki sagte in Script Formulierung:
Ups... das a ist 0. Ich hatte vorher ne for schleife, ....
alles klar.
Mich stört bei meiner Vorgehensweise, dass das Skript zweimal ausgeführt werden muss wenn der Datenpunkt nicht vorhanden ist. Beim ersten Start bei nicht Vorhandensein des Datenpunktes ein wird ein Fehler erzeugt.
-
Ein Teil aus einem meiner Skripte (nur kopiert nicht bearbeitet) der dein Problem löst:
createThisStates(); async function createThisStates() { objJson = getValuePerRoom({}, $(cHeater),'cHeater', false); objJson = getValuePerRoom(objJson, $(cSensorTemp), 'cSensorTemp'); objJson = getValuePerRoom(objJson, $(cWindows), 'cWindows'); objJson = getValuePerRoom(objJson, $(cNewTemp), 'cNewTemp'); for (let room in objJson) { let currentDir = mainDir+DOT+room+DOT; let roomId = getEnumIDbyName(room); try { if (!await existsStateAsync(currentDir + tHeater)) { await createStateAsync(currentDir + tHeater,false, true, {read:true, write:false, def:false, name:"Stellantrieb", type:"boolean" },); if (roomId != '') await addToEnum(roomId, currentDir + tHeater); } if ((!await existsStateAsync(currentDir + tSensorTemp))) { await createStateAsync(currentDir + tSensorTemp, {read:true, write:false, def:0, name:"Ist-Temperatur", type:"number", }, ); if (roomId != '') await addToEnum(roomId, currentDir + tSensorTemp); } if (!await existsStateAsync(currentDir + tWindows)) { await createStateAsync(currentDir + tWindows, {read:true, write:false, def:false, name:"Fenster offen", type:"boolean", }, ); if (roomId != '') await addToEnum(roomId, currentDir + tWindows); } if (!await existsStateAsync(currentDir + tTargetTemp)) { await createStateAsync(currentDir + tTargetTemp, {read:true, write:true, def:18, name:"Soll-Temperatur", type:"number", }, ); if (roomId != '') await addToEnum(roomId, currentDir + tTargetTemp); } } catch(error) { log(error); } // hier die nächste Funktion/Code aufrufen
EDIT: ah das ist ja noch das false, true im ersten Aufruf, kann weg ist noch vom Fehler suchen drin. getValuePerRoom sowie addToEnum sind eigene Funktionen
-
@senior1418 ich hatte die Frage auch schon mal gestellt:
-
@bananajoe sagte in Script Formulierung:
@senior1418 ich hatte die Frage auch schon mal gestellt:
So hab ich das vorher auch gemacht.
-
@ticaki sind auch ein paar einfache antworten dabei
-
@bananajoe
Ok dann überarbeite ich meine Antwort mal auf einfach(hast ja recht)
createThisStates('bla.blu.ding.dong'); async function createThisStates(id) { try { if (!await existsStateAsync(id)) { await createStateAsync(id, {read:true, write:false, def:false, name:"Hier steht was verständliches", type:"boolean" },); } } catch(error) { log(error); } states_erstellt_mache_was() // man kann natürlich auch einfach den Code hier einfügen, ich mache es halt häufig mit Funktionen } function states_erstellt_mache_was(){ log('sinnvolles'); };
das try ist nötig, weil sonst im Fehlerfall der Adapter neustartet.
-
@ticaki mal ganz dumm nachgefragt:
Wenn ich mir eine async Funktion baue um darin createStateAsync nutzen zu können und diese dann aufrufe ... dann läuft mein Script weiter?
// Global var s_DeviceName = "K58-Buero-Steckdose-oben"; var s_state_rootpath = "0_userdata.0.tasmota."; // Funktionen async function CreateMyStates(){ // POWER await createStateAsync(s_state_rootpath + s_DeviceName + ".POWER", false, { type: 'boolean', read: true, write: true, name: s_DeviceName + ': AN (=true) oder AUS (=false)' }); } //Hauptprogramm CreateMyStates(); setState(s_state_rootpath + s_DeviceName + ".POWER", true, true);
Würde beim ersten mal auf einen Fehler laufen weil es nach CreateMyStates(); sofort weiter geht?
Deshalb muss ich die Erstbefüllung mit in die Funktion packen, richtig? -
@BananaJoe
Ja, jedoch kannst du auch alles in die Funktion packen.so würde ich das machen
vorher
// Global var s_DeviceName = "K58-Buero-Steckdose-oben"; var s_state_rootpath = "0_userdata.0.tasmota."; // Funktionen createState(s_state_rootpath + s_DeviceName + ".POWER", false, { type: 'boolean', read: true, write: true, name: s_DeviceName + ': AN (=true) oder AUS (=false)' }); } //Hauptprogramm setState(s_state_rootpath + s_DeviceName + ".POWER", true, true);
zwischen Schritt der ist auch lauffähig wir packen alles außer den Variablen in einen Block der aus 2 * { besteht und fügen noch zusätzlich ein try und catch ein. Das fängt Fehler auf und ist zumindest um die Awaits zwingend nötig. Hab der Einfachheit halber alles in den Block gesetzt.
// Global var s_DeviceName = "K58-Buero-Steckdose-oben"; var s_state_rootpath = "0_userdata.0.tasmota."; { try { // Funktionen createState(s_state_rootpath + s_DeviceName + ".POWER", false, { type: 'boolean', read: true, write: true, name: s_DeviceName + ': AN (=true) oder AUS (=false)' }); //Hauptprogramm setState(s_state_rootpath + s_DeviceName + ".POWER", true, true); } catch(error) { log(error); } }
im letzten Schritt bauen wir Async und Await ein. Dazu setzten wir vor die erste { die Funktionsdefinition und benutzen die async Funktionen sowie await.
var s_DeviceName = "K58-Buero-Steckdose-oben"; var s_state_rootpath = "0_userdata.0.tasmota."; async function createMyStates(){ try { // Funktionen await createStateAsync(s_state_rootpath + s_DeviceName + ".POWER", false, { type: 'boolean', read: true, write: true, name: s_DeviceName + ': AN (=true) oder AUS (=false)' }); //Hauptprogramm setState(s_state_rootpath + s_DeviceName + ".POWER", true, true); } catch(error) { log(error); } } createMyStates();
EDIT: wobei man hier auch await setStateAsync() benutzen könnte.
EDIT2: ---
EDIT3: Edit 2 hat sich erledigt, man sollte halt in weiterführenden Funktionen nicht auf States zugreifen die tatsächlich noch nicht erstellt worden sind. -
@ticaki ok, ich hab mein Skript nun so geändert das die Erstellung und die Erstbefüllung in einer async function stattfindet. Sieht auch gut aus, beim ersten Start werden die Datenpunkte erstellt und es gibt keine Fehlermeldungen mehr.
Der Rest des Skriptes besteht aus on() Subscriptions, unter anderem auf einen der Datenpunkte die ggf. zuvor erst erstellt wurden:
// unsere JavaScript-Instanz um zu prüfen ob die Änderung durch MQTT oder durch VIS ausgelöst wurde const js = 'system.adapter.javascript.' + instance; // Datenpunkt POWER überwachen: on({ id: s_state_rootpath + s_DeviceName + ".POWER", change: "ne", fromNe: js }, function (obj) { var b_value_POWER = obj.state.val; ... ... }
Sollte ich da auch etwas beachten? Oder klappt das mit dem on auch wenn es den Datenpunkt noch nicht gibt?
-
@bananajoe
Das weiß ich nicht. bei mir sieht das so aus:ein kleiner ausschnitt
if (!await existsStateAsync(currentDir + endOfState)) { await createStateAsync(currentDir + endOfState, {read:true, write:true, def:18, name:"Soll-Temperatur niedrig H/K", type:"number", }, ); } endOfState = tTargetHeatingCoolingState; if (!await existsStateAsync(currentDir + endOfState)) { await createStateAsync(currentDir + endOfState, {read:true, write:true, def:0, name:"Zielstatus der Heizung/Kühlung", type:"number", }, ); } on({id:currentDir + tHeatingThresholdTemperature, change:'ne'}, setAckSetStell); on({id:currentDir + tCoolingThresholdTemperature, change:'ne'}, setAckSetStell); // hier fehlt viel function setAckSetStell(obj){ setStell(); setState(obj.id, obj.state.val, true); }
Ich versuche das alles zusammen zu halten damit ich die on() besser finde.