How to define global variables in Rules?

It will work the same in OH 3 with one difference. The System started trigger now does exactly what it says, it triggers when openHAB has started. If after OH has started the .rules file is reloaded, that rule will not trigger again because OH hasn’t restarted.

However, reloading a .rules file means everything in that file is unloaded/destroyed, including the “global” variables (they are not truly global, just global to this one file).

There is an issue open to add a “file loaded” trigger or somesuch but I don’t think it’s been implemented and merged. So in the mean time you can run the System started rule manually from MainUI or the Karaf Console. Or you can restart OH itself when you change this one file.

Yes if and only if they are in the same .rules file. The same general answer goes for the other languages too.

There is also a registry that has been implemented but not yet merged that can be used as a way to share data between rules not defined in the same file (UI rules don’t even have a file so without this there is no sharing at all). JS Scripting, in the mean time, has a cache which is simpler but serves the same purpose as the registry.

There used to a subtle issue in Rules DSL which required you to use unique names for all your “global” variables, even if they are defined in separate files. But I think that was fixed but maybe not.

See above.

If you really care about scalability, Rules DSL is a poor choice for language. Almost every language feature that addresses that sort of thing simply does not exist in Rules DSL or, when it exists, is significantly limited.

Another alternative would be to put your System started method into a lambda (see Reusable Functions: A simple lambda example with copious notes) and put the test for null in the rules that depend on these variables being initialized and call that lambda from the rule.

However, looking at what these variables do and what you use them for, I’m going to guess you don’t know about Groups and their various uses.

For example, you could replace the global variable hueLampsMWColor with a Group Item that has all those Items as its members. Then you can replace

                for(var int i = 0; i < hueLampsMWColor.size(); i++){
                        hueLampsMWColor.get(i).sendCommand(HSBType.RED)
                }

with

hueLampsMWColor.sendCommand(HSBType.RED)

So not only do your rules become simpler, you don’t even need the System started rule nor the global variables in the first place.

Using Groups the code above could be reduced to:

rule "WindowsOpen"
when
    Member of Windows changed to OPEN
then
    Windows.members.forEach[ i, window | logInfo("rules", "{} state {}", window.label, window.state)]

    logInfo("rules", "Windows open")

    // Let's just count the number of open windows
    if(Windows.members.filter[ window | window.state == "TILTED"].size > 0) {
        // You can turn on, turn up the brightness and set the color to RED in one command
        // A command to the Group goes to all its members
        hueLampsMWColor.sendCommand("0,100,100")
    } 
    else {
        logInfo("rules", "One or more windows are opened")
        hueLampsMWColor.sendCommand("0,100,100") // Why does it do the same thing?
    }
    logInfo("rules", "Windows open")
end

OK, now that I’ve done that I notice something, this rule will command the lights to RED no matter what. So why have the if statement in the first place. You want the light to be on and red if and window is open or tilted. So let’s just do that.

The same for closed but this actually doesn’t make sense. If any one window is OPEN or TILTED you set the light to red. But if, for example, you have 3 windows open and close one of them, all the light will turn off even though two windows are still open. Wouldn’t you want to wait for all the windows to be CLOSED?

To do that, set the aggregation function on Windows to AND(CLOSED,OPEN). That will set the state of Windows to CLOSED only when all its members are CLOSED.

rule "Windows Open or Tilted"
when
    Member of Windows changed to TILTED or
    Member of Windows changed to OPEN
then
    Windows.members.forEach[ i, window | logInfo("rules", "{} state {}", window.label, window.state)]

    logInfo("rules", "Windows OPEN or TILTED")
    
    // the fact that this rule triggered we know at least one window is now open or tilted
    hueLampsMWColor.sendCommand("0,100,100")

    logInfo("rules", "Windows open or tilted")
end

rule "Windows Closed"
when
    Item Windows changed to CLOSED
then
    Windows.members.forEach[ i, window | logInfo("rules", "{} state {}", window.label, window.state)]
    
    logInfo("rules", "All windows are closed")

    hueLampsMWColor.sendCommand("0,0,0")

    logInfo("rules", "Windows Closed")
end

I looked up the colors online to get the HSB values. But hopefully you can see by using Groups we can do the same thing without globals, a System started rule, and in fewer lines of code as just one of the three rules.

We could go even further.

rule "Windows Lights"
when
    Member of Windows changed
then
    Windows.members.forEach[ i, window | logInfo("rules", "{} state {}", window.label, window.state)]

    var newState = "0,0,0"

    if(Windows.members.filter[ window | window.state == "OPEN" || window.state == "TILTED" ].size > 0) {
        logInfo("rules", "At least one window is OPEN or TILTED")
        newState = "0,100,100"
    }
    else {
        logInfo("rules", "All windows are CLOSED")
    }

    // Adjust the lights
    hueLampsMWToggle.sendCommand(newState)
end
1 Like