NEWS
[solved] Objekte im Array dynamisch vom Skript aktualisieren
-
Hallo,
ich stehe vor einem Problem, bei welchem ich vielleicht nur eine Bestätigung suche.
Ich habe eine Rolladensteuerung implementiert, bei der der vis-Nutzer zwei Zeiten für eine Automatik editieren kann:

Tippt man auf die Kachel, öffnet sich eine view:

In dieser View kann man erstens je Rollo Hoch- und Runterfahren aktivieren und jeweils die Position bestimmen. Zu allen Informationen habe ich Datenpunkte erstellt (bspw. AutoTiming1_DG für ja/nein Hochfahren, AutoTiming2_DG für Herunterfahren; PositionHoch_DG; PositionRunter_DG...)Im Skript habe ich mir das wie folgt überlegt:
Ich erstelle ein Array mit den Objekten und Eigenschaften jedes Rollos:const idAutoModeEnabled='0_userdata.0.Rollo.ShutterAutoTiming'; const idAutoModeOnce='0_userdata.0.Rollo.ShutterAutoTimingOnce'; const rollos= [ { enabled1:getState('0_userdata.0.Rollo.AutoTiming1_DG').val, enabled2:getState('0_userdata.0.Rollo.AutoTiming2_DG').val, posDP:'alias.0.DG_Zimmer.Fenster.RolloPositionSoll', position1:getState('0_userdata.0.Rollo.PositionHoch_DG').val, position2:getState('0_userdata.0.Rollo.PositionRunter_DG').val, wait1_ms:60*1000, wait2_ms:60*1000 },Ändert sich die Zeit, erstellt er ein schedule mit einem foreach:
const idTime1 = '0_userdata.0.Rollo.AutoTiming1_time'; // Zeit als String "hh:mm" const idTime2 = '0_userdata.0.Rollo.AutoTiming2_time'; // Zeit als String "hh:mm" on(idTime1, function(dp) { var h=getState(idTime1_h).val; var min=getState(idTime1_min).val; clearSchedule(timer1); log('neuer Timer1 gesetzt'); timer1 = schedule(min + ' ' + h + ' * * *', function() { if (getState(idAutoModeEnabled).val) { rollos.forEach(rollo =>{ if(rollo.enabled1){ // für jedes Rollo bestimmbar setState(rollo.posDP,rollo.position1); //log('Rollo '+ rollo.posDP + ' auf '+rollo.position1 + ' bei Zeit ' + idTime1+' gefahren.'); } }); } }); }); // schedule 2 on(idTime2, function(dp) { var h=getState(idTime2_h).val; var min=getState(idTime2_min).val; clearSchedule(timer2); log('neuer Timer2 gesetzt'); timer2 = schedule(min + ' ' + h + ' * * *', function() { if (getState(idAutoModeEnabled).val) { rollos.forEach(rollo =>{ log(rollo.posDP + '='+ rollo.enabled2); var timeout=null; timeout=setTimeout(function(){ if (rollo.enabled2){ setState(rollo.posDP,rollo.position2); log('Rollo '+ rollo.posDP + ' auf '+rollo.position2 + ' bei Zeit ' + getState(idTime2).val+'('+ rollo.wait2_ms/1000+') gefahren ('+rollo.enabled2+')'); } clearTimeout(timeout); timeout=null; },rollo.wait2_ms); }); } if (getState(idAutoModeOnce).val) // reset wenn nur einmalig { setState(idAutoModeEnabled,false); setState(idAutoModeOnce,false); } }); }); //####################### INIT ################################ // init first time: var h=getState(idTime1_h).val; var min=getState(idTime1_min).val; clearSchedule(timer1); timer1 = schedule(min + ' ' + h + ' * * *', function() { if (getState(idAutoModeEnabled).val) {rollos.forEach(rollo =>{if(rollo.enabled1){ setState(rollo.posDP,rollo.position1);}});}}); var h=getState(idTime2_h).val; var min=getState(idTime2_min).val; clearSchedule(timer2); timer2 = schedule(min + ' ' + h + ' * * *', function() { if (getState(idAutoModeEnabled).val) {rollos.forEach(rollo =>{if (rollo.enabled2){setState(rollo.posDP,rollo.position2); }});}});Jetzt beschleicht mich das Gefühl, dass er vermutlich das Array nur ein einziges Mal beim Starten des Skripts einliest, oder?
Denn aktiviere ich nach Starten des Skripts andere Rollos und ändere auch zum "Aktualisieren" die Zeiten kurz (damit er die schedules einliest) verhalten sich die Rollos nicht, wie ich es mir denke.
Liege ich mit dem Auslesen der Datenpunkte richtig?
Dann wäre die Konsequenz, dass sobald sich etwas ändert, das Array Neu initialisiert werden muss.
Oder gäbe es eine elegantere Lösung für solche dynamischen "settings"Danke und ein frohes Neues Jahr!
PS: das timeout habe ich nachträglich implementiert, um zu verhindern, dass alle aktivierten Rollos gleichzeitig starten, sondern es eher aussieht "jemand schließt die Rollos händisch nacheinander". Funktioniert auch alles soweit.
-
Jetzt wo ich mir das anschaue, wäre es eventuell von Vorteil, das Array in die "on"s zu packen anstatt "const"?
Oder ich müsste noch listener auf einen der vielen Datenpunkte packen, damit er bei Änderung jedes Mal den schedule neu erstellt. Und nicht nur auf "time1" und "time2", aber dann habe ich das Problem ja, dass ich in jedem listener das Array definieren muss und somit bei Änderungen alle anfassen muss.Gibt es eine Möglichkeit, das Array neu einlesen zu lassen während der runtime, ohne dass ich es in jeder Funktion deklariere?
-
Was genau willst du denn im Array zur Laufzeit ändern ? Du kannst relativ einfach (obwohl das als
constdefiniert ist) ändern, viarollos[idx].enabled1 = getState('0_userdata.0.Rollo.AutoTiming1_DG').val; rollos[idx].enabled1 = getState('0_userdata.0.Rollo.AutoTiming2_DG').val; rollos[idx].position1 = getState('0_userdata.0.Rollo.PositionHoch_DG').val; rollos[idx].position2 = getState('0_userdata.0.Rollo.PositionRunter_DG').val;Das ganze lässt sich noch weiiter 'generalisieren' - dazu müsste ich aber wissen wie die DP's der Rollos wirklich heissen.
Schlüssel ist dabei:
- an Stelle eines Array wird ein Objekt genutzt, bei dem ein Teil des DP Namens als 'property Name' benutzt wird. { rollo1: {}, rollo2: {} }
- zum lesen der Daten machst du dann eine Funktion, der du den 'property namen' und die Rollos mitgibst:
function readDynamicRolloData(key, room, storage) { const robj = storage[key]; robj.enabled1=getState(`0_userdata.0.${key}.AutoTiming1_DG`).val; robj.enabled2=getState(`0_userdata.0.${key}.AutoTiming2_DG`).val; robj.position1:getState(`0_userdata.0.${key}.PositionHoch_DG'`.val; position2:getState(`0_userdata.0.${key}.PositionRunter_DG`).val; return robj; }an stelle vom
rollos.forEach(rollo ...kannst du dann mit
Object.keys(rollos).foreach (key) => { const rollo = readDynamicRolloData(key, rollos); ....Der rest der foreach Funktion kann so bleiben wie sie ist.
A.
-
Nachtrag: wenn die Alias Struktur entsprechend aufgebaut ist kannst du ggf. dann sogar mit einem einzigen 'on' alles erlegen. Das hier zu beschreiben ist aber dann doch etwas.. langatmig.
-
Nachtrag: in deinem Rollo-Array hast du 2 Arten von Informationen: Dynamische aus Datenpuntkten und statische (fest im Skript stehende). Warum legst du dafür nicht auch Datenpunkte im userdata Bereich an - neben den dynamischen, so das du da die eigentlichen Werte definieren kannst. Dann ist das Skript mit Ausnahme des
keyfür alle Rollos gleich und du hast im Skript nur die Keys zu definieren die existieren.
-
Danke für deinen Input und die Zeit, mein Problem zu verstehen.
Mit keys habe ich noch nicht gearbeitet, das werde ich mir mal anschauen und ja, hier soll mir keiner die gesamte Lösung präsentieren, eine Richtung, insbesondere zu generalisieren, ist von großem Vorteil.Ich würde jetzt nicht alle DP posten, aber grundsätzlich habe ich jedem Rollo (8x) folgende DP (userdata) zugeordnet:
- time1 (hochfahren) enabled?
- time2 (herunterfahren) enabled?
- ZielpositionHoch (bei time1)
- ZielpositionRunter (bei time2)
im Array wie du bereits bemerkt hast, habe ich statische Werte:
- Verzögerung time1
- Verzögerung time2
- Alias DP für die Rolloposition (der lässt das Rollo bewegen)
Hatte mir gedacht, ich spare mir "statische" Werte als DP anzulegen, könnte ich aber natürlich machen. Ich tue mich immer so schwer mit dem Anlegen von DP (da ich es noch mit Maus mache, sollte mir langsam das per Skript machen aneignen).

Nach "_" kommt immer Zimmername als Abkürzung (DG,AZ,SZ,Kueche,Essen,WZli,WZre,Zimmer).
Ich merke gerade du wolltest auf die aliase hin: Da habe ich immer die Struktur
alias.[Zimmername].Fenster.RolloPositionSollOkay, Mist eine Ausnahme bildet WZ:
alias.WZ.Fenster.RolloLinksPositionSoll alias.WZ.Fenster.RolloRechtsPositionSollDie brauche ich, um die Rollos Befehle zu versenden.
Das mit dem einem einzigen "on" erledigen habe ich tatsächlich bereits in mehreren Fällen für "Batteriestatus abfragen", "Sind Fenster offen" oder "fahre alle Rollos mit einem Klick hoch" gemacht. Die Erklärung müsste also nicht vollständig sein.
-
@asgothian
Habe es im ersten Schritt so gelöst, dass ich das Array neu einlesen lasse und statisch Werte erstmal im Skript lasse. Dafür habe ich ein Array erstellt für die key-Worte, welche in der gleichen Reihenfolge sein müssen:const room=['DG','SZ','AZ','Zimmer','Kueche','Essen','WZli','WZre']; const rollos= [ { posDP:'alias.0.DG_Zimmer.Fenster.RolloPositionSoll', wait1_ms:60*1000, wait2_ms:60*1000 }, { posDP:'alias.0.OG_SZ.Fenster.RolloPositionSoll', wait1_ms:40*1000, wait2_ms:40*1000 }, ...Mit der Funktion
function readArray(key,room,storage){ const robj =storage[key]; robj.enabled1=getState(`0_userdata.0.Rollo.AutoTiming1_`+room).val; robj.enabled2=getState(`0_userdata.0.Rollo.AutoTiming2_`+room).val; robj.position1=getState(`0_userdata.0.Rollo.PositionHoch_`+room).val; robj.position2=getState(`0_userdata.0.Rollo.PositionRunter_`+room).val; return robj; }erweitere ich dynamisch das Array mit aktuellen Werten. Mit den SetTimer Funktionen
function setTimer1(){ var h=getState(idTime1_h).val; var min=getState(idTime1_min).val; clearSchedule(timer1); log('neuer Timer1 gesetzt'); timer1 = schedule(min + ' ' + h + ' * * *', function() { if (getState(idAutoModeEnabled).val) { Object.keys(room).forEach (id => { const rollo = readArray(id,room[id],rollos); var timeout1=null; timeout1=setTimeout(function() { if(rollo.enabled1) { // für jedes Rollo bestimmbar setState(rollo.posDP,rollo.position1); //log('Rollo '+ rollo.posDP + ' auf '+rollo.position1 + ' bei Zeit ' + idTime1+' gefahren.'); } clearTimeout(timeout1); timeout1=null; },rollo.wait1_ms); }); } }); }und dem Listener auf alles was im Ordner "Rollo" ist:
$('state[id=0_userdata*Rollo*]').on(function(obj){ //log('sth changed'); setTimer1(); setTimer2(); });werden die Timer bei jeder Änderung neu erstellt. Noch nicht ganz schön sauber, aber es klappt. Erster Funktionstest steht aus. Die Grundfrage aber, ob ein Array mit DP als Objekte dynamisch angepasst werden kann, ist gelöst.