Grundlegende Ausdrücke
Literale
1234
- ganzzahlige Zahl.
1234.567
- Fliesskommazahl.
true
oderyes
- boolesches true, Zahlenwert ist 1.
false
oderno
- boolsch falsch, Zahlenwert ist 0.
null
oderundefined
- kein Wert. Beachten Sie, dass p44script über annotierte Nullwerte verfügt, was bedeutet, dass in vielen Fällen null/undefined auch einen kurzen Text trägt, der seine Herkunft beschreibt. Dies ist bei der Fehlersuche nützlich, da es hilft zu verstehen, warum ein Wert null/undefiniert ist.
12:30
- Zeitangabe in Stunden und Minuten. Numerischer Wert ist die Anzahl der Sekunden seit Mitternacht.
13:30:22
- Zeitangabe in Stunden, Minuten und Sekunden. Numerischer Wert ist die Anzahl der Sekunden seit Mitternacht.
2.9.
- Datumsangabe im Format Tag.Monat. (Punkt am Ende ist wichtig). Beispiel bedeutet 2. September. Der numerische Wert ist die Anzahl der Tage seit Beginn des aktuellen Jahres.
2.Sep
- Datumsangabe im Format Tag.Monatsname (kein Punkt am Ende!). Monatsnamen sind jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec. Numerischer Wert ist die Anzahl der Tage ab Beginn des aktuellen Jahres.
wed
- Wochentagsangabe. Wochentagsnamen sind sun, mon, tue, wed, thu, fri, sat. Numerischer Wert ist 0 für sonntags, 1..6 für montags..samstags.
'Text'
- String-Literal, verwenden Sie zwei einfache Anführungszeichen hintereinander, um das einfache Anführungszeichen einzuschliessen.
"Text"
- String-Literal mit C-ähnlichen Escapes (
\n
,\r
,\t
,\xHH
und\\
können verwendet werden). { 'field1':42, 'field2':'hello' }
- JSON-Objekt.
[ 42, 43, 'hallo' ]
- JSON-Array.
Hinweis
JSON-Literale sind wirklich literal, Sie können keine Variablen anstelle von Feldnamen verwenden, wie es in JavaScript möglich ist.
Benannte Werte
In Ausdrücken können benannte Werte (z. B. Sensorwerte in Auswertebedingungen, oder Variablen in Skripten) verwendet werden. Einfache Werte bestehen nur aus einem Namen, aber es gibt auch strukturierte Werte, auf die mit der Punkt- und tiefgestellten Notation zugegriffen werden kann, zum Beispiel JSON-Objekte.
Sensor1
- der Wert eines Sensors mit dem Namen sensor1.
sensor1.age
- die Zeit in Sekunden, seit der Sensor namens sensor1 von der Hardware aktualisiert wurde.
sensor1.valid
- true, wenn der Sensor mit dem Namen sensor1 einen aktuellen gültigen Wert hat (ein Sensor, der in Timeout gegangen ist oder seit dem letzten Neustart nie Daten von der Hardware erhalten hat, hat keinen Wert).
sensor1.oplevel
- ein Wert zwischen 0 und 100, der den "Betriebszustand" des Sensors angibt. 0 bedeutet ausser Betrieb, 100 bedeutet optimale Bedingungen, Werte dazwischen zeigen nicht-optimale Bedingungen an, wie z. B. schlechter Funkempfang oder niedriger Batteriestand. Wenn keine Informationen verfügbar sind, wird ein Nullwert zurückgegeben.
jsonobj.fieldA
- das Unterfeld namens fieldA in einem JSON-Objekt jsonobj. Mit jsonobj={ 'fieldA':42, 'fieldB':'hello' } wäre das Ergebnis also 42.
jsonarr[1]
- das Array-Element am numerischen Index 1 in einem JSON-Array jsonarr. Wenn also jsonvalue=[ 42, 'world' ], wäre das Ergebnis 'world'.
jsonobj['fieldB']
- das Teilfeld namens fieldA in einem JSON-Objekt jsonobj. Mit jsonobj={ 'fieldA':42, 'fieldB':'hello' } wäre das Ergebnis also 'hello'.
jsonobj[0]
- der Name des ersten (index==0) Teilfeldes in einem JSON-Objekt jsonobj. Mit jsonobj={ 'fieldA':42, 'fieldB':'hello' } wäre das Ergebnis also 'fieldA'. Auf diese Weise können json-Objekte untersucht werden, die Felder mit unbekannten Namen enthalten (z. B. von APIs zurückgegeben). Die Funktion elements() kann verwendet werden, um die Anzahl der Felder in einem Objekt zu ermitteln.
Operatoren
Operator | Vorrang | Beschreibung |
---|---|---|
! |
6 (am höchsten) | logisch NICHT |
* |
5 | Multiplikation |
/ |
5 | Division |
% |
5 | modulo (Rest) |
+ |
4 | numerische Addition, String-Verkettung, wenn der linke Ausdruck vom Typ String ist, JSON-Array-Verkettung, wenn beide Ausdrücke Arrays sind, JSON-Objekt-Merge, wenn beide Ausdrücke JSON-Objekte sind. |
- |
4 | Subtraktion |
== |
3 | Test auf Gleichheit |
= |
3 | Test auf Gleichheit (je nach Kontext kann = auch eine Zuweisung sein, verwenden Sie ==, um Mehrdeutigkeit zu vermeiden) |
!= oder <> |
3 | Test auf nicht gleich |
< |
3 | Test auf kleiner als |
> |
3 | Test auf grösser als |
<= |
3 | Test auf kleiner als oder gleich |
>= |
3 | Test auf grösser oder gleich |
& oder && |
2 | logisches UND |
| oder || |
1 | logisches ODER |
:= |
0 (niedrigste) | Zuweisung |
= |
0 (niedrigste) | Zuweisung (je nach Kontext kann = auch ein Test für gleich sein, verwenden Sie :=, um Mehrdeutigkeit zu vermeiden) |
Allgemeine Funktionen
abs(a)
- Absolutwert von a.
annotation(irgendwas)
- Gibt die annotation (die Anmerkung) von irgendwas als String zurück. Die Anmerkung beschreibt den Typ oder die Herkunft eines Wert u.U. genauer, z.B. bei null-Werten kann die Anmerkung oft Auskunft geben, weshalb ein Wert null ist.
binary(hexstring [, spacesallowed])
- Konvertiert hexstring in einen String (der nicht-druckbare binäre Zeichen enthalten kann, einschliesslich NUL), indem 2 Hex-Ziffern in hexstring für jedes Ausgabebyte erwartet werden. Hex-Bytes können durch Doppelpunkte oder Bindestriche getrennt werden. Wenn spacesallowed gesetzt ist, können Bytes auch durch Leerzeichen getrennt werden, und Bytes werden auch dann als vollständig angesehen, wenn sie nur aus einer Hex-Ziffer bestehen, wenn vor und nach ihnen ein Doppelpunkt, ein Bindestrich oder ein Leerzeichen steht. Siehe auch hex().
bit(bitno, value)
bit(frombitno, tobitno, value, [, signed])
- Die erste Form extrahiert ein einzelnes Bit, wie durch bitno angegeben (das niedrigstwertige Bit hat bitno==0), aus value.
- Die zweite Form extrahiert einen Bereich von Bits (zwischen frombitno und tobitno) als eine ganze Zahl. Wenn signed gesetzt ist, wird der Bitbereich als vorzeichenbehaftete Ganzzahl (2er-Komplement) behandelt.
boolean(irgendwas)
- Interpretiert irgendwas als boolean. Wenn irgenwas ein Leerstring, undefined, null oder die Zahl 0 ist, ist das Resultat false bzw. 0, andernfalls true bzw. 1.
chr(byte)
- gibt einen String zurück, die aus einem einzelnen byte (Wert zwischen 0 und 255) besteht. Zu beachten ist, dass nicht alle Werte für byte einen gültigen (UTF8) String erzeugen. Diese Funktion ist jedoch in erster Linie dafür gedacht, binäre Byte-Strings zu erzeugen - p44script-Strings können 0 Bytes enthalten. Siehe auch ord().
cyclic(x, a, b)
- gibt x mit Wrap-Around in den Bereich a..b zurück (ohne b, weil es gleichbedeutend mit a ist).
dawn([epochtime])
- gibt die ungefähre Zeit der Morgendämmerung am aktuellen, oder am von epochtime spezifizierten Tag zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
day([epochtime])
- gibt den aktuellen, oder den von epochtime spezifizierten Tag des Monats zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
describe(irgendwas)
- Beschreibt irgendwas als String, der Information zu Datentyp, internem Namen, Anmerkung etc. enthält. Diese Funktion ist v.a. nützlich zu Debug-Zwecken.
dusk([epochtime])
- gibt die ungefähre Zeit der Abenddämmerung am aktuellen, oder am von epochtime spezifizierten Tag zurück, aber kann keine zeitgesteuerten Aktionen auslösen.
elements(array_or_object)
- Wenn array_or_object ein Array ist, gibt elements die Anzahl der Array-Elemente zurück. Wenn array_or_object ein JSON-Objekt ist, wird die Anzahl Felder zurückgegeben (deren Namen mit array_or_object[numeric_index] ermittelt werden können). Wenn array_or_object vom Typ her keine per Index zugreifbare Elemente haben kann, wird undefined zurückgegeben (aber die Zahl 0, wenn das Objekt oder der Array im Moment leer ist).
epochtime()
epochtime(dayseconds [, yeardays, [, year]])
epochtime(hour, minute, second [, day [, month [, year]]])
- Ohne Argumente gibt die Funktion die Unix-Epochenzeit zurück (Sekunden seit dem 1.1.1970 Mitternacht UTC), kann aber nicht direkt zeitgesteuerte Aktionen auslösen.
- Mit nur dem Argument dayseconds gibt die Funktion die Epochenzeit vom heutigen Tag zurück mit der Tageszeit entsprechend dayseconds.
- Mit yeardays gibt die Funktion die Epochenzeit zurück, die durch dayseconds/yeardays im aktuellen Jahr repräsentiert wird.
- Mit year gibt die Funktion die Epochenzeit für den Zeitpunkt entsprechend dayseconds/yeardays/year. Achtung: Die Sommerzeit wird in dieser Variante nicht berücksichtigt.
- Mit hour, minute, second und optional day, month und year gibt die Funktion die Epochenzeit für den entsprechenden Zeitpunkt zurück, wobei das aktuelle Datum/Monat/Jahr verwendet wird, falls nicht angegeben.
epochdays()
- gibt die Unix-Epochenzeit (Tage seit dem 1.1.1970) zurück, mit der Tageszeit als Bruchteil, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
error(irgendwas)
- erzeugt einen Fehlerwert mit irgendwas als Fehlermeldung.
errorcode(errvalue)
- gibt den numerischen Fehlercode von errvalue zurück.
errordomain(errvalue)
- gibt den Fehlerdomänen-String von errvalue zurück.
errormessage(errvalue)
- liefert den String der Fehlermeldung von errvalue.
eval(string [, arg...])
- string als p44script-Skript auswerten. Die optionalen arg stehen als Variablen namens arg1, arg2 ... argN innerhalb der Auswertung zur Verfügung. Die Auswertung findet im maincontext des aktuellen Skripts statt und kann auf dessen Variablen, einschliesslich globaler Variablen, zugreifen. Die in der Auswertung definierten Variablen sind jedoch lokal für die Auswertung.
find(haystack, needle [, from [, caseinsensitive]])
- gibt die Position von needle in haystack zurück oder null, wenn sie nicht gefunden wurde. Wenn from angegeben wird, beginnt die Suche an dieser Position in haystack. Wenn caseinsensitive true ist, wird die Suche unabhängig von der Gross-/Kleinschreibung durchgeführt (einfache Gross-/Kleinschreibungsunterscheidung in ASCII).
flipbit(bitno, value)
flipbit(vonbitno, bisbitno, value)
- Die erste Form gibt value zurück, der durch das Umkehren (Invertieren) eines einzelnen Bits, wie durch bitno angegeben, verändert wurde (das niedrigstwertige Bit hat bitno==0).
- Die zweite Form gibt value zurück, der durch das Umkehren eines Bereichs von Bits (zwischen frombitno und tobitno) verändert wurde.
format(formatstring, value [, value...])
- formatiert value_s als String gemäss printf-ähnlichem _formatstring (beachten Sie aber, dass formatstring nur eine Teilmenge von printf unterstützt. Insbesondere sind keine 'h', 'l' oder 'll' Längenangaben erlaubt (und auch nicht nötig). Erkannte Formatierungszeichen sind d,u,x,X für Ganzzahlformatierung, e,E,g,G,f für Fliesskommazahlen und s für Zeichenketten.
formattime()
formattime(time)
formattime(formatstring)
formattime(time, formatstring)
- Gibt die formatierte Zeit zurück. Ohne Argumente gibt formattime das aktuelle Datum und die Uhrzeit zurück, z. B. 2020-08-19 11:04:42.
- Mit nur einem numerischen Argument zeit wird die angegebene Zeit formatiert. Wenn time eine Tageszeit (zwischen 0 und 24*60*60 Sekunden) ist, wird nur die Zeit wie 11:04:42 angezeigt, andernfalls wird time als absoluter Unix-Epochen-Zeitstempel (s. epochtime()-Funktion) behandelt und standardmässig als Datum+Zeit formatiert.
- Mit formatstring kann ein bestimmtes Datums- und Zeitformat eingestellt werden, wobei die Formatierungsmöglichkeiten der strftime() C-Standardfunktion genutzt werden.
frac(a)
- Nachkommaanteil von a (mit gleichem Vorzeichen wie a).
hex(string [, separator])
- gibt die in string enthaltenen Bytes (die eine binäre Zeichenkette mit nicht druckbaren und NUL-Zeichen sein können) als hexadezimale Zeichenkette zurück. Wenn separator angegeben ist, wird es als Trennzeichen zwischen zweistelligen Hex-Bytes verwendet (nur das erste Zeichen von separator wird verwendet). Siehe auch binary().
hour([epochtime])
- liefert die aktuelle, oder die von epochtime spezifizierte Stunde, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
if(c, a, b)
- gibt a zurück, wenn c als wahr ausgewertet wird, ansonsten b.
ifvalid(a, b)
- gibt a zurück, wenn a ein gültiger Wert ist (nicht null, Fehler, oder non-data), sonst b.
ifok(a, b)
- gibt a zurück, wenn Zugriff auf a keinen Fehler erzeugt, sonst b.
int(a)
- ganzzahliger Wert von a
isvalid(a)
- liefert true, wenn a ein gültiger Wert ist (nicht null, Fehler, oder non-data), sonst false.
isok(a)
- liefert true, wenn Zugriff auf a keinen Fehler erzeugt, false otherwise.
json(irgendetwas [, kommentare_erlaubt])
- versucht, irgendetwas als JSON zu interpretieren. Wenn irgendetwas ein String ist, wird der String als JSON-Text behandelt und geparsed (und nicht in einen einzelnen JSON-String umgewandelt). Wenn beim Parsen ein JSON-Syntaxfehler auftritt, gibt json einen entsprechenden Fehler zurück. Wenn kommentare_erlaubt auf true gesetzt ist, sind
/* ... */
-Kommentare im JSON-Text erlaubt (was nicht der JSON-Spezifikation entspricht, aber praktisch ist) Diese Funktion ist z.B. nützlich, um einen von einer API zurückgegebenen JSON-String als Struktur zugänglich zu machen. jsonresource(jsonfile)
- Lädt JSON aus jsonfile. Ohne absolute Pfadangabe oder ein spezielles Präfix wird jsonfile als relativ zum Ressourcenverzeichnis der Anwendung verstanden (das ist normalerweise /usr/share/Anwendungsname). In JSON-Ressourcendateien sind
/* ... */
Kommentare erlaubt (obwohl das nicht der JSON-Spezifikation entspricht). -
Die folgenden speziellen Präfixe können verwendet werden:
_/tempfile
: bedeutet, dass tempfile relativ zum (flüchtigen) temporären Verzeichnis (normalerweise /tmp) verwendet werden soll.+/resource
: ist gleichbedeutend mit resource allein, also relativ zum Ressourcenverzeichnis.=/datafile
: bedeutet, dass datafile relativ zum (permanenten) Anwendungsdatenverzeichnis (normalerweise /flash) verwendet werden soll.
Hinweis
Um andere Pfade oder Präfixe als
_/
für Dateiname zu verwenden, muss der userlevel >=1 sein (der Standard-userlevel für Produktionsgeräte ist 0). lastarg(a1, a2, ... , aN)
- gibt aN zurück. Dies kann nützlich sein, um Seiteneffekte anderer Argumente zuerst auszuführen, bevor aN zurückgegeben wird.
limited(x, a, b)
- gibt min(max(x, a), b) zurück, d.h. x begrenzt auf Werte zwischen und einschliesslich a und b.
lowercase(s)
- Gibt die (einfache ASCII-)Version des Strings s in Kleinbuchstaben zurück.
macaddress()
- gibt die MAC-Adresse der drahtgebundenen Netzwerkschnittstelle als String mit 12 Hex-Ziffern zurück (keine Trennzeichen zwischen den Bytes).
maprange(x, a1, b1, a2, b2)
- bildet x im Wertebereich a1..b1 linear auf den Wertebereich a2..b2 ab. Für alle Werte x vor a1 ist das Resulat a2, für alle Werte von x nach b1 ist das Resultat b2.
max(a, b)
- gibt den grösseren Wert von a und b zurück.
min(a, b)
- gibt den kleineren Wert von a und b zurück.
minute([epochtime])
- gibt die aktuelle Minute zurück, oder die von epochtime spezifizierte, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
month([epochtime])
- liefert den aktuellen, oder den von epochtime spezifizierten Monat (1..12), aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
nextversion()
- gibt die nächste installierbare Firmware-Version zurück, wenn diese bekannt ist (von der automatischen Überprüfung oder einer kürzlich durchgeführten manuellen Überprüfung für ein Firmware-Upgrade). Ansonsten wird ein leerer String zurückgegeben.
number(irgendwas)
- Versucht, irgendwas in eine Zahl zu konvertieren, gibt 0 zurück, wenn die Konvertierung fehlschlägt.
ord(string)
- Gibt das Byte (Wert zwischen 0 und 255) zurück, das am Anfang von string gespeichert ist. Zu beachten ist, dass dieses erste Byte nicht unbedingt ein komplettes UTF8-Zeichen ist, sondern evtl. nur ein Teil davon. Diese Funktion ist jedoch in erster Linie für die Dekodierung binärer Byte-Strings gedacht - p44script-Strings können 0 Bytes enthalten. Siehe auch chr().
productversion()
- gibt die Firmware-Version des Produkts zurück.
random(a, b)
random(a, b, step)
- gibt einen (Fliesskomma-)Pseudo-Zufallswert von a bis einschliesslich b zurück. Wenn step angegeben ist, ist das Resultat gleich a plus ein ganzzahliges Vielfaches von step. D.h. um eine Ganzzahl zwischen 1 und 7 zu erhalten, kann
random(1,7,1)
verwendet werden. Dabei wird die Zufälligkeit gleichmässig auf die möglichen Resultate verteilt (anders als etwaint(random(1,7))
, das für das Resultat 7 eine verschwindend kleine Wahrscheinlichkeit hätte gegenüber 1..6). step muss nicht ganzzahlig sein; wenn z.B. Viertel gefragt sind, kannrandom(1,7,0.25)
verwendet werden. replace(haystack, needle, replacement [, occurrences]])
- Gibt haystack mit den Vorkommen von needle ersetzt durch replacement, maximal occurrences mal (alle Vorkommen werden ersetzt, wenn occurrences nicht angegeben oder auf 0 gesetzt ist).
round(a [, p])
- rundet a auf die optional angegebene Genauigkeit p (1=ganzzahlig=Standard, 0.5=Halbe, 100=Hunderter, etc...).
second([epochtime])
- gibt die aktuelle, oder die von epochtime spezifizierte Sekunde zurück, kann aber nicht direkt zeitgesteuerte Aktionen auslösen.
setbit(bitno, newbit, value)
setbit(frombitno, tobitno, newvalue, value)
- Die erste Form gibt value zurück, der durch das Setzen eines einzelnen Bits, wie durch bitno angegeben (das niedrigstwertige Bit hat bitno==0), auf den booleschen Wert von newbit geändert wurde.
- Die zweite Form gibt Wert zurück, der durch das Setzen eines Bereichs von Bits (zwischen frombitno und tobitno) auf den ganzzahligen Wert newvalue geändert wurde (wobei so viele niederwertige Bits von newvalue verwendet werden, wie nötig sind, um den Bereich zu füllen).
string(irgendwas)
- gibt eine String-Repräsentation von irgendetwas zurück. Gibt immer eine beschreibende Zeichenkette zurück, auch für null/undefinierte und Fehlerwerte.
strlen(string)
- gibt die Länge von string zurück.
strrep(string, count)
- Gibt eine Zeichenkette zurück, die aus count mal dem string besteht, verkettet.
substr(zeichenkette, von [, anzahl])
substr(string, from [, count])
- gibt Zeichenkette ab from in string zurück, begrenzt auf count Zeichen, falls angegeben. Wenn von negativ ist, ist die Startposition relativ zum Ende von zeichenkette.
sunrise([epochtime])
- Gibt die ungefähre Zeit des Sonnenaufgangs am aktuellen, oder am von epochtime spezifizierten Tag zurück, kann aber nicht direkt zeitgesteuerte Aktionen auslösen (#timedtriggers).
sunset([epochtime])
- gibt die ungefähre Zeit des Sonnenuntergangs am aktuellen, oder am von epochtime spezifizierten Tag zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
timeofday([epochtime])
- gibt die aktuelle Uhrzeit in Sekunden seit Mitternacht zurück, oder die Sekunden seit Mitternacht von epochtime, kann aber nicht direkt zeitgesteuerte Aktionen auslösen.
uppercase(s)
- Gibt die (einfache ASCII-)Version der Zeichenkette s in Grossbuchstaben zurück.
weekday([epochtime])
- liefert den aktuellen, oder den von epochtime spezifizierten Wochentag (0..6), aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
year([epochtime])
- gibt das aktuelle, oder das von epochtime spezifizierte Jahr zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
yearday([epochtime])
- gibt das aktuelle, oder das von epochtime spezifizierte Datum als Tageszahl im Jahr zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
Funktionen für zeitgesteuertes Triggern
Bitte beachten Sie, dass Sie die folgenden Funktionen verwenden müssen, um auf Zeit/Datum in Evaluator- oder Triggerbedingungen zu testen. Einfach etwas zu schreiben wie timeofday()==12:30
wird nicht funktionieren!
Allgemeine Funktionen, die Zeitwerte wie sunrise oder dusk zurückgeben, sind dazu gedacht, als Argumente für is_time oder after_time verwendet zu werden, oder um zusätzliche Prüfungen zu dem Zeitpunkt zu implementieren, wenn der Ausdruck durch eine der folgenden Funktionen ausgelöst wird.
is_time(time)
- gibt true zurück, wenn die Tageszeit time ist (mit 5 Sekunden Toleranz) und löst die Auswertung einmal pro Tag aus, wenn time erreicht wird.
is_weekday(weekday1 [, weekday2, ...])
- gibt true zurück, wenn heute einer der angegebenen weekdayN-Argumente ist und löst die Auswertung zu Beginn der angegebenen Tage aus.
after_time(time)
- gibt true zurück, wenn die Tageszeit time oder später ist und löst die Auswertung einmal pro Tag aus, wenn time erreicht ist.
between_dates(date1, date2)
- gibt true zurück, wenn das aktuelle Datum zwischen date1 und date2 (einschliesslich) liegt und löst die Auswertung am Anfang von date1 und am Ende von date2 aus.
initial()
- gibt true zurück, wenn dies ein initialer Lauf eines Trigger-Ausdrucks ist, d.h. nach dem Start oder nach Änderungen des Ausdrucks.
every(interval [, syncoffset])
- gibt einmal alle interval (angegeben in Sekunden) true zurück und löst die Auswertung aus.
- Hinweis: Bei der ersten Auswertung wird true zurückgegeben, oder, wenn syncoffset gesetzt ist, bei der nächsten ganzzahligen Anzahl von Intervallen, berechnet ab Tagesbeginn + syncoffset.
-
So wird every(0:30,0:22) einmal jede halbe Stunde ausgelöst, beginnend um 0:22, dann 0:52, 1:22, ...
Achtung - Vorsicht mit kurzen Intervallen
Seien Sie vorsichtig mit
every()
und kurzen Intervallen, da das sehr häufige Ausführen von Skripten die Leistung des Geräts beeinträchtigen kann! testlater(Sekunden, timedtest [, retrigger])
- gibt beim ersten Aufruf undefined/null zurück, plant aber eine erneute Auswertung nach gegebenen Sekunden und gibt dann den Wert von test zurück. Wenn retrigger true ist, dann wird die Neuauswertung im Intervall von Sekunden wiederholt (mit Vorsicht verwenden!).
Skripte
Vollständige p44Skripte (über einzelne Ausdrücke wie oben beschrieben hinaus) können verschiedene Dinge tun, indem sie die unten beschriebenen Funktionen verwenden. Die Skriptsprache hat eine Syntax, die ähnlich zu JavaScript oder C ist, aber bei weitem nicht identisch.
Kommentare
/* Kommentar */
- Kommentar im C-Stil.
// comment
- Kommentar im C++-Stil wird bis zum Ende der Zeile fortgesetzt.
Deklarationen
Der erste (optionale) Teil eines Skripts kann Deklarationen von globalen Variablen und globalen Funktionen sein. (Handler können auch im Deklarationsteil stehen, wenn ihre Auslösebedingung keine erst zur Laufzeit des Skripts erzeugte Objekte referenziert) Die erste Nicht-Deklarationsanweisung in einem Skript beendet den Deklarationsteil. Deklarationen können also nicht mit eigentlichen Skriptanweisungen gemischt werden. Eine Ausnahme bilden Handler (s. oben) und globale Variablendeklarationen; diese können sowohl im Deklarationsteil als auch gemischt mit normalen Skriptanweisungen vorkommen.
Globale Variablen
glob g
global g
- Deklaration der globalen Variable g.
- Bitte beachten: eine Deklaration einer globalen Variablen ohne Initialisierung ist auch ausserhalb des Deklarationsteils eines Skripts möglich (insbesondere in Skripten, die keine Deklarationen zulassen, wie z. B. P44-DSB-Auswerteraktionen oder Szenenskripte).
glob g = 78/9
- Deklaration und Initialisierung der globalen Variable g.
- Zu beachten ist, dass der Initialisierungsausdruck nur aus Konstanten oder anderen globalen Variablen bestehen kann, die bereits definiert sind.
- Lokale Variablen und kontextabhängige Funktionen und Objekte sind zur Deklarationszeit nicht verfügbar.
- Die Initialisierung von globalen Variablen sind nur im Deklarationsteil eines Skripts möglich (und nicht alle Skripte erlauben überhaupt Deklarationen, z. B. können P44-DSB-Evaluator-Aktionen oder Szenenskripte keine Deklarationen enthalten).
Sichtbarkeit globaler Variablen
Globale Variablen sind wirklich global, d.h. sie sind aus allen Scripten und Script-Teilen (etwa Auslösebedigungen, Szenenscripte, Trigger etc.) im ganzen System sichtbar. Es ist daher zu empfehlen, eindeutige Namen zu verwenden, die nicht zufällig anderswo auch vorkommen können. Generell sollten auch keine globalen Variablen verwendet werden, wenn Kontext-Variablen auch ausreichen.
Funktionen
function hallo() { log('Hallo Welt'); }
- Deklariert eine global verfügbare Funktion namens hello, die den Text 'Hallo Welt' protokolliert.
global function sum(a,b) { var c = a+b return c }
- Deklariert eine global verfügbare Funktion namens sum mit zwei Argumenten, die in einer lokalen Variable c (die nur während der Ausführung der Funktion für diese privat existiert) die Summe berechnet und zurückgibt. Das Präfix global ist optional, dient aber der Übersichtlichkeit, wenn auch lokale Funktionen verwendet werden (siehe unten).
Sichtbarkeit von Variablen in Funktionen
In Funktionen sind die mit var
lokal definierten Variablen, die Variablen des Kontexts, aus dem die Funktion aufgerufen wird (s. Lokale Variablen) und schliesslich die globalen Variablen sichtbar. Wenn eine in der Funktion definierte Variable denselben Namen hat wie eine des aufrufenden Kontexts oder eine globale Variable, dann wird in Ausdrücken und Zuweisungen die lokale Variable verwendet.
local function diff(a,b) { var c = a-b return c }
- Deklariert eine Funktion, die nur im aktuellen Kontext sichtbar/aufrufbar ist (z. B.: mainscript, device, trigger). Dies kann nützlich sein, um zu verhindern, dass eine Funktion, die nicht allgemein verwendet wird (z. B. eine Funktion, die für eine bestimmte Scripted Device-Implementierung spezifisch ist), global in anderen Kontexten sichtbar ist, in denen ihr Aufruf keinen Sinn macht oder sogar Schaden anrichten würde.
Lokale Funktionen sind im Deklarationsteil nicht erlaubt
Obwohl lokale Funktionen eine Art "Deklaration" sind, müssen sie im Hauptskriptteil definiert werden, nicht im Deklarationsteil. Umgekehrt können globale Funktionen nur im Deklarationsteil definiert werden.
(Event-)Handler
Ein handler ist ein spezielles Konstrukt, das p44Script bietet, um auf einfache Weise auf asynchrone Ereignisse aller Art, wie z.B. die Veränderung eines Sensorwertes, einen Tastendruck, ein Web-Zugriff, das Beenden eines threads, das Erreichen einer bestimmten Tageszeit oder Ablauf eines Intervals u.v.a.m zu reagieren.
Im Prinzip sind handler Deklarationen (ähnlich wie Funktionen werden sie nicht sofort ausgeführt, sondern nur für eine spätere Ausführung definiert). Deshalb werden sie in diesem Abschnitt beschrieben.
Allerdings gibt es Situationen, wo die Auslösebedingung eines handlers sich auf ein Objekt bezieht, das erst im Verlauf der Skriptausführung überhaupt erzeugt wird. In solchen Fällen kann der Handler erst danach angelegt werden, und kommt so in den Anweisungs-Teil zu stehen, wie in diesem Beispiel:
var s = udpsocket("127.0.0.1", 4242, true, true); on (s.message()) as msg { log('Nachricht auf UDP-Port 4242: '+string(msg)) }
-
Dies erstellt und öffnet zunächst einen Netzwerk-Socket, der auf UDP-Pakete an Port 4242 lauscht, und definiert dann einen Handler, der ausgelöst wird, sobald ein UDP-Paket ankommt. (Um ein UDP-Testpaket auf einer Unix-Befehlszeile zu senden, geben Sie zum Beispiel ein:
echo -n "Test" | socat stdin "udp-sendto:127.0.0.1:4242"
)
Die folgenden Beispiele hingegen nehmen nur Bezug auf Objekte, die beim Start des Scripts schon existieren, und können deshalb im Deklarationsteil stehen:
on (after_time(12:00)) { log('Es war Mittag vor ' + string(timeofday()-12:00) + ' Sekunden') }
- Definiert einen Event-Handler (ein Stück Code, das ausgeführt wird, wenn eine bestimmte Bedingung erfüllt ist).
- Dieser Handler wird ausgelöst, wenn die lokale Zeit die Mittagszeit (12:00) überschreitet und protokolliert die Anzahl der seit Mittag tatsächlich verstrichenen Sekunden - denn wenn dieser Handler zu einem beliebigen Zeitpunkt nach 12:00 installiert wird, wird er sofort einmal ausgeführt.
- Standardmässig werden Handler ausgeführt, wenn sich die Triggerbedingung von false zu true ändert (aber nicht, wenn sie sich von true zu false ändert).
on (every(0:15, 0)) { log('eine weitere Viertelstunde ist vergangen'); }
- Definiert einen Eventhandler, der jede ganze Viertelstunde ausgeführt wird.
on (sensor1>20) toggling { log('sensor1 ist ' + sensor1); }
- Definiert einen Event-Handler, der immer dann ausgelöst wird, wenn die Bedingung von false auf true oder umgekehrt umschaltet. Zu beachten ist, dass sensor1 eine Ereignisquelle sein muss, damit dies funktioniert. Kontexte, die Hardware-Eingänge wie z. B. Sensoren haben, stellen diese normalerweise als Ereignisquellen zur Verfügung (z.B. in P44-DSB Evaluatoren). Beispielsweise sind valuesource() oder device.sensor() Ereignisquellen.
on (sensor2) changing { log('sensor2 ist ' + sensor2) }
- Definiert einen Event-Handler, der immer dann auslöst, wenn Bedingungsausdrücke neu ausgewertet werden und ein anderes Ergebnis als bei der letzten Auswertung erhalten.
on (sensor2) evaluating { log('sensor2 ist ' + sensor2) }
- Definiert einen Event-Handler, der immer dann ausgelöst wird, wenn der Bedingungsausdruck ausgewertet wird, auch wenn sich das Ergebnis nicht ändert. Hinweis: Verwenden Sie dies mit Vorsicht, je nach den beteiligten Ereignisquellen kann dies dazu führen, dass der Handler sehr oft ausgeführt wird, was Performance-Probleme verursachen kann.
on (sensor1+sensor2) changing as sensorsum { log('sensor1+sensor2 ist ' + sensorsum) }
- Definiert einen Event-Handler, der immer dann auslöst, wenn sich die Summe von sensor1+sensor2 ändert. Der Teil as sensorsum speichert das Ergebnis des Trigger-Ausdrucks in der Variablen sensorsum.
on (sensor1>22) stable 0:05 as sens { log('sensor1: stabil für 5 Minuten bei > 22, aktuell: ' + sens) }
- Definiert einen Event-Handler, der auslöst, wenn sensor1 für mindestens 5 Minuten über 22 bleibt.
on (featureevent()) as event { log('Ereignis empfangen ' + event) }
- Definiert einen Event-Handler, der immer dann auslöst, wenn featureevent() ausgelöst wird und speichert das Ereignis in der Variablen event.
- Beachten Sie, dass bei Ereignisquellen, die einmalige Ereignisse ausliefern, wie das featureevent() tut, das Speichern des tatsächlichen Auslösewerts in einer Variablen mit as wichtig ist.
Warnung
So mächtig die Handler auch sind, es ist auch möglich, Triggerbedingungen zu konstruieren, die zu einer sehr häufigen Ausführung des Handlers führen und damit potentiell den normalen Betrieb des Gerätes blockieren oder verschlechtern.
Lokale Variablen
var a
- erzeugt eine skriptkontext-lokale oder funktions-lokale (wenn in einer Funktion benutzt) Variable a (anfangs auf undefined gesetzt. Wenn a bereits existiert, passiert nichts.
var a = 7
- Definition einer skriptKontext-lokalen oder funktions-lokalen Variable a und Zuweisung eines Anfangswertes. Wenn die Variable bereits existiert, ist dies wie eine normale Zuweisung.
Variablenkontexte
Mit var erzeugte Variablen sind entweder komplett lokal (wenn sie innerhalb einer Funktion definiert werden) oder lokal zum jeweiligen Scriptkontext, und werden dann Kontextvariable genannt. Es gibt verschiedene Scriptkontexte mit je eigenen Variablen. In einem P44-DSB- oder LC-Gerät z.B. laufen alle Szenenskripte einer Leuchte in einem Scriptkontext dieser Leuchte, d.h. die Szenenscripte untereinander sehen dieselben lokalen Variablen, aber andere Leuchten oder sonstige Geräte haben darauf keinen Zugriff (und können deshalb ohne Konflikte eigene Variablen mit gleichem Namen verwenden).
threadvar t
threadvar t = 7
- Definition einer thread-lokalen Variable t (und ggf. Zuweisung eines Anfangswertes). Im Unterschied zu
var
hat diese Variable für jeden laufenden Ausführungsstrang (also z.B. Ausführung eineson(...) {...}
odercatch {...}
-Handlers) einen privaten Wert.
Automatisch erzeugte thread-Variablen
Thread-Variablen werden auch durch on(...) as variablenname {...}
oder catch as variablenname {...}
automatisch erzeugt, da der Wert bei jedem (u.U. parallel erfolgenden) Durchlauf des Codes u.U. ein anderer ist.
(Hingegen erzeugt concurrent as variablenname
keine Thread-Variable, sondern eine im Kontext des Scripts das concurrent
ausführt.)
Anweisungen
glob b
- Erzeugt eine globale Variable b (alle Skripte und Bedingungsausdrücke können auf sie zugreifen). Globale Variablen können (und sollten i.d.R.) auch vor dem Start des Scripts deklariert werden, siehe Deklarationen.
glob b = 42
- Erzeugt eine globale Variable b und weist ihr einen Anfangswert zu. Siehe auch Deklarationen.
unset b
- Entfernen einer (lokalen oder globalen) Variablen namens b.
unset a.b
unset a['b']
- Entfernen des Feldes b aus der Objektvariablen namens a.
unset c[2]
- Entfernen des Elements mit dem Index 2 (= drittes Element) aus einem Array namens c.
a := 3*4
- Zuweisung des Ergebnisses eines Ausdrucks an a.
a = 3*4
- Einfaches Gleichheitszeichen wird ebenfalls als Zuweisung behandelt.
let a = 3*4
- let kann verwendet werden, um deutlicher zu zeigen, dass es sich um eine Zuweisung handelt (ist aber völlig optional).
a = 7; b = 3; c = a*b;
- Mehrere Anweisungen können durch Semikolon getrennt in eine Zeile geschrieben werden.
{ a = 42; b = 23; scene("bright"); }
- Block aus mehreren Anweisungen.
Kontrollfluss
if (a>0) b := 7
- bedingte Ausführung der Zuweisung b:=7, wenn a grösser als Null ist.
if (a>0) b := 7 else b := 19
- Bedingte Ausführung der Zuweisung b:=7 mit else-Anweisung, die ausgeführt wird, wenn die Bedingung nicht wahr ist.
if (a>0) { b := 7; c := 44 } else { b := 0; c := "ein String" }
- Bedingung mit Anweisungsblöcken.
while (i>0) { i := i-1; log(5, "i ist jetzt: " + i); }
- Wiederholung (Schleife) des Anweisungsblocks, solange i grösser als Null ist.
break
- verlässt eine while Schleife.
continue
- startet die nächste Iteration in einer while Schleife.
foreach objectOrArray as value { do_something_with_value } foreach objectOrArray as key,value { do_something_with_key_and_value }
- iteriert durch jedes Feld (wenn objectOrArray ein Objekt ist) oder jedes Array-Element (wenn objectOrArray ein Array ist) und weist value und optional key entsprechend zu.
return
- beendet das aktuelle Skript mit null/undefiniertem Rückgabewert.
return value
- beendet das aktuelle Skript und gibt value zurück.
try { statements; } catch { handling_statements }
- Wenn eine der statements einen Laufzeitfehler erzeugt, werden die handling_statements ausgeführt.
try { statements; } catch as error_var { handling_statements }
- Wenn eines der statements einen Laufzeitfehler erzeugt, werden die handling_statements ausgeführt und können error_var verwenden, um den Fehler zu untersuchen.
return gilt als erfolgreich, auch wenn es einen Fehler zurückgibt
Ein return-Statement erzeugt selber keinen Fehler, auch wenn es einen Fehler zurückgibt. D.h.
try { return error('Fehler') } catch as e { ... }
wird den 'Fehler' nicht mit catch
abfangen, sondern der aufrufenden Funktion zurückgeben.
throw(anything)
- erzeugt einen Laufzeitfehler mit anything als Fehlermeldung. Wenn anything bereits ein Fehlerwert ist, wird der Fehlerwert als solcher erneut ausgelöst.
Gleichzeitigkeit (Threads)
p44script verfügt über ein sehr komfortables Konstrukt, um gleichzeitig ablaufende Sequenzen von Operationen zu erzeugen, etwas, das in der Automatisierung oft benötigt wird. Eine gleichzeitig ablaufende Sequenz von Skriptanweisungen wird auch als Thread bezeichnet Beachten Sie, dass Ereignis-Handler, wie oben gezeigt, auch gleichzeitig mit anderen Skript-Aktivitäten ausgewertet und ausgeführt werden. Bei komplexeren Setups kann es notwendig sein, für gewisse Sequenzen sicherzustellen, dass sie sequentiell ablaufen. Dazu gibt es lock(), womit threads sich abstimmen können.
concurrent { while(true) { log('blink1'); delay(3) } } concurrent { while(true) { log('blink2'); delay(2.7) } } delay(0:02); abort();
- lässt zwei asynchrone Blinksequenzen parallel laufen. Nach 2 Minuten werden beide gleichzeitigen Threads mit der Funktion abort() gestoppt.
concurrent as blinker { while(true) { log('blink'); delay(2) } } delay(0:02); abort(blinker)
- Startet eine gleichzeitige Blinksequenz und speichert eine Referenz darauf als Variable namens blinker.
- Nach zwei Minuten kann die Variable als Argument für abort() verwendet werden, um einen Thread gezielt zu stoppen.
concurrent as task1 { delay(random(2,5)); log('task1 done') }; concurrent as task2 { delay(random(2,5)); log('task2 done') }; await(task1,task2) log('eine der beiden Tasks erledigt') await(task1) await(task2) log('beide Tasks sind jetzt erledigt')
- Startet zwei gleichzeitige Tasks mit einer zufälligen Dauer zwischen 2 und 5 Sekunden.
- Die Funktion await() kann verwendet werden, um auf die Fertigstellung eines ihrer Argumente zu warten. Die Verwendung eines separaten await()-Aufrufs für jeden Thread führt zur Beendigung, wenn alle Aufgaben erledigt sind. Bei komplizierter Parallelität siehe auch lock() und signal() zur Synchronisierung von Threads.
Allgemeine Funktionen innerhalb von Skripten
abort()
- bricht alle gleichzeitig laufenden Threads im aktuellen Scriptkontext ab, ausser dem Thread, von dem aus abort() aufgerufen wird.
abort(a [, abortresult, [ self]])
- bricht den Thread namens a ab, d.h. den Thread, der mit
concurrent as a {...}
gestartet wurde. Wenn abortresult nicht null gesetzt ist, wird es als Endresultat des Threads verwendet (das z.B. von await() zurückgegeben wird). Wenn self nicht gesetzt ist, und a der eigene Thread ist (also der von dem aus abort() aufgerufen wurde), dann wird dieser nicht abgebrochen, weil das meistens nicht beabsichtigt ist und schwierig zu debuggen - falls doch, kann self gesetzt werden. await(a [,b,...] [, timeout])
- wartet auf ein oder mehrere Ereignisse, z. B. eintreffen eines Signals (s. signal()) oder die Beendigung von Threads, und gibt den Wert des Ereignisses zurück. Wenn timeout verwendet wird, beendet await das Warten nach der angegebenen Zeit und gibt undefined zurück.
delay(seconds)
- verzögert die Skriptausführung um seconds (Fliesskommazahl, auch Sekundenbruchteile sind möglich).
delayutil(dayseconds|epochtime)
- verzögert die Skriptausführung bis der Zeitpunkt dayseconds (Anzahl Sekunden seit Mitternacht, s. timeofday()-Funktion) oder epochtime (Anzahl Sekunden seit 1.1.1970 Mitternacht UTC, s. epochtime()-Funktion) erreicht ist.
- Hinweis: Zeitliterale geben auch die Sekunden seit Mitternacht zurück, also wird
delayutil(16:00)
bis vier Uhr warten. dnssdbrowse(type [, hostname])
-
Sucht nach Geräten im lokalen Netz, die ihre Services via das DNS-SD-Protokoll auffindbar machen (z.B. Drucker, aber auch viele andere Geräte, gerade im Home-Automation-Bereich, inklusive die P44-xx-Geräte). Mit type muss ein DNS-SD-Service-Typ angegeben werden. Um etwa Web-Oberflächen zu finden, kann
_http._tcp
angegeben werden (s. hier für eine vollständige Liste aller Service-Typen). Durch Angabe von hostname kann ein bestimmtes Gerät anhand seines Hostnames (in der Formdevicehostname.local
) gesucht werden. Als Resultat gibt dnssdbrowse einen JSON-Array zurück, der leer sein kann wenn kein Service vom angegebenen type (und ggf. hostname) gefunden wurde. Ansonsten enthält der Array JSON-Objekte, die den Service mit folgenden Feldern beschreibt:- name: Name des Services als Textbeschreibung.
- hostname : auf
.local
endender hostname. - hostaddress : die IP-Adresse im LAN, unter der der Service erreichbar ist
- ipv6 : 1/true wenn hostaddress eine IPv6-Adresse ist, 0/false sonst.
- port : Portnummer des Service
- interface : Interface-Index des Netzwerkinterfaces, über das der Service erreichbar ist (nur bei IPv6 allenfalls relevant).
- txts : JSON-Objekt mit den TXT-Records des Services als Felder (kann leer sein wenn der Service keine TXT-Records hat).
- url : Dieses Feld eine URL, das je nach Service-Art direkt in einem Browser verwendet werden kann (zusammengesetzt aus den obigen Feldern). Bei unbekannten Service-Namen wird http angenommen, was manchmal auch bei Services mit anderem Typ als http funktioniert, aber nicht immer.
geturl(url [,timeout])
- Gibt die Antwort einer GET-Anfrage an (http oder https) url zurück, bricht nach timeout Sekunden ab, falls angegeben.
- Wenn der url ein
!
vorangestellt wird, werden SSL/Zertifikatsfehler ignoriert. Für http(s)-Zugriff mit mehr Optionen siehe httprequest(). httprequest(request [, data])
-
Dies ist eine verallgemeinerte Funktion, die anstelle von geturl(), puturl(), posturl() verwendet werden kann, wenn http-Requests mit speziellen Headern, Zertifikatprüfung, Client-Zertifikat etc. benötigt werden. Alle Parameter sind ein einem request JSON-Objekt zu übergeben:
- url: Die komplette URL. Kann ein
!
als erstes Zeichen enthalten, um Zertifikatsprüfung bei https-Verbindungen zu verhindern. - method : Optional, ohne Angabe wird
GET
verwendet. - data : Zu sendende Daten für PUT und POST, kann auch als zweiter Parameter der Funktion direkt übergeben werden (insbesondere notwendig wenn es sich um einen binärstring handelt). Wenn data ein JSON-Wert ist, werden die Daten bei PUT und POST mit
Content_type: application/json
gesendet, ansonsten mit 'Content_type: text/html', solange nicht in headers anders angegeben. - formdata : optionaler Boolescher Wert. Wenn diese Option auf true gesetzt ist, und data ein JSON-Objekt ist, werden die Felder des JSON-Objekts als Formularfelder mit 'Content_type: application/x-www-form-urlencoded' gesendet.
- timeout : optionaler timeout in Sekunden.
- user : optionaler Username für http auth (kann auch als Teil der URL übergeben werden).
- password : optionales Passwort für http auth (kann auch als Teil der URL übergeben werden).
- basicauth : optionaler mode für die Verwendung http basic auth. Standardmässig ist basic auth nur auf https-Verbindungen möglich, und nur wenn der Server danach fragt. Mit basicauth=onrequest wird basic auth auch in nicht-SSL-Verbindungen erlaubt, wenn der Server danach fragt. Mit basicauth=immediate wird basic auth mit der ersten Anfrage mitgeschickt (was bei einfachen IoT-Webservern u.U. notwendig ist, aber ohne SSL sehr schlechte Sicherheit bietet). Mit basicauth=never wird basic auth gar nicht erlaubt, auch nicht für SSL-Verbindungen.
- headers : optionales JSON-Objekt, das zu sendende http-Header als Name/Wert-Paare enhält. Wenn kein
Content-Type
dabei ist, wird dieser Header automatisch aus den übergebenen Daten bestimmt (html oder json). - withmeta : optionaler Boolescher Wert. Wenn diese Option auf true gesetzt ist, gibt httprequest ein JSON-Objekt zurück, das ein status-Feld mit dem http-Statuscode (Erfolg oder Fehler), ein data-Feld mit der eigentlichen Antwort (falls vorhanden) und ein headers-Feld mit allen empfangenen Headern als Name/Wert-Paare enthält. Bitte beachten, dass in diesem Modus jede Anfrage, die zu einer http-Antwort führt, nicht als Fehler angesehen wird, unabhängig vom http-Status (während sonst nur der Status 200..203 als ok angesehen wird).
- clientcert : optionaler Pfad zu einem zu verwendenden Client-Zertifikat.
- servercert : optionaler Pfad zu einem zu verwendenden Root-Zertifikat oder Zertifikats-Directory für die Prüfung des Server-Zertifikats. Wird ein Leerstring angegeben, wird das Server-Zertifikat nicht geprüft (was auch mit einem
!
vor der url erreicht werden kann, s. oben). Standardmässig wird das Serverzertifikat mit den Root-Zertifikaten des Systems geprüft.
- url: Die komplette URL. Kann ein
lock([entered])
- Erzeugt ein Lock-Objekt, welches mit seinen zwei Methoden .enter() und .leave() zur Synchronisation parallel laufender Threads verwendet werden kann. Wird entered==true angegeben, dann wird das Lock bei der Erzeugung schon für den aufrufenden Thread reserviert. Normalerweise weist man ein erzeugtes lock einer Variablen zu:
var l = lock()
. Der Wert des Locks ist 0 wenn es frei ist, andernfalls gibt es die Verschachtelungstiefe der enter()-Aufrufe des Threads an, der momentan das Lock reserviert hat. -
Üblicherweise wird enter() in einer Bedingung verwendet:
var lk = lock() // ... if (lk.enter(0:03)) { // Sequentiell auszuführender Code // ... lk.leave() } else { log("3min timeout") }
lk.enter([timeout])
- Wartet, bis das Lock lk frei ist. Ohne Angabe von timeout wird unbestimmt lange gewartet. timeout kann 0 sein, um das Lock nur zu testen. Die Funktion gibt true zurück wenn das Lock für den aufrufenden Thread reserviert werden konnte. Sie gibt false zurück, wenn das Lock innerhalb des angegebenen timeout nicht reserviert werden konnte, oder wenn das Lock selber gelöscht wird (etwa mit unset).
lk.leave()
-
Gibt das Lock lk frei. Wenn andere Threads auf die Freigabe warten, erhält derjenige Thread Zugriff, der als erster lk.enter() aufgerufen hatte. leave() gibt true zurück, wenn damit tatsächlich eine Freigabe erfolgt ist. leave() von einem Thread aus, der nicht vorhher enter() aufgerufen hat, gibt false zurück und bewirkt nichts.
Warnung - Vorsicht vor deadlocks
Bei unsauberer Verwendung von Locks kann man leicht in die Situation geraten, dass zwei Threads je ein Lock reserviert haben, und gegenseitig auf die Freigabe des Locks des anderen Threads warten, was nie eintreffen kann. Das nennt sich deadlock und muss vermieden werden. Es empfiehlt sich deshalb, enter mit timeout zu verwenden, damit sich eine Script-Applikation nicht komplett blockieren kann.
log([loglevel, ] logmessage [, value...])
- schreibt eine Meldung in die Logdatei der Anwendung. Wenn loglevel weggelassen wird, wird 5 (LOG_NOTICE) verwendet. Wenn nach logmessage weitere Argumente folgen, wird logmessage als Formatstring behandelt, so wie in der Funktion format() (s. dort).
loglevel()
- gibt den aktuellen Log-Level zurück.
loglevel(level [, deltatime [, symbols [, coloring]]])
- ändert den Log-Level der Anwendung auf level. Wenn deltatime gesetzt ist, zeigen die Zeitstempel auch die Zeitdifferenz zum vorherigen Log-Eintrag in mS an. Wenn symbols gesetzt ist, werden UTF-8-Symbole verwendet, um Loglevels zu unterscheiden und das Kontextpräfix von der eigentlichen Protokollmeldung zu trennen. Wenn coloring gesetzt ist, werden im Log ANSI-Farben verwendet (geeignet für Terminalausgaben, nicht für Browser). Jedes der Argumente kann auch null sein, um den entsprechenden Wert nicht zu ändern.
logleveloffset(offset)
- Ändert den Log-Level-Offset des Scriptkontexts, in dem das Skript läuft, auf offset. Dies erlaubt es, einen Teil einer Anwendung selektiv mehr oder weniger ausführlich im Protokoll darzustellen.
maxblocktime([time, [currentonly]])
-
gibt die maximale ununterbrochene Ausführungszeit für Skripte (in Sekunden) zurück oder setzt sie (mit dem Argument time). Der Standardwert ist 0.05 Sekunden. Wenn currentonly true ist, wirkt sich die Einstellung nur auf den aktuellen Thread aus. Andernfalls wird die Vorgabe auch für alle zukünftigen Threads gesetzt, die von nun an gestartet werden (aber nicht für bereits laufende Threads, ausser dem aufrufenden Thread).
Eine hohe maxblocktime kann die Geräteleistung stark beeinträchtigen
Wenn maxblocktime zu hoch eingestellt ist, können Skripte ununterbrochen für die angegebene Zeit laufen, ohne dass andere Gerätefunktionen Zeit zum Ausführen bekommen, was zu einer trägen oder blockierten Reaktion einschliesslich der Weboberfläche führen kann. Die empfohlene maxblocktime liegt unter einer Sekunde.
maxruntime([time|undefined])
- Gibt das gesamte Laufzeitlimit für den aktuellen Thread (in Sekunden) zurück oder setzt es (mit dem Argument time). Die Vorgabe ist unendlich (dargestellt durch undefined).
posturl(url [,timeout][,data])
- Sendet data (muss ein String oder JSON sein) mit POST an url, gibt Antwort zurück, bricht nach timeout Sekunden ab, wenn angegeben. Wenn data ein JSON-Wert ist, werden die Daten mit
Content_type: application/json
gesendet, ansonsten mitContent_type: application/x-www-form-urlencoded
. - Wenn der url ein
!
vorangestellt wird, werden SSL/Zertifikatsfehler ignoriert. Für http(s)-Zugriff mit mehr Optionen siehe httprequest(). puturl(url [,timeout][,data])
- Sendet data (muss String oder JSON sein) mit PUT an url, gibt Antwort zurück, bricht nach timeout Sekunden ab, wenn angegeben. Wenn data ein JSON-Wert ist, werden die Daten mit
Content_type: application/json
gesendet, ansonsten mitContent_type: application/x-www-form-urlencoded
. - Wenn der url ein
!
vorangestellt wird, werden SSL/Zertifikatsfehler ignoriert. Für http(s)-Zugriff mit mehr Optionen siehe httprequest(). readfile(filename)
- Liest das File filename als String.
-
Ohne absolute Pfadangabe oder spezielles Prefix wird filename als relativ zum p44script-Datenverzeichnis (ein Verzeichnis im permanenten Flash-Speicher) aufgefasst. Folgende speziellen Prefixe können verwendet werden:
_/tempfile
: bedeutet, dass tempfile relativ zum (füchtigen) p44script Temporärverzeichnis zu verwenden ist.+/resource
: bedeutet, dass resource relativ zum Resourcenverzeichnis der Anwendung zu verwenden ist.=/file
: ist gleichbedeutend zu file allein, also relativ zum p44script-Datenverzeichnis.
Hinweis
Um Pfade oder Prefixe ausser
_/
für filename zu verwenden, muss der userlevel >=1 sein (Der Standard-userlevel für Produktionsgeräte ist 0). restartapp(["reboot"|"shutdown"|"upgrade"])
- Startet die Applikation (bei P44-xx-Geräten ist das der vdcd) neu. Ohne Argumente wird nur die Applikation neu gestartet, nicht das ganze Gerät (das dauert üblicherweise nur einige Sekunden, kann aber etwa bei vielen angeschlossenen DALI-Geräten auch länger dauern). Dieser "kleine" Restart ist v.a. nützlich bei der Entwicklung/Testen von komplexeren Scripts.
-
Mit "reboot" als Parameter wird das Gerät hingegen komplett neu gestartet, mit "shutdown" wird es heruntergefahren und angehalten, so dass es erst wieder startet wenn es von der Stromversorgung getrennt und wieder angeschlossen wird. Mit "upgrade" wird ein Upgrade auf die neueste verfügbare Firmware ausgelöst (was in jedem Fall, auch wenn keine neue Firmware verfügbar ist, einen Neustart auslöst).
Vorsicht
Die Verwendung von restartapp() in Scripten, die automatisch ausgeführt werden (z.B. das mainscript) kann ggf. dazu führen, dass das Gerät in eine Neustart-Schleife gerät, die u.U. nur noch mit einem Factory-Reset unterbrochen werden kann. Bitte also genau überlegen, ob die Bedingungen, die zur Ausführung von restartapp() führen, korrekt sind und nicht nach jedem Neustart sofort wieder auslösen!
shellquote(argument)
- Gibt argument als String interpretiert und in einfache Anführungszeichen gesetzt zurück, so dass es sicher als einzelnes Shell-Argument funktioniert (auch wenn argument selber einfache Anführungszeichen enthält), und keine Shell-Variablen expandiert werden.
signal()
- Erzeugt ein "signal"-Objekt, das einer (evtl. globalen) Variablen zugewiesen werden kann (unten: signal_var). Threads können mit await() auf das Eintreffen des Signals warten, oder die Signal-Variable kann in on()-Handlern als Auslösebedingung verwendet werden. Das Signalobjekt hat eine Methode send(), mit der das Signal versendet werden kann, s. unten.
signal_var.send()
- Sendet ein Signal mit dem Signalwert = true (numerisch 1)
signal_var.send(signalwert)
- Sendet ein Signal mit signalwert. signalwert kann ein beliebiger Wert sein, aber es gilt zu bedenken, dass einfache
on(condition) {}
Handler nur auslösen, wenn condition einen "wahren" Wert ergibt. Um alle Werte abzufangen, einschliesslich null, undefined, 0, false, kannon(condition) evaluating {}
verwendet werden. Das Warten auf ein Signal mit await() kehrt bei jedem Signalwert zurück, einschliesslich aller "falschen" Werte. system(commandline)
-
Führt commandline über die Shell der Plattform aus und gibt die Ausgabe als String zurück.
Hinweis
Die Funktion
system()
ist nur verfügbar, wenn der userlevel >=2 ist (Der Standard-userlevel für Produktionsgeräte ist 0)Warnung
Da die Funktion
system()
jeden Befehl auf OS-Level aufrufen kann, kann sie die Firmware ernsthaft beschädigen, bis hin zum Bricking des Gerätes. Deshalb ist sie standardmässig nicht aktiviert. Wenn Sie einen DIY P44-DSB-X haben, können Sie den userlevel auf >=2 setzen, um sie zu benutzen, aber seien Sie vorsichtig! udpsocket(host, port, receive, nonlocal, broadcast)
- gibt einen UDP-Socket zurück, der zum Senden von Paketen an host:port konfiguriert ist. Verwenden Sie die Methode .send(string), um Daten zu senden.
- Wenn receive true ist, kann der Socket auch UDP-Pakete empfangen (über seine Methode .message(), die in on()-Anweisungen als Trigger verwendet werden kann.
- Wenn nonlocal wahr ist, kann der Socket auch UDP-Pakete empfangen, die von anderen Hosts als nur localhost stammen (was der Standard ist).
- Wenn broadcast wahr ist, kann der Socket Broadcast-Pakete senden und empfangen.
- Üblicherweise weist man den Rückgabewert dieser Funktion einer Variablen zu:
var udpsocket_var = udpsocket(...)
um die weiter untenbeschriebenen Methoden aufrufen zu können. udpsocket_var.message()
- ist eine Ereignisquelle, die auf dem Socket empfangene UDP-Nachrichten zurückgibt. Sie kann in on()-Anweisungen als Triggerbedingung verwendet werden und kann auch await()-ed werden.
udpsocket_var.send(string)
- sendet string als UDP-Paket.
undeclare()
- Löscht alle Funktions- und Handler-Definitionen, die nicht Teil eines gespeicherten Skripts sind, sondern ad-hoc erstellt wurden, zum Beispiel aus einer interaktiven Skript-Befehlszeile (einer REPL = Read-Execute-Print-Loop).
- Beachten Sie, dass Funktionen und Handler, die in einem in der Anwendung gespeicherten Skripttext definiert sind (z. B. Trigger-Aktionen, Szenenskripte usw.), an diese Skripttexte gebunden bleiben, d. h. sie werden entsprechend den Änderungen im Skripttext aktualisiert (oder entfernt) und werden nicht durch undeclare() entfernt.
urlencode(text [, x-www-form-urlencoded])
- Encodiert den text für die Verwendung in einer URL oder, wenn x-www-form-urlencoded true ist, für das Senden von Formularfeldern in POST-Daten.
websocket(url [, protocol [,pinginterval]])
- Erstellt eine Websocket-Verbindung zur angebenen url mit dem Protokoll ("Sec-WebSocket-Protocol"-header) protocol und dem angegebenen pinginterval.
- Üblicherweise weist man den Rückgabewert dieser Funktion einer Variablen zu:
var websocket_var = websocket(...)
um die weiter untenbeschriebenen Methoden aufrufen zu können. websocket(configobj)
-
Bei dieser Variante werden alle Parameter in configobj übergeben. Damit sind weiterführende Optionen möglich. Die folgenden Felder sind unterstützt:
- url: Die Websocket-URL (ws: oder wss:)
- protocol: Das Subprotokoll ("Sec-WebSocket-Protocol"-header)
- pinginterval: das Ping-Intervall
- headers : optionales JSON-Objekt, das zu sendende http-Header als Name/Wert-Paare enhält.
-
Üblicherweise weist man den Rückgabewert dieser Funktion einer Variablen zu:
var websocket_var = websocket(...)
um die weiter untenbeschriebenen Methoden aufrufen zu können. websocket_var.message()
- ist eine Ereignisquelle, die auf dem Socket empfangene Websocket-Nachrichten zurückgibt. Sie kann in on()-Anweisungen als Triggerbedingung verwendet werden und kann auch await()-ed werden.
websocket_var.send(message [, opcode])
- sendet message als Websocket-Message. Wenn opcode nicht angegeben wird, wird die message als "text" (opcode 1) versendet.
websocket_var.close([closecode [, reason]])
- schliesst den Websocket mit dem close-code "Normal" (1000), oder dem angegebenen closecode. Wenn angegeben, wird reason in der close-message mitgesendet.
writefile(filename, irgendwas [, append])
- konvertiert irgendwas in einen String, und schreibt das Resultat als File filename. Wenn irgendwas == null ist, wird filename gelöscht. Wenn append auf true gesetzt ist, wird irgendwas am Ende des Files angehängt, wenn es schon existiert, ansonsten wird ein schon existierendes File überschrieben.
-
Ohne absolute Pfadangabe oder spezielles Prefix wird filename als relativ zum p44script-Datenverzeichnis (ein Verzeichnis im permanenten Flash-Speicher) aufgefasst. Folgende speziellen Prefixe können verwendet werden:
_/tempfile
: bedeutet, dass tempfile relativ zum (füchtigen) p44script Temporärverzeichnis zu verwenden ist.+/resource
: bedeutet, dass resource relativ zum Resourcenverzeichnis der Anwendung zu verwenden ist.=/file
: ist gleichbedeutend zu file allein, also relativ zum p44script-Datenverzeichnis.
Hinweis
Um Pfade oder Prefixe ausser
_/
für filename zu verwenden, muss der userlevel >=2 sein (Der Standard-userlevel für Produktionsgeräte ist 0).
Steuerung von geräte-/produktspezifischen Subsystemen
Hinweis - Produktspezifische Features
Die in den folgenden Abschnitten beschriebenen Features sind abhängig vom jeweiligen Gerätetyp, insbesondere die Ansteuerung von externer Hardware über GPIO-Anschlüsse, welche speziell dafür ausgerüstete Gerätevarianten braucht (z.B. den LED-Controller P44-LC-LED, oder den Automationscontroller P44-AC-DCM), oder dann DIY-Aufbauten wie das P44-DSB-X-Image auf Raspberry Pi.
P44-LC: Funktionen zur Verwendung in Trigger-Aktionen oder globalen Skripten
scene(name [, transition_time])
- Aufruf der Szene durch name, mit optionaler transition_time in Sekunden.
scene(id, zone_or_device [, transition_time])
- Aufruf der Lichtszene durch id (numerische digitalSTROM-Szene oder generischer Name wie 'preset 1') in zone (Raum/Zone nach Name), mit optionaler transition_time in Sekunden.
scene(id, zone_or_device, transition_time, group)
- in der Gruppe group ('light', 'shadow', 'heating'...) die Szene nach id (numerische digitalSTROM-Szene oder generischer Name wie 'preset 1') in zone_or_device (in Raum/Zone nach Name oder in einem einzelnen Gerät nach Name oder dSUID) aufrufen, mit optionaler transition_time in Sekunden.
sceneid(name)
- Gibt die id der Szene (als generische Aktionsbeschreibung wie 'preset 1') mit dem Namen name zurück, oder null, wenn keine solche Szene existiert.
sceneno(name)
- gibt die (interne, dS) Szenennummer der vom Benutzer benannten Szene name oder mit der id (generische Aktionsbeschreibung) name zurück, oder null, wenn keine solche Szene existiert.
set(zone_or_device, value [, transition_time])
- Helligkeitswert eines Raums/einer Zone oder eines einzelnen Geräts (nach Name oder dSUID) auf value (0..100) setzen, mit optionaler transition_time in Sekunden.
set(zone_or_device, value, transition_time, channelid)
- setzt den durch channelid angegebenen Kanal ('hue', 'saturation', 'hPos'... etc. - Hinweis: channelIDs sind case-sensitive! Es kann 0 angegeben werden für den Zugriff auf den Standardkanal der jeweiligen Gerät(e)) auf value, in Raum/Zone oder in einem einzelnen Gerät (nach Name oder dSUID), mit transition_time in Sekunden.
trigger(triggername)
- führt die Aktion des Triggers mit dem Namen triggername aus.
savescene(name)
savescene(id, zone [, group])
- speichert die Szene nach name oder nach id, zone und group (Standardgruppe ist 'light').
P44-DSB/P44-LC: Zugriff auf Geräte und API
Geräteebene
Hinweis: Die folgenden Funktionen sind in Skripten auf Geräteebene verfügbar (z. B. in Szenenskripten). Aber grundsätzlich sind diese Funktionen Mitglieder des Geräteobjekts oder eines seiner Mitglieder. Ein Geräteobjekt kann auch von globalen Skripten mit der Funktion device gefunden werden, siehe unten. Dies ermöglicht Anweisungen wie: device('mydevicename').output.channel('brightness',75).
output
- Stellt den Ausgang eines Geräts dar (bei Geräten ohne Ausgang ist dies undefiniert).
output.applychannels([force [, transitionTimeOverride]])
- übernimmt Änderungen an Kanälen, die mit dimchannel und channel vorgenommen wurden. Wenn force wahr ist, werden auch unveränderte Kanäle wieder auf die Hardware angewandt. Wenn transitionTimeOverride gesetzt ist, werden die Übergangszeiten aller sich ändernden Kanäle mit dem angegebenen Wert überschrieben. Ansonsten gelten die Übergangszeiten, wie sie mit loadscene aus einer Szene geladen oder mit channel individuell eingestellt wurden.
output.syncchannels()
- erzwingt das Rücklesen der aktuellen Kanalwerte aus der Hardware.
output.channel(channelid)
- gibt den aktuellen Wert von channelid zurück ('brightness', 'hue', 'saturation', 'colortemp', 'hPos' - Hinweis: channelIDs sind case sensitive! Es kann 0 angegeben werden für den Zugriff auf den Standardkanal des jeweiligen Geräts)
- Bitte beachten, dass Kanäle Unterfelder haben mit den Namen .valid (true, wenn der Kanal mit der Hardware synchronisiert ist), .age (Sekunden seit der letzten Änderung) und .oplevel (ein Wert zwischen 0..100, der den allgemeinen "Gesundheitszustand" des Geräts angibt, zu dem der Kanal gehört, was Funkempfang, Batteriestand usw. umfasst). Ein Kanal ist eine Ereignisquelle und kann in on()-Anweisungen als Triggerbedingung verwendet werden und kann auch await()-ed werden.
output.channel(channelid, value[, transition_time])
- Setzt den Wert von channelid auf value, mit optionaler transition_time in Sekunden.
- Bitte beachten, dass die Änderung des Kanalwerts nicht automatisch auf die Hardware angewendet wird; dazu muss applychannels() aufgerufen werden. Hinweis: channelIDs sind case sensitive! Es kann 0 angegeben werden für den Zugriff auf den Standardkanal des jeweiligen Geräts.
output.dimchannel(channelid, valuechange, transition_time)
- ändert den Wert von channelid um valuechange, mit optionaler transition_time in Sekunden.
- Bitte beachten, dass die Änderung des Kanalwerts nicht automatisch auf die Hardware angewendet wird; dazu muss applychannels() aufgerufen werden. Hinweis: channelIDs sind case sensitive! Es kann 0 angegeben werden für den Zugriff auf den Standardkanal des jeweiligen Geräts.
output.movechannel(channelid, direction [, time_per_unit])
- startet die Bewegung des Wertes von channelid in die durch das Vorzeichen von direction angegebene Richtung oder stoppt die Bewegung, wenn direction gleich 0 ist. Wenn die optionale Angabe time_per_unit in Sekunden gesetzt ist, bestimmt sie die Geschwindigkeit, indem sie die Zeit festlegt, die pro Änderung von 1 Einheit des Kanalwertes benötigt wird. Wenn time_per_unit nicht angegeben oder auf 0 gesetzt ist, wird stattdessen die Standard-Dimmgeschwindigkeit des Kanals verwendet.
- Bitte beachten, dass die Kanalbewegung nicht automatisch gestartet wird; dazu muss applychannels() aufgerufen werden. Hinweis: channelids sind case sensitive! Es kann 0 angegeben werden für den Zugriff auf den Standardkanal des jeweiligen Geräts.
output.loadscene(sceneIdOrNo [, transitionTimeOverride])
- lädt die Kanäle des Ausgangs mit den in der Szene sceneIdOrNo gespeicherten Werten. Die Szene wird durch ihre Nummer oder den Preset-Namen (z.B. 'bell 1' oder 'preset 2') bestimmt. Wenn transitionTimeOverride angegeben ist, werden die Überganszeiten aller Kanäle auf diesen Wert gesetzt, auch wenn die Szene andere Werte definiert.
- Bitte beachten, dass die geladenen neuen Kanalwerte nicht automatisch auf die Hardware angewendet werden; dazu muss applychannels() aufgerufen werden. loadscene() kann z.B. dazu verwendet werden, eine Szene zu laden, aber vor der Anwendung auf die Ausgänge noch mit channel() zu justieren.
output.runactions(sceneIdOrNo)
- führt die Szenen-Aktionen (z.B. Blinken, oder Start eines Szenenscripts) der Szene sceneIdOrNo auf. Die Szene wird durch ihre Nummer oder den Preset-Namen (z.B. 'bell 1' oder 'preset 2') bestimmt.
output.stopactions()
- stoppt alle laufenden Szenen-Aktionen (Szenenscripts, Blinken, etc.) und alle laufenden Transitionen von Kanalwerten.
sensor('id_or_index')
- gibt den aktuellen Wert eines Sensoreingangs zurück. id_or_index kann entweder der Index des Sensors sein (beginnend bei 0), oder die Text-ID (eine Zeichenkette wie 'temperature' oder 'humidity', je nachdem, was das Gerät tatsächlich liefert).
- Beachten Sie, dass Sensoren Unterfelder mit den Namen .valid (true, wenn der Sensor einen aktuellen Wert hat), .age (Sekunden seit der letzten Aktualisierung) und .oplevel (ein Wert zwischen 0..100, der den allgemeinen "Gesundheitszustand" des Sensors angibt, der Funkempfang, Batteriestand usw. umfasst) haben. Ein Sensor ist eine Ereignisquelle und kann in on()-Anweisungen als Triggerbedingung verwendet werden und kann auch await()-ed werden.
input('id_or_index')
- gibt den aktuellen Wert eines binären Eingangs zurück. id_or_index kann entweder der Index des Binäreingangs sein (beginnend bei 0), oder die Text-ID (eine Zeichenkette wie 'rain' oder 'window_handle', je nachdem, was das Gerät tatsächlich bereitstellt). Die Eingangswerte sind normalerweise 0 oder 1, aber spezielle Eingänge wie Fenstergriffe können mehrere Zustände haben (2,3...).
- Beachten Sie, dass Eingänge Unterfelder mit den Namen .valid (true, wenn der Zustand des Eingangs bekannt ist), .age (Sekunden seit der letzten Aktualisierung) und .oplevel (ein Wert zwischen 0..100, der den allgemeinen "Gesundheitszustand" des Eingangs angibt, der den Funkempfang, den Batteriestand usw. einschliesst) haben. Ein Eingang ist eine Ereignisquelle und kann in on()-Anweisungen als Triggerbedingung verwendet werden und kann auch await()-ed werden.
button('id_or_index')
- gibt den aktuellen Wert eines Tasters zurück. id_or_index kann entweder der Index des Tasters sein (beginnend bei 0), oder die Text-ID (eine Zeichenkette wie 'up' oder 'down', je nach Art des Tasters). Der Wert repräsentiert die Anzahl der erkannten Klicks (1..4) bzw. >4, wenn die Schaltfläche gedrückt gehalten wird.
- Beachten Sie, dass Taster Unterfelder mit den Namen .valid (true, wenn der Zustand der Schaltfläche bekannt ist), .age (Sekunden seit der letzten Betätigung der Schaltfläche) und .oplevel (ein Wert zwischen 0..100, der den allgemeinen "Gesundheitszustand" des Tasters angibt, der den Funkempfang, den Batteriestand usw. umfasst) haben. Ein Taster ist eine Ereignisquelle und kann in on()-Anweisungen als Triggerbedingung verwendet werden und kann auch await()-ed werden.
view
- Nur P44-DSB-X, P44-LC-E+L, P44-LC-LED mit WS281x Ledchains: Zugriff auf die Root-Ansicht eines Ledchain-Geräts. Das zurückgegebene Objekt ist eine P44lgrView, die über eigene Funktionen zur (Re)Konfiguration und zum Auffinden von Ansichten in ihrer Ansichtshierarchie verfügt.
Globale Ebene
Hinweis
Diese globalen Funktionen ermöglichen den Zugriff auf bestimmte Geräte im P44-DSB/P44-LC direkt oder indirekt über die vDC-API. Diese können für fortgeschrittene benutzerdefinierte Anwendungen benötigt werden, z. B. für komplexe Effekt-Setups, die eine koordinierte Steuerung mehrerer Geräte erfordern, normalerweise von einem globalen Hauptskript aus. Für Skripte, die sich auf ein einzelnes Gerät beziehen, ist die Verwendung von Skripten auf Geräteebene, wie z. B. das Szenenskript, vorzuziehen. Bitte seien Sie vorsichtig, wenn Sie direkt auf Geräte und API zugreifen.
device('devicename')
device('dSUID')
- Zugriff auf ein Gerät über den devicename oder die dSUID. Beachten Sie, dass die Verwendung von Namen zwar bequemer ist, Skripte aber möglicherweise nicht mehr funktionieren, wenn Benutzer Geräte umbenennen. Daher ist die Verwendung von dSUID empfohlen für Skripte, die lange Zeit installiert und funktionstüchtig bleiben sollen.
valuesource('valuesource_id')
- findet eine valuesource (Quelle von Werten, die on(xxx) Ausdrücke oder Auswertebedingungen auslösen können) anhand ihrer internen ID. Beachten Sie, dass es in der Regel besser ist, auf diese Werte mit device(x).sensor(y) zuzugreifen.
vdcapi(json_request)
- Gibt einen vDC-API-Methodenaufruf oder eine Benachrichtigung aus. Gibt die Antwort der Methode oder nur ein leeres Ergebnis für eine Benachrichtigung zurück.
webrequest()
- Ist eine Eventquelle, die Anfragen an den scriptapi-Endpoint der JSON-API liefert. webrequest kann z.B. mit
on(webrequest()) as request { ... }
verwendet werden, um API-Kommandos in Scripten zu implementieren. Der Returnwert von webrequest() hat die Methode answer() um den Request zu beantworten. Der ganze Mechanismus kann verwendet werden, wenn neben der Standard-Weboberfläche kundenspezifische Spezialwebseiten implementiert werden sollen. request.answer(json_oder_error)
-
Beantwortet einen Web-Request (im Beispiel oben die Variable request) mit einer JSON-Antwort oder einem Fehler. Üblicherweise wird eine Antwort als Teil eines on() Handlers gesendet, mit einem Konstrukt wie z.B.:
on (webrequest()) as request { // request ausführen // ... request.answer({ "status": "alles ok"}); }
webrequest muss beantwortet werden!
Wenn ein
on(webrequest()) as request { ... }
installiert wird, muss sichergestellt sein, dass der Handler-Code den request in jedem Fall mit answer() auch wirklich beantwortet. Andernfalls hängt der anfragende Web-Client bis zum Timeout.
LED-Ketten: p44 low resolution graphics (p44lrgraphics) Zugriff
p44lrgraphics ist ein Graphik-Subsystem speziell für Smart-LEDs. Hier wird nur das p44script-Interface dazu beschrieben. Mehr Informationen dazu findet sich in der technischen Dokumentation online.
lrg
- liefert den Rootview der gesamten, aktuell konfigurierten LED-Ketten-Anordnung, d.h. die Ansicht, die den gesamten Bereich enthält, der von allen gemappten LED-Ketten-Segmenten abgedeckt wird.
addledchain(ledchainconfigstring)
- fügt eine LED-Kette zur LED-Kettenanordnung hinzu. ledchainconfigstring hat das gleiche Format wie der Kommandozeilenparameter --ledchain:
[LED-Typ:[ledGerätename:]]AnzahlLEDs:[x:dx:y:dy:ersterOffset:offsetDazwischen][XYSA][W#Weisston]
removeledchains()
- entfernt alle ledchains aus der LED-Kettenanordnung.
ledchaincover()
- gibt den rechteckigen Bereich (x,y,dx,dy) zurück, der aktuell (evtl. teilweise) von den installierten ledchains abgedeckt wird.
neededledpower()
- gibt die (ungefähre) elektrische Leistung in Milliwatt zurück, die für die aktuell angezeigten Pixel der LED-Ketten-Anordnung benötigt wird.
- Beachten Sie, dass dies die Leistung zurückgibt, die benötigt würde, um alle Pixel ohne Begrenzung anzuzeigen (auch wenn sie aktuell begrenzt ist, siehe setmaxledpower()).
currentledpower()
- Gibt die (ungefähre) elektrische Leistung in Milliwatt zurück, die für die aktuell angezeigten Pixel tatsächlich verbraucht wird. Diese sollte den mit setmaxledpower() eingestellten Wert nicht wesentlich überschreiten.
setmaxledpower(maxpower)
- Setzt die (ungefähre) maximale elektrische Leistung in Milliwatt, die die gesamte LED-Kettenanordnung verbrauchen darf. Wenn die aktuell angeforderten Pixelfarben das Limit überschreiten würden, werden alle Pixel nach Bedarf gedimmt, um das Leistungslimit nicht zu überschreiten.
setledrefresh(refreshinterval [, priorityinterval])
- Setzt das minimale Refresh-Interval für die angeschlossenen LED-Ketten, d.h. wie oft maximal ein neuer Zustand auf die LEDs ausgegeben wird. Mit steigender Anzahl LEDs pro Kette muss dieses Interval u.U. grösser gewählt werden, damit alle LEDs angesteuert werden können (Default is 15mS). Das optionale priorityinterval gibt an, wie lange prioritäres Timing (z.B. Scrolling) Vorrang gegenüber sekundärem Timing (etwa in sich animierte Bestandteile eines gescrollten Bereichs) haben. Das kann wichtig sein für ruckelfreie Laufschriften etc. (Default ist 50mS).
setrootview(newrootview)
- Setzt newrootview als neuen root view.
makeview(json)
makeview(resource_file)
- erzeugt einen View entsprechend der Konfiguration aus dem direkt angegebenen json oder aus einem resource_file gelesenen JSON-text.
findview(viewlabel)
- sucht nach einem Subview mit dem angegebenen viewlabel, gibt den View oder undefined zurück.
addview(view)
- fügt view als Subview hinzu (wenn dieser View Subviews unterstützt).
parent()
- gibt den übergeordneten View zurück (oder null, wenn keiner vorhanden ist).
remove()
- entfernt den View von seinem übergeordneten View (wenn es einen übergeordnete View gibt), gibt true zurück, wenn der View entfernt werden konnte.
.configure(json)
.configure(resource_file)
- (re)konfiguriert den View mit dem direkt angegebenen json oder mit einem resource_file gelesenen JSON-text.
.set('propertyname', new_value)
- Setzt eine einzelne Eigenschaft des Views. Dies ist einfach ein bequemerer Weg als die Verwendung von configure mit einem json-Objekt, das ein einzelnes Feld enthält, bewirkt aber technisch gesehen das Gleiche.
.animator('propertyname')
- Ermittelt einen Animator für die angegebene Eigenschaft der Ansicht. Animierbare p44lrg-Eigenschaften sind: alpha, rotation, x, y, dx, dy, content_x, content_y, rel_content_x, rel_content_y, rel_center_x, rel_center_y, content_dx, content_dy, color, bgcolor. Farben haben die animierbaren Unterkomponenten r,g,b,a,hue,saturation,brightness.
- Wenn ein gültiger animierbarer Eigenschaftsname übergeben wird, wird ein animator-Objekt zurückgegeben. Siehe Animator-Beschreibung für Details.
.stopanimations()
- Stoppt alle in der Ansicht laufenden Animationen.
hsv(hue [, saturation [, brightness]])
- konvertiert die angegebenen Werte hue (0..359), saturation (0..1) und brightness (0..1) in das hexadezimale 6-stellige Web-Farbformat, das zum Setzen von bgcolor oder color in p44lrg-Ansichten verwendet werden kann. saturation und brightness stehen standardmässig auf 1, wenn sie nicht angegeben werden.
Animators
Animatoren erzeugen eine Reihe von Änderungen eines numerischen Wertes über die Zeit, mit einer Reihe von Parametern wie Gesamtdauer, zu verfolgende Kurve (Funktion), Wiederholung, Verzögerung usw. Animatoren werden von animator(...)-Funktionen von Objekten zurückgegeben, die Animation unterstützen, wie z. B. p44lrg-Ansichten oder PWM/analoge Ausgänge.
Die Animator-Methoden, die nur einen Parameter setzen, geben den Animator selbst zurück, so dass Aufrufe wie animator('dx').delay(2).function('easein',4).runto(50,0:01:02) verkettet werden können. Animationen laufen gleichzeitig mit dem Skript, das sie startet, aber await() oder on() {}-Handler können verwendet werden, um auf das Ende einer Animation zu warten.
.delay(Sekunden)
- verzögert den Start der Animation um die angegebene Zeit.
.runafter(animation)
- startet die Animation erst, nachdem die angegebene animation beendet ist.
.repeat(cycles [, autoreverse])
- Bewirkt, dass die Animation insgesamt cycles mal wiederholt wird. Wenn cycles auf 0 gesetzt ist, wird die Animation für immer wiederholt (bis sie explizit mit stop() gestoppt wird). Wenn autoreverse true ist, wird die Animation jeden zweiten Zyklus rückwärts ausgeführt.
.function('animationfunction')
- Setzt die Animationsfunktion, die eine der folgenden sein kann: linear,easein,easeout,easeinout.
.from(startvalue)
- Legt den Startwert für die Animation fest. Standardmässig ist der Startwert der aktuelle Wert der zu animierenden Eigenschaft, aber in einigen Fällen ist dieser nicht zum Auslesen verfügbar (z.B. Farbton oder Sättigung eines ausgeschalteten RGB-Lichts ist undefiniert), daher erlaubt from, einen Startwert explizit zu setzen.
.step(minsteptime [, stepsize])
- Hiermit kann die Auflösung der Animation beeinflusst werden. Dabei gibt minsteptime die minimale Zeit in Sekunden zwischen zwei Änderungen des animierten Werts an (0 = Standard für die jeweilige Hardware verwenden) und stepsize die Grösse der Schritte auf dem animierten Wert (0 = automatisch entsprechend minsteptime).
.runto(endvalue, duration)
- Nur diese Methode startet tatsächlich die Animation, um endvalue innerhalb der angegebenen duration zu erreichen.
- Normalerweise sieht eine Anweisung zum Einrichten einer Animation so aus: animator('color.hue').delay(2).from(0).runto(360,20), wobei runto die letzte Methode in der Kette ist.
- runto kann auch mehrmals aufgerufen werden, ohne die anderen Animationsparameter neu zu setzen zu müssen (ausgenommen runafter und delay, sowie from).
.reset()
- Setzt die Animation auf ihre Standardwerte zurück (und stoppt sie vorher, falls sie gerade noch läuft).
.stop()
- Hält eine Animation an.
.current
- gibt den aktuellen Wert des Animators zurück.
.running
- Gibt die Zeit (in Sekunden) zurück, die der Animator bereits läuft, oder null, wenn er nicht läuft.
Digital I/O (Signale und Indikatoren)
p44script kann verschiedene Hardware als digitale Ein- oder Ausgänge verwenden. Die Hardware wird über einen pinspec-String definiert wie folgt:
gpio.11
- GPIO Nummer 11.
led.ledname
- System-LED mit dem Namen ledname.
i2c0.MCP23017@22.7
-
Pin #7 eines i2c-I/O-Expander-Chips MCP23017, auf i2c-Adresse 0x22, an i2c-Bus Nummer 0. Unterstützte Chips sind TCA9555, PCF8574, MCP23017.
spi12.MCP23S17@0.6
- Pin #5 eines SPI-I/O-Expander-Chips MCP23S17, am SPI-Bus 1.2 (Bus 1, Chip Select 2).
Der pinspec für digitale Eingänge kann ein /
vorangestellt werden, um die Signalpolarität zu invertieren, sowie ein +
oder ein -
um an einem Eingang einen Pullup bzw. Pulldown zu aktivieren (sofern die Hardware das unterstützt):
+/gpio.22
- GPIO Nummer 22 mit aktiviertem Pullup, und invertierter Signalpolarität (z.B. für einen Endschalter, der den Eingang mit GND verbindet, wenn er aktiv ist).
Bitte Vorsicht - Hardwarezugriff
Diese Funktionen bieten Zugriff auf die Hardware und können bei unsachgemässer Verwendung deshalb Fehlfunktionen und je nach angeschlossenen Geräten auch Schäden verursachen. Die Verwendung dieser Funktionen setzt daher mindestens userlevel 1 voraus.
Funktionen für digitale Ein/Ausgänge
var d = digitalio(pinspec, Ausgang [, Initialzustand])
- Erzeugt ein Objekt zur Ansteuerung eines digitalen Eingangs- (Ausgang=false) oder Ausgangssignals (Ausgang=true) gemäss pinspec. Mit Initialzustand wird der initiale Zustand festgelegt. Bei Eingängen spielt Initialzustand als erwarteter Ruhezustand eine Rolle, wenn Änderungen am Zustand einen Event auslösen sollen (nicht aber das Einrichten des Eingangs).
Das von digitalio() zurückgegebene Objekt hat folgende Methoden (d = mit digitalio erzeugtes Objekt):
d.toggle()
- Für Ausgänge: ändert den Ausgangszustand auf das Gegenteil des aktuellen Zustands.
d.state()
- Gibt den aktuellen Zustand des Signals zurück. Wenn ein Einganssignal mit detectchanges() auf Signaländerungsüberwachung eingestellt ist, ist state() eine Ereignisquelle und kann in
on(...) {...}
-Handlern als Auslöser verwendet werden. d.state(newstate)
- Setzt ein Ausgangssignal auf den Wert newstate.
d.detectchanges([Entprellungszeit [, Pollintervall]])
d.detectchanges(null)
- Schaltet die Signaländerungsüberwachung ein oder aus. Beim Einschalten kann optional die Entprellungszeit und (für Eingänge, deren Hardware nicht von sich aus Signaländerungen erkennt) das Pollinterval eingestellt werden. Mit eingeschalteter Signalüberwachung kann state() in
on(...) {...}
-Handlern in der Auslösebedingung verwendet werden.
Funktionen für digitalen Bus
Ein digitaler Bus kann aus zwei bis zu 32 digitalen Leitungen gebildet werden, die dann zur Eingabe oder Ausgabe eines aus mehreren Bits bestehenden Wertes verwendet werden können.
var bus = digitalbus(pinspec-list, output [, initial-pin-state])
- Erzeugt ein Objekt für einen digitalen Eingangs- (output=false) oder Ausgangsbus (output=true), gebildet aus den in pinspec-list angegebenen Pins.
- pinspec-list ist eine durch Kommata getrennte Liste von digital pin specs, die die digitalen Eingänge auflistet, die den Bus bilden, beginnend mit dem höchstwertigen Bit (MSB). Um die Auflistung mehrerer Pins desselben Typs zu vereinfachen, kann ein gemeinsames Präfix in Paranthese vor dem ersten Pin angegeben werden.
- Zum Beispiel könnte ein 3-Bit-Bus, der aus GPIO22..24 besteht, als
gpio.22,gpio.23,gpio.24
oder unter Verwendung der gemeinsamen Präfix-Notation angegeben werden:(gpio.)22,23,24
. - initial-pin-state bestimmt den Anfangszustand aller Pins nach dem Erstellen des Busses (alle 0 oder alle 1)
Das von digitalbus() zurückgegebene Objekt hat die folgenden Methoden (bus = mit digitalbus erzeugtes Objekt):
bus.value()
- Liest den aktuellen Wert von einem Eingangsbus (oder den aktuell eingestellten Wert für Ausgänge).
bus.value(val)
-
nur für Ausgänge: setzt den Ausgangswert auf val.
Glitches bei Werteübergängen!
Da sich die Ausgänge nicht synchron, sondern kurz hintereinander ändern, zeigt der Bus für kurze Zeit andere Übergangswerte als den vorherigen Wert oder val an (ein "Glitch"). Dies muss bei der Gestaltung der Schaltungen, die an den Bus angeschlossen werden, berücksichtigt werden.
bus.buswidth()
- Gibt die Anzahl der Bits zurück, aus denen der Bus besteht.
bus.maxvalue()
- Gibt die höchste Zahl zurück, die der Bus darstellen kann.
Funktionen für Indikatoren
Ein Indikator ist ein Ausgang, der üblicherweise zur optischen Signalisation an den User benutzt wird (also meist eine LED), und dazu einige Komfortfunktionen für die üblichen Anwendungsfälle wie kurzes Aufleuchten oder Blinken zur Verfügung stellt.
var i = indicator(pinspec [, initialValue])
- Erzeugt ein Objekt zur Ansteuerung eines digitalen Ausgangs gemäss pinspec, der als Indikator genutzt werden soll.
Das von indicator() zurückgegebene Objekt hat folgende Methoden (i = mit indicator erzeugtes Objekt):
i.on()
i.on(Einschaltdauer)
- schaltet den Indikator ein. Wenn eine Einschaltdauer angegeben wird, schaltet der Indikator nach der angegeben Zeit automatisch wieder aus. Die on()-Methode wartet aber die Einschaltdauer nicht ab; der Indikator läuft parallel im Hintergrund.
i.off()
- schaltet den Indikator aus (und stoppt allfälliges Blinken).
i.blink([Periode [, ProzentEin [, Dauer]]])
- Lässt den Indikator mit der Periode blinken, wobei ProzentEin das Tastverhältnis des Blinkens angibt (z.B. 20 = 20% ein und 80% aus). Wenn die Dauer angegeben wird, stoppt das Blinken automatisch. Das Blinken läuft parallel im Hintergrund.
i.stop()
- stoppt das Blinken im gerade aktiven Zustand.
Analog I/O
p44script kann verschiedene Hardware als analoge Ein- oder Ausgänge verwenden. Die Hardware wird über einen pinspec-String definiert wie folgt:
pwmchip0.1
- PWM-Signal-Ausgang Nummer 1 an PWMChip Nummer 0, mit Default-PWM-Periode.
pwmchip0.2.100000
-
PWM-Signal-Ausgang Nummer 2 an PWMChip Nummer 0, mit PWM-Periode von 100000nS (= 0.1mS, = 10kHz).
Bitte beachten: Hardware-Limitationen
Je nach PWM-Hardware sind die möglichen Werte für die PWM-Periode unterschiedlich, und möglicherweise nicht für jeden Ausgang einzeln einstellbar.
i2c0.MAX1161x@33.2
- Eingang #2 eines i2c-A/D-Wandlers vom Typ MAX1161x, auf i2c-Adresse 0x33, an i2c-Bus Nummer 0. Aktuell unterstützte Chips sind MAX11612..617, MCP3021 sowie der Temperatursensor LM75.
spi12.MCP3008@0.6
- Eingang #6 eines SPI-A/D-Wandlers vom Typ MCP3008 am SPI-Bus 1.2 (Bus 1, Chip Select 2). Aktuell unterstütze Chips sind MCP3008 und MCP3002.
Bitte Vorsicht - Hardwarezugriff
Diese Funktionen bieten Zugriff auf die Hardware und können bei unsachgemässer Verwendung deshalb Fehlfunktionen und je nach angeschlossenen Geräten auch Schäden verursachen. Die Verwendung dieser Funktionen setzt daher mindestens userlevel 1 voraus.
Funktionen für analoge Ein/Ausgänge
var a = analogio(pinspec, Ausgang [, Initialwert])
- Erzeugt ein Objekt zur Ansteuerung eines analogen Eingangs- (Ausgang=false) oder Ausgangssignals (Ausgang=true) gemäss pinspec. Mit Initialwert wird der initiale Wert festgelegt.
Das von analogio() zurückgegebene Objekt hat folgende Methoden (a = mit analogio erzeugtes Objekt):
a.value()
- Liest den aktuellen Aanlog-Wert des Eingangs (bzw den aktuell eingestellten Wert bei Ausgängen). Wenn ein Analogeingang mit poll() auf regelmässig Abfrage eingestellt ist, ist _value() eine Ereignisquelle und kann in
on(...) {...}
-Handlern als Auslöser verwendet werden. a.value(val)
- nur für Ausgänge: setzt den Ausgangswert auf val. Der mögliche Wertebereich kann mit range ausgelesen werden.
a.range()
- Gibt den Wertebereich für das Analogsignal, und wenn bekannt, die Auflösung als Objekt mit den Feldern min, max und ggf. resolution zurück.
a.poll(Intervall [, Toleranz])
a.poll()
- richtet eine regelmässige Abfrage eines Analogeingangs mit angegebenem Intervall (und ggf. Toleranz) ein. Mit eingeschalteter regelmässiger Abfrage kann value() in
on(...) {...}
-Handlern in der Auslösebedingung verwendet werden. _poll() ohne Parameter schaltet die regelmässige Abfrage aus. a.filter(Typ, [Intervall [, Sammelzeit]])
- installiert einen Filter Typ ("average", "simpleaverage", "min", "max"), der die gelesenen Werte über das angegebene Intervall mit einem "sliding window" filtert. Die Sammelzeit gibt das Zeitfenster pro Datenpunkt an. Die Filterfunktion "average" berücksichtigt auch unregelmässige Zeitabstände zwischen den Datenpunkten, "simpleaverage" nicht.
a.animator()
- gibt einen Animator zurück, mit dem der Wert eines Analogausgangs über die Zeit animiert werden kann.
Low-Level i2c-Zugriff
Für den direkten Zugriff auf i2c-Geräte, die nicht direkt als analoge oder digitale I/O-Pins unterstützt werden, erlaubt p44script den Low-Level-Zugriff auf i2c-Geräte.
var i = i2cdevice(busno,devicespec)
- Erzeugt ein i2c-Gerät am Bus busno unter Verwendung von devicespec zur Angabe des Treibers und der Geräteadresse. Für den Low-Level-Zugriff ist der verwendete Treiber fast immer der "generic" Treiber, so dass devicespec normalerweise etwas wie
generic@20
(generisches i2c-Gerät an Adresse hex 20) ist.
Das von i2cdevice() zurueckgegebene Objekt hat die folgenden Methoden (i = mit i2cdevice erzeugtes Objekt):
i.smbusread(reg [,type])
- Gibt den Wert des Registers reg über das SMBus-Protokoll zurück. Ohne Einstellung von type wird das Register als einzelnes Byte gelesen und als Zahl zurückgegeben. Wird type auf "word" gesetzt, wird ein 16-Bit-Wort als Zahl gelesen, und "block" liest einen Block variabler Länge, der als (binärer) String zurückgegeben wird.
i.smbuswrite(reg, value [,type])
- Schreibt value in das Register reg. Ohne die Einstellung von type wird der numerische value als einzelnes Byte geschrieben. Wenn type auf "word" gesetzt wird, wird der numerische value als 16-Bit-Wort geschrieben, und "block" interpretiert value als (binären) String und schreibt ihn als Block variabler Länge.
i.rawread([count])
- Ohne Angabe von count liest ein einzelnes Byte ohne Registeradressierung (kein SMBus) und gibt es als Zahl zurück. Mit count angegeben (auch wenn auf 1 gesetzt) werden count Bytes gelesen und als (binärer) String zurückgegeben.
i.rawwrite(byte)
- Schreibt das byte in das Gerät ohne jegliche Registeradressierung (kein SMBus).
Bitte Vorsicht - Hardware-Zugriff
I2C-bezogene Funktionen stellen einen Zugriff auf die Hardware dar und können daher bei unsachgemässer Verwendung zu Fehlfunktionen und, je nach angeschlossenen Geräten, zu Schäden führen. Die Nutzung dieser Funktionen erfordert daher mindestens userlevel 1.
Low-Level SPI-Zugriff
Um direkt auf SPI-Geräte zuzugreifen, die nicht direkt als analoge oder digitale I/O-Pins unterstützt werden, erlaubt p44script den Low-Level-Zugriff auf SPI-Geräte.
var s = spidevice(busno_and_cs, devicespec)
-
Erzeugt ein SPI-Gerät an der Busnummer (busno_and_cs DIV 10) unter Verwendung der CS (Chip Select)-Pin-Nummer (busno_and_cs MOD 10) unter Verwendung von devicespec zur Angabe des Treibers und der Geräteadresse sowie optionaler SPI-Modusoptionen. Für den Low-Level-Zugriff ist der verwendete Treiber fast immer der "generic" Treiber, so dass devicespec normalerweise etwas wie
generic@15
(generisches SPI-Gerät an Adresse hex 15) ist. Wenn Ihr Gerät keine Adressierung/Registersemantik verwendet, muss der Adressteil dennoch angegeben werden, aber die tatsächliche Adresse ist irrelevant. SPI-Modus-Optionen sind Zeichen, die an die Treibernummer nach einem Bindestrich angehängt werden, z. B.generic-NP@0
für die Nichtverwendung der CS-Leitung und invertierte Polarität. Die folgenden Optionen sind verfügbar:H
: invertierte Phase (im Vergleich zum ursprünglichen Microwire-SPI)P
: invertierte Polarität (im Vergleich zum ursprünglichen Microwire-SPI)C
: Chip Select aktiv 1 (statt aktiv 0)N
: keine Chip-Select3
: 3-Draht-ModusR
: SPI ready indication: Slave zieht nach 0 um zu pausierenS
: langsame Geschwindigkeit - 1/10 der normalen Geschwindigkeit für den Buss
: sehr langsame Geschwindigkeit - 1/100 der normalen Geschwindigkeit für den Bus
Das von spidevice() zurückgegebene Objekt hat die folgenden Methoden (s = mit spidevice erzeugtes Objekt):
s.regread(reg [,type, [, count])
- Liefert den Wert des Registers reg unter Verwendung der Geräte-/Register-Adressierung ähnlich wie beim i2c SMBus (2-Byte-Request-Header bestehend aus (Geräteadresse<<1) und Bit0=Leseflag im ersten Byte und reg im zweiten Byte). Ohne Einstellung von type wird das Register als einzelnes Byte gelesen und als Zahl zurückgegeben. Setzt man type auf "word", wird ein 16-Bit-Wort als Zahl gelesen, und "bytes" liest so viele Bytes, wie durch count angegeben, die als (binärer) String zurückgegeben werden.
s.regwrite(reg, wert [,typ])
- Schreibt in das Register reg unter Verwendung der Geräte/Register-Adressierung (siehe regread()). Ohne type zu setzen, wird das Register mit value als einzelnes Byte geschrieben und als Zahl zurückgegeben. Wenn type auf "word" gesetzt wird, wird der numerische value als 16-Bit-Wort geschrieben, und "bytes" interpretiert value als (binäre) Zeichenkette und sendet alle seine Bytes.
s.writeread(bytes_to_write [, num_bytes_to_read [, fullduplex]])
- Diese Methode stellt die grundlegende SPI-Transaktion dar, die darin besteht, einige Bytes entweder gleichzeitig (Vollduplex) oder nacheinander (Halbduplex) zu schreiben und zu lesen.
- Die zu sendenden Bytes müssen als (binäre) Zeichenkette in bytes_to_write übergeben werden; wenn keine Bytes gesendet werden sollen, ist eine leere Zeichenkette anzugeben. num_bytes_to_read gibt die Anzahl der zu lesenden Bytes an und wird als (binäre) Zeichenkette zurückgegeben. Wenn fullduplex angegeben und auf true gesetzt ist, erfolgt das Lesen gleichzeitig mit dem Schreiben. Andernfalls beginnt das Lesen, nachdem die in bytes_to_write enthaltenen Bytes geschrieben wurden.
Bitte Vorsicht - Hardware-Zugriff
SPI-bezogene Funktionen stellen einen Zugriff auf die Hardware dar und können daher bei unsachgemässer Verwendung zu Fehlfunktionen und, je nach angeschlossenen Geräten, zu Schäden führen. Die Nutzung dieser Funktionen erfordert daher mindestens userlevel 1.
Modbus
Die Modbus-Unterstützung basiert auf der Bibliothek libmodbus und ermöglicht die Instanziierung von Modbus-RTU und TCP -Slaves und -Mastern.
Hinweis: benötigt Userlevel >= 1
Der Modbus-Zugriff erfordert userlevel >=1, da der RTU-Modus Zugriff auf die Hardware (serielle Schnittstelle, Sender-/Empfänger-Freigabeleitungen) benötigt, was den normalen Gerätebetrieb stören kann, wenn er nicht richtig gewählt wird.
Erstellen von Modbus-Instanzen
var mbm = modbusmaster()
- Erzeugt eine Modbus-Master-Instanz. Sie hat Methoden, die sowohl für Master als auch für Slave gelten, und einige Funktionen, die speziell für den Modbus-Master-Betrieb gelten, siehe unten.
var mbs = modbusslave()
- Erzeugt eine Modbus-Slave-Instanz. Sie verfügt über Methoden, die sowohl für den Master als auch für den Slave gelten, sowie über einige Funktionen, die speziell für den Modbus-Slave-Betrieb gelten (siehe unten).
Die zurückgegebenen Objekte werden im Folgenden als mbm und mbs für Master bzw. Slave referenziert; und als mb in der Beschreibung der gemeinsamen Funktionen.
Gemeinsame Funktionen für Modbus-Master und -Slave
Hinweis: Im Folgenden wird angenommen, dass mb eine Variable ist, die eine Modbus-Master- oder -Slave-Instanz enthält.
mb.connection(connectionpec [, txenablepin|RS232|RTS [, rxenablepin, [, txdisabledelay]])
-
Konfiguriert die Verbindungsmethode für die Modbus-Instanz.
- Für Modbus RTU besteht connectionspec aus einer seriellen Gerätespezifikation der Form
/dev[:commParams]
, wobei dev ein serielles Gerät ist und commparams[Baudrate][,[Bits][,[Parität][,[Stopbits][,[H]]]]]
sein kann, wobei die ParitätO
,E
oderN
sein kann undH
bedeutet, dass Hardware-Handshake aktiviert ist. Das zweite Argument kann entweder einen E/A-Pin angeben, der zum Aktivieren des Senders verwendet wird, RTS, um die RTS-Leitung des UART zum Aktivieren des Senders zu verwenden, und RS232, um überhaupt keine Sendertreiber zu verwalten (z. B. bei einer seriellen Punkt-zu-Punkt-Verbindung). rxenablepin kann eine zweite Leitung angeben, um den Empfänger separat zu aktivieren. txdisabledelay gibt die Verzögerung an, die zwischen dem Ende der Übertragung und der Deaktivierung der Sendertreiber eingefügt werden soll. - Für Modbus TCP gibt connectionspec den Hostnamen und den Port als
host[:port]
an. Für Slave-Instanzen ist host die lokale IP-Adresse, an die der TCP-Server gebunden werden soll - normalerweise 0.0.0.0 (alle Adressen). Für Master-Instanzen ist host der Hostname oder die Adresse des entfernten Slaves, zu dem eine Verbindung aufgebaut werden soll.
- Für Modbus RTU besteht connectionspec aus einer seriellen Gerätespezifikation der Form
mb.bytetime(byte_time_in_seconds)
- Legt die Zeit fest, die für die Übertragung eines Bytes benötigt wird. Dies ist nur für RTU relevant und wird nur benötigt, wenn der UART eine ungenaue Baudrate hat, so dass die tatsächlichen Byte-Zeiten erheblich von den theoretischen präzisen Werten abweichen, die von der Baudrate abgeleitet werden.
mb.recoverymode(link, protocol)
- Aktiviert/deaktiviert die Wiederherstellung auf der Link- und/oder Protokollebene, indem link und protocol auf true oder false gesetzt werden.
mb.debug(enable)
- Aktiviert die libmodbus-Debug-Ausgabe (direkt nach stderr, unter Umgehung des normalen p44-Log-Systems), indem enable auf true gesetzt wird.
mb.connect([autoflush])
- Öffnet die Modbus-Verbindung. Je nach RTU oder TCP und Master- oder Slave-Rolle öffnet dies die serielle Verbindung oder initiiert eine TCP-Verbindung zu einem Slave oder startet einen lauschenden TCP-Socket. Wenn autoflush auf true gesetzt ist, werden Daten, die möglicherweise bereits in einem seriellen Eingangspuffer vorhanden sind, gelöscht (verworfen), bevor begonnen wird, auf Modbus-Protokolldaten (PDUs) zu warten.
- Ein explizites Öffnen ist im Master-Modus nicht unbedingt erforderlich - nur wenn die Verbindung über mehr als eine einzige Modbus-Transaktion hinweg offen gehalten werden soll. Andernfalls öffnet der Aufruf einer der Master-Funktionen (siehe unten) die Verbindung, führt die Operation durch und schliesst die Verbindung automatisch.
mb.close()
- Schliesst die Modbus-Verbindung.
Funktionen für Modbus-Master
Hinweis: Im Folgenden wird davon ausgegangen, dass mbm eine Variable ist, die eine Modbus-Master-Instanz enthält.
mbm.slave(slaveaddress)
- Setzt die slaveaddress für Modbus RTU. Die Funktion gibt das Modbus-Master-Objekt zurück, so dass es zur Verkettung mit einem aktuellen Zugriff wie
mbm.slave(1).readreg(100)
verwendet werden kann. mbm.readinfo()
- Liest den Info-String vom aktuell adressierten Slave und gibt ihn zurück.
mbm.findslaves(idmatch, from, to)
- Versucht, die info (slave id) fuer jede Slave-Adresse ab from bis einschliesslich to zu lesen, und gibt ein Array mit Slave-Adressen zurück. Wenn idmatch kein leerer String ist, werden nur Slaves mit Slave-id-Strings, die idmatch enthalten, zurückgegeben.
mbm.readreg(regaddr [, input])
- liest das Register regaddr (input auf true setzen, um auf ein schreibgeschütztes Register zuzugreifen) und gibt es als Zahl ohne Vorzeichen zurück.
mbm.readsreg(regaddr [, input])
- liest das Register regaddr (input auf true setzen, um auf ein Nur-Lese-Register zuzugreifen) und gibt es als Zahl mit Vorzeichen zurück.
mbm.readbit(bitaddr [, input])
- Liest das Bit (Coil) bitaddr (input auf true setzen, um auf ein Nur-Lese-Bit zuzugreifen) und gibt seinen Zustand als booleschen Wert zurück.
mbm.writereg(regaddr, value)
- schreibt einen numerischen value in das Register regaddr.
mbm.writebit(bitaddr, state)
- schreibt den booleschen Zustand state in das Bit (Coil) bitaddr.
Funktionen für Modbus-Slave
Hinweis: Im Folgenden wird angenommen, dass mbs eine Variable ist, die eine Modbus-Slave-Instanz enthält.
mbs.slaveaddress([slave_address])
- Gibt die aktuell konfigurierte Slave-Adresse dieses Slaves zurück, oder setzt die Slave-Adresse, wenn slave_address angegeben ist. Die Slave-Adresse ist nur für Modbus RTU relevant.
mbs.slaveid(slave_id_string)
- Setzt die Slave-ID (den Info-String) auf slave_id_string. Dies ist der String, der von einem Master mit readinfo() gelesen werden kann.
mbs.setmodel(registermodel_json)
-
Definiert das Registermodell des Slaves (Coils, Bits, Registers und Inputs). registermodel_json muss ein JSON-Objekt der folgenden Form sein:
{ "coils" : { "first":100, "num":10 }, "bits" : { "first":100, "num":10 }, "registers" : { "first":100, "num":20 }, "inputs" : { "first":100, "num":20 } }
-
Von "coils", "bits", "registers", "inputs" können die nicht benötigten weggelassen werden.
mbs.setreg(regaddr, wert [, input])
- Schreibt den numerischen Wert value in das Register regaddr (oder in das Eingaberegister regaddr, wenn input auf true gesetzt ist).
mbs.setbit(bitaddr, state [, input])
- Schreibt den booleschen Wert state in das Bit (Coil) bitaddr (oder in das Eingabebit bitaddr, wenn input auf true gesetzt ist).
mbs.getreg(regaddr [, input])
- Liefert den vorzeichenlosen numerischen Wert des Registers regaddr (oder des Eingangsregisters regaddr, wenn input auf true gesetzt ist).
mbs.getsreg(regaddr [, input])
- Holt den vorzeichenbehafteten numerischen Wert des Registers regaddr (oder des Eingaberegisters regaddr, wenn input auf true gesetzt ist).
mbs.getbit(bitaddr [, input])
- Ermittelt den booleschen Zustand des Bits (Coil) bitaddr (oder des Eingangsbits bitaddr, wenn input auf true gesetzt ist).
mbs.access()
-
Dies ist eine Ereignisquelle. Wenn es in einem Handler verwendet wird wie z.B.
on(mbs.access()) as acc { ... }
, dann wird der Handler jedes Mal ausgeführt, wenn ein Master auf den Slave zugreift, wobei acc ein json-Objekt mit den folgenden Feldern zurückgibt:- "reg" : die Registeradresse, auf die zugegriffen wurde oder null
- "bit" : die Bit-Adresse, auf die zugegriffen wurde oder null
- "addr" : die Bit- oder Registeradresse, auf die zugegriffen wurde
- "input" : true, wenn auf ein Eingangsbit oder Eingangsregister zugegriffen wurde
- "write" : true, wenn auf ein Bit oder Register zum Schreiben zugegriffen wurde
DC-Motoren
Das DC-Motor-Objekt kann einen Analogausgang für den Motor-Power (meist PWM), ein oder zwei Digitalausgänge für die Kontrolle der Drehrichtung und bei geeigneter Hardware auch Umschaltung von Fahren/Bremsen, einen Analogeingang für die Überwachung des Motorstroms für Abschaltung bei Überlast, und schliesslich 2 Digitaleingänge für Endschalter zu einer komfortablen Motoransteuerung kombinieren.
var m = dcmotor(Ausgang [, Richtung1 [, Richtung2 ]])
- Erzeugt ein dcmotor-Objekt. Die Parameter können entweder schon vorher erzeugte I/O-Objekte sein, oder pinspec-Strings um die entsprechenden Signale direkt zu erzeugen. Ausgang muss ein analoger Ausgang sein, üblicherweise ein PWM, der die Motorleistung regelt. Für einen Motor mit nur einer Laufrichtung müssen keine weiteren Signale angegeben werden. Wenn der Motor (über eine H-Brücke) in beide Richtungen laufen kann, dann gibt entweder Richtung1 allein die Drehrichtung an, oder - wenn auch Richtung2 angegeben ist - steuert Richtung1 die eine Seite und Richtung2 die andere Seite der H-Brücke. In dieser Konfiguration ist dann auch aktives Bremsen und Halten des Motors möglich (beide Seiten der H-Brücke gleichgeschaltet).
Das von dcmotor() zurückgegebene Objekt hat folgende Methoden (m = mit dcmotor erzeugtes Objekt):
m.outputparams(Skalierung [, Offset])
- Wenn der Ausgang für die Motorleistung nicht den Wertebereich 0..100 hat (was nur bei PWMs normalerweise der Fall ist), kann die Umsetzung der 0..100 Leistungsangabe in den effektiven Ausgangswert angepasst werden. Standardmässig ist Skalierung = 1 und Offset = 0.
m.power(Power [, Drehrichtung [, Rampenzeit [, Rampenexponent]]])
- Verändert die Motorleistung auf Power (Wertebereich 0..100%). Drehrichtung kann 1 (default, vorwärts), -1 (rückwärts) oder 0 (bremsen) sein. Eine positive Rampenzeit gibt die Zeitdauer an, während der die Veränderung erfolgen soll. Eine negative Rampenzeit gibt die Zeitdauer für eine 0..100-Rampe an, die effektive Zeit wird proportional zur Veränderung bestimmt (kleine Leistungsänderung gibt kürzere Rampe). Standardmässig wird die Rampenzeit -1 (also 1 Sekunde für eine 0..100-Änderung) verwendet.
m.stop()
- Schaltet die Leistung ohne Verzögerung auf 0. Der Motor stoppt und läuft aus. Um zu bremsen, kann stattdessen z.B.
m.power(50,0)
verwendet werden (Bremsen mit 50% Leistung). m.status()
- Gibt den Status des Motors als Objekt mit den Feldern power, direction und falls ein Stromsensor konfiguriert ist, current zurück. Wenn der Motor von einem Endschalter oder der Strombegrenzung gestoppt wurde, gibt ein zusätzliches Feld stoppedby das an (enthält entweder "overcurrent" oder "endswitch"). status() ist eine Ereignisquelle und kann in
on(...) {...}
-Handlern als Auslöser verwendet werden. Der Status wird als Ereignis ausgelöst, wenn eine mit power() gestartete Rampe ihren Endwert erreicht hat, oder wenn der Motor von Endschaltern oder Strombegrenzung gestoppt wird. m.endswitches(PositivesEnde, NegativesEnde [, Entprellungszeit [, Pollinterval]])
- Definiert zwei digitale Eingänge (PositivesEnde, NegativesEnde, entweder als digitalIO-Objekt oder als pinspec) als Endschalter für die Endanschläge des Antriebs. Optional kann die Entprellungszeit und (für Eingänge, deren Hardware nicht von sich aus Signaländerungen erkennt) das Pollinterval eingestellt werden.
- Wenn ein Endschalter anspricht, wird der Motor auf jeden Fall gestoppt, auch wenn er eigentlich in die Richtung des anderen Endschalters läuft (sichert die Mechanik bei falsch herum angeschlossenem Motor). Damit der Motor aber aus dem Endschalter wegbewegt werden kann, wird power() nur jeweils in die Richtung zum aktiven Endschalter hin blockiert, aber es ist möglich den Motor in die andere Richtung zu starten.
m.currentsensor(Sensor [, Abfrageintervall])
m.currentsensor(null)
- Definiert (oder entfernt, wenn Sensor null ist) einen analogen Eingang Sensor als Motorstrom-Sensor, mit dem angegebenen Abfrageintervall. Wird kein Abfrageintervall angegeben, erfolgt die Abfrage 3 mal pro Sekunde.
m.currentlimit(Limit [, Anfahrzeit [, MaxAnfahrstrom]])
- Setzt die Strombegrenzung auf Limit. Der Wert ist nicht in A oder mA, sondern einfach der Wert wie ihn der Analogeingang zurückliefert (üblicherweise ein roher A/D-Wert, z.B. 0..4095 bei einem MAX11615). Wenn dieser Wert überschritten wird, stoppt der Motor. Da beim Anfahren u.U. ein höherer Strom benötigt wird als während der Fahrt, kann mit Anfahrzeit eine Dauer nach der Ausführung eines power()-Befehls angegeben werden, in der erlaubte Strom bis MaxAnfahrstrom gehen darf.
P44features - spezifische Funktionalität/Hardware-Unterstützung
Die folgenden Funktionen sind globale Funktionen, die den Zugriff auf "Features" (siehe hier für Details) und deren API von Skripten aus ermöglichen. P44features sind eine Bibliothek spezifischer Funktionalitäten, die in der Regel von plan44 für einen bestimmten Ausstellungs-/Kunst-Anwendungsfall entwickelt wurden (Beispiele siehe hier). Da einige dieser Funktionen auch für andere Anwendungen nützlich sein können, sind sie in einigen P44-XX-Produkten für die Verwendung mit p44script enthalten.
Hinweis: benötigt Aktivierung auf der Commandline
Weil die features jeweils spezielle Hardware voraussetzen, die nur in speziellen Geräten vorhanden ist, müssen zu verwendende features auf der Commandline beim Start des vdcd-daemons mit entsprechenden optionen aktiviert werden. Wenn gar kein feature aktiviert ist, dann wird das gesamte feature-System nicht aktiviert (auch um RAM zu sparen) und die folgenden Funktinen existieren nicht.
feature(name)
- Zugriff auf ein Merkmal über Name. Jedes Feature unterstützt einige allgemeine Methoden, wie unten gezeigt, und eine Reihe von Befehlen und Eigenschaften, die spezifisch für das Feature sind.
featurecall(json_request)
- Gibt json_request als eine Feature-API-Anfrage aus und gibt deren Antwort zurück (oder null, wenn der Aufruf keine Antwort hat). Das bedeutet, dass json_request auf die gleiche Weise verarbeitet wird, wie wenn es von einem Feature-API-Client über TCP gesendet wird. Ein Feature-API-Client ist eine externe Instanz, die über eine TCP-Socket-Verbindung auf die Feature-API zugreift, in der Regel ein System, das grosse Installationen, die aus vielen P44-xx-Modulen bestehen koordiniert.
featurecall()
- Ist eine Eventquelle, die Anfragen an die Feature-API liefert, die nicht anderweitig verarbeitet werden konnten. featurecall kann z.B. mit
on(featurecall()) as call { ... }
verwendet werden, um zusätzliche Feature-API-Kommandos in Scripten zu implementieren. Der Returnwert von featurecall() hat die Methode answer() um den Request zu beantworten. call.answer(json_oder_error)
-
Beantwortet einen Feature-API-Request (im Beispiel oben die Variable call) mit einer JSON-Antwort oder einem Fehler. Üblicherweise wird eine Antwort als Teil eines on() Handlers gesendet, mit einem Konstrukt wie z.B.:
on (featurecall()) as call { // request ausführen // ... call.answer({ "status": "alles ok"}); }
featurecall muss beantwortet werden!
Wenn ein
on(featurecall()) as call { ... }
installiert wird, muss sichergestellt sein, dass der Handler-Code den request in jedem Fall mit answer() auch wirklich beantwortet. Andernfalls hängt u.U. der anfragende Client. featureevent(json_event)
- Sendet einen Event an den aktuellen Feature-API-Client (falls vorhanden). Mit featureevent() kann ein Gerät den API-Client (das koordinierende System) über lokale Ereignisse wie aktivierte Sensoren usw. informieren.
featureevent()
- gibt Events (in JSON) zurück, die von aktiven Features ausgegeben werden, wie z. B. erkannte RFID, Eingabeänderungen usw. featureevent() kann an await() übergeben oder in
on (featureevent()) as event { do something with event }
-Handlern verwendet werden. Zu beachten ist, dass die Ereignisse auch an einen verbundenen API-Client gesendet werden, falls vorhanden.
Das Folgende sind Methoden des Feature-Objekts (siehe feature(...) oben). Für die folgenden Beispiele wird angenommen, dass das feature einer Variablen f zugewiesen ist.
f.status()
- gibt den Status des Merkmals zurück. Ist false, wenn das Feature nicht initialisiert ist, oder wenn initialisiert, ein Feature-spezifischer JSON-Wert oder ein Objekt.
f.init(init_json)
- initialisiert das Feature mit feature-spezifischen init_json Parametern. Einige einfache Features können keine Parameter haben, für diese können Sie true als init_json übergeben.
f.cmd(Befehl [, json_param_object])
- sendet command an die Funktion, mit optionalem json_param_object, das Befehlsparameter enthält.
f.set(property, value)
f.set(properties)
- setzt ein einzelnes property im Feature auf einen neuen Wert value, oder mehrere Eigenschaften zusammen gemäss den Feldern in properties.