@apollon77 hat einen Artikel verfasst (c't 20-2021, Seiten 162 - 165).
NEWS
Best posts made by paul53
-
c't 20-2021
-
[Vorlage] Alias per Skript erzeugen
Im folgenden Skript wird ein Alias zu einem Datenpunkt mit dessen common-Eigenschaften erstellt. Bei gewollten Abweichungen von common-Eigenschaften des Alias zum Original muss man die zugehörigen // (Kommentar) entfernen und den gewünschten Wert zuweisen.
// Original-Datenpunkt const idOrigin = 'mqtt.0.switch.status'; // Optional: Status-Datenpunkt, wenn Kommando und Status getrennt. // Bei Nicht-Verwendung Leerstring '' zuweisen const idRead = ''; // Alias-Datenpunkt const idAlias = 'Pool.Pumpe.Schalter'; var typeAlias, read, write, nameAlias, role, desc, min, max, unit, states, custom, raum, gewerk; // Folgende kommentieren, wenn keine Änderung der Eigenschaft erforderlich nameAlias = 'Poolpumpe Ein'; desc = 'per Script erstellt'; // typeAlias = 'boolean'; // oder 'number' // read = "val < 0 ? -val : 0"; // Erkennung "Aus" --> false erfolgt automatisch // write = "val ? String(1) : String(0)"; // role = 'value'; // min = 0; // nur Zahlen // max = 100; // nur Zahlen // unit = '%'; // nur für Zahlen // states = {0: 'Aus', 1: 'Auto', 2: 'Ein'}; // Zahlen (Multistate) oder Logikwert (z.B. Aus/Ein) custom = {}; // verhindert doppelte Ausführung von history, ... // raum = 'EG_Flur'; // Groß-/Kleinschreibung in der ID beachten ! // gewerk = 'Licht'; // Groß-/Kleinschreibung in der ID beachten ! // Ab hier nichts ändern !! function createAlias(idDst, idSrc, idRd) { if(existsState(idDst)) log(idDst + ' schon vorhanden !', 'warn'); else { var obj = {}; obj.type = 'state'; obj.common = getObject(idSrc).common; obj.common.alias = {}; if(idRd) { obj.common.alias.id = {}; obj.common.alias.id.read = idRd; obj.common.alias.id.write = idSrc; obj.common.read = true; } else obj.common.alias.id = idSrc; if(typeAlias) obj.common.type = typeAlias; if(obj.common.read !== false && read) obj.common.alias.read = read; if(obj.common.write !== false && write) obj.common.alias.write = write; if(nameAlias) obj.common.name = nameAlias; if(role) obj.common.role = role; if(desc) obj.common.desc = desc; if(obj.common.type == 'number') { if(min !== undefined) obj.common.min = min; if(max !== undefined) obj.common.max = max; if(unit) obj.common.unit = unit; } else { if(obj.common.min !== undefined) delete obj.common.min; if(obj.common.max !== undefined) delete obj.common.max; if(obj.common.unit) delete obj.common.unit; } if(states) obj.common.states = states; if(custom && obj.common.custom) obj.common.custom = custom; obj.native = {}; setObject(idDst, obj, function() { if(idRd) setState(idRd, getState(idRd).val, true); else setState(idSrc, getState(idSrc).val, true); }); if(raum && existsObject('enum.rooms.' + raum)) { let obj = getObject('enum.rooms.' + raum) obj.common.members.push(idDst); setObject('enum.rooms.' + raum, obj); } if(gewerk && existsObject('enum.functions.' + gewerk)) { let obj = getObject('enum.functions.' + gewerk) obj.common.members.push(idDst); setObject('enum.functions.' + gewerk, obj); } } } createAlias('alias.0.' + idAlias, idOrigin, idRead);
Beispiele für Konvertierung (write);
write = "val ? 1 : 0"; // boolean --> binary write = "val ? 'On' : 'Off'"; // boolean --> string write = "val.toString()"; // number --> string dezimal write = "val.toFixed(2)"; // number --> string mit 2 Nachkommastellen
EDIT(20.12.2019): obj.native ergänzt.
EDIT(16.01.2020): Abfrage (Zeile 20) geändert
EDIT(06.02.2020): obj.common.custom ergänztEDIT(17.02.2020): Da man Raum und Gewerk in die Struktur der Alias-ID einbringen kann, sind enums für Raum und Gewerk oftmals nicht erforderlich. Für diejenigen, die den erzeugten Alias-Datenpunkt zu enum.rooms und/oder enum.functions hinzufügen wollen, wurde das Skript erweitert.
EDIT(21.04.2020): Erweiterung für getrennte Kommando- und Status-Datenpunkte ab js-controller 3.x.
EDIT(05.12.2020): Wenn Alias-Typ keine Zahl ist, werden min, max und unit gelöscht, falls vorhanden
EDIT(16.02.2021): Zeile 23 geändert von leerem Array in leeres Objekt
-
[Vorlage] Skript: Erstellen von User-Datenpunkten
// Datenpunkt unter 0_userdata.0 erstellen const idUser = 'EG_Kueche.Rollo.Schaltzeit2'; const commonUser = { // nicht benötigte Attribute auskommentieren type: 'number', read: true, write: true, name: 'Rollo Küche Schaltzeit 2', desc: 'Laufzeit Rollo', def: 60, min: 0, // nur bei Zahlen max: 200, // nur bei Zahlen unit: 's', // nur bei Zahlen // states: { // nur bei Zahlen, Logikwerten // 0: 'Aus', // 1: 'Auto', // 2: 'Ein' // }, role: 'level.timer' }; function createDp(id, common) { if(existsState(id)) log('Datenpunkt ' + id + ' existiert bereits !', 'warn'); else { var obj = {}; obj.type = 'state'; obj.common = common; obj.native = {}; setObject(id, obj, function (err) { if (err) log('Cannot write object: ' + err) else { var init = null; if(common.def === undefined) { if(common.type === 'number') init = 0; if(common.type === 'boolean') init = false; if(common.type === 'string') init = ''; } else init = common.def; setState(id, init, true); } }); } } createDp('0_userdata.0.' + idUser, commonUser);
EDIT: Abfrage geändert, ob Datenpunkt schon vorhanden ist.
-
[Vorlage] Wechselseitige Aktualisierung und Bedienung von Datenpunkten
In Fällen, in denen der Wert von einem Gerät auf ein anderes Gerät (und umgekehrt) übertragen werden soll, muss man ein endloses Hin- und Her vermeiden. Damit ein Triggern auf das setState() verhindert wird, kann im Trigger die Quelle (from) ausgewertet werden.
// Wechselseitige Aktualisierung von Datenpunkten const id1 = '...'; // Datenpunkt-ID vom Gerät 1 const id2 = '...'; // Datenpunkt-ID vom Gerät 2 const js = 'system.adapter.javascript.' + instance; on({id: id1, change: 'ne', fromNe: js}, function(dp) { setState(id2, dp.state.val); }); on({id: id2, change: 'ne', fromNe: js}, function(dp) { setState(id1, dp.state.val); });
In Blockly muss die aktuelle JS-Instanz fest eingetragen werden.
-
RE: [gelöst] Möglichkeit herausfinden welches Script dp schaltet
@bishop sagte: welches Script oder Adapter ein DP geschalten hat?
Debug-Trigger auf den DP:
-
RE: braucht Pi4 8GB ein Gehäuse mit Lüfter?
@tt-tom sagte: wenn man die Kiste mal festhalten muss
Topflappen?
-
RE: Variable einmal Pro Monat Protokollieren
@codierknecht sagte: Dann läuft der Kram 30x im Monat ohne etwas zu tun.
Das kann man im CRON eingrenzen. Es ist weniger aufwändig und funktioniert auch nach einem Skript-Neustart.
-
RE: [gelöst]Fenster Zählen mit Blockly
@MyzerAT Ganz schön kompliziert - Dein Ansatz.
Einfaches Prinzip (Beispiel mit 3 Fenstern): -
RE: Datenpunkte in Ordner verschieben?
@saeft_2003 sagte:
Wann würdest du zu einem Skript kommen?
Jetzt.
// Kopieren von Datenpunkten nach 0_userdata.0 // Pfade anpassen ! const pathSrc = 'meineDP.0.'; // abschließenden Punkt angeben const pathDst = '0_userdata.0.'; const idsSrc = $(pathSrc + '*'); idsSrc.each(function(id, i) { // Schleife über alle Datenpunkte im Pfad let idDst = pathDst + id.substring(pathSrc.length); if(existsObject(idDst)) log('Datenpunkt ' + idDst + ' existiert bereits !', 'warn'); else { let obj = getObject(id); setObject(idDst, obj, function (err) { if (err) log('Cannot write object: ' + err) else { let init = null; if(existsState(id)) init = getState(id).val; else { let common = obj.common; if(common.def === undefined) { if(common.type === 'number') init = 0; if(common.type === 'boolean') init = false; if(common.type === 'string') init = ''; if(common.type === 'array') init = []; } else init = common.def; } setStateDelayed(idDst, init, true, 20 * i + 50); } }); } });
Falls ioBroker.linkeddevices verwendet wird: Vorher die Instanz deaktivieren !
Vor dem manuellen Löschen der alten Datenpunkte (Ordner) sollten alle Scripte angepasst werden, da man sonst viele Warnungen und Fehlermeldungen riskiert.
Latest posts made by paul53
-
RE: Steuerung Heizunterlage für kleines Gewächshäuschen
@erich-1 sagte: Das ist im Moment mein Script
-
RE: JavaScript 7.9.0 - Neue Objekt- und HTTP-Bausteine
@codierknecht sagte: gleich die Variable anlegen?
@haus-automatisierung
Möglichst so, dass sie optisch als lokale Variablen erkennbar sind - so wie die Trigger-Variablen - und nicht unter den globalen Variablen auftauchen. Und bitte nicht übersetzen. -
RE: JavaScript 7.9.0 - Neue Objekt- und HTTP-Bausteine
@diginix sagt: viele request Blöcke bei denen die Weiterverarbeitung über result läuft.
Das ist auch bei Version 7.9.0 so.
Die Erweiterung umresult
soll bei den neuen HTTP-Blöcken erfolgen. -
RE: ioBroker startet unregelmäßig neu
@david-g sagte: der Kiste geht der RAM aus.
1,5 GB verfügbarer RAM sollten auch für Backup ausreichen.
@cyqx13 sagte in ioBroker startet unregelmäßig neu:
Active iob-Instances: 35
Gibt es "scheduled" Instanzen, die zur gleichen Zeit starten können?
-
RE: Datenpunktabfrage nach Neustart
@arniworx sagte: festzustellen ob der ioBroker nach einem neustart wieder komplett hochgefahren ist? (bzw. alle adapter geladen und bereit sind).
Per Javascript:
const instances = 6; // alle aktiven Instanzen im Mode "daemon" const ids = $('system.adapter.*.alive'); var alive = 0; ids.each(function(id) { // alle bereits laufenden Instanzen if(existsState(id) && getState(id).val) alive++; }); ids.on(function(dp) { // alle nach Javascript startenden Instanzen if(dp.state.val) alive++; if(alive == instances) log('Alle Instanzen laufen'); });
Anstelle der Log-Ausgabe kann auch ein Datenpunkt geschrieben werden.
-
RE: Licht über Anwesenheit und Helligkeit schalten [gelöst]
@duffy sagte: nachgebaut:
Dieser Vergleich ist falsch herum:
Richtig: <= 20
@duffy sagte in Licht über Anwesenheit und Helligkeit schalten [gelöst]:
Ist die Spanne zwischen 11 und 20 möglicherweise zu gering
Das kann natürlich sein. Man kann es mittels Log-Ausgaben des Wertes prüfen.
-
RE: Steuerung Heizunterlage für kleines Gewächshäuschen
@erich-1 sagte: Jetztr verstehe ich deinen Hinweis doch nicht.
In deinem Skript wird bei allen Temperaturwerten außerhalb der Hysterese gesendet - nicht nur dann, wenn der jeweilige Grenzwert erstmalig unter- bzw. überschritten wird.
-
RE: JavaScript funktioniert nicht wie gewünscht! Bitte Hilfe.
@skorpil
Die eigentliche Meldung erfolgt 3 s vorher in der Zeile darüber.
Man kann auch "Entprellen", wenn man die Verzögerung vermeiden will (nur Programm):var timer = null; on(IDAusloeser, function (dp) { // if (IDAnwesend) { if(!timer) { timer = setTimeout(function() { timer = null; }, 5000); sendTo("pushover.0", { message: dp.state.val ? 'OFFEN!' : 'ZU!', // mandatory - your text message title: titel, // optional - your message's title, otherwise your app's name is used sound: 'siren', // optional - the name of one of the sounds supported by device clients to override the user's default sound choice // pushover, bike, bugle, cashregister, classical, cosmic, falling, // gamelan, incoming, intermission, magic, mechanical, pianobar, siren, // spacealarm, tugboat, alien, climb, persistent, echo, updown, none priority: 1, // optional // -1 to always send as a quiet notification, // 1 to display as high-priority and bypass the user's quiet hours, or // 2 to also require confirmation from the user }); } // }; });
Diese Version reagiert auf das erste "geschlossen" und sperrt dann für 5 s weitere Trigger.
-
RE: JavaScript funktioniert nicht wie gewünscht! Bitte Hilfe.
@skorpil sagte: folgende Scripte
Das macht man in einem Skript mit nur einem Trigger.
@skorpil sagte in JavaScript funktioniert nicht wie gewünscht! Bitte Hilfe.:
nur auf die allerletzte Meldung reagiert wird
Das kann man mit einem Timeout (z.B. 5 s) erreichen.
// ################################## // Deklarationen // ################################## const IDAusloeser ='hm-rpc.0.JEQ0498248.1.STATE'/*Neigungssensor Gar ALT (HM-Sec-TiS JEQ0498248:1) STATE*/; // const IDAnwesend = 'hm-rega.0.39533'/*Anwesend*/; const titel = "Garagentor ALT:"; // ################################## // Programm // ################################## var timer = null; on(IDAusloeser, function (dp) { // if (IDAnwesend) { clearTimeout(timer); timer = setTimeout(function() { sendTo("pushover.0", { message: dp.state.val ? 'OFFEN!' : 'ZU!', // mandatory - your text message title: titel, // optional - your message's title, otherwise your app's name is used sound: 'siren', // optional - the name of one of the sounds supported by device clients to override the user's default sound choice // pushover, bike, bugle, cashregister, classical, cosmic, falling, // gamelan, incoming, intermission, magic, mechanical, pianobar, siren, // spacealarm, tugboat, alien, climb, persistent, echo, updown, none priority: 1, // optional // -1 to always send as a quiet notification, // 1 to display as high-priority and bypass the user's quiet hours, or // 2 to also require confirmation from the user }); }, 5000); // }; });
-
RE: Steuerung Heizunterlage für kleines Gewächshäuschen
@erich-1 sagte: was du hier meinst. Kannst du mir das als absoluter Anfänger mal etwa erklären?
Bei jeder Ausführung von "steuere" wird gesendet. Wenn also "ein" gesendet wird obwohl der Aktor schon eingeschaltet ist, ist das unnötig und kann zur Störung der Kommunikation anderer Teilnehmer führen.