Das erwartet dich in diesem Artikel
Einstellungen im Javscript-Adapter
Damit das Script wie gewünscht nach dieser Anleitung funktioniert, musst du zuerst in den Instanz-Einstellungen des Javascript-Adapters die Einstellung „Erlaube das Kommando setObject“ aktivieren:
Sensoren einer Aufzählung zuweisen
Um später den richtigen Datenpunkt deiner Geräte anzusteuern, solltest du den State jedes Geräts einer „Funktion“ hinzufügen, zum Beispiel „schalten“. Lege dazu unter „Aufzählungen“ eine neue Funktion an:
Danach wechselst du in „Objekte“ und wählst das erste Gerät, dass du schalten möchtest, aus. In diesem Beispiel steuerst du alle Steckdosen an. Daher weist du dem Datenpunkt „on“ die Funktion „schalten“ zu:
Diesen Vorgang wiederholst du mit allen weiteren Geräten, die du über das Script ansteuern möchtest. Beachte, dass alle Datenpunkte dieser Geräte den gleichen Wert zum Schalten besitzen müssen, also zum Beispiel „true“ und „false“. Wenn du Geräte mit unterschiedlichen States ansteuern möchtest, lies bitte weiter unten.
Blockly-Script aufbauen
Als Nächstes baust du dir folgendes Blockly-Script auf. Füge aus dem Bereich „Schleifen“ eine neuen „für jeden Wert aus Liste“-Block ein. An den Eingang hängst du einen „IDs vom Selektor“-Block aus dem Bereich „System mit folgendem Inhalt (mehr zum Thema „IDs vom Selektor“ erfährst du hier):
state[id=*](functions=schalten)
Variante 1: Schalten mit Javascript-Funktion
Das Script ruft nun in einer Schleife alle Datenpunkte, deren die Funktion „schalten“ zugewiesen ist, ab. Damit das Script diese auch schaltet, benötigst du eine neue „Javascript“-Funktion aus dem Bereich „Funktionen“:
Ziehe dir die Funktion in deinen Blockly-Bereich und füge über das blaue Zahnrad zwei Variablen dazu, die du wie oben abgebildet „zustand“ und „device“ nennst. Die Funktion selbst nennst du einfach „schalten“:
Über die 3 Punkte „…“ öffnest du den Funktions-Editor. In diesen fügst du folgenden Code ein:
setState(device, zustand);
Anschließend speicherst du die Funktion und schließt den Editor. Klicke dann mit der rechten Maustaste auf die Funktion und wähle „Erzeuge ‚Aufruf schalten'“:
Den erzeugen Block ziehst du in deine grüne Schleife. Stecke dann an den Eingang „device“ das „i“ aus der Schleife (entweder über Rechtsklick auf die Schleife „Erzeuge ‚Lese i'“ oder über den Bereich „Variablen“). An den Eingang „zustand“ steckst du den „wahr“-Block aus dem Bereich „Logik“:
Je nach dem, wie du die Geräte steuern möchtest, stellst du den Mathematik-Block auf „wahr“ („true“) oder „falsch“ („false“).
Damit das Script nach dem Start nur Geräte steuert, die nicht schon den Wert haben, den du schalten möchtest, brauchst du eine Logik-Abfrage. Baue also um diesen „schreiben mit“-Aufruf einen Logik-Block „falls mache“ und stecke an den Eingang einen „=“-Vergleich, den du auf „≠“ stellst:
In das erste Feld vom Vergleich fügst du aus dem Bereich „Syetem“ einen „Wert von Objekt ID“ mit dem grünen Attribut-Feld:
Stecke dann in das Attribut-Feld das „i“ aus der Schleife (entweder über Rechtsklick auf die Schleife „Erzeuge ‚Lese i'“ oder über den Bereich „Variablen“) und als zweiten Vergleichswert fügst du den „wahr“-Block aus dem Bereich „Logik“ ein. Stelle diesen auf den gleichen Wert wie den vom Logik-Block am Eingang „zustand“:
Ist der Wert des Geräts also ungleich <false> wird er durch das Script auf <false> gesetzt. Ist er schon auf <false>, passiert nichts. Möchtest du die Geräte einschalten, stellst du im Vergleich den Block auf „wahr“ und am Eingang „zustand“ auf „wahr“.
Wenn du das Script nun manuell, über einen Button in deiner Visualisierung oder ein anderes Script startest, werden alle Geräte der Aufzählung „schalten“ gesteuert.
Du kannst dieser Aufzählung jederzeit weitere Datenpunkte hinzufügen, ohne das Script anpassen zu müssen.
Geräte mit underschiedlichen States steuern
Solltest du Geräte mit unterschiedlichen Datenpunkten steuern wollen, darfst du die Funktion „schalten“ NICHT dem Datenpunkt zuweisen, sondern dem übergeordneten Gerät. Danach kannst du mit dem „IDs vom Selektor“-Block gezielt auf die einzelen States zugreifen und diese steuern (mehr zum Thema „IDs vom Selektor“ erfährst du hier):
Fertiges Script
Blockly
<xml xmlns="https://developers.google.com/blockly/xml">
<variables>
<variable id="eS!L3|7E$+[`m0M+Yl.%">zustand</variable>
<variable id="t}+]T(vUwfak5UbTg?Zi">device</variable>
<variable id="xUX]g|q4Lt:~sYj;);g}">i</variable>
</variables>
<block type="procedures_defcustomnoreturn" id="_22s5]gRFyvsP-_]Hlzl" x="13" y="-212">
<mutation statements="false">
<arg name="zustand" varid="eS!L3|7E$+[`m0M+Yl.%"></arg>
<arg name="device" varid="t}+]T(vUwfak5UbTg?Zi"></arg>
</mutation>
<field name="NAME">schalten</field>
<field name="SCRIPT">c2V0U3RhdGUoZGV2aWNlLCB6dXN0YW5kKTs=</field>
<comment pinned="false" h="80" w="160">Beschreibe diese Funktion …</comment>
</block>
<block type="controls_forEach" id="+[t/(d.07toVu:~W6^di" x="13" y="-187">
<field name="VAR" id="xUX]g|q4Lt:~sYj;);g}">i</field>
<value name="LIST">
<block type="selector" id="kQNm?FZg_7qq67_CJyc_">
<field name="TEXT">state[id=*](functions=schalten)</field>
</block>
</value>
<statement name="DO">
<block type="controls_if" id="eVl;(jQsbZ)AClAGcK6)">
<value name="IF0">
<block type="logic_compare" id="j3u?d:FVW{*Bb9DDMqUW">
<field name="OP">NEQ</field>
<value name="A">
<block type="get_value_var" id="Em7n7Zc(?ooT17~s)bel">
<field name="ATTR">val</field>
<value name="OID">
<shadow type="text" id="(#`!*29fT-s5VYVh6`su">
<field name="TEXT"></field>
</shadow>
<block type="variables_get" id="H%fpy8XeiSWP~tc0870G">
<field name="VAR" id="xUX]g|q4Lt:~sYj;);g}">i</field>
</block>
</value>
</block>
</value>
<value name="B">
<block type="logic_boolean" id="pewE2Gog3rH4#UPtK4n[">
<field name="BOOL">FALSE</field>
</block>
</value>
</block>
</value>
<statement name="DO0">
<block type="procedures_callcustomnoreturn" id="Muw4CQ_rkX?,pDC/q?WZ">
<mutation name="schalten">
<arg name="zustand"></arg>
<arg name="device"></arg>
</mutation>
<value name="ARG0">
<block type="logic_boolean" id=")CoqgG.G!g;b9ro~],3T">
<field name="BOOL">FALSE</field>
</block>
</value>
<value name="ARG1">
<block type="variables_get" id="s#J![BoyQ5z#KqE[pP-:">
<field name="VAR" id="xUX]g|q4Lt:~sYj;);g}">i</field>
</block>
</value>
</block>
</statement>
</block>
</statement>
</block>
</xml>
Javascript
let zustand, device, i;
function schalten(zustand, device) {
setState(device, zustand);
}
let i_list = Array.prototype.slice.apply($("state[id=*](functions=schalten)"));
for (var i_index in i_list) {
i = i_list[i_index];
if (getState(i).val != false) {
schalten(false, i);
}
}
Variante 2: Schalten mit „schreibe“-Block
Das Script ruft nun in einer Schleife alle Datenpunkte, deren die Funktion “schalten” zugewiesen ist, ab. Damit nach dem Start nur Geräte gesteuert werden, die nicht schon den Wert haben, den du schalten möchtest, brauchst du eine Logik-Abfrage. Ziehe dir also einen Logik-Block „falls mache“ ein die Schleife und stecke an den Eingang einen „=“-Vergleich, den du auf „≠“ stellst:
In das erste Feld vom Vergleich fügst du aus dem Bereich „Syetem“ einen „Wert von Objekt ID“ mit dem grünen Attribut-Feld:
Stecke dann in das Attribut-Feld das „i“ aus der Schleife (entweder über Rechtsklick auf die Schleife „Erzeuge ‚Lese i'“ oder über den Bereich „Variablen“) und als zweiten Vergleichswert fügst du den „wahr“-Block aus dem Bereich „Logik“ ein. Stelle diesen auf den Wert, auf den deine Geräte umgeschaltet werden sollen:
Damit das Script auch schaltet, benötigst du einen „schreibe“-Block aus dem Bereich „System. An den Eingang „Objekt ID“ steckst du das „i“ aus der Schleife und an den Eingang „Wert“ den gleichen Logik-Block von eben (mit dem gleichen Wert):
Ist der Wert des Geräts also ungleich <false> wird er durch das Script auf <false> gesetzt. Ist er schon auf <false>, passiert nichts. Möchtest du die Geräte einschalten, stellst du im Vergleich den Block auf „wahr“ und am Eingang „zustand“ auf „wahr“.
Wenn du das Script nun manuell, über einen Button in deiner Visualisierung oder ein anderes Script startest, werden alle Geräte der Aufzählung „schalten“ gesteuert.
Du kannst dieser Aufzählung jederzeit weitere Datenpunkte hinzufügen, ohne das Script anpassen zu müssen.
Fertiges Script
Blockly
<xml xmlns="https://developers.google.com/blockly/xml">
<variables>
<variable id="xUX]g|q4Lt:~sYj;);g}">i</variable>
</variables>
<block type="controls_forEach" id="+[t/(d.07toVu:~W6^di" x="38" y="-237">
<field name="VAR" id="xUX]g|q4Lt:~sYj;);g}">i</field>
<value name="LIST">
<block type="selector" id="kQNm?FZg_7qq67_CJyc_">
<field name="TEXT">state[id=*](functions=schalten)</field>
</block>
</value>
<statement name="DO">
<block type="controls_if" id="eVl;(jQsbZ)AClAGcK6)">
<value name="IF0">
<block type="logic_compare" id="j3u?d:FVW{*Bb9DDMqUW">
<field name="OP">NEQ</field>
<value name="A">
<block type="get_value_var" id="ZQ^uSAAgY|yUqwTdN2#!">
<field name="ATTR">val</field>
<value name="OID">
<shadow type="text" id="fn4xEEO@`^xSp_n@c8+,">
<field name="TEXT"></field>
</shadow>
<block type="variables_get" id="yRp-]{39)wm95E,(I[cs">
<field name="VAR" id="xUX]g|q4Lt:~sYj;);g}">i</field>
</block>
</value>
</block>
</value>
<value name="B">
<block type="logic_boolean" id="pewE2Gog3rH4#UPtK4n[">
<field name="BOOL">FALSE</field>
</block>
</value>
</block>
</value>
<statement name="DO0">
<block type="control_ex" id="/W)abx4/`Fu8;sLNvL(e">
<field name="TYPE">false</field>
<field name="CLEAR_RUNNING">FALSE</field>
<value name="OID">
<shadow type="field_oid" id="5*z=;fipNpO^r5G036kg">
<field name="oid">Object ID</field>
</shadow>
<block type="variables_get" id="H%fpy8XeiSWP~tc0870G">
<field name="VAR" id="xUX]g|q4Lt:~sYj;);g}">i</field>
</block>
</value>
<value name="VALUE">
<shadow type="logic_boolean" id="%T.g5e|]e(%z/X$6+.WD">
<field name="BOOL">TRUE</field>
</shadow>
<block type="logic_boolean" id=")CoqgG.G!g;b9ro~],3T">
<field name="BOOL">FALSE</field>
</block>
</value>
<value name="DELAY_MS">
<shadow type="math_number" id=",,)+03KIu:i!i+wD-*8L">
<field name="NUM">0</field>
</shadow>
</value>
</block>
</statement>
</block>
</statement>
</block>
</xml>
Javascript
let i;
let i_list = Array.prototype.slice.apply($("state[id=*](functions=schalten)"));
for (var i_index in i_list) {
i = i_list[i_index];
if (getState(i).val != false) {
setStateDelayed(i, false, false, parseInt(0, 10), false);
}
}
16 Kommentare
KommentierenHallo,
das sieht sehr stimmig für mich aus, scheitere aber leider schon beim Zuweisen der Funktion an einem Datenobjekt. Die neu erstellte Funktion aus den Aufzählungen taucht gar nicht als mögliche Option auf – ich sehe wahrscheinlich den Wald vor lauter Bäumen nicht.
Bitte mal die Seite neuladen und ggf. ab- und wieder anmelden. Viele Grüße
Super elegante Lösung auf die man ohne Programmierkenntnisse niemals kommen würde. Ich habe das Script selbst nachgebaut und bin sehr zufrieden. Vielen Dank für die Bereitstellung.
Viele Grüße
Enriko
Vielen Dank für die tolle Anleitung. Ich hatte im javascript Adapter noch kein Haken bei setObject und exec gesetzt und daher hat es bei mir nicht funktioniert und ich habe einen anderen Weg gefunden. Aber werde das Fleiß Übung nochmal wiederholen.
Viele Grüße
Marcus
Super Erklärung. Genau das hatte ich gesucht! Habe das blocky Beispiel umgesetzt. Idee war: Cron Täglich 18-20Uhr, Jede Std. Schaue ob Mülltonne (Typ) ansteht. und gebe mir einen Sound gefolgt von einer Ansage auf alle Amazon fire 7. Es kam mal das eine und mal das andere Tablett, mal nur der Sound und mal nur Text. Jedoch nicht zuverlässig. Mit viel fließ jedes einzeln über eine Subrutine geht…
Welche Ordnerstruktur empfiehlst Du in dem IOBroker anzulegen. Ich sehe immer wieder das Leute deren Sensoren in dem Adapter anlegen andere machen alle Sensoren in einem Ordner , was empfiehlt sich wirklich und was ist smart?
In der Regel wird die Struktur von den Adaptern vorgegeben bzw. eingerichtet. Eigene Datenpunkte kannst du unter 0.userdata anlegen. Viele Grüße
„Wenn du das Script nun manuell, über einen Button in deiner Visualisierung“
Wie bzw mit welchem Widget/Symbol realisiert du es bei Der VIS?
Ansonsten mega. Hab das direkt in mehrere Fällen übernommen.
Mit einem Bool-Button, welches einen Datenpunkt schaltet. Als Quelle nimmst du entweder das Script selbst (unter JavaScript.0.Common) oder einen eigenen Datenpunkt und überwachst ihn mit einem Trigger, der dann die eigentliche Funktion aus dem Artikel enthält. Viele Grüße
Ok. Genauso habe ich es auch gemacht. Würde aber gerne wirklich einen Schalter nehmen. Der also On geht sobald das erste Licht angeschaltet wird. Habe es mit einer immer andauernden Schmeife und Deinem Script hier versucht. Dann bricht aber mein System zusammen wegen fehlendem RAM. Idee?
Ohne dein Script zu kennen, ist es schwierig, das Problem zu erkennen. Ich würde allen Lichter eine Aufzählung zuweisen und auf diese Aufzählung (function) mit dem ID-Selektor (https://www.machs-smart.de/iobroker-blockly-id-selektor/) auf „ist kleiner als letzte“ triggern. Sobald ein Licht in der Aufzählung aus geht (also „falsch“ und somit kleiner als „true“), kannst du einen Datenpunkt schalten.
Ich denke, du hast aktuell einfach eine Dauerschleife programmiert, die dein System in die Knie zwingt.
Ja.. Das habe ich auch gerade gefunden. Die Seite ist echt mega. Großes Lob.
Bau gerade um.
Funktioniert das auch mit Rolladen mit % Angabe?
Habe ich nicht getestet, sollte aber funktionieren — du kannst jeden Wert übergeben. Viele Grüße
Okay, danke für die Antwort.Dann werde ich das mal versuchen. Mit welchem Wert muss ich dann den Zustand beschreiben? True oder false stimmt ja dann nicht.
Noch eine Frage, wo finde ich dann den Datenpunkt den ich in ein Widget einfügen kann?
Bin zwar kein blutiger Anfänger mehr, aber so tief bin ich in die Materie noch nicht eingetaucht. Ich lerne gerne weiter.
Hallo,
wie immer super einfach erklärt! Vielen Dank.
Ich habe das Script so nachgebaut und ergänzt und erhalte jetzt wenn ich das script aktiviere mehrere Warnungen:
09:58:47.965 warn javascript.0 (15701) at Object. (script.js.common.Allgemein.AbwesenheitSchalten:23:13)
09:58:47.971 warn javascript.0 (15701) at schalten (script.js.common.Allgemein.AbwesenheitSchalten:5:5)
Muss ich, wenn ich zwei Funktionen in einem Blockly anspreche, eine andere Variable nhemen als i?
Also: Funktion GehenHeizung = i // Funktion GehenLicht = k ?
Liebe Grüße