Using Variables in Rules

I’m using openhab with ui. I need to create a rule which checks if a power level is higher than a threshold for a certain amount of time. Therefore I need the possibility for a variable which survives the rule runtime.

In the Forum I found the possibility to declare global variables in the config file. However, as I use ui, this seems to be not working, at least I did not find a way how to do it.

So I tried it via items which are defined as numbers. I can get item values via items.getItem and write them via postUpdate, but that is only valid for Strings. So this

items.getItem(‘itemPower2’).postUpdate(items.getItem(‘itemPower1’).state);

leads to successfully transfer the content of the item. However, I need to deal with the numbers and perform calculations on it. So what I would need to work is for example:

items.getItem(‘itemPower2’).postUpdate(items.getItem(‘itemPower1’).state * 10);

but does not work.

I tried

items.getItem(‘itemPower2’).postUpdate((parseInt(items.getItem(‘itemPower1’).state) * 10).toString);

but it won’t work. I believe I don’t understand the whole concept yet. Any help would be appreciated.

you can try
items.getItem(‘itemPower2’).postUpdate((items.getItem(‘itemPower1’).state as Number).doubleValue * 10);

For UI rules, see JavaScript Scripting - Automation | openHAB. private is only visible to that one script action/condition that uses it whereas the values stored in shared is available to all script actions/conditions.

That won’t work in JS Scripting. I assume that’s what is being used here given the syntax.

@lobin, if you’ve installed the latest version of openhab-js using npm (version 4.0) than you can use

items.itemPower1.numericState * 10

If using an older version of the library:

Number.parse(items.getItem('itemPower1').state) * 10

Both of these assume that the Items are simple Number Items, not Number:Power types which have units.

Thank you for this answer. For the shared values:

I found this in the code (cache.js - Documentation):

  1. * Shared cache that is shared across all rules and scripts, it can therefore be accessed from any automation language.
  2. * The access to every key is tracked and the key is removed when all scripts that ever accessed that key are unloaded.
  3. * If the key that has been auto-removed stored a timer, that timer is cancelled.

Wouldn’t that mean that even the shared variable is lost as soon the script in the rule is finished?
If they are not lost, where should this be defined since they should not be redefined with every script call?

Unfortunately, I did not install openhab 4.0. However, do you think it is stable enough to work with?
Since I just started with openhab 1 week ago, it would be easier and not a bad idea to rebuild everything since I know now better how it works.

Can you elaborate a bit more?

If I understand you correctly, it is super easy to do this in jruby

File based rule:

threshold = 1 | 'kW' # Assuming itemPower1 is a quantity type with a Unit of Measurement
duration = 15.minutes

rule "Trigger when itemPower1 is high for a while" do
  changed itemPower1, to: ->(state) { state > threshold }, for: duration
  run do 
    # Do what you want to do when itemPower1 became 
    # higher than threshold and remains so for duration
  end
end

For UI based rule, set your rule to trigger on every change to itemPower1, then in the rule body:

threshold = 1 | 'kW'
return timers[itemPower1]&.cancel if event.state <= threshold

after(15.minutes, id: itemPower1) do
  # Do what you want to do when itemPower1 is high for a while
end
1 Like

No because it’s not a variable. It’s a library call that addresses the cache through OH’s API. It’s not a local variable.

You don’t define them. They are already defined for you as part of the library.

The 4.0 snapshot builds of OH are rapidly changing. There are a number of know this that are broken. I don’t recommend it unless you want to test and help find and report and fix bugs.

However, the openhab-js library version is independent from the OH version. I don’t think there’s anything in it that requires OH 4. You could install is using npm and see if you have any problems. It’s ready enough to remove if you do.

First: Thanks a lot for your effort with a beginner of openhab

After a long time trying I finally got it. To be honest, I am super confused with all the different script syntaxes used and found in the community. So I have the js standard library ECMA 262 Edition 5.1 and
ECMAScript 262 Edition 11 installed.
In the standard library I can get item values only with this:

itemRegistry.getItem('StatusWP1').getState()
while with Edition 11 it is more like you wrote:
items.getItem('StatusWP1').state

however getting the numeric values never worked with Number.parse, I have to use parseInt which works just fine.

The timer command

myTimer = createTimer(now.plusSeconds(500),function() { code })

never worked in neither of the script versions. But the standard JS Timer functions fortunately do work.

In both scripts, the “this.” variable worked which is fine for this purpose, so I did not have to use the cache variant.
So this is the final working code:

 var SchwelleHigh = 1200;
 var SchwelleLow = 1100;
 var ZeitOn = 2000;
 var ZeitOff = 5000;
 this.myTimer = (this.myTimer === undefined) ? null : this.myTimer;
 if (items.getItem('StatusWP1').state == "ON") {
   if (parseInt(items.getItem('DebugControl').state) < SchwelleLow) {
 	if (this.myTimer==null) {
 	  this.myTimer = setTimeout( function(){
 		items.getItem('StatusWP1').sendCommand("OFF");
 		items.getItem('StatusWP2').sendCommand("OFF");
              this.myTimer = null;
 	  },ZeitOff);
 	} 
   } else {
     if (this.myTimer!==null) {
       clearTimeout(this.myTimer);
       this.myTimer=null;
     }
   }
 } else {
   if (parseInt(items.getItem('DebugControl').state) > SchwelleHigh) {
 	if (this.myTimer==null) { 
 	  this.myTimer = setTimeout( function(){
 		items.getItem('StatusWP1').sendCommand("ON");
 		items.getItem('StatusWP2').sendCommand("ON");
              this.myTimer = null;
 	  },ZeitOn);
 	}
   } else {
     if (this.myTimer!==null) {
       clearTimeout(this.myTimer);
       this.myTimer=null;
     }
   }
 }

(DebugControl is a slider to simulate the input)

I don’t know what you mean by this.

There are add-ons which provide support for a given language.
There are libraries that go with those add-ons (sometimes they come with it, sometimes they are separately installable).

Which do you mean?

Well yes, they are two separate rule engines. The ECMA 5.1 is a nearly ten year old version of JavaScript. It’s almost completely different syntax, has its own helper library (installable separately) and lacks a ton of useful language features. It comes with Java so it’s there by default in OH 2.0-3.4 but needs to be separately installed in OH 4 where it’s mostly made available for legacy support.

You should not be writing new rules in ECMAScript 5.1 (aka Nashron).

That’s what I get by going from memory. I have too many languages rolling around in my brain, I get them mixed up sometimes. But this is standard JavaScript so “how to parse a String to an integer in JavaScript” search should give you lots of examples.

Please don’t just guess at syntax or from looking at examples on the forum. Read and use the docs for the add-on as a reference. There is full documentation for everything you can do with/to openHAB documented there for JS Scripting. For example: JavaScript Scripting - Automation | openHAB which covers JavaScript setTimeout as well as JavaScript Scripting - Automation | openHAB which covers openHAB Timers using createTimer.

Note that the mechanism that allows this to work to save a variable from one run of the rule to the next is not officially supported by the JS Scripting add-on and that mechanism actually causes some problems. Consequently it is not guaranteed to continue to work forever.

Just to see the difference, here is what it would look like using the latest opehab-js 4.0 library (which was just merged into the add-on and will come with the next OH 4.0 SNAPSHOT), using the cache and openHAB timers.

 var SchwelleHigh = 1200;
 var SchwelleLow = 1100;
 var ZeitOn = 2000;
 var ZeitOff = 5000;

var myTimer = cache.private.get('timer');
if(items.StatusWP1.state == 'ON') {
  if(items.DebugContol.numericState < SchwelleLow) {
    if(myTimer === null) {
      cache.private.put('timer', actions.ScriptExecution.createTimer(time.toZDT(ZeitOff), () => {
        items.StatusWP1.sendCommand('OFF');
        items.StatusWP2.sendCommand('OFF');
        cache.private.put('timer', null);
      });
    }
  } else {
    if(myTimer) {
        myTimer.cancel();
        private.cache.put('timer', null);
    }
  }
} else {

...

I stuck with your original logic so it’s easier to compare the two versions. But this rule could be adjusted to not duplicate so much code.

// Generates the function called by the timer
var timerFuncGenerator = (cmd) => {
    // OK, this is a hack but it shows one way to reduce code
    return () => { for(let num in ['1', '2']) items['StatusWP'+num].sendCommand(cmd); };
};

// Get the timer from the cache
var myTimer = cache.private.get('timer');

// If the timer exists always cancel it
if(myTimer) {
  myTimer.cancel();
  cache.private.put('timer', null);
}
// First condition, we already know the timer is null so we don't need to test for it again
else if(items.StatusWP1.state == 'ON' && items.DebugControl.numericState < 1100 ) {
  // Because the only differences between the timer bodies is the command sent, use the func generator
  cache.private.put('timer', actions.ScriptExecution.createTimer(time.toZDT(2000), timerFuncGenerator('ON'));
}
// Second condition, we already know the timer is null
else if(items.DebugControl.numericState > 1200) {
  cache.private.put('timer', actions.ScriptExecution.createTimer(time.toZDT(5000), timerFuncGenerator('OFF'));
}
1 Like