NEWS
[gelöst]Skript als Alternative zum Scenenadapter
-
Hallo zusammen,
ich bin dabei ein Script zu schreiben, mit der ich eine Scene aktivieren/schalten kann, da ich den Adpater Scene nicht mehr nutzen möchte.
Ich habe versucht das ganze so aufzubauen, dass ich mit wenigen Eingaben eine neue Scene nutzen kann. (Script kopieren und nur den Namen der Scene ändern und die Datenpunkte). Das ganze ist natürlich noch ganz am Anfang und habe nun das erste Problem. Ich habe bisher nur versucht einzbauen, dass wenn 2 Datenpunkte auf true sind, der Datenpunkt "is ative" auf true geschaltet wird. Ist einer oder beide Datenpunkte auf false, soll der Datenpunkt "is active" auf false gesetzt werden. Das passiert jedoch nicht, ich muss also wahrscheinlich ein Fehler bei den Triggern ganz unten haben. Kann mir da jemand helfen?
Wenn das Problem gelöst ist, werde ich versuchen, dass durch drücken des Buttons die Scene aktiviert/deaktiviert wird, aber das werde ich erst danach versuchen einzubauen.
Vielleicht schaffe ich es auch irgendwann mal, dass ich mit einem Script verschiedene Scenen bauen kann, nur bin ich was programmieren angeht noch am Anfang daher reicht es mir eine Scene pro Script zu nutzen.
Hier das Script://Grundeinstellungen const logging = true; // Logs ausgeben const praefix = "javascript.0.Szenen."; //Grundpfad für Script DPs - Muß innerhalb javascript.x sein. const SzeneData = []; const SzeneID = []; //Name der Szenen eingeben const SzenenName = ["Chillen"]; //Hier die States eingeben SzeneID[0] = ["zigbee.0.f0d1b8000010bb79.state"]; //Stehlampe SzeneID[1] = ["zigbee.0.7cb03eaa0a068b8d.state"]; //Laterne //Schleife durch die ID's und Werte for (let x = 0; x < SzeneData.length; x++) { SzeneData[x] = [SzeneID[x][0], getState(SzeneID[x][0]).val]; }; //Datenpunkte erstellen createState(praefix + SzenenName[0] + ".activate", { name: "Szene aktivieren/deaktivieren", role: "button" }); createState(praefix + SzenenName[0] + ".is activ", { name: "is acitv?", role: "state" }); //Ab hier eigentliches Skript function Szene() { let Status = false; for (let x = 0; x < SzeneData.length; x++) { if (SzeneData[x][1] == true) { Status = true; } else { Status = false; } } if (logging) { if (Status == true) { log("Szene " + SzenenName[0] + " aktiv") } else { log("Szene " + SzenenName[0] + " inaktiv") } } setState(praefix + SzenenName[0] + ".is activ", Status); }; Szene(); for (let x = 0; x < SzeneData.length; x++) { //Trigger in Schleife erstellen on({ id: SzeneData[x][0], change: "any" }, function (dp) { SzeneData[x][1] = dp.state.val; }) };
-
@Dominik-F sagte in Skript als Alternative zum Scenenadapter:
Hier das Script:
Du verwendest Arrays, wo keine Arrays benötigt werden. Versuche es mal so:
//Grundeinstellungen const logging = true; // Logs ausgeben const praefix = "javascript.0.Szenen."; //Grundpfad für Script DPs - Muß innerhalb javascript.x sein. const SzeneData = []; const SzeneID = []; //Name der Szenen eingeben const SzenenName = "Chillen"; //Hier die IDs eingeben SzeneID[0] = "zigbee.0.f0d1b8000010bb79.state"; //Stehlampe SzeneID[1] = "zigbee.0.7cb03eaa0a068b8d.state"; //Laterne //Schleife durch die ID's und Werte for (let x = 0; x < SzeneID.length; x++) { SzeneData[x] = getState(SzeneID[x]).val; }; //Datenpunkte erstellen createState(praefix + SzenenName + ".activate", { name: "Szene aktivieren/deaktivieren", role: "button" }); createState(praefix + SzenenName + ".is_activ", { name: "is acitv?", role: "indicator" }); //Ab hier eigentliches Skript function Szene() { let Status = true; for (let x = 0; x < SzeneData.length; x++) { if (!SzeneData[x]) Status = false; } if (logging) { if (Status) log("Szene " + SzenenName + " aktiv"); else log("Szene " + SzenenName + " inaktiv"); } setState(praefix + SzenenName + ".is_activ", Status, true); }; Szene(); for (let x = 0; x < SzeneID.length; x++) { //Trigger in Schleife erstellen on(SzeneID[x], function (dp) { // triggert bei Wertänderung SzeneData[x] = dp.state.val; Szene(); }); }
-
Das funktioniert ,vielen Dank. Ich habe gerade erst gelernt Arrays zu verwenden, daher hab ich die wahrscheinlich überall eingebaut
Könntest du mir vielleicht Zeile 28 und deine Änderungen ab Zeile 39 erklären?
Das logging stimmt noch nicht so ganz. Ich bekomme das logging im Moment doppelt. hast du da noch eine idee? -
@Dominik-F sagte:
Zeile 28
Auf deutsch: Wenn nicht Element x im Array, dann Status = false (ein Element ist nicht true, also false)
@Dominik-F sagte in Skript als Alternative zum Scenenadapter:
Änderungen ab Zeile 39 erklären?
- Einem Trigger (on) muss die ID übergeben werden. Wenn nur die ID als String übergeben wird, wird nur bei Wertänderung getriggert.
- Der Wert des Trigger-Datenpunktes wird in die passende Position des Daten-Arrays geschrieben und anschließend wird der Inhalt des Daten-Arrays durch Aufruf der Funktion Szene() ausgewertet.
@Dominik-F sagte in Skript als Alternative zum Scenenadapter:
Ich bekomme das logging im Moment doppelt. hast du da noch eine idee?
? Habe mal die geschweifte Klammern gegen Semikolon ausgetauscht.
-
Vielen Dank für deine Erklärung, wieder was dazu gelernt
Das Logging ist immer noch nicht so wie es sein soll. Sobald sich einer der Werte ändert, wird das logging ausgelöst. Mache ich nur einen der beiden Datenpunkte auf true, dann wird szene inaktiv geloggt. Das überschwemmt ja total den log. Es reicht ja, wenn das log ausgelöst wird, wenn der Datenpunkt is_active sich ändert.
-
@Dominik-F sagte:
Es reicht ja, wenn das log ausgelöst wird, wenn der Datenpunkt is_active sich ändert.
Dann erweitere Zeile 30.
if (logging && Status != getState(praefix + SzenenName + ".is_activ").val) {
-
Super, jetzt funktioniert es.
Könntest du mir kurz erklären wie du darauf gekommen bist diese 'Änderung vorzunehmen?
Sorry wenn ich so viel frage, ich möchte das verstehen damit ich in Zukunft selber Lösungen finden kann. -
@Dominik-F sagte:
diese 'Änderung vorzunehmen?
Zusätzlich zu logging muss der neue Status sich vom im Datenpunkt enthalten Wert unterscheiden, damit log() ausgeführt wird.
-
Ich danke dir für deine Erklärung. Ich habe nun den Datenpunkt Button durch einen State ersetzt und versucht alle Datenpunkte damit zu schalten. Das funktioniert soweit auch nur habe ich jetzt noch 2 Probleme.
Das logging zeigt für aktiv richtig an, für false wird 4x inaktiv angezeigt bei 4 states die auf false gesetzt werden.
2tes Problem ist, das ich ja grundsätzlich möchte, dass wenn wenn ich den aktivieren state auf true setze, die szene aktiviert wird und alle states auf true gesetzt werden und andersherum eben auf false. Die states werden nun aber unabhängig davon geschaltet. Sind alle states auf true und ich schalte den Datenpunkt aktivieren auf true, sollte eigentlich nichts passieren, es wird jedoch alles auf false gesetzt. Hast du da noch eine Idee?//Grundeinstellungen const logging = true; // Logs ausgeben const praefix = "javascript.0.Szenen."; //Grundpfad für Script DPs - Muß innerhalb javascript.x sein. const SzeneData = []; const SzeneID = []; //Name der Szenen eingeben const SzenenName = "Chillen"; //Hier die IDs eingeben SzeneID[0] = "zigbee.0.f0d1b8000010bb79.state"; //Stehlampe SzeneID[1] = "zigbee.0.7cb03eaa0a068b8d.state"; //Laterne SzeneID[2] = "zigbee.0.7cb03eaa00b1b716.state"; //Sideboard SzeneID[3] = "zigbee.0.7cb03eaa0a06f6b0.state"; //Vitrine //Schleife durch die ID's und Werte for (let x = 0; x < SzeneID.length; x++) { SzeneData[x] = getState(SzeneID[x]).val; }; //Datenpunkte erstellen createState(praefix + SzenenName + ".activate", false, { name: "Szene aktivieren/deaktivieren", role: "state" }); createState(praefix + SzenenName + ".is_activ", { name: "is acitv?", role: "indicator" }); //Ab hier eigentliches Skript function Szene() { let Status = true; for (let x = 0; x < SzeneData.length; x++) { if (!SzeneData[x]) Status = false; } if (logging && Status != getState(praefix + SzenenName + ".is_activ").val) { if (Status) log("Szene " + SzenenName + " aktiv"); else log("Szene " + SzenenName + " inaktiv"); } setState(praefix + SzenenName + ".is_activ", Status); // Szene einschalten/ausschalten on({ id: praefix + SzenenName + ".activate", change: "any" }, function (obj) { for (let x = 0; x < SzeneID.length; x++) { if (obj.newState.val && praefix + SzenenName + ".is_activ") { setState(SzeneID[x], false); } else { setState(SzeneID[x], true); } } }); }; Szene(); for (let x = 0; x < SzeneID.length; x++) { //Trigger in Schleife erstellen on(SzeneID[x], function (dp) { // triggert bei Wertänderung SzeneData[x] = dp.state.val; Szene(); }); };
-
@Dominik-F sagte:
wenn wenn ich den aktivieren state auf true setze, die szene aktiviert wird und alle states auf true gesetzt werden und andersherum eben auf false.
Einen Trigger darf man nicht innerhalb einer Funktion definieren, weil dann bei jedem Aufruf der Funktion eine weiterer Trigger mit der gleichen Funktionalität erzeugt und so das System irgendwann überlastet wird. Außerhalb der Funktion (z.B. am Ende des Scripts):
// Szene einschalten/ausschalten on(praefix + SzenenName + ".activate", function (obj) { // triggert bei Wertänderung for (let x = 0; x < SzeneID.length; x++) { setState(SzeneID[x], obj.state.val); } });
-
Es funktioniert
Vielen Dank mal wieder. Ich glaube ich muss mir das Thema Trigger nochmal genauer anschauen
Hast du auch noch eine Idee zu den Logs? -
@Dominik-F sagte:
Hast du auch noch eine Idee zu den Logs?
Bei Szenenwechsel werden 4 Trigger ausgelöst und somit 4 mal die Funktion aufgerufen. Um den Funktionsaufruf bei gewolltem Szenenwechsel zu vermeiden, kann man die Quelle auswerten. Versuche mal
for (let x = 0; x < SzeneID.length; x++) { //Trigger in Schleife erstellen on(SzeneID[x], function (dp) { // triggert bei Wertänderung SzeneData[x] = dp.state.val; if(dp.state.from != 'system.adapter.javascript.' + instance) Szene(); }); };
-
Bevor ich das versuche hab ich noch eine Frage. Wieso passiert das ganze nur beim ausschalten der Szene? Beim einschalten wird nur ein Log ausgelöst.
-
@Dominik-F sagte:
Wieso passiert das ganze nur beim ausschalten der Szene?
Gute Frage. Ich vermute, dass
setState(praefix + SzenenName + ".is_activ", Status);
unterschiedlich lange benötigt, bis der richtige Wert bei der Abfrage
if (logging && Status != getState(praefix + SzenenName + ".is_activ").val) {
zurück geliefert wird. Oder die Trigger reagieren unterschiedlich schnell auf den Wechsel ?
-
ja, Gefühlt schalten alle states gleichzeitig ein, aber unterschiedlich aus bzw. mit leichter Verzögerung.
-
@Dominik-F sagte:
unterschiedlich aus bzw. mit leichter Verzögerung.
Um Laufzeitprobleme bei Verwendung von getState() zu vermeiden, sollte man besser eine Variable verwenden.
//Ab hier eigentliches Skript var lastState = getState(praefix + SzenenName + ".is_activ").val; function Szene() { let Status = true; for (let x = 0; x < SzeneData.length; x++) { if (!SzeneData[x]) Status = false; } if (Status != lastState) { if(logging) { if (Status) log("Szene " + SzenenName + " aktiv"); else log("Szene " + SzenenName + " inaktiv"); } setState(praefix + SzenenName + ".is_activ", Status, true); } lastState = Status; }
-
Super, jetzt funktioniert wirklich alles
Ich vermute das liegt daran, dass die states den Befehl mit leichter Verzögerung untereinander erhalten?