Über p44script

p44script ist eine spezielle Skriptsprache, die vollständig in Ihr P44-Gerät eingebettet ist, einschliesslich einer voll funktionsfähigen integrierten Entwicklungsumgebung (p44script IDE) und dieser kurzen Referenz. Es hat keine externen Abhängigkeiten und läuft völlig eigenständig ohne Internetzugang.

p44script wurde entwickelt, um auf einfache Weise zuverlässige Automatisierungen für Lichter, Sensoren, bewegliche Elemente und vieles mehr im Rahmen einer Smart-Home-Installation oder als völlig unabhängige Steuerungen zu erstellen. p44script-Automatisierungen können jahrelang unbeaufsichtigt laufen, lassen sich aber bei Änderungen einfach warten - ausser einem Webbrowser sind keine externen Tools erforderlich. Geschriebene Skripte sind Teil der Konfiguration des P44-Geräts, werden mit ihm gesichert und können im Falle eines Hardwaretauschs leicht auf einem anderen Gerät wiederhergestellt werden.

p44script hat eine ähnliche Syntax wie Javascript oder C, verfügt aber über eine Reihe von Funktionen, die auf Automatisierungsaufgaben zugeschnitten sind und sich von den Möglichkeiten von Javascript unterscheiden. Insbesondere concurrent in-line execution und on()-handlers sind mächtige Werkzeuge, die spezifisch für p44script sind.

Diese Seite ist kein Tutorial - bitte besuchen Sie die on-line docs und das plan44 user forum für zusätzliches Material.

Doku beschreibt die neueste Version von p44script

Diese Kurzreferenz bezieht sich auf die neueste verfügbare Version von p44script. Wenn etwas nicht so funktioniert wie hier beschrieben kann es daran liegen, dass auf Ihrem Gerät nicht die neueste Firmware installiert ist. Die zur jeweiligen Firmware passende Kurzreferenz ist aber immer in die Firmware fest eingebaut und mit dem kleinen Link rechts unten bei den Script-Feldern oder in der p44script IDE aufrufbar.


Kommentare

Kommentare sind nützlich, um Hinweise zum Quellcode hinzuzufügen, die den Code oder wichtige Zusammenhänge für einen menschlichen Leser des Skripts oder Ausdrucks erklären. Kommentare werden von der Skript-Engine ignoriert, ebenso wie Leerzeilen und Leerzeichen.

/* Kommentar */
Kommentar im C-Stil, beginnt mit /* und setzt sich, möglicherweise über mehrere Zeilen, fort, bis ein */ gefunden wird
// comment
Einzeiliger Kommentar im C++-Stil, beginnt mit // und endet am Ende der gleichen Zeile

Literals

Es gibt verschiedene Möglichkeiten, Zahlen, Strings und einige spezielle Werte einzugeben:

1234
Ganzzahlige Zahl, dezimal
0x89ab
Ganzzahlige Zahl in hexadezimaler Darstellung.
1234.567
Fliesskommazahl.
true oder yes
boolesches true, numerischer Wert ist 1.
false oder no
boolescher Wert false, numerischer Wert ist 0.
null oder undefined
kein Wert. Hinweis: p44script verfügt über annotierte Nullwerte, was bedeutet, dass in vielen Fällen null/undefined auch mit einem kurzen Text versehen ist, der seine Herkunft beschreibt. Dies ist bei der Fehlersuche nützlich, da es hilft zu verstehen, warum ein Wert null/undefiniert ist. Sie können kommentierte Null-Werte mit der Funktion null() erstellen.
12:30
Zeitangabe in Stunden und Minuten. Der numerische Wert ist die Anzahl der Sekunden seit Mitternacht.

13:30:22 Zeitangabe in Stunden, Minuten und Sekunden. Der numerische Wert ist die Anzahl der Sekunden seit Mitternacht.

2.9.
Datumsangabe im Format Tag.Monat. (Punkt am Ende ist wichtig!). Das Beispiel bedeutet 2. September. Der numerische Wert ist die Anzahl der Tage seit Beginn des laufenden 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. Der numerische Wert ist die Anzahl der Tage ab dem Beginn des laufenden Jahres.
wed
Wochentagsangabe. Wochentagsnamen sind sun, mon, tue, wed, thu, fri, sat. Numerischer Wert ist 0 für Sonntag, 1..6 für Montag..Samstag.
''Text''
String-Literal, verwenden Sie zwei einfache Anführungszeichen hintereinander, um ein einfaches Anführungszeichen einzuschliessen.
"Text"
String-Literal mit C-ähnlichen Escapes (\n, \r, \t, \xHH und \\ können verwendet werden).
{ 'field1':42, 'field2':'hello' } // strict JSON
{ field1:42, field2:'hello' } // JavaScript-Stil, Feldnamen brauchen keine Anführungszeichen
Objekte mit benannten Feldern.
[ 42, 43, 'hello' ]
Array.

Benannte Werte

In Ausdrücken können benannte Werte (z.B. Sensorwerte in Auswertebedingungen, oder Variablen in Skripten, siehe unten) verwendet werden. Einfache Werte bestehen nur aus einem Namen, aber es gibt auch strukturierte Werte, auf die mit Punkt- und Eckige-Klammer- Notation zugegriffen werden kann, zum Beispiel JSON-Daten oder Objekte/Arrays.

Sensor1
der Wert eines Sensors mit dem Namen sensor1.
sensor1.age
die Zeit in Sekunden, seit der Sensor mit dem Namen 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.
object.fieldA
das Unterfeld namens fieldA in einem object. Mit object={ 'fieldA':42, 'fieldB':'hello' } wäre das Ergebnis also 42.
array[1]
das Array-Element mit dem numerischen Index 1 in einem Array array. Der Index ist null-basiert, wenn also array=[ 42, 'world' ], wäre das Ergebnis 'world'.
object['fieldB']
das Unterfeld namens fieldB in einem Objekt object. Mit object={ 'fieldA':42, 'fieldB':'hello' } wäre das Ergebnis also 'hello'.
object[0]
der Name des ersten (Index==0) Teilfeldes in einem Objekt. Mit object={ 'fieldA':42, 'fieldB':'hello' } wäre das Ergebnis also 'fieldA'. Auf diese Weise können Objekte untersucht werden, die Felder mit unbekannten Namen enthalten (wie sie z. B. von APIs zurückgegeben werden). Die Funktion elements() kann verwendet werden, um die Anzahl der Felder in einem Objekt zu ermitteln. Eine andere Möglichkeit, alle Felder eines Objekts durchzugehen, ist die Anweisung foreach, siehe unten.

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, Array-Verkettung, wenn beide Ausdrücke Arrays sind, Zusammenführung von Objektfeldern, wenn beide Ausdrücke Objekte sind.
- 4 Subtraktion
== 3 Test auf Gleichheit
= 3 Test auf Gleichheit (je nach Kontext kann = auch eine Zuweisung sein, verwenden Sie ==, um Zweideutigkeiten 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)
++ 0 (niedrigste) inkrementiert die Variable auf der linken Seite (a++ ist äquivalent zur Zuweisung a = a+1)
-- 0 (niedrigste) dekrementiert die Variable auf der linken Seite (a-- entspricht der Zuweisung a = a-1)
+= 0 (niedrigste) addiert zu der Variablen auf der linken Seite (a += 2 entspricht der Zuweisung a = a+2)
-= 0 (niedrigste) subtrahiert von der Variablen auf der linken Seite (a -= 2 entspricht der Zuweisung a = a-2)
*= 0 (niedrigste) Multiplizieren der Variablen auf der linken Seite (a *= 2 entspricht der Zuweisung a = a*2)
/= 0 (niedrigste) Multiplizieren der Variablen auf der linken Seite (a /= 2 entspricht der Zuweisung a = a/2)

Hinweis: p44script verfügt nicht über bitweise und/oder Operatoren, sondern bietet stattdessen die bit(), setbit() und flipbit() Funktionen, die Bit(feld)-Manipulationen auf intuitivere Weise ermöglichen.

Variablen

Lokale Variablen

Lokale Variablen stellen Werte dar, die für den Kontext, in dem das Skript läuft, lokal und privat sind. In einem P44-DSB oder LC-Gerät beispielsweise laufen alle Szenenskripte eines Geräts in einem Skriptkontext dieses Geräts, d.h. die Szenenskripte untereinander sehen dieselben lokalen Variablen, aber andere Geräte oder andere Geräte haben keinen Zugriff darauf (und können daher ihre eigenen Variablen mit demselben Namen ohne Konflikte verwenden). Variablen, die in einer Benutzerfunktion definiert sind, sind privat/lokal für diese Funktion. Mit threadvar definierte Variablen sind sogar lokal für einen einzelnen Ausführungslauf (Thread), auch wenn derselbe Code mehrfach parallel ausgeführt wird.

var a scriptcontext: erzeugt eine scriptcontext lokale oder funktionslokale (wenn in einer Funktion verwendet) Variable a (anfangs auf undefined gesetzt. Wenn a bereits existiert, passiert nichts.

var a = 7
Definition einer scriptcontext lokalen oder funktionslokalen Variable a und Zuweisung eines Anfangswertes. Wenn die Variable bereits existiert, ist dies wie eine normale Zuweisung.
threadvar t
threadvar t = 7
Definition einer thread-lokalen Variable t (und Zuweisung eines Anfangswertes, falls nötig). Im Gegensatz zu var hat diese Variable einen privaten Wert für jeden laufenden Ausführungs-Thread (d.h. die Ausführung eines on(...) {...} oder catch {...} Handlers, von denen es mehr als einen parallel geben kann).

Hinweis "Automatisch erzeugte Thread-Variablen" Thread-Variablen werden auch automatisch durch on(...) as var {...}, concurrent passing var {...} oder catch as var {...} erzeugt, da der Wert für jeden (möglicherweise parallelen) Lauf eines solchen Codes unterschiedlich sein kann. (Wohingegen concurrent as var nicht eine Thread-Variable erzeugt, sondern eine im Kontext des Skripts, das concurrent ausführt).

Globale Variablen

Globale Variablen sind Werte, die von jedem Skript in einem P44-Gerät unter einem gemeinsamen Namen geschrieben und gelesen werden können.

Sichtbarkeit von globalen Variablen

Da globale Variablen von allen Skripten aus sichtbar sind, empfiehlt es sich, eindeutige Namen zu verwenden, die nicht zufällig auch anderswo vorkommen. Im Allgemeinen sollten globale Variablen sparsam verwendet werden und nur dann, wenn lokale Variablen nicht ausreichen.

glob g
global g
Deklaration der globalen Variable g. Deklaration bedeutet, dass der Name g allen Skripten im Gerät bekannt gemacht wird. Werte, die g von einem Skript zugewiesen werden, werden für alle Skripte zugänglich. Der Wert einer globalen Variable bleibt erhalten, solange das Gerät läuft (es sei denn, er wird explizit mit unset entfernt, siehe unten).
glob g default 78/9
Deklaration der globalen Variable g und Initialisierung mit einem Standardwert, wenn die Variable noch nicht existiert.
Bitte beachten, dass der Initialisierungsausdruck nur aus Konstanten oder anderen globalen Variablen bestehen kann, die bereits definiert sind.

Die Initialisierung erfolgt einmalig und vor der Ausführung

Die Initialisierung von globalen Variablen erfolgt nur einmal, normalerweise lang vor der eigentlichen Ausführung, wenn das Skript zum ersten Mal von der p44script-Engine geparst wird (beim Gerätestart oder wenn ein Skript geändert wird).

Das kann verwirrend sein, denn wenn das Skript tatsächlich ausgeführt wird, sind alle globalen Variablen bereits initialisiert, wenn die erste Skriptzeile ausgeführt wird, auch wenn der Initialisierer mitten im Text geschrieben wurde. Wenn außerdem mehrere Skripte versuchen, dieselbe globale Variable mit unterschiedlichen Werten zu initialisieren, wird nur das erste Skript einen Standardwert setzen.

Um Konfusion zu vermeiden, ist es ratsam, die Deklarationen globaler Variablen an den Anfang von Skripten zu stellen und default-Initialisierer nur zu verwenden, wenn sie wirklich gebraucht werden. Hinweis: In früheren Versionen von p44script war es möglich, = oder := für die Zuweisung des Default-Werts zu verwenden, was noch mehr wie eine reguläre Zuweisung aussah, deshalb wird diese Syntax nicht mehr unterstützt.

Zuweisung von Werten an Variablen

a := 3*4
Zuweisung des Ergebnisses eines Ausdrucks an a.
a = 3*4
Ein einzelnes 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).
obj = {}
Erzeugt ein leeres Objekt
obj.c = 7+8
weist das Ergebnis des Ausdrucks einem Feld namens c der Objekt-Variablen obj zu. Wenn das Feld c in obj nicht existiert, wird es erzeugt. Andernfalls wird der frühere Wert des Feldes c durch das Ergebnis des Ausdrucks ersetzt.
arr = []
ein leeres Array erzeugen
arr[] = 6
Anhängen des neuen Elements 6 an das Array arr

arr[2] = 7 7 als das 3. (nicht das 2., Array-Indizes sind nullbasiert!) Element des Arrays arr zuweisen

obj = {
  a: 42, b:'a string', c:[ 1, 2, a*2], d: { n:1, sin:sin(a), ["a_is_"+a)]:13 }
}
In einer einzigen Anweisung können komplexe Objekt-/Array-Strukturen erstellt und mehreren Feldern Ausdrücke zugewiesen werden. Sogar Feldnamen können aus Ausdrücken erzeugt werden, indem sie in eckige Klammern eingeschlossen werden (siehe letztes Feld im Beispiel).

Löschen von Variablen

ungesetzt b Löscht eine (lokale oder globale) Variable namens b.

unbestimmt a.b
unset a['b']
Entfernen des Feldes b aus der Objektvariablen a.
unset c[2]
Entfernen des Elements mit dem Index 2 (= drittes Element) aus einem Array namens c.

Mehrere Script-Anweisungen

a = 7; b = 3; c = a*b;
Mehrere Anweisungen können in eine einzige Zeile geschrieben werden, getrennt durch Semikolon.
{ a = 42; b = 23; scene(_'bright'_); }
Block (Gruppe) von mehreren Anweisungen

Kontrollfluss

Konditionale

if (a>0) b := 7
bedingte Ausführung der Zuweisung b:=7, wenn a grösser als Null ist.

wenn (a>0) b := 7 sonst 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 := "a string }
Bedingung mit Anweisungsblöcken.

Schleifen

while (i>0) { i := i-1; log(5, "i ist jetzt: " + i); }
Wiederholung (Schleife) des Anweisungsblocks, solange i grösser als Null ist.
for (var j:=0; j<5; j++) { log(5, "j ist jetzt: " + j); }
initialisiert zunächst die Schleife, indem j erzeugt und auf 0 gesetzt wird, und wiederholt dann den (Schleifen-)Anweisungsblock, solange j kleiner als 5 ist, und erhöht j am Ende jeder Iteration (_j++).
break
Beendet eine while, for oder foreach Schleife.
continue
Startet die nächste Iteration in einer while, for oder foreach Schleife.

foreach objectOrArray as value { do_something_with_value }
foreach objectOrArray as key,value { do_something_with_key_and_value }
foreach objectOrArray as 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.

Rückgabe

return
Return: Beendet das aktuelle Skript oder die benutzerdefinierte Funktion mit null/undefined Rückgabewert.
Rückgabewert
Beendet das aktuelle Skript und gibt Wert zurück.

Fehler abfangen

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

return wird als erfolgreich angesehen, auch wenn es einen Fehler zurückgibt

Eine return-Anweisung erzeugt selbst keinen Fehler, auch wenn sie einen Fehler zurückgibt. D.h.. try { return error('error') } catch as e { ... } fängt den 'Fehler' nicht mit catch ab, sondern gibt ihn an die aufrufende Funktion zurück.

throw(irgendwas)
erzeugt einen Laufzeitfehler mit irgendwas als Fehlermeldung. Wenn irgendwas bereits ein Fehlerwert ist, wird der Fehlerwert als solcher zurückgeworfen.

Benutzerdefinierte Funktionen

function hello()
{
  log('Hello World');
}
Definiert eine global verfügbare Funktion namens hello, die den Text 'hello world' protokolliert.
global function sum(a,b)
{
  var c = a+b
  return c
}
Definiert eine global verfügbare Funktion namens sum mit zwei Argumenten, die die Summe berechnet und in einer lokalen Variablen c zurückgibt (die nur während der Ausführung der Funktion privat existiert). 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 lokal mit var definierten Variablen, die Variablen des Kontextes, aus dem die Funktion aufgerufen wird (siehe lokale Variablen) und schliesslich die globalen Variablen sichtbar. Wenn eine in der Funktion definierte Variable den gleichen Namen hat wie eine des aufrufenden Kontexts oder eine globale Variable, dann wird die lokalste Variable in Ausdrücken und Zuweisungen 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 vermeiden, dass eine Funktion, die keinen allgemeinen Nutzen hat (z.B. eine, die spezifisch für eine bestimmte Scripted Device-Implementierung ist), global aus anderen Kontexten sichtbar ist, wo ihr Aufruf keinen Sinn macht oder sogar Schaden anrichten würde.

(Ereignis-)Handler

Ein Handler ist ein spezielles Konstrukt, das p44Script zur Verfügung stellt, um auf einfache Weise auf asynchrone Ereignisse aller Art zu reagieren, wie z.B. die Änderung eines Sensorwertes, einen Tastendruck, einen Web-Zugriff, die Beendigung eines Threads, das Erreichen einer bestimmten Uhrzeit oder das Ablaufen eines Intervalls und vieles mehr.

Im Grunde sind Handler wie Funktionen, die automatisch aufgerufen werden, wenn eine bestimmte Bedingung erfüllt ist (und nicht von anderem Skriptcode aufgerufen werden).

Handler machen die Implementierung asynchroner Aufgaben - die in der Automatisierung sehr häufig vorkommen, aber in vielen Standard-Skriptsprachen nicht so einfach zu implementieren sind - recht einfach und leicht verständlich.

var s = udpsocket("127.0.0.1", 4242, true, true);
on (s.message()) as msg
{
  log('Nachricht auf UDP-Port 4242: '+string(msg))
}
Zuerst wird ein Netzwerk Socket erstellt und geöffnet, der auf UDP-Pakete an Port 4242 wartet, und dann ein Handler definiert, 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")

Behandlung von zeitlich begrenzten Ereignissen

Kombiniert mit Funktionen zur zeitgesteuerten Auslösung können Handler zeitabhängige Aufgaben ausführen:

on (after_time(12:00))
{
  log('Mittag ist vergangen ' + string(timeofday()-12:00) + ' vor Sekunden')
}
Definiert einen Eventhandler (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 Sekunden, die seit der Mittagszeit vergangen sind - denn wenn dieser Handler irgendwann nach 12:00 installiert wird, wird er sofort einmal ausgeführt.
Standardmässig werden die Handler ausgeführt, wenn die Auslösebedingung von false auf true wechselt (aber nicht, wenn sie von true auf false wechselt).
on (every(0:15, 0))
{
  log('eine weitere Viertelstunde ist vergangen');
}
Definiert einen Event-Handler, der jede volle Viertelstunde ausgeführt wird.

Behandlung von Wertänderungen über Ereignisquellen

on (sensor1>20) toggling
{
  log('sensor1 ist ' + sensor1);
}
Definiert einen Event-Handler, der immer dann ausgelöst wird, wenn die Bedingung von false zu true oder umgekehrt wechselt. Hinweis: sensor1 muss eine Ereignisquelle sein, damit dies funktioniert. Kontexte, die Hardware-Eingänge wie z.B. Sensoren haben haben, stellen diese normalerweise als Ereignisquellen zur Verfügung (z. B. in P44-DSB-Evaluatoren). Zum Beispiel sind valuesource() oder device.sensor() Ereignisquellen.
on (sensor2) changing
{
  log('sensor2 ist ' + sensor2)
}
Definiert einen Event-Handler, der immer dann ausgelöst wird, wenn die Bedingungsausdrücke zu einem anderen Ergebnis als bei der letzten Auswertung führen.
on (sensor2) auswerten
{
  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 Ereignisquellen kann dies dazu führen, dass der Handler sehr oft ausgeführt wird, was zu Leistungsproblemen führen kann.
on (sensor1+sensor2) changing as sensorsum
{
  log('sensor1+sensor2 ist ' + sensorsum)
}
Definiert einen Event-Handler, der ausgelöst wird, 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 ausgelöst wird, wenn sensor1 für mindestens 5 Minuten über 22 bleibt.
on (featureevent()) as event
{
  log('Ereignis empfangen ' + Ereignis)
}
Definiert einen Event-Handler, der ausgelöst wird, wenn featureevent() ausgelöst wird und speichert das Ereignis in der Variablen event.
Hinweis: Für Ereignisquellen, die einmalige Ereignisse liefern, wie featureevent() es tut, ist es wichtig, den tatsächlichen Auslösewert in einer Variablen mit as zu speichern.
var midi = midibus('/dev/midi1')
on (midi.control(0,42)) stable 0.1 as knob42 {
  log('Control #42: %d', knob42.value)
}
Definiert einen Event-Handler, der auf Änderungen am MIDI-Controller #42 auf Kanal 0 reagiert.
Bei Handlern, die von Ereignisquellen (wie control() eine ist) ausgelöst werden, hat stable eine leicht andere Bedeutung: Im Gegensatz zu Zuständen - wie z.B. "ein Sensorwert ist grösser als x" - sind Ereignisse einmalig und per Defintion nicht "stabil", und stable definiert das minimale Zeit-Interval zwischen zwei Auslösungen des Handlers. Wenn die Ereignisse schneller erfolgen, werden sie verwworfen, bis auf das zuletzt aufgetretene. Im gezeigten MIDI-Beispiel bewirkt stable 0.1 dass die Control-Änderungen nicht öfters als eimal pro Zehntelssekunde den Handler auslöst.

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 möglicherweise den normalen Betrieb des Geräts blockieren oder verschlechtern.

Gleichzeitigkeit (Threads)

p44script verfügt über ein sehr komfortables Konstrukt zur Erstellung von gleichzeitig ablaufenden Sequenzen von Operationen, etwas, das in der Automatisierung oft benötigt wird. Eine gleichzeitig ablaufende Folge von Skriptanweisungen wird auch Thread genannt. Beachten Sie, dass die oben gezeigten Ereignis-Handler auch gleichzeitig mit anderen Skript-Aktivitäten ausgewertet und ausgeführt werden.

concurrent {
  while(true) { log('blink1'); delay(3) }
}
concurrent {
  while(true) { log('blink2'); delay(2.7) }
}
delay(0:02); abort();
Zwei asynchrone Blinksequenzen werden parallel ausgeführt. Nach 2 Minuten werden beide gleichzeitigen Threads mit der Funktion abort() gestoppt.
concurrent als Blinker {
  while(true) { log('blink'); delay(2) }
}
delay(0:02); abort(blinker)
Starte eine gleichzeitige Blinksequenz und speichere 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(aufgabe1,aufgabe2)
log('eine der beiden Aufgaben erledigt')
await(aufgabe1)
await(aufgabe2)
log('beide Aufgaben sind jetzt erledigt')
Starten zweier gleichzeitiger Aufgaben mit einer zufälligen Dauer zwischen 2 und 5 Sekunden.
Die Funktion await() kann verwendet werden, um auf den Abschluss eines ihrer Argumente zu warten. Die Verwendung eines separaten await()-Aufrufs für jeden Thread führt zur Beendigung, wenn alle Aufgaben erledigt sind. Für komplizierte Parallelität siehe auch lock() und signal() zur Synchronisierung von Threads.

Gleichzeitige Übergabe x, y=2*a { log("x=%s, y=%s", x, y) } Das Schlüsselwort passing erzeugt threadvar_s (Variablen mit privaten Werten für den gestarteten Thread) und übergibt Werte (_x von einer externen Variablen namens x, y durch Zuweisung des Ausdrucks 2*a) an den Thread. Dies ermöglicht die Verwendung von concurrent mehrere Male in schneller Folge, während sichergestellt wird, dass jeder gestartete Thread seinen eigenen Satz von Parametern erhält.

Eingebaute Funktionen

p44Script hat viele eingebaute Funktionen und Methoden (Funktionen, die Teil eines Objekts wie eines Geräts oder einer Ansicht usw. sind). Nicht alle von ihnen sind in jedem Kontext verfügbar. Daher hat dieser Abschnitt Unterabschnitte, und einige eingebaute Funktionen/Methoden sind weiter unten zusammen mit der Beschreibung ihres Kontexts (Gerät, Hardware usw.) aufgeführt

Allgemeine Berechnungsfunktionen

Diese Funktionen stehen in jedem Ausdruck/jeder Berechnung zur Verfügung und sind echte Funktionen im mathematischen Sinne (keine Seiteneffekte, nur Bereitstellung der Berechnungsergebnisse).

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. Siehe auch die Funktionen flipbit() und setbit().
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, der 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().
cos(Winkel)
Gibt den Kosinus von Winkel zurück (in Grad)
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.
Siehe auch bit() und setbit() Funktionen.
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 der Zugriff auf a keinen Fehler ergibt, andernfalls 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 jsonfile 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) zu verwenden ist.

Hinweis

Um andere Pfade oder Präfixe als _/ für jsonfile zu verwenden, muss der userlevel >=1 sein (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.
ln(n)
gibt den natürlichen Logarithmus von n zurück (Logarithmus zur Basis e = 2.718281828459045). ln() ist die Umkehrfunktion von exp().
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 (aus einer automatischen Prüfung oder einer kürzlich durchgeführten manuellen Prüfung auf ein Firmware-Upgrade). Andernfalls wird ein leerer String zurückgegeben.
null(annotation)
p44script erzeugt einen annotierten Nullwert, d.h. einen null Wert, der mit einem Text versehen ist, der in Protokollen und bei der Fehlersuche angezeigt wird - dieser Anmerkungstext hat keine andere Funktion als die der Dokumentation. p44script selbst erklärt oft, warum etwas null ist, indem es Anmerkungen verwendet; die Funktion null() erlaubt es Skriptautoren, dies ebenfalls zu tun.
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 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.
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).
Siehe auch flipbit() und bit()-Funktionen.
sin(winkel)
Gibt den Sinus von Winkel (in Grad) zurück.
string(irgendwas)
gibt eine String-Repräsentation von irgendwas zurück. Gibt immer einen beschreibenden String zurück, auch für null/undefined 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, hintereinander zusammengefügt, besteht.
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 zeitgesteuerte Triggerung

Dies ist eine spezielle Klasse von Funktionen, die es nur in p44script gibt. Diese können in Ausdrücken verwendet werden, die eine Aktion zu einem bestimmten Zeitpunkt auslösen sollen. Bitte beachten Sie, dass Sie diese Funktionen verwenden müssen, um auf Zeit/Datum in Auslösebedingungen von Evaluatoren oder Handlern zu testen. Einfach etwas zu schreiben wie timeofday()==12:30 wird nicht funktionieren!

Allgemeine Funktionen, die Zeitwerte wie sunrise() oder dusk() zurückgeben, sind als Argumente für is_time() oder after_time() gedacht, 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(seconds, timedtest [, retrigger])
gibt beim ersten Aufruf undefined/null zurück, plant aber eine erneute Auswertung nach angegebenen seconds und gibt dann den Wert von test zurück. Wenn retrigger true ist, wird die erneute Auswertung im Intervall von seconds wiederholt (mit Vorsicht verwenden!). Hinweis: testlater() ist hauptsächlich aus Gründen der Abwärtskompatibilität mit älteren Setups verfügbar, bevor die allgemeinere every() Funktion in p44script existierten.

Script-Programmierfunktionen

Die meisten dieser Funktionen sind keine Funktionen im mathematischen Sinne, da sie Nebeneffekte haben, d.h. sie bewirken etwas im System, wie z.B. den Zugriff auf das Netzwerk oder die Gerätehardware oder allgemeine Änderungen des aktuellen Systemzustands (wie z.B. die Änderung des Loglevels).

abort()
bricht alle gleichzeitig laufenden Threads im aktuellen Scriptkontext ab, ausser dem Thread, von dem aus abort() aufgerufen wird. Die abgebrochenen Threads erhalten einen Error als Endresultat. Diese Form von abort() ist v.a. für Debugging-Zwecke in der IDE/REPL gedacht.
abort(a [, abortresult, [ self]])
bricht gezielt 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.
breakpoint().
Wenn ein Skript in der p44script IDE debuggt wird, hält der Debugger an, wenn er auf die Funktion breakpoint() trifft. Ohne aktivierte Fehlersuche bewirkt breakpoint() nichts. Hinweis: Normalerweise ist es einfacher, Haltepunkte in der p44script IDE zu setzen, indem man in die Zeilennummernleiste des Editors klickt, so dass diese Funktion selten benötigt wird.
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 Netzwerk, die ihre Dienste über das DNS-SD-Protokoll auffindbar machen (z.B. Drucker, aber auch viele andere Geräte, insbesondere im Bereich der Heimautomatisierung, einschliesslich der P44-xx-Geräte). Mit type muss ein DNS-SD-Service-Typ angegeben werden. Um z.B. Web-Interfaces zu finden, kann _http._tcp angegeben werden (siehe hier für eine vollständige Liste von Service-Typen). Durch die Angabe von hostname kann ein bestimmtes Gerät anhand seines Hostnamens gesucht werden (in der Form devicehostname.local). Als Ergebnis gibt dnssdbrowse ein Array zurück, das leer sein kann, wenn kein Dienst des angegebenen Typs (und Hostname, falls zutreffend) gefunden wurde. Andernfalls enthält das Array Objekte, die den Service mit den folgenden Feldern beschreiben:

  • 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().
hsv(hue [, saturation [, brightness [, alpha]]])
hsv(object)
hsv(webcolor_string)
Die erste Form konvertiert die angegebenen Werte hue (0..359), saturation (0..1), brightness (0..1) und alpha (0..1) in das hexadezimale 6-stellige (oder 8-stellige, wenn alpha angegeben und ungleich 1.0) Web-Farbformat (#rrggbb oder #aarrggbb), das für die Einstellung von bgcolor oder color in p44lrg-Ansichten oder wo auch immer Web-Farben benötigt werden, verwendet werden kann. saturation, brightness und alpha sind standardmäßig auf 1 gesetzt, wenn sie nicht angegeben werden.
Die zweite Form konvertiert ein object der Form { hue:60, saturation:0.85, brightness:0.5, a:1.0 } in eine Webfarbe auf die gleiche Weise wie die erste Form.
Die dritte Form konvertiert webcolor_string in ein Objekt der gleichen Form wie oben beschrieben.
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 vollständige URL. Kann ein ! als erstes Zeichen enthalten, um die Zertifikatsprüfung für 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 Stammzertifikat oder einem Zertifikatsverzeichnis, das für die Überprüfung des Serverzertifikats verwendet werden soll. Wird ein leerer String angegeben, wird das Serverzertifikat nicht geprüft (dies kann auch mit einem ! vor der URL erreicht werden, siehe oben). Standardmässig wird das Serverzertifikat mit den Stammzertifikaten des Systems geprüft.
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 Loglevel 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 ein Objekt/Array sein) mit POST an url, gibt die Antwort zurück und beendet den Vorgang nach timeout Sekunden, falls angegeben. Wenn data ein Objekt/Array-Wert ist, werden die Daten in JSON konvertiert und mit Content_type: application/json gesendet, ansonsten mit Content_type: application/x-www-form-urlencoded.
Wenn der url ein ! vorangestellt ist, werden SSL-/Zertifikatsfehler ignoriert. Für http(s)-Zugriff mit mehr Optionen siehe httprequest().
puturl(url [,timeout][,data])
sendet data (muss ein String oder ein Objekt/Array sein) mit PUT an url, gibt die Antwort zurück und bricht nach timeout Sekunden ab, falls angegeben. Wenn data ein Objekt/Array-Wert ist, werden die Daten mit Content_type: application/json gesendet, ansonsten mit Content_type: application/x-www-form-urlencoded.
Wenn der url ein ! vorangestellt ist, 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 Anwendung neu (auf P44-xx-Geräten ist dies der vdcd). Ohne Argumente wird nur die Anwendung neu gestartet, nicht das ganze Gerät (dies dauert normalerweise nur ein paar Sekunden, kann aber länger dauern, wenn viele DALI-Geräte angeschlossen sind). Dieser "kleine" Neustart ist besonders bei der Entwicklung/Testung komplexerer Skripte nützlich.

Mit "reboot" als Parameter wird das Gerät 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 (Vorsicht, wenn Sie nicht vor Ort sind!). Mit "Upgrade" wird ein Upgrade auf die neueste verfügbare Firmware ausgelöst (was in jedem Fall einen Neustart auslöst, auch wenn keine neue Firmware verfügbar ist).

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!

rgb(rot [, grün [, blau [, alpha]])
rgb(objekt)
rgb(webcolor_string)
Die erste Form konvertiert die angegebenen Werte rot (0..1), grün (0..1), blau (0..1) und alpha (0..1) in das hexadezimale 6-stellige (oder 8-stellige, wenn alpha angegeben und ungleich 1.0 ist) Web-Farbformat (#rrggbb oder #aarrggbb), das zum Setzen von bgcolor oder color in p44lrg-Ansichten oder wo immer sonst Web-Farben benötigt werden, verwendet werden kann. green, blue und alpha sind standardmäßig auf 1 gesetzt, wenn sie nicht angegeben werden.
Die zweite Form konvertiert ein object der Form { r:0.8, g:0.4, b:0, a:1.0 } in eine Webfarbe auf die gleiche Weise wie die erste Form.
Die dritte Form konvertiert webcolor_string in ein Objekt der gleichen Form wie oben beschrieben.
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, kann on(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 Auslösebedingung 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).

Introspektion - Einblick in Variablen, Funktionen, Handler, Threads

Diese Funktionen sind weniger für die Verwendung in Scripts gedacht, sondern vor allem nützlich bei der Fehlersuche in der Konsole/REPL der IDE.

globalvars()
gibt ein Objekt mit allen globalen Variablen als Felder zurück.
contextvars()
gibt ein Objekt mit Variablen des aktuellen Scriptkontexts zurück.
localvars()
gibt ein Objekt mit den aktuellen lokalen Variablen der gerade ausgeführten Funktion zurück. Wenn keine Funktion ausgeführt wird, ist das Resultat dasselbe wie bei contextvars().
threadvars()
gibt ein Objekt mit den aktuellen Thread-lokalen Variablen zurück. Das sind diejenigen Variable, die mit threadvar definiert wurden oder implizit bei on (…) as v {…} oder catch as {…} erzeugt werden.
threads()
gibt einen Array mit Informationen zu den Threads (Ausführungssträngen) des aktuellen Scriptkontexts zurück. Für jeden Thread enthält die Liste ein Objekt mit den Feldern id, source, status, sowie thread zurück. Letzteres ist das thread-Objekt selber (erscheint als null oder enthält das thread-Resultat wenn der Thread schon beendet ist).
globalhandlers()
gibt einen Array mit Informationen zu den globalen Ereignis-Handlern (global on (…) {…}) zurück.
contexthandlers()
gibt einen Array mit Informationen zu den Ereignis-Handlern im aktuellen Scriptkontexts zurück.
globalbuiltins()
gibt ein Objekt zurück, dessen Felder Informationen zu den global verfügbaren eingebauten Funktionen und globalen Objekten beinhalten.
contextbuiltins()
gibt ein Objekt zurück, dessen Felder Informationen zu den im aktuellen Scriptkontext (zusätzlich zu den global vorhandenen) verfügbaren eingebauten Funktionen und Variablen beinhalten.
builtins(object)
gibt ein Objekt zurück, dessen Felder Informationen zu den im übergebenen object verfügbaren eingebauten Methoden und Feldern beinhalten.

Steuerung von geräte- und 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 Digital Strom-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 Digital Strom-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. scene scripts). Aber im Grunde sind diese Funktionen Methoden des Geräteobjekts oder eines seiner Felder. Das 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()
webrequest(sub_endpoint)
webrequest(sub_endpoint, peer)
Ist eine Ereignisquelle, die JSON-Anfragen zurückgibt, die an den Endpunkt scriptapi der JSON-API (/api/json/scriptapi) gesendet wurden. webrequest kann zum Beispiel mit `on(webrequest()) as request { ... }" verwendet werden, um API-Befehle in Skripten zu implementieren. Der Rückgabewert von webrequest() hat die Methode answer(), um die Anfrage zu beantworten. Dieser Mechanismus kann verwendet werden, wenn neben dem Standard-Webinterface kundenspezifische Spezial-Webseiten implementiert werden sollen, oder um externen Systemen eine angepasste API zur Verfügung zu stellen.
Die erste Form ohne Argument fängt alle Anfragen ab, die an den Endpunkt scriptapi gesendet werden
Die zweite Form spezifiziert einen sub_endpoint, wie z.B. "myendpoint" in /api/json/scriptapi/myendpoint und empfängt nur Anfragen, die an genau diesen Endpunkt gesendet werden. Dies erlaubt es, mehrere Script-API-Endpunkte völlig unabhängig voneinander zu implementieren, indem man einfach mehrere on(webrequest(...))-Handler mit verschiedenen sub_endpoint-Argumenten für webrequest hat.
Die dritte Form gibt zusätzlich peer an, was die IP-Adresse des Absenders der Anfrage repräsentiert. Es ist auch möglich, undefined für sub_endpoint anzugeben, um nur nach dem Absender zu filtern.
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.

Das folgende sind Methoden, d.h. sie gehören zu einem view und müssen über diesen aufgerufen werden. lrg ist der rootview und steht immer zur Verfügung, andere Views können mit findview() gefunden und ggf. in Variablen gespeichert werden. Für das Folgende sei v eine Variable, der ein View zugewiesen wurde (etwa: var v = lrg.findview('LIGHT')):

v.findview(viewlabel)
sucht nach einem Subview mit dem angegebenen viewlabel, gibt den View oder undefined zurück.
v.addview(view)
fügt view als Subview hinzu (wenn dieser View Subviews unterstützt, z.B. stack).
v.parent()
gibt den übergeordneten View zurück (oder null, wenn keiner vorhanden ist).
v.remove()
entfernt den View von seinem übergeordneten View (wenn es einen übergeordnete View gibt), gibt true zurück, wenn der View entfernt werden konnte.
v.configure(json)
v.configure(resource_file)
(re)konfiguriert den View mit dem direkt angegebenen json oder mit einem resource_file gelesenen JSON-text.
v.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.
v.animator('propertyname')
Erzeugt einen Animator für die angegebene Eigenschaft des Views. Animierbare p44lrg-Eigenschaften sind: alpha, x, y, dx, dy, content_x, content_y, rel_content_x, rel_content_y, rel_center_x, rel_center_y, content_dx, content_dy, rotation, scroll_x, scroll_y, zoom_x, zoom_y, colour, 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.
v.stopanimations()
Stoppt alle in der Ansicht laufenden Animationen.
v.reset()
Setzt alle Transformationen zurück (Zoom=1, Scroll=0, Rotation=0)
v.clear()
Löscht den Inhalt - je nach View-Typ bedeutet das Entfernen aller Subviews, oder löschen des Textes etc.

Dies sind die grundlegenden Methoden, Views haben weitere Methoden und Eigenschaften, die in der technischen Dokumentation online im Detail beschrieben sind.

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.

Für das Folgende sei a eine Variable, der ein Animator zugewiesen wurde, etwa var a = lrg.animator('rotation').

a.delay(Sekunden)
verzögert den Start der Animation um die angegebene Zeit.
a.runafter(animation)
startet die Animation erst, nachdem die angegebene animation beendet ist.
a.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.
a.function('animationfunction')
Setzt die Animationsfunktion, die eine der folgenden sein kann: linear,easein,easeout,easeinout.
a.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.
a.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).
a.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).
a.reset()
Setzt die Animation auf ihre Standardwerte zurück (und stoppt sie vorher, falls sie gerade noch läuft).
a.stop()
Hält eine Animation an.
a.current
gibt den aktuellen Wert des Animators zurück.
a.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-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ässer Verwendung zu Fehlfunktionen und, je nach angeschlossenen Geräten, zu Schäden führen. Die Nutzung dieser Funktionen erfordert daher mindestens userlevel 1.

Serielle Schnittstellen

Hinweis: benötigt Userlevel >= 1

Der Zurgriff auf serielle Schnittstellen erfordert userlevel >=1, da zum Erzeugen eines serial-Objekts Zugriff auf die Hardware erforderlich ist, was den normalen Gerätebetrieb stören kann, wenn die connection nicht richtig gewählt wird.

var s = serial(connectionspec [,delimiter])
Erzeugt ein Objekt das eine serielle Schnittstelle betreiben kann.
connectionspec hat die Form /xxx/serialdevice:commparams oder host:port. serialdevice muss ein serielles Device sein (z.B. /dev/ttyS1) oder ein anderer serieller Kanal. Die Form mit host und port dient dazu, eine serielle Schnittstelle übers Netzwerk anzusprechen, z.B. eines das mit serialfwd angesteuert wird.
Mit commparams können die Schnittstellenparameter eingestellt werden in der Form: [Baudrate][,[Bits][,[Parität][,[Stopbits][,[H]]]]], wobei die Parität O, E oder N sein kann und H bedeutet, dass Hardware-Handshake aktiviert werden soll.
delimiter kann ein String mit einem Zeichen sein, welches die empfangenen Daten in Meldungen separiert. Wenn delimiter den Wert "\n" (Linefeed, 0x0A), dann werden auch vorangehende "\r"-Zeichen entfernt. delimiter = true bewirkt dasselbe wie delimiter = "\n".

Hinweis: Im Folgenden wird angenommen, dass s eine Variable ist, die eine serial-Instanz enthält.

s.send(something)
Sendet something konvertiert in einen String, auf der seriellen Schnittstelle.
s.received()
Dies ist eine Ereignisquelle. Wenn es in einem Handler verwendet wird wie z.B. on(s.received()) as data { ... }, dann wird der Handler jedes Mal ausgeführt, wenn etwas auf der seriellen Schnittstelle empfangen wird, oder - wenn delimiter gesetzt wurde beim Aufruf von serial() - jeweils dann, wenn eine komplette Meldung, die mit delimiter abgeschlossen ist, eingetroffen ist.
s.dtr(active)
Aktiviert oder deaktiviert das DTR-Signal (falls an der Schnittstelle unterstützt) gemäss active.
s.rts(active)
Aktiviert oder deaktiviert das RTS-Signal (falls an der Schnittstelle unterstützt) gemäss active.
s.sendbreak()
Sendet einen break (langes Signal), falls von der Schnittstelle unterstützt.

MIDI

Mit der einfachen MIDI-Unterstützung können MIDI-Input-Geräte, insbesondere Controller mit Analog-Dreh- und Schiebereglern, die teilweise sehr günstig zu kaufen sind, mit Parametern einer Licht- oder anderen Automation verbunden werden und dadurch bequem steuerbar. Umgekehrt können auch MIDI-Kommandos abgesetzt werden, um andere MIDI-Geräte zu steuern, beispielsweise um Sound-Effekte für eine Installation zu erzeugen.

Hinweis: benötigt Userlevel >= 1

Der MIDI-Zugriff erfordert userlevel >=1, da zum Erzeugen eines midibus-Objekts Zugriff auf die Hardware (serielle Schnittstellen /dev/ttySx oder ALSA USB-MIDI devices wie /dev/midix) erforderlich ist, was den normalen Gerätebetrieb stören kann, wenn die connection nicht richtig gewählt wird.

var midi = midibus(connectionspec)
Erzeugt eine midibus-Instanz, die ein MIDI-Interface betreiben kann.
connectionspec hat die Form /xxx/mididevice oder host:port. mididevice muss ein serielles Device sein (z.B. /dev/midi1) oder ein anderer serieller Kanal, der MIDI-Daten überträgt. Die Form mit host und port dient dazu, ein MIDI-Interface übers Netzwerk anzusprechen, z.B. eines das mit serialfwd angesteuert wird.

Hinweis: Im Folgenden wird angenommen, dass midi eine Variable ist, die ein midibus-Instanz enthält. Grundsätzliche MIDI-Kenntnisse sind vorausgesetzt, Details finden sich im MIDI-Standard.

midi.send(command, value)
midi.send(command, key, value)
midi.send(sysex_string)
Sendet eine MIDI-Message. command ist das Status-Byte der MIDI-Message (also etwa 0x90 für note on auf Kanal 0, oder 0xB2 für control change auf Kanal 2). Die erste Form mit zwei Parametern ist für MIDI-Kommandos die nur einen Parameterwert haben (wie z.B. pitch bend oder program change). Die zweite Form ist für Kommandos mit zwei Parametern. Bei Note-Kommandos ist key die note number und value die velocity. Bei control change ist key die control number und value der Einstellwert. Bei der dritten Form wird der (binär)-String sysex als system_exclusive message gesendet. Das EOX wird automatisch am Ende eingefügt und sollte nicht in sysex enthalten sein.
Bei 14-bit controls (Nummern 0..31) werden u.U. 2 Kommandos gesendet, um die ganzen 14 bits zu übertragen (je nach dem, wie der Wert sich geändert hat). Bei 14-bit Werten die in einem Kommando Platz haben (etwa pitch bend) wird value automatisch richtig in die zwei MIDI-Datenbytes aufgeteilt.
midi.message()
midi.message(commandfilter, channelfilter, keyfilter)

Dies ist eine Ereignisquelle. Wenn es in einem Handler verwendet wird wie z.B. on(midi.message()) as m { ... }, dann wird der Handler jedes Mal ausgeführt, wenn eine Midi-Message eintrifft, und gibt ein Objekt mit den folgenden Feldern zurück:

  • "command" : das MIDI-Kommando (status), wobei bei channel voice messages die Kanal-Bits immer 0 sind (da separiert in channel verfügbar)
  • "channel" : ist nur vorhanden bei channel voice messages und gibt den Kanal an.
  • "key" : note number oder control number, ist 0 bei Kommandos ohne diesen Selector-Wert.
  • "value" : der Daten-Wert wie velocity oder control value, kann 14-bit sein je nach Kommando (z.B. bei control numbers 0..63).

Mit den filter-Werten kann bestimmt werden, welche Kommandos, Kanäle und Keys (Noten, Controls) zurückgegeben werden sollen. So wird etwa message(0x90,2,60) nur note on von Kanal 2 mit der Note 60 ("middle C") zurückgeben. Jeder der filter-Werte kann auf undefined gesetzt werden, wenn der entsprechende Filter nicht benötigt wird.

midi.note()
midi.note(channelfilter, notefilter, on_only)
Wie message, gibt aber nur note-Messages (on und off wenn on_only nicht true oder false gesetzt ist) zurück.
midi.control()
midi.control(channelfilter, controlnumberfilter)
Wie message, gibt aber nur control change-Messages zurück.
midi.pitchbend()
midi.pitchbend(channelfilter)
Wie message, gibt aber nur pitch bend-Messages zurück.
midi.program()
midi.program(channelfilter)
Wie message, gibt aber nur program change-Messages zurück.

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()
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.