ECMA rule to trigger after system fully functional

OH 4.0.4 I´d like to write an ECMAScript 11

I´d like to write a rule, which triggers for changes in a number item. So far no issue.

During OH startup, this item changes 3 times, so I receive unnecessary messages.
Is it possible to trigger AFTER startup level 100 is reached?

I didn´t find a way to get to known startup level in Javascript so far. An if-then loop could be an approach then.

Create a rule which gets triggered when startLevel reaches 70, 80 and 100 and write startLevel into an item.
In your rule you can check this item if a certain level is reached.

Potential problem: if you check for systemLevel == 100 that level never might be reached if for example one of your things is offline.

1 Like

Use Delay Start [4.0.0.0;4.9.9.9]. That rule template can be configured to trigger at runlevel 40 and disable this rule. Then it triggers again at runlevel 100 and sets a timer to wait an additional configurable amount of time before reenabling the rule.

I use this to keep some sensor readings from pounding on a rule all at once when the Things start to come online. I wait about a minute after runlevel 100 and on my system that works great. You’ll have to experiment to determine the ideal amount of time after runlevel 100.

If runlevel 100 isn’t reliable on your system you should look into why and fix that. But in the mean time you can change the rule triggers to 80 or 70 or whatever works best for you.

Based on the idea of @Oliver2 another approach I´d like to share from learning Javascript…
Initial issue was several occurences of level 70, so conflict with the whish to have a “run-rule-once” situation.

How?

  • new text item “OH_Systemlevel”
  • item “OH_Systemlevel” is initially NULL
  • (Rule 1) start level 70 is reached for the first time
    – item “OH_Systemlevel” gets the “ok” status exactly once AND 2 minutes later.
    –This period is valid for my system, so take a stop watch and add enough buffer. It´s still vague, but works for me.
    – reaching the runlevel several times is ignored, once item set
  • (Rule 2) this item status can be used in any rule from now on

1.New rule “Systemlevel_reached_070” with
Trigger=“When the system has reached start level 70”

configuration: {}
triggers:
  - id: "1"
    configuration:
      startlevel: 70
    type: core.SystemStartlevelTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var ScriptExecution =
        Java.type("org.openhab.core.model.script.actions.ScriptExecution");

        var ZonedDateTime = Java.type("java.time.ZonedDateTime");  
        if (items.OH_Systemlevel.state.toString() != "ok"){ 
          var timer = ScriptExecution.createTimer(ZonedDateTime.now().plusMinutes(2), function(){
            items.OH_Systemlevel.postUpdate("ok")
          });
        }
    type: script.ScriptAction

2.New rule “Init_Openhab”

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: OH_Systemlevel
      state: ok
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: |-
        if (items.OH_Systemlevel.state == 70){
          items.KNX_Lueftung_Spannung.postUpdate("4000")
        }
    type: script.ScriptAction

Have you actually seen this happen? It’s not supposed to and deserves an issue if you’ve seen it happen before. Each runlevel only occurs once per startup.

Note, the code as written is Nashorn style JS, not GraalVM style JS with the helper library.

Written with the newer JS style the code would be something like

// Don't import stuff from Java, you don't need to, use the helper library instead
if(items.OH_SystemLevel.state != "ok") { // .state is already a String
  setTimeout( () => items.OH_SystemLevel.postUpdate("ok"), time.toZDT('PT2M').getMillisFromNow()); // you throw away the handle to the timer anyway so just use a timeout
}

Though I’d move the test of OH_SystemLevel.state to be a rule Condition and avoid scheduling a new Timer if there is one already scheduled.

configuration: {}
triggers:
  - id: "1"
    configuration:
      startlevel: 70
    type: core.SystemStartlevelTrigger
conditions:
  - inputs: {}
    id: "2"
    configuration:
      itemName: OH_Systemlevel.state
      state: ok
      operator: !=
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var timer = cache.private.get('timer');
        if(timer === null) {
          cache.private.put(actions.scriptExecution.createTimer(time.toZDT('PT2M'), () => {
            items.OH_Systemlevel.postUpdate('ok');
          });
        }
        else {
         timer.reschedule(time.toZDT('PT2M'));
        }
    type: script.ScriptAction

But when you look at the second rule, you trigger it based on the Item changing. If runlevel 70 occurred twice for some reason, even without all the logic above the Item would only change the one time (that’s why your current implementation works even though if runlevel 70 were reached twice before your 2 minute timer goes off you end up with two timers and the Item gets update to “ok” twice). So throw out all the checks and stuff and it becomes a couple lines of code.

configuration: {}
triggers:
  - id: "1"
    configuration:
      startlevel: 70
    type: core.SystemStartlevelTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var timer = cache.private.get('timer');
        if(timer === null) {
          cache.private.put(actions.scriptExecution.createTimer(time.toZDT('PT2M'), () => {
            items.OH_Systemlevel.postUpdate('ok');
          });
        }
    type: script.ScriptAction

You need to exclude OH_Systemlevel from restoreOnStartup to achieve this.

Why not a Switch or Contact? It’s used as a binary flag after all.

You already know that runlevel 70 passed at least two minutes ago, so why do you need to to test for this again here? Also, what if it’s runlevel100? Do you not need to update that Item in that case?

Your question about the several occurences of start levels.
I had a deeper look, why I came to that perception. Every time I save the rule with trigger “…start level 70 reached”, the rule is executed. So that was confusing during development until being understood. The “ok” item was a supposedly workaround.

No more necessary. After development, I took the 2min timer and am fine with that solution. Thanks for the hint about JS flavors and cache usage.

I´m still wondering about the recurring start level. Docs say “70=User interface is up and running”. Why does saving a rule with that trigger fire up level 70 again? Isn´t “up & running” achieved only once and enough?

When a rule is loaded it’s like that rule comes into existence for the very first time. If it has a system runlevel trigger, it looks to see if that runlevel has already been reached. If so it triggers the rule. Because OH startup happens in parallel, this ensures that a system runlevel rule always runs, even if it gets loaded after the runlevel has been reached. But it also means that if you modify the rule, it will run when it’s reloaded because the runlevel has been reached.

Thanks for the background.
Good to know that a rule triggering on a lower runlevel (e.g. 70) is also executed, if the system already reached a higher runlevel (e.g 100).