Seite 1 von 1

Subroutine/Funktion in Rule aufrufen

Verfasst: 23. Jan 2024 20:07
von Ekki
Hallo,

ich möchte in einer .rules - Datei eine einfache Subroutine ohne Parameterübergabe verwenden, weiß aber nicht wie.
Die Suche gestaltet sich nicht einfach, da es ja tausend Möglichkeiten und Scriptsprachen für Rules gibt.

So soll es im Prinzip aussehen:

Code: Alles auswählen

var Subroutine() {
  XYZ.postUpdate("Hallo")
}

rule "Test"
when
   xxx
then
   Subroutine()
end   
Geht sowas überhaupt?

Danke für Eure Hilfe!

Re: Subroutine/Funktion in Rule aufrufen

Verfasst: 24. Jan 2024 11:59
von peter-pan
...hab auf die Schnelle nur das gefunden, aber da gibt es hier User (@udo1toni), die können dir das viel besser erklären. Auf jeden Fall gibt es da Möglichkeiten.

Re: Subroutine/Funktion in Rule aufrufen

Verfasst: 24. Jan 2024 15:05
von udo1toni
Grundsätzlich bietet openHAB keine Funktionen oder Unterprogramme.
Für den vorliegenden Fall - es soll einfach ein Anweisungsblock ausgelagert werden, z.B. weil er (in identischer Form) in mehreren Rules verwendet wird - kannst Du die Action callScript() verwenden.

Du legst dazu eine Datei mit dem auszuführenden Code an (Speicherort $OPENHAB_CONF/scripts/, Dateiendung .script), anschließend kannst Du diese Datei in der Rule aufrufen. Beispiel:

/etc/openhab/scripts/holidays.script

Code: Alles auswählen

var boolean holiday = false
var String holidayName = null

// Osterferien Hessen
if (
   (LocalDate.of(2024,3,25).isBefore(LocalDate.now) && LocalDate.of(2024,4,13).isAfter(LocalDate.now))
|| (LocalDate.of(2025,3,25).isBefore(LocalDate.now) && LocalDate.of(2025,4,22).isAfter(LocalDate.now))
|| (LocalDate.of(2026,3,25).isBefore(LocalDate.now) && LocalDate.of(2026,4,22).isAfter(LocalDate.now))
) {
    holidayName = "easter_holiday" // Osterferien
    holiday = true
}

// Sommerferien Hessen
if (
   (LocalDate.of(2024,7,15).isBefore(LocalDate.now) && LocalDate.of(2024,8,23).isAfter(LocalDate.now))
|| (LocalDate.of(2025,7, 7).isBefore(LocalDate.now) && LocalDate.of(2025,8,15).isAfter(LocalDate.now))
|| (LocalDate.of(2026,6,29).isBefore(LocalDate.now) && LocalDate.of(2026,8, 7).isAfter(LocalDate.now))
|| (LocalDate.of(2027,6,28).isBefore(LocalDate.now) && LocalDate.of(2027,8, 6).isAfter(LocalDate.now))
|| (LocalDate.of(2028,7, 3).isBefore(LocalDate.now) && LocalDate.of(2028,8,11).isAfter(LocalDate.now))
|| (LocalDate.of(2029,7,16).isBefore(LocalDate.now) && LocalDate.of(2029,8,24).isAfter(LocalDate.now))
|| (LocalDate.of(2030,7,22).isBefore(LocalDate.now) && LocalDate.of(2030,8,30).isAfter(LocalDate.now))
) {
    holidayName = "summer_holiday" // Sommerferien
    holiday = true
}

// Herbstferien Hessen
if (
   (LocalDate.of(2024,10,23).isBefore(LocalDate.now) && LocalDate.of(2024,10,28).isAfter(LocalDate.now))
|| (LocalDate.of(2025,10,23).isBefore(LocalDate.now) && LocalDate.of(2025,10,28).isAfter(LocalDate.now))
) {
    holidayName = "autumn_holiday" // Herbstferien
    holiday = true
}

// Weihnachtsferien Hessen
if (
   (LocalDate.of(2024,12,27).isBefore(LocalDate.now) && LocalDate.of(2025,1,13).isAfter(LocalDate.now))
|| (LocalDate.of(2025,12,27).isBefore(LocalDate.now) && LocalDate.of(2026,1,13).isAfter(LocalDate.now))
|| (LocalDate.of(2026,12,27).isBefore(LocalDate.now) && LocalDate.of(2027,1,13).isAfter(LocalDate.now))
) {
    holidayName = "christmas_holiday" // Weihnatsferien
    holiday = true
}

HoliDayName.postUpdate(if (holidayName!==null) holidayName else "")

Holidays.postUpdate(if (holiday) ON else OFF)
/etc/openhab/rules/startup.rules

Code: Alles auswählen

rule "Feiertage"
 when
    Time cron "5 0 0 * * ?" or
    System started
 then
    callScript("holidays")
end
Solange der Codeblock allerdings nur einmalig gebraucht wird, ist es einigermaßen sinnfrei :) im vorliegenden Beispiel ist es vor allem interessant, weil beim Ändern des Scripts die Rules nicht erneut geladen werden müssen - ich kann also die Ferienliste ergänzen bzw. alte Ferientermine löschen, ohne die Rule direkt zu triggern (wegen System started)

Der Code ist "uralt", ich denke, ich sollte das mal etwas schicker lösen... ;)

Im von peter-pan verlinkten Thread geht es um die andere Möglichkeit, "Funktionen" in openHAB Rules zu ermöglichen, dazu wird der auszuführende Code als Lambda definiert und dann dieses Lambda aufgerufen. Auch diese Form funktioniert, ist aber mit vielen Fallstricken versehen, ursprünglich war nicht vorgesehen, Rules so umfangreich zu gestalten - es ging zunächst nur um einfache Automationen, nicht darum, ein selbst denkendes Haus zu entwickeln... ;)

Re: Subroutine/Funktion in Rule aufrufen

Verfasst: 25. Jan 2024 19:25
von udo1toni
Und da vermutlich jemand drüber stolpern wird, hier die aktualisierte Version des Ferienscripts:

Code: Alles auswählen

var boolean holiday = false
var String holidayName = null

// Ferientermine Hessen laut schulferien.org
val termin = newArrayList(
    "2024-03-25","2024-04-13","easter", "2024-07-15","2024-08-23","summer", "2024-10-14","2024-10-25","autumn", "2024-12-23","2025-01-10","christmas",
    "2025-04-07","2025-04-21","easter", "2025-07-07","2025-08-15","summer", "2025-10-06","2025-10-17","autumn", "2025-12-22","2026-01-10","christmas",
    "2026-03-30","2026-04-10","easter", "2026-06-29","2026-08-07","summer", "2026-10-05","2026-10-16","autumn", "2026-12-23","2027-01-12","christmas",
    "2027-03-22","2027-04-02","easter", "2027-06-28","2027-08-06","summer", "2027-10-04","2027-10-15","autumn", "2027-12-23","2028-01-11","christmas",
    "2028-04-03","2028-04-14","easter", "2028-07-03","2028-08-11","summer", "2028-10-09","2028-10-20","autumn", "2028-12-27","2029-01-12","christmas",
    "2029-03-29","2029-04-13","easter", "2029-07-16","2029-08-24","summer", "2029-10-15","2029-10-26","autumn", "2029-12-24","2030-01-11","christmas",
    "2030-04-08","2030-04-22","easter", "2030-07-22","2030-08-30","summer"
    )
var i = 0
while(i < termin.size) {
    logDebug("holidays", "0: {} 1: {} 2: {}", termin.get(i), termin.get(i+1), termin.get(i+2))
    val start = LocalDate.parse(termin.get(i))
    val end   = LocalDate.parse(termin.get(i+1))
    logDebug("holidays", "start: {} end: {} name: {}", start, end, termin.get(i+2))
    if(start.isBefore(LocalDate.now) && end.plusDays(1).isAfter(LocalDate.now)) {
        holidayName = termin.get(i+2) + "_holiday"
        holiday     = true
        logInfo("holidays", "Ferien! start: {} end: {} name: {}", start, end, holidayName)
    }
    i += 3
}

HoliDayName.postUpdate(if (holidayName!==null) holidayName else "")
Holidays.postUpdate(if (holiday) ON else OFF)
Die Listenformatierung habe ich als ein Jahr pro Zeile angelegt, die fehlenden Leerzeichen sind nur der Optik geschuldet :) immer drei Einträge bilden einen "Datensatz", das erste Datum ist der Startzeitpunkt, das zweite Datum ist der Endzeitpunkt,das dritte Datum ist der Ferienname.
LocalDate.parse() erwartet den String in exakt diesem Format (also mit führenden Nullen)
Die Schleife könnte man noch etwas flotter machen, die Liste ist ja chronologisch sortiert, man könnte also den Durchlauf abbrechen, sobald das Startdatum in der Zukunft liegt.
Als Start-/Enddatum habe ich jeweils exakt die in schulferien.org angegebenen Termine verwendet, deshalb muss man für das Enddatum noch einen Tag hinzuzählen.
Weiterhin habe ich eine map, welche mir die englischen Strings nach deutsch übersetzt - im Grunde ist diese Zwischenstufe unnötig, ich weiß...

Re: Subroutine/Funktion in Rule aufrufen

Verfasst: 29. Jan 2024 19:19
von Ekki
Vielen Dank für Eure Antworten!
Schade, dass es doch nicht so einfach ist. Für meine Anwendung ist es tatsächlich übersichtlicher, die paar Zeilen Code zu wiederholen, anstatt eine zweite Datei zu erstellen.

Trotzdem Danke!