Leider gibt es keine gute Dokumentation der DSL (also vor allem in der Art eines Best Practice), aber platt gesagt ist die Sprache für die angenommene Verwendung fast perfekt designt.
Man schreibt keine Programme (damit geht es mal los), sondern Rules (oder Regeln).
Eine Regel läuft nicht, sie wird abgearbeitet (mithin sollte eine Rule nicht so entworfen sein, dass sie irgendwelche Schleifen dreht, weil ihr gerade langweilig ist)
Eine Regel reagiert auf ein (oder mehrere) Ereignis(se). Wann immer das Ereignis auftritt, wird die Rule aufgerufen und abgearbeitet.
Die Rule kümmert sich zu 99% um Items. Sie wertet also die Status von einem, oder mehreren Items aus und bestimmt daraus die Status anderer Items oder auch Befehle, die an Items gesendet werden.
Da es sich bei Items um Objekte handelt, gibt es Methoden zur Manipulation der Items, und wenn wir z.B. von Group Items sprechen, so kann man sich über die Eigenschaft
.members eine Liste aller in der Gruppe zusammengefassten Items ausgeben lassen. Die Liste ist wieder ein Objekt und kann entsprechend gefiltert und sortiert werden, man kann die Größe der Liste bestimmen oder auch auf ganz bestimmte Elemente der Liste zugreifen - z.B. auf das letzte oder das erste Element eines Filterergebnisses. Oder man durchläuft alle Elemente der Liste mit
.forEach(), um z.B. an alle Items einer Gruppe einen bestimmten Befehl zu senden - zusammen mit der Triggerquelle
Member of (die stellt in der Rule dann ein Objekt zur Verfügung, welches das auslösende Item enthält) kann man z.B. eine einzige Rule schreiben, die sich aber um beliebig viele Items des selben Typs kümmern kann.
Beispiel: Ich habe knx Raumtemperaturregler (RTR). Die Betriebsart (Nachtabsenkung, Komfort, Frostschutz...) wird über eine Zahl gesetzt, die Rückmeldung erfolgt aber über eine andere Zahl (weil dort noch weitere Daten enhalten sind, z.B. ob gerade Kühlbetrieb oder Heizbetrieb aktiv ist)
Ich muss also für jeden RTR zwei Items vorsehen, das eine zum Senden, das andere zum Empfangen der Betriebsart, denn ich möchte natürlich in openHAB auch eine Änderung der Betriebsart sehen, wenn ich das am RTR selbst bediene. In der Oberfläche will ich aus verständlichen Gründen keine zwei Items haben, also brauche ich eine Rule, welche den Itemstatus des einen Items passend zum gelieferten Wert setzt. Ich habe neun RTR, brauche aber nur eine einzige Rule:
Code: Alles auswählen
rule "Betriebsart RTR"
when
Member of gHeat_Mode changed
then
var Integer newMode
val mode = (triggeringItem.state as DecimalType).toBigDecimal.toBigInteger
val iName = triggeringItem.name.split("_").get(0).toString
logDebug("rtr","Name is: {}, Mode is: {}",iName,mode)
switch (mode) {
case mode.testBit(0) : newMode = 1
case mode.testBit(2) : newMode = 3
case mode.testBit(3) : newMode = 4
default : newMode = 2
}
var myItem = gHeat_Set.members.filter[ f | f.name.startsWith(iName) ].head
var Integer oldMode = 0
if(myItem.state instanceof Number) oldMode = (myItem.state as Number).intValue
if(oldMode != newMode) {
logDebug("rtr","Name is: {}, oldMode is: {}, newMode is: {}",myItem.name,oldMode,newMode)
myItem.postUpdate(newMode)
}
end
Die Rule triggert, wenn über ein Item der Gruppe gHeat_Mode ein geänderter Status gemeldet wird.
Daraufhin bestimmt die Rule den neuen Status anhand des Bitmusters (so ist das in der Rückmeldung codiert)
Anschließend sucht die Rule anhand des Namens des Items, welches die Rule getriggert hat das passende Gegenstück heraus und setzt dieses auf den errechneten Status.
Mit einem Unterprogramm würde man für jedes Itempaar eine Rule anlegen und von dort das Unterprogramm aufrufen, welches den Wert errechnet - umständlich. Ich brauche nur diese eine Rule, und wenn mir einfällt, 10 weitere Räume hinzuzubauen (mit eigenem Heizkreis), so lege ich einfach je zwei weitere Items an und füge sie den beiden Gruppen hinzu. Alles andere geschieht vollautomatisch, ohne eine einzige zusätzliche Zeile Code.
Unabhängig davon, ob man den Code oben jetzt direkt versteht

denke ich, wird klar, dass man mit der DSL extrem effizient programmieren kann

openHAB5.1.3 stable in einem Debian-Container (trixie, OpenJDK 21 headless runtime - LXC, 4 Kerne, 3 GByte RAM)
Hostsystem Proxmox VE 9.1.9 - AMD Ryzen 5 3600 6 Kerne, 12 Threads - 64 GByte RAM - ZFS Pools: Raid Z1, 3 x 20 TB HDD -> 40 TByte und Raid Z0-Mirrored 4 x 1 TByte NVMe -> 2 TByte