Skip to content

[rules] JSRule: Add support for creating "standard" rules (not SimpleRule)#490

Merged
florian-h05 merged 7 commits intoopenhab:mainfrom
florian-h05:rules
Oct 8, 2025
Merged

[rules] JSRule: Add support for creating "standard" rules (not SimpleRule)#490
florian-h05 merged 7 commits intoopenhab:mainfrom
florian-h05:rules

Conversation

@florian-h05
Copy link
Contributor

No description provided.

…Rules)

Signed-off-by: Florian Hotze <dev@florianhotze.com>
@florian-h05
Copy link
Contributor Author

@Nadahar This enables JSRule to create "standard" openHAB rules in addition to its ability of creating SimpleRules.
Please let me know what you think about the naming of the option that enables this dedicatedContext and the JSDoc documentation for it.

@florian-h05 florian-h05 added this to the to be released milestone Oct 8, 2025
@florian-h05 florian-h05 added the enhancement New feature or request label Oct 8, 2025
@florian-h05 florian-h05 requested a review from Copilot October 8, 2025 12:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the JSRule functionality by adding support for creating "standard" rules (RuleImpl) in addition to the existing SimpleRule implementation. The key enhancement is introducing a dedicatedContext option that allows rules to run in isolated contexts, preventing interference between rule executions.

  • Added support for creating standard rules with dedicated contexts via the dedicatedContext configuration option
  • Refactored JSRule implementation to support both SimpleRule and standard Rule creation paths
  • Updated variable naming and documentation for consistency and clarity

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
@florian-h05 florian-h05 requested a review from Copilot October 8, 2025 12:41
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 1 out of 3 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
Signed-off-by: Florian Hotze <dev@florianhotze.com>
@florian-h05 florian-h05 requested a review from Copilot October 8, 2025 12:47
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 1 out of 3 changed files in this pull request and generated 1 comment.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@florian-h05
Copy link
Contributor Author

FYI you may use this for testing:

const { rules, triggers, utils } = require('openhab')

function myFunction() {
  console.log('myFunction()')
}

rules.JSRule({
  name: 'Shared Context Rule',
  execute: (event) => {
    myFunction()
    utils.dumpObject(this)
  }
})

rules.JSRule({
  name: 'Dedicated Context Rule',
  triggers: triggers.ItemCommandTrigger('test_switch'),
  execute: (event) => {
    myFunction() // expect this to fail
    utils.dumpObject(this)
    java.lang.Thread.sleep(5000)
  },
  dedicatedContext: true
})

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

@Nadahar This enables JSRule to create "standard" openHAB rules in addition to its ability of creating SimpleRules.
Please let me know what you think about the naming of the option that enables this dedicatedContext and the JSDoc documentation for it.

Please give me some time to study it, I'm "slow" when it comes to these things 😉

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

I added a comment to one of the CoPilot comments that have been "resolved". I'm not sure if you'll see that or not, so this is a heads-up.

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

I have to ask some really stupid questions here, but how do I proceed to use this version instead of the standard, so that I can test it? Can I just overwrite some file somewhere?

@florian-h05
Copy link
Contributor Author

florian-h05 commented Oct 8, 2025

That‘s actually no stupid question at all. You have three options:

  1. Download the dist-without-license artefact from the latest CI run of this PR, e.g. https://github.com/openhab/openhab-js/actions/runs/18345135519?pr=490, and unzip it and put the openhab.js file into the $OPENHAB_CONF/automation/js/node_modules folder.
  2. Install with npm: npm install git+https://github.com/florian-h05/openhab-js.git#rules
  3. If already manually installed (i.e. node_modules/openhab dir is there), you can simply override the node_modules/openhab/src/rules/rules.js file with the one from this PR.

Then, disable injection caching in the settings of the JS Scripting add-on.

@florian-h05
Copy link
Contributor Author

Just let me know once you are finished with testing.

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

Just let me know once you are finished with testing.

You don't have to wait on my testing for this, you probably know that it works. But, for me to evaluate it, I must test and try to understand what happens, and this is slow because I'm in so unfamiliar territory. But I can just as well do that after you merge it.

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

About the naming, I think it looks good 👍

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

Did you ever find my comment about how to attach the source?

Actually, there is a better way to do this now. I've just never gotten around to dealing with it. When I added the "Source" tab for rules in MainUI, I also made a new way to attach the source with the rules. Instead of attaching it to the first Action, it can be attached to the Rule itself. But, if it can't find anything there, it "falls back" to checking the first Action, so that nothing would break. Still, it would be better/more correct to attach the source to the rule itself, which would also eliminate this potential corner case.

https://github.com/openhab/openhab-webui/blob/892ad5006688b12f1adfaaf5a71c067bf9821763/bundles/org.openhab.ui/web/src/pages/settings/rules/rule-edit.vue#L963-L976

I always made to raise this anyway, so when I saw the code here, I might as well mention it now. It's very little change, basically, you move it to the Rule instead of the Action, and use the key source instead of script:

  ruleConfiguration.put('type', SCRIPT_TYPE);
  ruleConfiguration.put('source', '// Code to run when the rule fires:\n// Note that Rule Builder is currently not supported!\n\n' + ruleConfig.execute.toString());

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

This is amazing 🥳

This opens up the possibility for JS rules that can run in parallel (if you don't need shared functions) and that can be duplicated in MainUI (if that has any value). But, you could also implement support for making rule templates from JS (if there is any point in doing that, of course) using this technique.

The approach could also be used to enable scripted conditions, also for SimpleRules (but the Condition would still be in a dedicated context). It can probably also be used to enabled scripted triggers, if they have any actual use (it's hard to see exactly what they would achieve, given that they'd need some data to "trigger on").

Edit: This means that I'm done "testing". All I really wanted to do was to inspect the rule object it produced, and it looks great 👍

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

If you want to take this "far out there", maybe you could find a way to define another "dedicated context" in the same file where shared functions could be defined. This "dedicated context" could then be "injected" into the other dedicated context, for example by registering it as a Source in the GraalJSEngine. That way, both Actions and Conditions could utilize the shared functions (and in theory, other JS source files as well).

Just disregard if you're currently not looking for a challenge, this is just ideas that pop into my head of what might be possible to achieve 👼

*/
function _createRule (ruleUID, ruleConfig) {
let script = ruleConfig.execute.toString();
script = script.match(/(function)?[^{]+\{([\s\S]*)}$/);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this is where "the magic" happens? I don't quite get that you can extract the source code just by calling toString() on the function, but it's great that you can.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can call toString on any JS function and get its code, except it's a function provided by the runtime, then you'll only receive a string saying the function body is native code. All I have to do here is extract the body of the function.

Copy link

@Nadahar Nadahar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To the extent that I understand what happens, this LGTM!

@florian-h05
Copy link
Contributor Author

I will adjust the attachment of the source as you suggested and write docs before merging

Signed-off-by: Florian Hotze <dev@florianhotze.com>
@florian-h05
Copy link
Contributor Author

The approach could also be used to enable scripted conditions, also for SimpleRules (but the Condition would still be in a dedicated context). It can probably also be used to enabled scripted triggers, if they have any actual use (it's hard to see exactly what they would achieve, given that they'd need some data to "trigger on").

The thing is, JSRule has to support both SimpleRule and standard Rule, and:

a. There is no SimpleConditionHandler
b. As JSRule users write code for their rules, are used to write conditions right in the execute method. Usually, you need to take different actions based on conditions, at least I need to, so I couldn't separate the conditions into an actual Condition.

If you want to take this "far out there", maybe you could find a way to define another "dedicated context" in the same file where shared functions could be defined. This "dedicated context" could then be "injected" into the other dedicated context, for example by registering it as a Source in the GraalJSEngine. That way, both Actions and Conditions could utilize the shared functions (and in theory, other JS source files as well).

If you want to share across multiple contexts, you can create your own npm module. This would be the JS-way of sharing functions and classes.
For example: https://github.com/florian-h05/openhab-js-tools

@Nadahar
Copy link

Nadahar commented Oct 8, 2025

The thing is, JSRule has to support both SimpleRule and standard Rule, and:

a. There is no SimpleConditionHandler b. As JSRule users write code for their rules, are used to write conditions right in the execute method. Usually, you need to take different actions based on conditions, at least I need to, so I couldn't separate the conditions into an actual Condition.

It should be possible to make a SimpleConditionHandler, the bad thing is that it would have to be a different class than SimpleRule that also had an evaluate or whatever method that could be implemented. But, as far as I can tell, you'd have to have 3 different Java classes that you would have to juggle, one where only the Action was done this way (SimpleRule), one where only the Condition was done this way, and one where both were done this way. It could probably be done, but would be a bit of hassle.

When I'm talking about Conditions, I'm not really thinking of the evaluations that you do when you decide how to execute the Action. I'm thinking of Conditions that prevents the Rule from running entirely (those that would essentially just return from the Action without doing anything) - basically what handles the nuance that the Trigger doesn't. Doing it this way may not be much more performant (except that as long as we're not dealing with a "SimpleCondition", it can run in parallel with other operations), but it would certainly make the log/events "more logical", so that a rule wasn't reported as "run" all the times when it isn't really, but just returns.

However, when I think about it, a "SimpleCondition" might be a bad idea because of the locking, at least for JavaScript (and other engines that require an engine lock): As far as I can remember, the RuleEngine evaluates conditions, and it's a single thread. Forcing it to wait for a lock before evaluating a condition could cause backlogs.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
Signed-off-by: Florian Hotze <dev@florianhotze.com>
@florian-h05
Copy link
Contributor Author

but it would certainly make the log/events "more logical", so that a rule wasn't reported as "run" all the times when it isn't really, but just returns.

That's correct in case of events, I have never noticed a log about rule runs though.

However, when I think about it, a "SimpleCondition" might be a bad idea because of the locking, at least for JavaScript (and other engines that require an engine lock): As far as I can remember, the RuleEngine evaluates conditions, and it's a single thread. Forcing it to wait for a lock before evaluating a condition could cause backlogs.

And you also add the overhead (even if its little) of an additional engine ...

@florian-h05 florian-h05 marked this pull request as ready for review October 8, 2025 22:08
@florian-h05 florian-h05 requested a review from a team as a code owner October 8, 2025 22:08
@florian-h05 florian-h05 merged commit eb9f6eb into openhab:main Oct 8, 2025
5 checks passed
@florian-h05 florian-h05 deleted the rules branch October 8, 2025 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants