Skip to content

Allow rule with no trigger in DSL Rule file format#5462

Merged
mherwege merged 5 commits into
openhab:mainfrom
lolodomo:dsl_rule_no_trigger
May 13, 2026
Merged

Allow rule with no trigger in DSL Rule file format#5462
mherwege merged 5 commits into
openhab:mainfrom
lolodomo:dsl_rule_no_trigger

Conversation

@lolodomo
Copy link
Copy Markdown
Contributor

No description provided.

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
@lolodomo lolodomo requested a review from a team as a code owner March 30, 2026 13:40
@dilyanpalauzov
Copy link
Copy Markdown
Contributor

This is requested by #3655.


If there are no triggers, then when should be optional.

Copy link
Copy Markdown
Contributor

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 updates the openHAB DSL rules grammar to allow rule declarations whose when section contains no triggers, and adds integration tests to validate that such rules are loaded with an empty trigger list.

Changes:

  • Make the EventTrigger list in the when clause optional in Rules.xtext.
  • Extend existing rule-loading assertions to check trigger/action list sizes.
  • Add an integration test covering a rule with no triggers (when immediately followed by then).

Reviewed changes

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

File Description
itests/org.openhab.core.model.rule.tests/src/main/java/org/openhab/core/model/rule/runtime/DSLRuleProviderTest.java Adds coverage for “no trigger” DSL rules and strengthens assertions for trigger/action list sizes.
bundles/org.openhab.core.model.rule/src/org/openhab/core/model/rule/Rules.xtext Updates Xtext grammar so when can contain zero triggers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

… parameter

It was already called with StandardCharsets.UTF_8 for certain tests in that test class.

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Copy link
Copy Markdown
Contributor

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 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented Apr 1, 2026

Reading the discussion in #3655 , I am now not sure this change is expected, @kaikreuzer was against it, arguing that a rule with no trigger is in fact a DSL Script. And this is not wrong :)

My intention was just to allow the conversion of such UI rules in DSL Rule file format.

@rkoshak
Copy link
Copy Markdown

rkoshak commented Apr 1, 2026

But without this change, there will be DSL UI rules that cannot be converted to DSL rules, right? That seems like a good enough reason to support it. If not, maybe the export to DSL file format could generate a .script, but there needs to be some way to tell the user that's what happened. And if one selects multiple rules to export the scripts need to be filtered out so they are not all put in the same file with the rules.

@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented Apr 1, 2026

But without this change, there will be DSL UI rules that cannot be converted to DSL rules, right?

Yes.
Let's see if it is a sufficient argument for Kai ;)

@dilyanpalauzov
Copy link
Copy Markdown
Contributor

In the current discussion, I do not see why a trigger, which can never trigger, allows writing a rule, but a rule without a trigger should be illegal. An example for a trigger, which never triggers, is to wait for an ONOFF/Switch item to change its state to OPEN. If a SimpleRule can be created without triggers, then a DSL Rule without triggers should also be possible.

Such rules have no practical usages, but DSL Rules/Scripts/Xbase is a programming language and programming languages allow programmers to write things, which have no practical usages. It is all in the risk zone of the one, who writes the rules.

@kaikreuzer
Copy link
Copy Markdown
Member

Let's see if it is a sufficient argument for Kai ;)

My main goal is to have consistent concepts towards our users. Back in the discussion in #3655, a "block of logic" that can be executed (manually on the console, called from within a rule or as the execution block of a rule) was a "Script".

The concept of Scripts does not only exist in DSL, but is also embraced by the UI - Scripts are here described as "Rules dedicated to running code". I would hence claim that rules without triggers are either not yet completely defined or they should be listed as Scripts in the UI - and with that there would be no issue in transforming them to DSL Scripts.

If we say that the concept of Rules is generally also meant to merely define reusable blocks of logic and that triggers are not an important part of a rule, we should imho think about deprecating the concept of Scripts.

I'm fine with either way.

@rkoshak
Copy link
Copy Markdown

rkoshak commented Apr 2, 2026

The concept of Scripts does not only exist in DSL, but is also embraced by the UI - Scripts are here described as "Rules dedicated to running code" ...
we should imho think about deprecating the concept of Scripts.

I would actually be in favor of this because the term "Script" has too many meanings in OH already. But that points to a limitation with your comparison between Rules DSL Scripts and MainUI Scipts. Today the term "Script" referrs to all of these separate concepts:

  • A Rules DSL block of code placed in $OH_CONF/scripts/somename.script
  • In the UI, a block of code that can be created as a rule's action (Script Action)
  • In the UI, a block of code that can be created as a rule's condition (Script Condition)
  • The files loaded by automation add-ons (e.g. JS Scripting) which are executed to created rules are called scripts
  • In the context of the Exec binding and executeCommandLine, a .sh file is called a "script"
  • In the UI, a rule consisting of a single script action (no conditions, no triggers) with the "Script" tag is categorized in the UI under "Scripts". The UI does an OK job of making these distinct from rules except any time you need another rule to call one it's clear that it's a rule. For example, in the UI when you choose to run a script, you have to click on "Rule" to select which one to run. If writing code by hand, you need to use runRule(uid) to call a script (note that Rules DSL can only call Rules DSL scripts using callScript and does not have the ability to run another rule at all).

. I would hence claim that rules without triggers are either not yet completely defined or they should be listed as Scripts in the UI - and with that there would be no issue in transforming them to DSL Scripts.

It is possible to create a rule in the UI without triggers now without making them a Script. And there are some good reasons to want to do so. One example I personally use is I have handlers which are called by rule templates. It is a lot easier if those handlers are there next to the rule that calls them rather than a completely different place in the UI (which is where they would be if they were made as a UI script). But another case is I'm not always ready to add the triggers yet because the rule is under development.

It would be a problem if I created a rule in the UI without a trigger and the UI automatically moved it to Scripts. First of all, UI Scripts only support Script Actions. There are all sorts of other Actions possible in UI rules. Any Rule that uses these cannot become a Script. Secondly, it's possible for a rule without triggers to have a condition. UI Scripts cannot have a condition. Finally, consider the following scenario:

  1. create a new rule under Settings -> Rules
  2. fill out the pro forma and add a Script Action
  3. save and exit
  4. because the rule only consists of a single Script Action OH decides it's a Script
  5. Where's my rule? It's no longer under Settings -> Rules, it's disappeared there and now appears under Settings -> Scripts. And now that it's there, it cannot be changed back into a Rule. Maybe I didn't add triggers yet because I wasn't ready to, but I always intended to but needed to save and do something else first. But that's impossible once it's a Script.

Rules can be complicated things requiring multiple editing sessions to create fully. It would have a negative impact on usability if we made that impossible.

Obviously, all of these restrictions and work flow can change, but the above is what would happen as of today if we rejected rules without triggers and/or automatically moved them to be scripts.

@kaikreuzer
Copy link
Copy Markdown
Member

It would be a problem if I created a rule in the UI without a trigger and the UI automatically moved it to Scripts.

I absolutely agree, that was definitely not what I suggested. Most/all of your examples fall into the category that I called "not yet completely defined" rules, which are not scripts and must not appear/moved to Scripts. Clearly, there is no visual or technical differentiation between a "completely defined" and "not completely defined" rule in openHAB and I am not sure whether it would make sense (as it would possibly require to introduce a new rule status like "incomplete", which again complicates things and needs to be explained to the users).

I would actually be in favor of this because the term "Script" has too many meanings in OH already.

I'd be interested to know the current usage of "Scripts" (both in UI and DSL), which I cannot really judge. If we'd go and deprecate them, how many users would be annoyed because they are building on it (and like the distinction)? I personally could live with it, but I have no clue about others...

@rkoshak
Copy link
Copy Markdown

rkoshak commented Apr 6, 2026

If deprecated Rules DSL Scripts and added runRule to Rules DSL in addition to callScript we could at least remove one of these uses of the term "Script". It would also align Rules DSL more with the way that all the other rules languages work. In fact, I think it's already possible for runRule to invoke a Rules DSL .script "script".

Another/additional approach could be to figure out how to make a Rules DSL script that behaves more like what we would call a library in other languages, particularly adding the ability for scripts to accept arguments. Then we could use "callLibraryFunction" or something like that and treat DSL scripts like libraries instead of calling another rule.

Of course, callScript and all the Rules DSL script stuff would have to stick around for a good long time (OH 6.0 at the earliest I expect). This is just some brain storming.

My main goal is to have consistent concepts towards our users

I think we would need to decide, is callScript intended to be more like a library, or more like calling another rule? If the former, they present a different concept to users because you cannot pass arguments to a DSL Script which is a basic fundamental capability for a library supported by all the other rules languages (even Block libraries). If the latter, runRule in most places of OH allows also the passing of arguments too, so the concept is still different. In addition, DSL Scripts stand out as different as they have a dedicated callScript Action in core which none of the other languages use to call other rules.

For quite some time now, Rules DSL Scripts have been somewhat of an outlier in how they are written, how they are invoked, and what they can do when compared to all the other rules languages. If consistency is our goal, we need additional work to make DSL Scripts behave more like the other languages, either through a runRule type action or additions to make their use more like libraries.

@dilyanpalauzov
Copy link
Copy Markdown
Contributor

In the current changes, when a rule has no triggers, I suggest to allow skipping the when keyword: rule "name" then … end should be a valid rule form without triggers, equivalent to rule "name" when then … end.

What will mean from users’ perspective that DSL Scripts are deprecated?

For me this means removing the distinction between DSL Rules and DSL Scripts. Allowing in DSL Rules to write after the import section arbitrary expressions. Converting the rule … then …_ end contruct to an expression, returing a Rule instance. Merging the bundles org.openhab.core.model.rule and org.openhab.core.model.script into one bundle, the bundles org.openhab.core.model.rule.ide with org.openhab.core.model.script.ide in one; the bundles org.openhab.core.model.rule.runtime and org.openhab.core.model.script.runtime into one. Having a single (JSR223) implementation, which handles rules/*.rule, scripts/*.script, transforms/*.dsl and inline DSL: transformations in an uniform way. Possibly switching from rules/*.rule file to automation/jsr223/*.xbase files, likewise switching from transforms/*.dsl to transforms/*.xbase files.

In other words, in the same way there is no distinction between Groovy Scripts and Groovy Rules - these are only JSR223 Groovy lines of code - there should be no distinction between DSL Rules and DSL Scripts - these are just JSR223 Xbase (with openHAB amendments to the syntax) lines of code.

DSL Rules and DSL Scripts should be understood, as if they were an add-on, which can be substituted with the Groovy, Nashorn, Jython, etc automation add-on.

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Apr 8, 2026

If we say that the concept of Rules is generally also meant to merely define reusable blocks of logic and that triggers are not an important part of a rule, we should imho think about deprecating the concept of Scripts.

I'm for deprecating the concept of Scripts too, because it's a "fragile construct" that is full of challenges. I looked at the UI code, and this is solved simply by filtering rules. For all intents and purposes, they are rules. This is reflected in the REST API, and when you e.g. want to export these to YAML. As more possibilities are added around rules, it gets harder and harder to "maintain the illusion".

That doesn't mean that Scripts must be "deprecated". The UI menu option (which just applies a predefined filter to rules) can stay as long as it's seen useful. They wouldn't disappear or stop working in any way, but they would show up together with other rules as well. Which would be consistent with what can be observed through the REST API, YAML, the JSON DB etc.

If we are to try to preserve the concept of "Scripts" as a different entity than rules, I think my only option when making the conversion code is to block conversion of "Scripts". Alternatively, a completely new structure to handle these must be designed and implemented.

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Apr 8, 2026

Whatever is decided, my work with converting rules is blocked until a decision is made, because I must know what to do with these "objects".

@openhab-bot
Copy link
Copy Markdown
Collaborator

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/rules-and-rule-templates-yaml-integration/168568/206

@lolodomo
Copy link
Copy Markdown
Contributor Author

@kaikreuzer : what is the final decision? Depending on the decision, either I close that PR or I need to resolve the conflicts.

@kaikreuzer
Copy link
Copy Markdown
Member

@lolodomo I cannot take a final decision here. I'd suggest to have @openhab/core-maintainers agree on the solution.

I personally am ok with this change, but as discussed, I would then also suggest to retire/deprecate the concept of scripts. Might reduce the overall complexity.
@openhab/core-maintainers What's your take?

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented Apr 30, 2026

I would then also suggest to retire/deprecate the concept of scripts.

When you say this, I assume you mean to remove the special "view" in the UI that shows rules with Script tags separately, and remove the filtering that hides these in the normal rule view. But, except for in the UI, what else? Is it primarily about the documentation?

Does it include stopping support for the .script extension?

Does it also include removing the callScript() command from DSL? Because, that would be a breaking change, and without a real replacement. I have #5481 as a draft, that allows calling other rules from DSL in the same way that you do form other scripting languages, but there are still some aspects that need to be figured out about how best to integrate the extra commands, so I'm not sure if I'll be able to move that to a PR without further feedback.

Personally I would leave callScript() in place to not needlessly break anything. It will always only be able to call other DSL "scripts", because it calls the target by file name, and will only look in the scripts folder for the file.

In my mind, enabling DSL to call other rules doesn't mean that you have to remove what's there. One option would be to remove it from the documentation (and document the "new way" of doing it instead), but still leave the functionality intact. That still prevents breaking stuff. Or, is "the confusion" of having this old "script functionality" enough of a burden that it justifies breaking installations that happen to use it?

@mherwege
Copy link
Copy Markdown
Contributor

mherwege commented May 1, 2026

In my view, Rules DSL should be aligned as much as possible with the other languages. The scripts concept for Rules DSL has always been different then what we call scripts for other languages and what you see under scripts in the UI.
Do I understand it well that, if we have support for rules with no triggers (this PR) and the ability to call other rules from DSL (@Nadahar), we should have that parity?

Under the above two, I am all for deprecating the specific DSL script support. In my view, that does not mean remove it immediately, but remove it from the documentation and put in some logging when it is used, discouraging its usage.

Would it be an option to parse DSL scripts into rules without trigger? DSL callScript() could then just be calling a rule without trigger instead. That would me enable full backward compatibility.

@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented May 1, 2026

Do I understand it well that, if we have support for rules with no triggers (this PR) and the ability to call other rules from DSL (@Nadahar), we should have that parity?

As far as I can tell, yes. There is also the challenge of receiving arguments in DSL (like triggering event, sent parameters etc.), but I dabbled with that and found ways it could be achieved as well, and since it's not possible with DSL scripts today either, it doesn't really affect "parity" here.

Would it be an option to parse DSL scripts into rules without trigger? DSL callScript() could then just be calling a rule without trigger instead. That would me enable full backward compatibility.

I haven't looked at the details, but can't quite understand that it should be hard to achieve. I think they are already treated internally as just that, a rule without a trigger (that automatically gets the "Script" tag attached), so this "change" would probably mostly be cosmetic. I'm not sure how UID collisions are avoided though. DSL UIDs are created from the filename, which must be unique within the same folder, but this isn't the case since we're dealing with two different folders rules and scripts.

callScript() is still different from rule.runNow() in that the former takes a file name as an argument, the latter takes a rule UID.

@dilyanpalauzov
Copy link
Copy Markdown
Contributor

In my view, Rules DSL should be aligned as much as possible with the other languages.

Yes, this means: after the import section it should be possible to enter any expression, not just variable definitions, and rule … end should be an expression, returning a Rule instance. And also joining the core.module.script* and core.module.rule* bundles together. And moving DSL Rules and DSL scripts, together with their .ide and .runtime bundle counterparts in a separate automation add-on, which can be installed or not. However I think nobody knows how to implement the above steps.

Comment thread bundles/org.openhab.core.model.rule/src/org/openhab/core/model/rule/Rules.xtext Outdated
@Nadahar
Copy link
Copy Markdown
Contributor

Nadahar commented May 1, 2026

Yes, this means: after the import section it should be possible to enter any expression, not just variable definitions, and rule … end should be an expression, returning a Rule instance. And also joining the core.module.script* and core.module.rule* bundles together. And moving DSL Rules and DSL scripts, together with their .ide and .runtime bundle counterparts in a separate automation add-on, which can be installed or not. However I think nobody knows how to implement the above steps.

I certainly don't follow your reasoning here. I read it as "aligned from a user perspective", not attempted forced to behave like other scripting languages internally. I'm not saying that all the things you suggest are "bad", but I think each one of them would have to make sense on their own.

I really think the whole idea of separating DSL out as an add-on is bad, it doesn't only handle "scripting", it also provides file formats. Making it an add-on would mean that .things, .items etc. files wouldn't work without installing the add-on - how could that possibly be considered an improvement? It would just lead to user frustration, for very little gain.

@dilyanpalauzov
Copy link
Copy Markdown
Contributor

I have received this message over email:

I certainly don't follow your reasoning here. I read it as "aligned from a user perspective", not attempted forced to behave like other scripting languages internally. I'm not saying that all the things you suggest are "bad", but I think each one of them would have to make sense on their own.

I really think the whole idea of separating DSL out as an add-on is bad, it doesn't only handle "scripting", it also provides file formats. Making it an add-on would mean that .things, .items etc. files wouldn't work without installing the add-on - how could that possibly be considered an improvement? It would just lead to user frustration, for very little gain.

When I visit https://github.com/openhab/openhab-core/pull/5462#issuecomment-4361084468 without authenticating, I see the above message. When I authenticate, the message disappears.

Moving the bundles org.openhab.core.model.thing, org.openhab.core.model.thing.runtime and org.openhab.core.model.thing.ide into their own un/installable add-on is a good idea. It will allow faster startup and lower memory requirements for users, who do not use .things files. Likewise for .items. Currenlty this effect can be achieved in a cumbersome way by stopping the bundles after each openhab upgrade:

openhab-cli console
openhab>  bundle:stop org.openhab.core.model.thing org.openhab.core.model.thing.runtime org.openhab.core.model.thing.ide

However this was not what I suggested.

I proposed moving the bundles org.openhab.core.model.script, org.openhab.core.model.script.runtime, org.openhab.core.model.script.ide, org.openhab.core.model.rule, org.openhab.core.model.rule.ide and org.openhab.core.model.rule.runtime in their own binding.

@mherwege
Copy link
Copy Markdown
Contributor

mherwege commented May 2, 2026

Externalizing everything that uses xtext in separate addons is certainly not what I meant. This is another, and much bigger, debate. It has been debated before, and it is not on the table right now.

@lolodomo
Copy link
Copy Markdown
Contributor Author

lolodomo commented May 2, 2026

And off-topic in the context of my PR.

@kaikreuzer
Copy link
Copy Markdown
Member

So back to the topic:

Under the above two, I am all for deprecating the specific DSL script support. In my view, that does not mean remove it immediately, but remove it from the documentation and put in some logging when it is used, discouraging its usage.

This was my idea here, yes. Not breaking compatibility, but discouraging users to use it and rather document the "no-trigger rules" as the way to go instead.

I do not see anybody arguing against this PR here, so @lolodomo, I would say that you might want to rebase it!

@lolodomo lolodomo force-pushed the dsl_rule_no_trigger branch from ee16e99 to d2d3b3b Compare May 10, 2026 17:57
Copy link
Copy Markdown
Contributor

@mherwege mherwege left a comment

Choose a reason for hiding this comment

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

LGTM

@mherwege mherwege merged commit e8406ca into openhab:main May 13, 2026
5 checks passed
@mherwege mherwege added this to the 5.2 milestone May 13, 2026
@mherwege mherwege added the enhancement An enhancement or new feature of the Core label May 13, 2026
@mherwege
Copy link
Copy Markdown
Contributor

@lolodomo I assume there is some documentation to be updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement An enhancement or new feature of the Core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants