Init rules are not executed upon startup of openHAB

I’m running openHAB 2.5.10 on docker. Host system is an Ubuntu 18.04.5 LTS.

I’ve noticed for quite some time, I guess starting with openHAB 2.5.0 that at the startup of openHAB the rules scripts doesn’t load and start correctly. There are two issues that I’ve noticed:

  1. Types declared in import statements or using of implicit types of the rules produce warnings/errors in the log. I’ve attached examples of these logs below.
  2. Init rules are not executed. I have to touch the files manually after openHAB startup to get the init rules working.

Here is an example of the warnings/errors concerning import statements:

2020-11-23 14:40:05.700 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'wind.rules', using it anyway:
The field Tmp_windRules.convertToCentiGrade refers to the missing type Angle
The field Tmp_windRules.convertToCentiGrade refers to the missing type Angle

The code producing this log is as follows:

val convertToCentiGrade = [ QuantityType<Angle> angle |
    if (angle !== NULL) {
        angle.multiply(100|°).divide(360|°)
    } else {
        0|°
    }
]

rule "Init"
when
    System started
then
    Zentralfunktion_Windrichtung_Proxy.state = convertToCentiGrade.apply(Zentralfunktion_Windrichtung.state)
end

rule "Wind direction changes"
when
    Item Zentralfunktion_Windrichtung changed
then
    Zentralfunktion_Windrichtung_Proxy.state = convertToCentiGrade.apply(newState as QuantityType<Angle>)
end

It seems that upon parsing/compiling the rule file the quantity types (here Angle, type in the default scope) is not yet loaded and therefore declared as missing. This case is only a minor issue, because as the logs states, the missing type is ignored and the rule is used anyway.

I have a more complex rule that implements a scheduling service for my heating actors. It uses imports that are not in the default scope:

import java.util.ArrayList
import java.util.stream.Collectors
import java.util.HashMap
import java.util.List
import java.util.Map

import org.eclipse.xtext.util.Triple
import org.eclipse.xtext.util.Tuples

val loggerName = "Zeitschaltuhr"

val List<String> keys = newArrayList("vacation", "holiday", "weekend", "workday")
val Map<String, List<Triple<LocalTime, String, List<GenericItem>>>> schedules = new HashMap
val List<Timer> timers = new ArrayList

val scheduleTimers = [ String loggerName, List<String> keys, Map<String, List<Triple<LocalTime, String, List<GenericItem>>>> schedules, List<Timer> timers, boolean onVacation |
    // Reset scheduled timers for the current day
    timers.forEach[ timer |
        timer.cancel()
    ]

    timers.clear()

    // Select schedule key based on date and item state
    val currentDate = LocalDate.now()

    val selectedSchedule = switch (currentDate) {
        case onVacation : keys.get(0)
        case isBankHoliday() : keys.get(1)
        case isWeekend () : keys.get(2)
        default : keys.get(3)
    }

    logInfo(loggerName, "Schedule " + selectedSchedule + " selected")

    val currentTime = DateTime.now()

    schedules.get(selectedSchedule).forEach[triple |
        val DateTime time = triple.getFirst().toDateTimeToday()
        val String value = triple.getSecond()
        val List<GenericItem> items = triple.getThird()

        if (time.isBefore(currentTime)) {
            items.forEach[ item |
                item.sendCommand(value)
            ]

            val String itemsString = items.stream().map([ item | item.name]).collect(Collectors.joining(", "))
            logInfo(loggerName, String.format("HVAC Mode set to %s on %tR for items %s", value, time.toGregorianCalendar(), itemsString))
        } else {
            timers.add(
                createTimer(
                    time, [ |
                        items.forEach[ item |
                            item.sendCommand(value)
                        ]

                        val String itemsString = items.stream().map([ item | item.name]).collect(Collectors.joining(", "))
                        logInfo(loggerName, String.format("HVAC Mode set to %s on %tR for items %s", value, time.toGregorianCalendar(), itemsString))
                    ]
                )
            )
        }
    ]
]

rule "Init"
when
    System started
then
    // Clear schedules
    schedules.clear()

    // Add vacation schedule
    schedules.put(keys.get(0), newArrayList(
        Tuples.create(LocalTime.parse("00:00"), "Building Protection", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Schlafzimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart, Lounge_Klima_Betriebsart, Badezimmer_Klima_Betriebsart))
    ))

    // Add holiday schedule
    schedules.put(keys.get(1), newArrayList(
        Tuples.create(LocalTime.parse("06:30"), "Comfort", newArrayList(Badezimmer_Klima_Betriebsart, Lounge_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("08:30"), "Comfort", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("19:30"), "Comfort", newArrayList(Schlafzimmer_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("22:30"), "Economy", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Schlafzimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart, Lounge_Klima_Betriebsart, Badezimmer_Klima_Betriebsart))
    ))

    // Add weekend schedule
    schedules.put(keys.get(2), newArrayList(
        Tuples.create(LocalTime.parse("06:30"), "Comfort", newArrayList(Badezimmer_Klima_Betriebsart, Lounge_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("08:30"), "Comfort", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("19:30"), "Comfort", newArrayList(Schlafzimmer_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("22:30"), "Economy", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Schlafzimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart, Lounge_Klima_Betriebsart, Badezimmer_Klima_Betriebsart))
    ))

    // Add workday schedule
    schedules.put(keys.get(3), newArrayList(
        Tuples.create(LocalTime.parse("05:30"), "Comfort", newArrayList(Badezimmer_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("06:30"), "Comfort", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart, Lounge_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("08:30"), "Standby", newArrayList(Hannah_Klima_Betriebsart, Lounge_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("10:30"), "Standby", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Badezimmer_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("16:00"), "Comfort", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart, Lounge_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("19:30"), "Comfort", newArrayList(Schlafzimmer_Klima_Betriebsart, Badezimmer_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("21:30"), "Economy", newArrayList(Wohnzimmer_Klima_Betriebsart, Esszimmer_Klima_Betriebsart, Mariam_Klima_Betriebsart, Hannah_Klima_Betriebsart)),
        Tuples.create(LocalTime.parse("22:30"), "Economy", newArrayList(Schlafzimmer_Klima_Betriebsart, Badezimmer_Klima_Betriebsart, Lounge_Klima_Betriebsart))
    ))

    // create timers for current day
    scheduleTimers.apply(loggerName, keys, schedules, timers, Zentralfunktion_Urlaub.state == ON)

    logInfo(loggerName, "Initialization done")
end

rule "Vacation state changed"
when
    Item Zentralfunktion_Urlaub changed
then
    // create timers for current day due to vacation state change
    scheduleTimers.apply(loggerName, keys, schedules, timers, newState == ON)
    logInfo(loggerName, "Vacation state change to " + newState + " processed")
end

rule "Passed midnight"
when
    Time is midnight
then
    // create timers for current day due to date change
    scheduleTimers.apply(loggerName, keys, schedules, timers, Zentralfunktion_Urlaub.state == ON)
    logInfo(loggerName, "Passed midnight processed")
end

This rule is loaded/parsed/compiled with no error/warning to be seen in the logs, but the init rule that initializes the scheduling structures is not called. In order to get this work, I have to touch the file after openHAB is fully started. Then the rule is updated and the init rule is executed.

From the missing responses, I get that I’m the only one having this problem.

Maybe I’m missing something? Any hints?