I want to put my way here too. Maybe it is complex, but working perfect for me. Big advantage is, that you can control runtime length of the timer via a variable. This variable is an item which can be changed via setpoint in sitemap.
I covered everything in a function:
start of .rules file:
import org.eclipse.smarthome.model.script.ScriptServiceUtil
import java.util.HashMap
var HashMap<String, Timer> tTimersWithCancel = newHashMap()
Function:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters:
// relatedItem = Item which should be changed if timer ends
// tTimersWithCancel = pointer to the timer variable
// timerlengthinseconds = length of timer in sec.
// 0 = timer should end before normal end but end action is executed
// -1 = timer should end before normal end but end action is not executed
// newitemstate = State of the item after times end (end action)
//
val Procedures$Procedure4<GenericItem, HashMap<String, Timer>, Integer, String> startTimerWithCancel=[ relatedItem, tTimersWithCancel, timerlengthinseconds, newitemstate |
// timer should end before normal end but end action is executed
if(timerlengthinseconds == 0)
{
if(tTimersWithCancel.get(relatedItem.name) !== null)
tTimersWithCancel.get(relatedItem.name).reschedule(now)
return;
}
// timer should end before normal end but end action is not executed
if(timerlengthinseconds < 0)
{
if(tTimersWithCancel.get(relatedItem.name) !== null)
{
tTimersWithCancel.get(relatedItem.name).cancel()
tTimersWithCancel.put(relatedItem.name, null)
tTimersWithCancel.remove(relatedItem.name)
}
return;
}
// Timer stated
// wait a little bit, if two timers end on the same time not all of them are execute the commands
Thread::sleep(100)
// create the timer and do the action at the end of the timer
if(tTimersWithCancel.get(relatedItem.name) === null || tTimersWithCancel.get(relatedItem.name).hasTerminated)
{
tTimersWithCancel.put(relatedItem.name, createTimer(now.plusSeconds(timerlengthinseconds)) [|
tTimersWithCancel.get(relatedItem.name).cancel()
tTimersWithCancel.put(relatedItem.name, null)
tTimersWithCancel.remove(relatedItem.name)
relatedItem.sendCommand(newitemstate.toString)
])
}
// timer is running, just reschedule with full value of time
else
{
tTimersWithCancel.get(relatedItem.name).reschedule(now.plusSeconds(timerlengthinseconds))
}
]
How to use:
Start timer with :
startTimerWithCancel.apply(ITEMSWITCH1, tTimersWithCancel, 60, “OFF”)
This command will start a timer and after 60sec send command OFF to item ITEMSWITCH1
Extend timer time with :
startTimerWithCancel.apply(ITEMSWITCH1, tTimersWithCancel, 60, “OFF”)
This command will reschedule a running timer after 60sec send command OFF to item ITEMSWITCH1
Cancel timer but timer end command will run
startTimerWithCancel.apply(ITEMSWITCH1, tTimersWithCancel, 0, “OFF”)
Cancel timer but timer end command will not run
startTimerWithCancel.apply(ITEMSWITCH1, tTimersWithCancel, -1, “OFF”)
Real example, switch on light and switch it off after 60sec.:
ITEM:
Switch Dimmer02_Switch1 "Treppe EG/OG Dimmer [%s]"
Number nuTreppe_TimerTime "Treppe Bewegungslaufzeit [%d sec.]"
in rule:
startTimerWithCancel.apply(Dimmer02_Switch1, tTimersWithCancel, (nuTreppe_TimerTime.state as Number).intValue, "OFF")