Timers and loops

I need an example of the following:

Upon a switch turning on
While the switch stays on:
Fire some code, every 20 seconds

So Switch ON -> while switch is on -> every 20 seconds ->some code -> repeat loop until switch is turned off

I’ve experimented with a while loop then a timer of 20 secs in the loop
That ends disasterously

It’s like it keeps firing every second over and over

I’m unsure how to accomplish this…i don’t want to use sleep as sleep stops everything in openhab from running
Times seem to allow all other rules to execute which is what i want, i don’t want to freeze up my system with a sleep every 20 seconds
I’ve messed around moving destroying the timer inside and outside the loop; etc.

rule "Sample Rule"
when
Item TestSwitch changed from OFF to ON
then
var Timer TestTimer= null
while (TestSwitch.state == ON){
TestTimer= createTimer(now.plusSeconds(20))[|
Execute some block of code every 20 seconds
]
}
TestTimer = null
end

The root problem here is the “every N seconds”. One-shot Timers are not well suited to spawning the next Timer in sequence, and trying to get around that can result in making a great many parallel Timers (which I think is what your current rule does)

But there are ways to do it
See @rlkoshak example of “fridge door” alarm

Not true. Sleep only blocks the one rule thread from running. The rest of OH and other rules threads will continue to run happily along. You only run into problems if you have lots of rules sleeping at the same time and run out of rules threads.

You can flip the rule around and trigger it every 20 seconds using a cron trigger and only send the commands when the switch is on.

To make it work in timers you would need to reschedule/recreate the next timer inside the body of the last timer which can be done but is awkward.

2 Likes

Thanks…good to know…how many threads are there total?
I only stated that from experience…when i was doing in upwards of 10-20 sleeps i noticed that other things would be delayed in other rule sets.

Why not use a rule triggered by a Cron statement (every 20 seconds) and place an if statement inside the rule to check if the switch is ON? I’ve not tested this - just a first idea…

Ah, sorry - just saw that this was already suggested…

seems like a lot of overhead for the system to constantly check every 20 seconds via cron for something on then run; i’d rather find a solution where there is a while loop and while it’s on, as long as its still on, do something every 20 seconds

but im by no means an expert

Think about it, using a loop will ask the rule to constantly run. A Cron triggered rule will only be started after each time step. The starting is done by a system service that is running anyway.

Cron is not a good solution here because it is unclear after how many seconds after swithing the switch on the code will run. This could be more or less anything between 0 and 20 seconds.

There are a couple of problems with your sample rule. One of them is that you create the TestTimer inside the rule. It needs to be created outside of the rule otherwise the garbage collector will collect it when your rule exits. Another is that you keep recreating timers endlessly in your while loop while the switch is on.

The following (untested code) should do what you are trying to achieve. I know that there are people that don’t like rescheduling timers, but so far I have never had any problems doing so.

var Timer testTimer = null

rule "Sample Rule"
when
Item TestSwitch changed from OFF to ON
then

	if (testTimer == null)
	{
		logInfo("testTimer", "Schedule timer")
		testTimer = createTimer(now.plusSeconds(20))
		[|

			// Run your code here

			if (TestSwitch.state == ON)
			{	
				logInfo("testTimer", "Reschedule timer - TestSwith is still ON")
				testTimer.reschedule(now.plusSeconds(20))
			}
		]
	}
	else
	{
		// this should not be possible, Sample Rule2 should prevent this
		logInfo("testTimer", "Reschedule timer")
		testTimer.reschedule(now.plusSeconds(20))
	}
end

rule "Sample Rule2"
when
Item TestSwitch changed from ON to OFF
then
	if(testTimer != null)
	{
		logInfo("testTimer", "Cancel timer")
		testTimer.cancel
		testTimer = null
	}
end
2 Likes

Yeah i see that point; however my initial goal was to only run the loop when the switch was ON as oppossed to having a 20 sec cron checking every 20 seconds forever

I shall try this out tonight…thanks!

THAT…worked like a charm…thanks!

1 Like