Design Pattern: Looping Timers

Please correct me if I’m wrong, but I’m getting the following error in the log

 - Validation issues found in configuration model 'zigbee.rules', using it anyway:
The operator '!=' should be replaced by '!==' when null is one of the arguments.

I think this is where the

if(ceilingTimer != null) return;

checks the state of

var Timer ceilingTimer = null

, and it compares the same type with each other. (as I read here)

So the code has to be

if(ceilingTimer !== null) return;

because null and null are of the same type?


not correct. You need to use !== because null is literally one of the operands. When you see null on either side of the comparison you need to use the operator that has the extra =.

Hi Rich,

Is there a way to extend this design rule to several actions? Do something, wait for condition, do another thing, wait for other condition, do another thing, wait for new condition, … . Can i just place multiple timers below? Thanks

See my reply on your original post for how I would do what you originally described as wanting to do (i.e. do something, wait some amount of time and do something else, then wait a different amount of time and do something else). For that use case, I think just creating separate Timers is the better way to go.

To answer your specific question here, all things are possible but does it make sense? For your use case on the other thread, separate non-looping timers are probably a better choice. If you have a different use case in mind, I’d need more details about what you are trying to accomplish (don’t confuse what you are trying to accomplish with how you are trying to accomplish it, sometimes referred to as the XY Problem).

1 Like

Thanks for your reply. Sorry i just posted here when i saw you responded to my original post.

What is the best way to implement a daemon process which runs always? I don’t want to start it in the “system startup” event because if I develop it I don’t want to restart the system in every minute. So I have created this cron below to start it. But if I change the code, I got I huge exception in the logs. I don’t have problem with that, but is this the best solution for it?

var Timer timer = null

rule "cron process"
	Time cron "0/10 * * * * ? *"
	if (timer === null)
		logInfo("cron", "start timer")
		timer = createTimer(now, [ |
        	//do the job here....

The exception in the logs is because the timer will still be running when you save the code, but the code that is supposed to run is recompiled. So this creates a mess when the timer expires and tries to run the code which is no longer where the timer thought it was. So this is quite normal when developing with timers.

I guess this is a possible way to always have some code triggered, but the question always is: Is there not a better way, like reacting to a more specific event to execute some code.

If it is some piece of code that evaluates something and then does nothing in 99% of cases, I would try to make it as efficient as possible in evaluating the case of “nothing to do”.

1 Like

Arno has nailed the answer. But I have my own additional concerns.

OH is not a real time system. It really is not equipped to handle polling something from Rules as fast as you are trying to do. Personally, I’d say anything faster than 750 msec should be moved outside of OH Rules and placed in an external service that polls whatever is being polled and reports changes to OH through the REST API or MQTT.

I don’t think JSR223 Rules will necessarily have the same issue but even there I’d consider polling something this fast as inappropriate. Rapid polling like this really needs to be in the binding or in the device that is reporting to OH.

I suppose you don’t realize that if you change a .rules file, OH will rerun the System started Rules when it reloads the file. There is no need to restart anything to trigger System started Rules. Furthermore, for testing while you are developing, you can always add a testing Switch as a trigger and manually kick off the Rule.

I usually recommend against creating permanent solutions to temporary problems.

Basically what I want to do is to create some kind of “RGB led daemon” for xiaomi gateway which handles and executes the different kind of lightning effects coming from the different rules. For example if I ask this service (via a virtual Item) to dim the light with red color, then this service would turn on the light, change the color, and change the brightness of the light in every 200ms (or 500ms, we will see). This is the reason why I want to implement this logic in OpenHAB. Embedding and copy-pasting this logic for several rules is not the best way, I think.
On the other hand, I would like to implement another service (which runs e.g. in every seconds) which is responsible to handle the “light requests” in the gateway. For example I have a yellow light effect, which means a window is open (remains yellow, until the window is closed). But I have another light effect (blinkling 5 times the blue light), which means the entrance door has been opened. So if the yellow light is on, but someone enters the door, then the gateway should blink the blue light 5 times and then it should switches back to yellow. And this scenario should be more convenient to be implemented with a daemon process, which “remembers” the previously used light color.
These scenarios are only in my theory yet. We will see, what the xiaomi gateway let me to do. :slight_smile:

I’m not sure that Xiaomi gateway can accept and process commands that fast. I know Hue can’t based on what others have reported.

But, based on what you describe, you should trigger the rule when your virtual Item receives a command, then run the looping timer only until the light reaches the desired state. Then the timer exits. Don’t have the CPU sit there spinning and doing nothing if you can help it.

You don’t need to loop for this. You just need to trigger a Rule when the entrance door is opened. Save the current state of the light to a variable. Then blink the light how ever you want. Finally restore the light to the state stored in the variable.

It is absolutely NOT more convenient to implement with a looping timer.

OH is event driven. Write your Rules to respond to events. Don’t have it sitting there looping waiting for things to happen. Let is sleep and react when something does happen.

Thank you for the answer and the suggestion. I also would like to approach most of the scenarios in event-based way. Basically only the first example (light dimming) lead me to this “Looping timers” pattern, but I also have fears that if I force the gateway to dim the whole night, then probably next day I have to order a new device :slight_smile:
If the gateway does not support native dimming effect, then probably I should not force it to do.

I have an oscillating light rule in my rules. It reacts to a dummy switch and then changes light intensity and color.

This has not really been edited for publication, so code hygiene and readability may not be up to rich’ standards, but you can hopefully see how I did it. My frequency is much much lower, if I do it faster my zigbee network clogs up. The randomness I have coded in will result in each lamp being changed every 4 seconds on average.

It will react to the command to the dummy switch, and it will retrigger the command (and thus the rule) each second as long as the dummy switch is not switched off or the lights are all switched off.

rule "Oscillate Colors Gently"
    //will gently vary lamp colors in a room when dummy switch is set
    Item Wellengang_SZ_Dummy received command
    //cycle through lamps and randomly change them by a bit
    gColorSZ.members.forEach[lamp |

        //pick a lamp at random
        if ((rand.nextInt(100) > 75) && (lamp.state !== null)){
            if ((lamp.state as HSBType).getBrightness() > 0) {

                // get base color once
                if (baseColor === null){
                    baseColor = (lamp.state as HSBType)

                //pick a new hue within 10 of the current hue and 45 of the base hue
                newHueOffset =  ((lamp.state as HSBType).getHue() - baseColor.getHue() + rand.nextInt(51) - 25).intValue()
                if (newHueOffset < -45) {newHueOffset = -45}
                if (newHueOffset > 45) {newHueOffset = 45}
                newHue = (baseColor.getHue() + newHueOffset).intValue()
                if (newHue < 1) {newHue = 359 - newHue} 
                if (newHue > 359) {newHue = newHue - 359}

                //pick a new brightness within 10 of the current and 30 below the base brightness, slightly skewed towards staying bright
                newBrightnessOffset = ((lamp.state as HSBType).getBrightness() - baseColor.getBrightness() + rand.nextInt(20) - 9).intValue()
                if (newBrightnessOffset < -30) {newBrightnessOffset = -30}
                if (newBrightnessOffset > 0) {newBrightnessOffset = 0}
                newBrightness = (baseColor.getBrightness() + newBrightnessOffset).intValue()
                if (newBrightness < 12) {newBrightness = 12} //no switching off
                if (newBrightness > 100) {newBrightness = 100}

                //pick a new saturation within 10 of the current and 20 above the base saturation
                newSaturationOffset = ((lamp.state as HSBType).getSaturation() - baseColor.getSaturation() + rand.nextInt(30) - 16).intValue()
                if (newSaturationOffset < 0) {newSaturationOffset = 0}
                if (newSaturationOffset > 20) {newSaturationOffset = 20}
                newSaturation = (baseColor.getSaturation() + newSaturationOffset).intValue()
                if (newSaturation < 70) {newSaturation = 70} //always use color
                if (newSaturation > 100) {newSaturation = 100}

                var newHSB = new String (newHue +","+ newSaturation +","+ newBrightness)
                    sendCommand( lamp, newHSB )

    if ( Wellengang_SZ_Dummy.state == ON) {
        //If lights go off, or switch goes off then end the show. Otherwise retrigger in 1 s    
        wellengangTimer = createTimer(now.plusSeconds(1)) [|
            val totalBright =[(state as HSBType).getBrightness()].reduce[ sum, n | sum + n ]
            if  ( totalBright == 0 ) {
                gColorSZ.sendCommand( "30,40,0")
                baseColor = null
            else if ( Wellengang_SZ_Dummy.state == OFF) {
                //reset lamps
                sendCommand(gColorSZ, baseColor)
                baseColor = null
            else if ( Wellengang_SZ_Dummy.state == ON) {
                Wellengang_SZ_Dummy.sendCommand( ON )


To understand how the rules “Looping Timers with Expire Binding” works, I’ve created a program flowchart. (Hopefully without mistakes)
Maybe it will help others too.

Design Pattern - Looping Timers with Expire Binding.pdf (222.9 KB)

Is this still true? I thought I had seen a more recent post from you that indicated that not blocking Rules would still be a problem with JSR223/NGRE.

If it is still the case that it’s not a problem, the above approach would still be a viable solution for JavaScript which, IIRC, has no sleep function.

By ‘threading issues’, I was referring to the thread pool limitations, which do not exist for the NGRE. This means you could potentially have 1000 rules sleeping at the same time and other rules will continue to trigger.

However, a rule’s action must complete before it can be executed again. So, if a rule is triggered again before the action has completed, like if there is a long sleep in the action, the triggers will occur but the actions will just queue up. For example…

from core.rules import rule
from core.triggers import when
from core.utils import validate_uid

from time import sleep

@rule("Test overlapping rule actions")
@when("Time cron 0/1 * * * * ?")
def test_overlapping_actions(event):
    uid = validate_uid(None)
    test_overlapping_actions.log.warn("Start sleep: [{}]".format(uid))
    test_overlapping_actions.log.warn("Sleep complete: [{}]".format(uid))
2019-08-20 20:08:18.349 [DEBUG] [org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher] - Script loaded: python/personal/test/
2019-08-20 20:08:19.348 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:19.367 [WARN ] [jsr223.jython.Test overlapping rule actions] - Start sleep: [c7f9c35ec3a711e985f5001bb952f560]
2019-08-20 20:08:20.357 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:21.360 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:22.360 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:23.361 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:24.362 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:25.362 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:26.363 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:27.364 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:28.365 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:29.365 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:29.368 [WARN ] [jsr223.jython.Test overlapping rule actions] - Sleep complete: [c7f9c35ec3a711e985f5001bb952f560]
2019-08-20 20:08:29.369 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is executed.
2019-08-20 20:08:29.371 [WARN ] [jsr223.jython.Test overlapping rule actions] - Start sleep: [cdf2b19ec3a711e9bc7e001bb952f560]
2019-08-20 20:08:30.366 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:31.367 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:32.368 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:33.368 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:34.369 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:35.369 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:36.370 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:37.371 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:38.372 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:39.372 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:39.372 [WARN ] [jsr223.jython.Test overlapping rule actions] - Sleep complete: [cdf2b19ec3a711e9bc7e001bb952f560]
2019-08-20 20:08:39.373 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is executed.
2019-08-20 20:08:39.378 [WARN ] [jsr223.jython.Test overlapping rule actions] - Start sleep: [d3e92ee1c3a711e9a1c4001bb952f560]
2019-08-20 20:08:40.373 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:41.373 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:42.374 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:43.375 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:44.376 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:45.377 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:46.377 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:47.413 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:48.413 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The trigger 'Time_cron_0_1_c75fcd51c3a711e9ad56001bb952f560_c75fcd52c3a711e9b522001bb952f560' of rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is triggered.
2019-08-20 20:08:49.380 [WARN ] [jsr223.jython.Test overlapping rule actions] - Sleep complete: [d3e92ee1c3a711e9a1c4001bb952f560]
2019-08-20 20:08:49.381 [DEBUG] [org.openhab.core.automation.internal.RuleEngineImpl] - The rule 'dd203c0f-1cd2-416a-8141-4bd3c4c94ba2' is executed.
2019-08-20 20:08:49.385 [WARN ] [jsr223.jython.Test overlapping rule actions] - Start sleep: [d9e0214fc3a711e9a200001bb952f560]

I’ve never had an issue with this behavior, but IMO this is a weak point in the NGRE that deserves some attention at some point.

Hi there,
I’m asking this question here, because I think it’s related.
I need to have a timer that I need to show the progress. Say a 30second timer, and every second I need to update a message. I have it working with a thread, but I doubt this is the best approach.

	while(Alarm_State.state==STATUS_ARMING && Exit_Countdown.state as Number > 0 && gAlarmAway.state==CLOSED) {
    	logInfo("sss","count down -1")
        Exit_Countdown.postUpdate((Exit_Countdown.state as Number)-1)


You’re right, that’s the worst possible approach because it is a system resource hog (the sleep).

Why not use a looping timer instead?

JSR223 JavaScript

TODO… I believe JavaScript lacks a sleep command so the looping timer approach would work for it.

The ECMA Script 5.1 engine used in OH3 does not support setTimeout or Promise. Both of which would give you the possibility to do this natively with javascript.

But you can use the same approach as with sleep or timers. Here are my examples:

'use strict';

var ZonedDateTime = Java.type("java.time.ZonedDateTime");
var ChronoUnit = Java.type("java.time.temporal.ChronoUnit");
var CompletableFuture = Java.type("java.util.concurrent.CompletableFuture");
var Thread = Java.type("java.lang.Thread");

var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.core.model.script.FutureTest");

function waitWithTimer(timeInMilliSeconds) {
  var future = new CompletableFuture();
  var timer = ScriptExecution.createTimer(, ChronoUnit.MILLIS), function() {

function waitWithThread(timeInMilliSeconds) {
}"Waiting with timer ...");
waitWithTimer(5000);"Timer done");"Waiting with thread ...");
waitWithThread(5000);"Thread done");

It’s way worth to get an idea, of what can be done with using the java.util.concurrent package of the JVM.


@franks Question: Via DSL Rules i use a global variable and set timer to null. If the rule will triggered again, i must check if the timer is set or null. How i do this with Javascript? I missing this in you post. How can i set the timer global and check on next trigger if its running or not.


That’s two different questions.

  1. How do I set a variable as a global? The answer is the same way you do is in .rules files. You define it outside the Rule. But, as with Rules DSL, in the UI there is no way to define a variable outside the rule so there is no way to create a global variable.

  2. How do I keep the value of a variable from one run of a Script Action to the next?

    this.myTimer = (this.myTimer === undefined) ? null : this.myTimer;

1 Like