Grundlegende Ausdrücke

Literale

1234
ganzzahlige Zahl.
1234.567
Fließkommazahl.
true oder yes
boolesches true, Zahlenwert ist 1.
false oder no
boolsch falsch, Zahlenwert ist 0.
null oder undefined
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 einzuschließen.
"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 außer 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ößer als
<= 3 Test auf kleiner als oder gleich
>= 3 Test auf größer 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.
binary(hexstring [, spacesallowed])
Konvertiert hexstring in einen String (der nicht-druckbare binäre Zeichen enthalten kann, einschließlich 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().
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()
gibt die ungefähre Zeit der Morgendämmerung am aktuellen Tag zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
day()
gibt den aktuellen Tag des Monats zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
dusk()
gibt die ungefähre Zeit der Abenddämmerung am aktuellen 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).
elements(object)
gibt die Anzahl der Elemente in object zurück, oder undefiniert, wenn array kein json-Wert ist.
epochtime()
gibt die Unix-Epochenzeit zurück (Sekunden seit 1.1.1970 Mitternacht UTC), aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
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)
wertet Zeichenkette als Ausdruck aus.
find(haystack, needle [, from])
liefert die Position von needle in haystack wahlweise ab from, oder null, wenn nicht gefunden.
format(formatstring, value [, value...])
formatiert value_s als String gemäß 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 Fließkommazahlen 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 behandelt und standardmäßig 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()
liefert die aktuelle 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 oder Fehler), sonst b.
int(a)
ganzzahliger Wert von a
isvalid(a)
liefert true, wenn a ein gültiger Wert ist (nicht null oder Fehler), sonst false.
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(path)
Lädt JSON aus path. Wenn path nicht absolut ist, wird er relativ zum Ressourcenpfad der Anwendung interpretiert (der normalerweise /usr/share/Anwendungsname ist). In JSON-Resourcefiles sind /* ... */-Kommentare erlaubt (obwohl das nicht der JSON-Spezifikation entspricht).
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 einschließlich a und b.
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ößeren Wert von a und b zurück.
min(a, b)
gibt den kleineren Wert von a und b zurück.
minute()
gibt die aktuelle Minute zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
month()
liefert den aktuellen 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 (Fließkomma-)Pseudo-Zufallswert von a bis einschließlich 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 etwa int(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, kann random(1,7,0.25) verwendet werden.
round(a [, p])
rundet a auf die optional angegebene Genauigkeit p (1=ganzzahlig=Standard, 0.5=Halbe, 100=Hunderter, etc...).
second()
gibt die aktuelle Sekunde zurück, kann aber nicht direkt zeitgesteuerte Aktionen auslösen.
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.
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()
Gibt die ungefähre Zeit des Sonnenaufgangs am aktuellen Tag zurück, kann aber nicht direkt zeitgesteuerte Aktionen auslösen (#timedtriggers).
sunset()
gibt die ungefähre Zeit des Sonnenuntergangs am aktuellen Tag zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
timeofday()
gibt die aktuelle Uhrzeit in Sekunden seit Mitternacht zurück, kann aber nicht direkt zeitgesteuerte Aktionen auslösen.
weekday()
liefert den aktuellen Wochentag (0..6), aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
year()
gibt das aktuelle Jahr zurück, aber kann nicht direkt zeitgesteuerte Aktionen auslösen.
yearday()
gibt das aktuelle Datum als Tageszahl im aktuellen 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 (einschließlich) 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 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 außerhalb 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.

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.

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.

(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äßig 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 ' + Ereignis)
}

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 skript-Kontext-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 skript-Kontext-lokale oder funktions-lokale 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 Script-Kontext, und werden dann Kontextvariable genannt. Es gibt verschiedene Kontexte mit je eigenen Variablen. In einem P44-DSB- oder LC-Gerät z.B. laufen alle Szenenskripte einer Leuchte in einem Script-Kontext 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 eines on(...) {...} oder catch {...}-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ößer 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ößer als Null ist.

break

verlässt eine while Schleife.

continue

startet die nächste Iteration in einer while Schleife.

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 eine der statements einen Laufzeitfehler erzeugt, werden die handling_statements ausgeführt und können error_var verwenden, um den Fehler zu untersuchen.

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 ab, außer 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).
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).
  • 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).
  • 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).
  • 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.
geturl(url [,timeout])
Gibt die Antwort einer GET-Anfrage an (http oder https) url zurück, bricht nach timeout Sekunden ab, falls angegeben.
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])
ä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.
logleveloffset(offset)
Ändert den Log-Level-Offset des Kontexts, in dem das Skript läuft, auf offset. Dies erlaubt es, einen Teil einer Anwendung selektiv mehr oder weniger ausführlich im Protokoll darzustellen.
posturl(url [,timeout][,data])
sendet data (muss ein String sein) mit POST an url, gibt Antwort zurück, bricht nach timeout Sekunden ab, wenn angegeben.
puturl(url [,timeout][,data])
sendet data (muss String sein) mit PUT an url, gibt Antwort zurück, bricht nach timeout Sekunden ab, wenn angegeben.
readfile(filename)
liest das File filename als String.

Hinweis

Um Pfade für filename zu verwenden, muss der Userlevel >=1 sein (Standard-Userlevel für Produktionsgeräte ist 0). Andernfalls können nur Files aus dem p44script Datenverzeichnis gelesen werden, oder wenn der Pfad mit "_/" beginnt, aus dem p44script-Temporärverzeichnis.

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. 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 ausgelöst (versendet) werden kann.
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 (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äßig 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()
Gibt die letzte auf dem Socket empfangene UDP-Nachricht zurück. Das Ergebnis ist eine Ereignisquelle und kann in on()-Anweisungen als Trigger-Bedingung 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()
Gibt die letzte auf dem Socket empfangene Websocket-Nachricht zurück. Das Ergebnis ist eine Ereignisquelle und kann in on()-Anweisungen als Trigger-Bedingung 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.

Hinweis

Um Pfade für filename zu verwenden, muss der Userlevel >=2 sein (Standard-Userlevel für Produktionsgeräte ist 0). Andernfalls können Files nur in das p44script Datenverzeichnis geschrieben werden, oder wenn der Pfad mit "_/" beginnt, ins p44script-Temporärverzeichnis.

Steuerung von geräte-/produkt­spezifischen 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 namens name (als generischen Preset-Namen wie etwa 'preset 1') 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'...... - Hinweis: channelIDs sind case-sensitive!) 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', 'ct', 'hPos' - Hinweis: channelIDs sind case sensitive!)
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!
output.dimchannel(channelid, valuechange, transition_time)
ändert den Wert von channelid um valuechange, mit optionaler transition_time in Sekunden. Hinweis: channelIDs sind case sensitive!
output.loadscene(sceneIdOrNo [, transitionTimeOverride])
lädt die Kanäle des Ausgangs mit den in der Szene sceneIdOrNo gespeicherten Werte. 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.).
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. einschließt) 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 abbrechen, wenn Benutzer Geräte umbenennen. Daher wird die Verwendung von dSUID für Skripte empfohlen, die lange Zeit installiert bleiben und funktionieren 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()
Gibt den letzten JSON-Request an den scriptapi-Endpoint der JSON-API zurück. webrequest ist eine Eventquelle und 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 einer Konstruktion wie z.B.:
on (webrequest()) as request {
  // request ausführen
  // ...
  request.answer({ "status": "alles ok"});
}

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 (0..1) und brightness stehen standardmäßig 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() kann 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äßig 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.

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 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äßer 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-Select
  • 3 : 3-Draht-Modus
  • R : SPI ready indication: Slave zieht nach 0 um zu pausieren
  • S : langsame Geschwindigkeit - 1/10 der normalen Geschwindigkeit für den Bus
  • s : 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äßer 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ät O, E oder N sein kann und H 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.
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()
Schließt 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 große Installationen, die aus vielen P44-xx-Modulen bestehen koordiniert.
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.