I’ve done this. maybe you can reuse it.
What you need:
items:
// This item stores the length of the timer tiem in sec.
Number xxx_TimerLength
// This item is set to a given state if timer end
Switch xxxItemToManipulate
Function:
import java.util.HashMap
var HashMap<String, Timer> tTimersWithCancel = newHashMap()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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)
logInfo("Logger", "TimerWithCancel for Item " + relatedItem.label + " canceled. No action executed.")
}
return;
}
// Timer started
// 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)
{
logInfo("Logger", "TimerWithCancel for Item " + relatedItem.label + " started with " + timerlengthinseconds + " sec.")
tTimersWithCancel.put(relatedItem.name, createTimer(now.plusSeconds(timerlengthinseconds)) [|
tTimersWithCancel.get(relatedItem.name).cancel()
tTimersWithCancel.put(relatedItem.name, null)
tTimersWithCancel.remove(relatedItem.name)
logInfo("Logger", "TimerWithCancel for Item " + relatedItem.label + " reached end. Action " + newitemstate.toString + " executed.")
relatedItem.sendCommand(newitemstate.toString)
])
}
// timer is running, just reschedule with full value of time
else
{
logInfo("Logger", "TimerWithCancel for Item " + relatedItem.label + " rescheduled with " + timerlengthinseconds + " sec.")
tTimersWithCancel.get(relatedItem.name).reschedule(now.plusSeconds(timerlengthinseconds))
}
]
How to use it in a rule:
Keep in mind, rule and function must be in the same rule-file
// Start the timer and set Item to OFF at timer end
startTimerWithCancel.apply(xxxItemToManipulate, tTimersWithCancel, (xxx_TimerLength.state as DecimalType).intValue, "OFF")
// If you want to end timer before time runs out and Item should be changed
startTimerWithCancel.apply(xxxItemToManipulate, tTimersWithCancel, 0, "OFF")
// If you want to end timer before time runs out and Item should not be changed
startTimerWithCancel.apply(xxxItemToManipulate, tTimersWithCancel, -1, "OFF")
Maybe this will help you. I have the same use case. I use a proxy item to trigger additional actons if timer runs out. This will keep the function universal.
Example for my fire alert timer
rule "Fire alert proxy"
when
Item swSmokeAlertProxy changed
then
Thread::sleep(100)
if(swSmokeAlertProxy.state == ON)
{
SmokeSensor01.sendCommand(ON)
SmokeSensor02.sendCommand(ON)
startTimerWithCancel.apply(swSmokeAlertProxy, tTimersWithCancel, (nuSmokeAlarmAlertTime.state as DecimalType).intValue, "OFF")
}
else
{
startTimerWithCancel.apply(swSmokeAlertProxy, tTimersWithCancel, -1, "OFF")
logInfo("Logger", "Feueralarm beendet")
SmokeSensor01.sendCommand(OFF)
SmokeSensor02.sendCommand(OFF)
}
end
I have a different code for escalation timer too. Escalation timer will do some escalation, if timer ends and the status it not the status which is expected. This function will check in a loop if the status is not the wished one and reduce the timer interval to half one every run. This will increase the message interval for maybe 1h to 30min and the to 15 min. At the end, if it is not solved, the functions gives up
Any improvements are welcome