OH 4.0.3 Rule created with JSRule command initially works but then disappears (No restart)

  • Platform information:
    • Hardware: Proxmox VM 4 x amd64 CPU 8G
    • OS: Debian 11 6.1.0-11-amd64
    • Java Runtime Environment: Debian 17.0.8
    • openHAB version: 4.0.3

Created an ECMAScript (ECMAScript 262 Edition 11) rule using MainUI that uses the JSRule code as follows based on variables ruleName and RuleID:

                rules.JSRule({
                    name: ruleName,
                    description: "Script auto created by rule GUI-VACUUM",
                    triggers: [triggers.GenericCronTrigger(cronJob)],
                    execute: (event) => { 
                      items.getItem("ROBSched1StartNow").sendCommand("ON") 
                    },
                    id: ruleID,
                });

The rule created looks as follows when opened from developer sidebar:

The JSRule created code is as follows:

configuration: {}
triggers:
  - id: 55cc5cde-a98b-4013-ac3c-57302b524320
    configuration:
      cronExpression: 0 0 16 ? * MON,TUE,WED,THU,FRI,SAT,SUN *
    type: timer.GenericCronTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      privId: i182
      type: application/javascript;version=ECMAScript-2021
      script: >-
        // Code to run when the rule fires:
        // Note that Rule Builder is currently not supported!
        (event) => { 
                              items.getItem("ROBSched4StartNow").sendCommand("ON") 
                            }
    type: jsr223.ScriptedAction

The rule shows in the Rules section of the MainUI as follows:

When I test if the rule tiggers setting a time in the next 10 minutes the rules trigger fine and all works as intended. After some time (min was 20 min but some 2 hours, no pattern and no restarts) the rule disappears and cannot be found in the UI. When deleting/creating rules the logs show a message like:

2023-09-27 13:02:47.660 [INFO ] [.openhab.automation.openhab-js.rules] - Removing rule: Vacuum rule - Schedule 1
2023-09-27 13:02:47.661 [INFO ] [.openhab.automation.openhab-js.rules] - Adding rule: Vacuum rule - Schedule 1

Checking the logs there is no message showing the system Removes the rule and I’m at a loss why the rule is disappearing. Verified that the rule is gone by trying to delete it and there is no log message that it is "Removing rule: " making me conclude it is really gone.

Did notice that when clicking on the rule in the MainUI when the rule is under the rules section that the MainUI jumps to the script section but not sure if that is intended behaviour:




First time I used the JSRule command, did I misunderstand or miss something?

So, are you trying to create a rule that creates a rule? What’s your use case?

This feels like an XY Problem.

I’ve done some experimentation along these lines and ultimately it doesn’t really work out well. At a minimum, the rule that created the other rule can’t delete the created rule later on.

There is part of the problem. That’s not valid for a Script Action. Furthermore, the type is wrong for OH 4. It should just be application/javascript. But you say it works so :person_shrugging:

I doubt that there is anyone else besides me who has experimented with creating a rule from a UI rule. The environment is different from file based rules and that seems to get in the way.

That’s part of the issues I found. The rules registry keeps track of references to the rules it contains. When the reference count goes to zero it deletes the rule. But the reference to the rule from a UI Script Action is tenuous. Sometimes the creating Script Action can no longer access the rule. Sometimes the rule just self destructs because the reference was lost.

I’m guessing based on what I know and what I’ve observed. I’ve not actually looked at the code to confirm this.

I don’t think an error is thrown when you try to remove a rule that doesn’t exist.

You can change the logging level or openhab.event.RuleAddedEvent and openhab.event.RuleRemovedEvent to INFO and you will see those events logged in events.log.

It’s treated as a read-only unmanaged rule. Rules created in .js files appear the same.

JSRule is not designed nor tested for use in managed rules (i.e. rule created in MainUI).

The fact that you are trying to do so, even if you were using .js files, smells of an XY Problem. There is almost certainly a better approach even if this did work.

Hi Rich,
Thank you for your detailed feedback, appriciated. Did see but not fully understood the file-based comment, when the MainUI came out I migrated completely and as JSRule “works” in MainUI didn’t (my mistake) consider the title file-based sufficiently. Now, JSRule made me look at it as in creating a rule. Noticed now that type: jsr223.ScriptedAction is set by JSRule instead of a script.ScriptAction for rules. The type: application/javascript; version=ECMAScript-2021 seems to be correct but the version information interferes with the code as it does not show in the GUI. Guess this may also be why the MainUI GUI jumps to scripts when opening the rule.
(Ps. Creating/Deleting rules with JSRule works fine, no issue there and indeed if deleting a non-existing rule, it does not throw an error).
Currently have a workaround refreshing the JSRule every 5 minutes pending figuring this out and that works but understand that is not sustainable at it might break in future release if JSRule is changed.


As to my use case, there are currently 4 user configurable schedules for my vacuum cleaner. The user can set the following:




First rule with JSRule creates a new rule with a cronjob that only commands an item to ON with a 1 second expiration timer when any time/day/enable items is changed on the MMI. When the cronjob trigger the item ON it kicks a rule to build the rooms array and start the vacuum cleaner. There is a supporting rule to provide a 3-minute voice announcement if doors must be opened and a subroutine in the script when the house goes to sleep to have a voice announcement for schedules during the night that requires doors to be opened.

Critical part is to have a rule with a user configurable cronjob which I thought JSRule was kinda nice. Based on your feedback (not sure why I didn’t think about this before) I’m thinking I should generate a real text file-based rule with actions.Exec.executeCommandLine('echo', 'Hello World!'); to make is dynamically permanent :slight_smile:
Ps. Did look at the Timers & delays but becomes very complex very fast vs cronjob.

Please let me know what your recommendation is on dealing with my scheduling scenario. Thanks in advance.

Hi Rich,
Went ahead and have my rule create a DSL rule with the cronjob using the actions.Exec.executeCommandLine instead of continuing with JSRules , works so happy with that. Basically, just pass the schedule number and cronjob info to a script and have that generate a file.rules.

Still interested if you suggest a better way for my use case, please do let me know. Thanks.

Yes, I am aware that is where the type comes from and that may need to be looked into separately. But the reason why it jumps straight to the scripts is because when you create a rule using JSRule it’s created as an unmanaged rule. Unmanaged rules cannot be edited in MainUI so all it shows is kind of read only snippet of the rule. If you define a rule in a .js file it will appear the same in MainUI.

That’s the XY part of the problem. The critical part is to have a rule which triggers based on a user configurable time. A cron trigger is but one among many approaches.

Why not a DateTime Item and a Time is <Item> trigger? There are even date time picker widgets on the marketplace so the users can choose the exact time as well as a new Input widget for Sitemaps which support DateTimes. For the rule, there is an option to only look at the time portion of the DateTime Item’s state, in cases like this where you want it to run every day.

Not really given what we’ve seen in this thread. But it’s even simpler to use a DateTime Item with a Time trigger.

actions.ScriptExecution.createTimer(time.toZDT(items.SetTime.state+':00', () =>
  items.ROBSched4StartNow.sendCommand('ON');
};

Maybe I’m missing something but that looks simpler than creating a whole new rule with a cron trigger. Pay attention to the JS Scrupting docs, in particular time.toZDT(). It handles almost all of the complexities that come about when you need to manipulate date times. In this case, you can pass it a 24 hour time as a String (e.g. `16:001) and it will return a ZonedDateTime for 16:00 today.

But again, the even better way is to use the Time is <Item> trigger, so timers are kind of moot at this point for this discussion.

Given all the other resources available in OH, this is still overly complicated and hackey.

Thanks for pointing me here, was not aware of the input with time. Have updated the sitemap, item and rule and is really nice to pick the exact time.

Agree, needs more elegant solutions within MainUI but getting there.

This is definitely a great improvement and works well.

Your not missing something, I missed the function part in createTimer before and just did some testing with it to understand it. So if I understand correctly to implement my time/day schedule I need to:

  • Every MMI schedule change: createTimer() or reschedule() if exsists allready
  • At midnight: cancel() or skip it based on if the schedule runs in the next 24 hours
  • Every restart: createTimer()

Please let me know if I got this right and I’ll implement.

actions.ScriptExecution.createTimer(
  time.toZDT('12:19'),
  () => {items.getItem("ROBSched5StartNow").sendCommand('ON')}
);

Made small correction to command due to missing { if somebody likes to try.

At a high level this seems reasonable. Often the devil is in the details though.

Sometimes it’s easier to just always cancel and recreate the timer. One gotcha is if the new time is in the past. You have to decide what’s appropriate in that case (e.g. skip it for today or run it now).

You shouldn’t have to cancel anything. At midnight there shouldn’t be any more timers for that day. But at midnight it is the perfect time to create new timers for the new day so look at the Items and figure out if it’s a day where it needs to run.

Trigger the same rule that runs at midnight at system runlevel 100 (or what ever startup level you feel is appropriate).

Over all, this is kind of how the Time State Machine works: Time Based State Machine [4.0.0.0;4.9.9.9]. It could be used for inspiration (note because it’s a rule template roughly half of the code is there strictly for error checking so don’t be scared away by it’s length and complexity). You might even be able to use that rule template directly. Let the rule template handle the creation of the timers for you.

I’ve also a bunch of timer management utilities in openHAB Rules Tools which could be used for this as well. LoopingTimer (once created the timer will reschedule itself based on the value returned by the timer function) and Deferred (simple way to schedule a command to an Item).

But over all, I still think using a Time is <Item> trigger would be the easiest way to handle this. All you’d really need to do is manipulate the DateTime Items used to trigger the rule and won’t have to mess with Timers at all. You’d still need to do some work though.

  1. Create a proxy DateTime Item to trigger the rule.
  2. Use the trigger that takes into account the date as well as the time on the rule that commands the device using the Item created in 1.
  3. Create a rule that:
    a. use the Items to determine if today is a day that it should run.
    b. If so, copy the value in the “SetTime” Item to the proxy Item.
    c. If not set the proxy Item to NULL (prevents the rule from triggering).
    e.Trigger this rule at midnight and when the “SetTime” Item changes. You don’t need to trigger it at system startup if you have restoreOnStartup on the Items, but it doesn’t hurt either.

There are no timers to manage with this approach. You just have to manage the state of an Item.

Thanks for pointing again to Time is <Item> rule trigger, my one track mind skipped over it thinking fixed time, not that the <Item> was not obvious enough :slight_smile: . This is indeed the route to take for my use case and will implement next week.

@rlkoshak, thank you very much for all your help and patience.