It gets added to a Timer thread which executes the code in a separate thread at the scheduled time. If you want to stop execution for a period of time use Thread::sleep(msec)
.
Each triggered rule runs in a separate thread. So if you have a rule that is sleeping (Thread::sleep) and the rule is triggered again two instances of the Rule will be running at the same time in parallel. See this post I just make discussing the difference between Timer, Thread::sleep, and the updatedSince/changedSince.
You donāt know which will be executed first. They will be both executing in parallel.
In this case I think that does make sense as you want to make sure the Bar_Delay executes first.
Iāve not seen that issue before nor has it been reported on these forums that I can recall.
Updated code needs to be to be syntactically correct (edits have a comment indicating what I changed):
import org.openhab.model.script.actions.* // need to import Timer and createTimer
var Timer BarTimer=null
rule "Wine"
when
Item bar_wine changed
then
if(bar_wine.state == OFF && bar_delay.state == OFF ){ // && instead of and, == instead of =
Arduino.sendCommand("3")
BarTimer = createTimer(now.plusSeconds(12))[|
bar_delay.sendCommand(ON)
]
}
else if (bar_wine.state == ON && bar_delay.state == OFF) { // put if clause in ( ), && instead of and, == instead of =
Arduino.sendCommand("4")
BarTimer = createTimer(now.plusSeconds(12))[|
bar_delay.sendCommand(ON)
]
}
else{
bar_wine.sendCommand(if(bar_wine.state == OFF) ON else OFF)
}
end
Do you have bar_wine and bar_delay declared in an .item file? Your screenshot of Designer indicates that neither are defined. You must declare all your Items in a .items file in order to reference them from inside a rule.
While the code above is now syntactically correct, it doesnāt make much sense to me logically.
I would write your rule something like to following (note, the most common coding style you will find in examples on this forum and the OH wiki is to use first cap camel case for Item names and first letter lower-case for variables which I use here):
#Items
Switch BarWine // whatever your binding is
Switch BarSpirits // whatever your binding is
Switch BarDelay
import org.openhab.model.script.actions.*
import java.util.concurrent.locks.ReentrantLock
var Timer barTimer = null
val ReentrantLock lock = new ReentrantLock // used so we can use the same Timer for both spirits and wine
rule "Wine"
when
Item BarWine changed
then
lock.lock // prevents the rule from being triggered more than once at a time or BarSpirits as well
try {
// Only do something it BarDelay is off
if(BarDelay.state == OFF){
// Send the command to the Arduino
if(BarWine.state == OFF) Arduino.sendCommand("3")
else Arduino.sendCommand("4")
// Turn on BarDelay so this and spirit rule won't execite
BarDelay.sendCommand(ON)
// Probably not needed here but it is good to get in the habit of cleaning up Timers before creating new ones
if(barTimer != null){
barTimer.cancel
barTimer = null
}
// Turn off BarDelay in 12 seconds
barTimer = createTimer(now.plusSeconds(12), [|
BarDelay.sendCommand(OFF)
barTimer = null
]
}
}
// Catch any error
catch(Throwable t) { }
// Make sure the lock gets unlocked even in the case there is an error
finally {
lock.unlock
}
end
rule "Spirits"
// Pretty much a copy of the above. It is probably possible to merge the two but get it working as two separate rules first
In words what is happening above is a lock is set which will make it so only one instance of the code between the lock and unlock can execute at the same time. This is me being overly cautious as I doubt it will ever be a case that you can press the button fast enough that multiple copies of the rule would be executing in parallel, but this will ensure that is the case. By putting the lock in both rules it also prevents changes the Spirits and Wine rules from executing in parallel as well.
Next I simply check for the state of BarDelay. The main part of the rule will only execute if it is OFF. The rule exits without doing anything but unlocking the lock if BarDelay is ON.
If BarDelay is OFF, if BarWine is OFF we send ā3ā to Arduino. If BarWine is ON we send ā4ā to Arduino.
Now we set BarDelay to ON which activates the delay. Because we put the locks around the rule, this will block the execution of the logic in any instances of the rule that have queued up while this instance was running (unlikely as that may be) and all subsequent instances until it is turned OFF.
Next we do some bookkeeping clean up and cancel the timer if it is already running and set it to null. This is really not needed here because of the lock and the fact that we only get to this point if BarDelay is OFF we should never be in a situation where an existing barTimer is running. I include the code here for informational purposes as in most cases it is something you will want/need to do if you donāt have these guards around the creation of the timer.
Now we create a Timer set to execute in 12 seconds from now with a body that sets BarDelay to OFF, reenabling the rule. Setting barTimer to null as the last line of the Timer body is again just some good Timer bookkeeping and not really needed here. NOTE: when you create a Timer, the lambda you pass to the Timer (i.e. the stuff between the [ ]) inherits the context that exists where it was created so it has access to all the same variables that were available to the rule when it was created.
Finally we catch any error that may have been thrown and unlock the lock. The use of try/catch/finally guarantees that when the rule exits the lock has been unlocked, even in the event of an error.