Declaring variables in a coded rule in OH3

RPi 4, Openhabian, OH3

I have copied an existing DSL rule from OH2 to OH3. the rule has variables declared as follows. However these do appear in the rule tab in the UI. Is declaring variables like this valid in OH3?

First few rows of DSL rule in the file system (from VSCode):

var Number SetPoint = 1.00
var Number CurrentTemp = 1.00
var Number Error = 1.00
var Timer LoopTimerBR = null
var Timer HeatTimerBR = null
val Number kp = 1.3
val Number ki = 0.02
var Number IntegralError = 0
var Number ProportionalError = 1.00
val Number MorningSetPoint = 16.00

rule "TPIRuleBR"
    Item BackRoomSetPoint changed
    logInfo ("TPIBackRoom  ", "triggered1")

This is what I see in the code tab in the GUI:

And in the code tab:

  - id: "0"
      itemName: BackRoomSetPoint
    type: core.ItemStateChangeTrigger
conditions: []
  - inputs: {}
    id: script
      type: application/vnd.openhab.dsl.rule
      script: >
        // context: tpithermostatbackroom-1

        logInfo ("TPIBackRoom  ", "triggered1")

Yes but only if you keep the rule in a .rules file.

The UI is unable to show you those things defined outside of the rule itself but that doesn’t mean they are not there nor that they won’t work.

1 Like

What if I’m not using a rule file, where/how do I declare variables via the UI? Is it sufficient to declare it somewhere (e.g. in a THEN action) and then use it wherever (within the rule) or do I need to put the declaration at thevl very beginning?

Depends what you are trying to do.
The UI allows to create rules.
The UI does not allow you create stuff outside of rules - like the “global” variables shown in your first example, which are outside of any rule and indeed may be shared among several file-based rules. (Sharing is usually the point of using files-based globals.)
For any particular task, there is always some other approach.

So, what are you trying to achieve?

I’m mainly trying to learn how stuff works in OH3. But here is a concrete example of a rule that I would like to improve with a variable:

A device is turned on when the rule is triggered. The rule also turns starts a timer that will turn the device off after 15.

Works fine.

Now I’d also prevent the rule from being triggered again for 30 minutes after it was triggered.

So I would define another THEN action that sets a flag to true, and sets it back to false after 30 minutes:

var VVC_locked = true;
createTimer(now.plusMinutes(30), [ | 
VVC_locked = false;])

I would then add a BUT ONLY IF condition VVC_locked == false; for the rule.

So the question in this example is: will that work if I declare the variable in the action as above or do I need to move the declaration to the beginning and then switch the variable in the action?

No, because every time you run the code a new version of the variable is created.
Every time the code exits, the variable is discarded. It has no existence between runs of the rule (unlike the file-based global).

For the simple task at hand, a dummy Item (which obviously has a continued existence independent of any rules) can be used. Setting a 30 minute expire on that would be simple.

There are other ways around in other rule scripting languages.

Or if you are comfortable with file-based, stick with it.

1 Like

Excellent! (I’m assuming it makes no difference in this case whether I send a command to the item or update its status?)

Now I’m just wondering what the most failsafe solution is: rules-based timer using global variables or a dummy switch-item with expire. By failsafe I mean that I rather want the dummy item to get stuck on OFF than ON in case something goes wrong (because ON will prevent the rule from being executed). My hunch is that expiring the item internally is more failsafe than updating it from a rule, but maybe there are better options?

I have very little experience with file-based rules, so I’m trying to figure out whether I should use those or the improved UI-based rules. If I understand correctly from the example in the OP, file-based rules will now be shown in the UI (if not always completely), which is good to keep an overview. But can I edit them there in the code tab?

My intuition so far has been to use the UI to avoid typos and not having to learn the code. However, I keep running into limitations: Blockly doesn’t do timers and can’t compare states with numbers. And now I learn that global variables are not available in the UI. There are always workarounds, but as long as the UI has these limitations, I might be better off going directly for file-based rules…

They do different things to the Item and anything associated with that Item but expire doesn’t care, no.

Expire is deliberately simple; you cannot tell if it is running a timer, you cannot vary runtime you cannot vary what it does upon expiry or do anything more complex than a single Item interaction
For a yes/no flag, that’s good enough.

There is no failsafe. You’d be running either an expire or a ‘proper’ Timer under control of your rules and looking for results in your rules. If you cock your rules up you cock them up in either case.

Nope. By design, the UI will never update your files.

Failsafe is hard. It takes a good deal of thinking and planning and coding to create something failsafe. You have to think about it from end to end and consider all the possible paths between those ends. What happens in OH restarts? What happens if the rule is edited while a timer is set?

Either a timer or Expire could be made to be failsafe, but it’s going to take some work to make it so.

Consider the Expire case with an Item. What happens if OH is restarted when the Item is ON? If you have restoreOnStartup on that Item will get restored to ON at startup but the Expire timer will not be reset. So you’d need a rule to update it to ON again at startup to kick off Expire again. If you do not have restoreOnStartup on the Item it will be set to NULL and NULL != ON so your rule could run.

No, if the rule is in a text file it can only be edited through the text file.

You will not get away from that. Even in Blockly you have to learn some coding.

That’s exactly the kind of reasoning I was after. I didn’t know about restoreOnStartup so I didn’t set it, but my intuition told me that I better make the “But only if”-condition “different than ON” instead of “is equal to OFF”. So now I know why.

I don’t mind coding. But when I choose Blockly to create a simple rule with a timer and an if condition for some of the actions, only to find that timers are not yet available, then it would have been easier to go for the coding right away, instead of trying to save time via Blockly.

I understand the difference in the case of items derived from things (simply put: making the thing change its state vs updating what OH thinks its current state is) but in the case of a dummy item used as a variable in rules, I can’t see any difference. If you have an edge case where there might be a difference, please let me know.

By default OH 3 installs rrd4j for persistence. And it’s configure by default to persist all supported Items and restoreOnStartup.

For many Items they support different commands than it’s state. You can command a Dimmer Item to ON but you cannot update a Dimmer to ON.

Thinking about commands as causing an Item to change state is not a good model. A command is to cause a device to do something. An update is to change the state of an Item. You should always use updates unless you really mean to use a command to cause something to happen.

The reason for this is that a command doesn’t actually change the state of an Item. Even if it isn’t linked to a Channel a command doesn’t change the state of an Item. Instead a command is “caught” by Autoupdate and Autoupdate interprets the command and generates an update. When you turn Autoupdate off (via Item metadata) the Item won’t receive that update and therefore change state until the binding issues the update. And the binding usually wont to that until the device itself reports back that it changed state.

When you issue a command you are engaging all sorts of the OH subsystem and generating a whole lot of events, each of which might cause something to happen. But ultimately it’s an update that will change the state of the Item. So skip all that stuff and go straight to the update.