The following describes the necessary Functions and Procedures to create a small "Sleep Timer Library" for OpenHAB (Don't know, if Library is the correct title, but you know what I mean ;-)). Using this approach, it's easy for you to create your own custom sleep timers.
NOTE: This stuff doesn't depend on any add-on.
The usage example does not cover initialization or recovery on system start and demonstrates a VERY BASIC example. But it's not very hard for you to integrate and customize it to your needs ;-)
import org.eclipse.xtext.xbase.lib.Functions
import java.util.Map
import org.eclipse.xtext.xbase.lib.Procedures.Procedure0
var Map<String, Integer> gOldSleepTimerValues = newHashMap
var Map<String, Timer> gSleepTimers = newHashMap
var Functions$Function3<GenericItem, Integer, Map<String, Integer>, Integer> getNextSleepTimerValue =
[_sleepTimerItem, _sleepTimerValueInterval, _oldTimerValueMap |
val String logPrefix = "SleepTimer::getNextSleepTimerValue()"
// Get the current value of the number item
var int currentValue = (_sleepTimerItem.state as Number).intValue
// Get the old value of the number item.
var int oldValue = _oldTimerValueMap.get(_sleepTimerItem.name)
// If no old value exists, then initialize it with 0.
if (oldValue == null)
oldValue = 0
// Get the change direction: +1 == UP, -1 == DOWN
var int changeDirection = Math.signum(currentValue - oldValue).intValue
// Calculate the multiplier to get the next sleep timer value.
var int multiplier = (currentValue / _sleepTimerValueInterval)
if( changeDirection == -1 && (currentValue % _sleepTimerValueInterval) != 0)
multiplier += 1
var int newValue = multiplier * _sleepTimerValueInterval
// Store the new value inside the _oldTimerValueMap.
_oldTimerValueMap.put(_sleepTimerItem.name, newValue);
// DEBUGGING ->
logInfo(logPrefix, "oldValue : " + oldValue)
logInfo(logPrefix, "currentValue : " + currentValue)
logInfo(logPrefix, "changeDirection : " + changeDirection)
logInfo(logPrefix, "multiplier : " + multiplier)
logInfo(logPrefix, "nextValue : " + newValue)
// <- DEBUGGING
return newValue
]
var Procedures$Procedure5<GenericItem, Integer, Map<String, Timer>, Procedures$Procedure1<Integer>, Procedures$Procedure0> startSleepTimer =
[_sleepTimerItem, _sleepTimerUpdateInterval, _sleepTimerMap, _intervalExpiredAction, _timerExpiredAction |
val String logPrefix = "SleepTimer::startSleepTimer()"
// Check if a timer for the Item is already running and cancel it.
// NOTE: At this point it's possible that a synchronization problem occurs. If this condition is checked while
// the createSleepTimer routine updates the _sleepTimerMap, it's possible that the timer is not canceled.
var Timer sleepTimer = _sleepTimerMap.get(_sleepTimerItem.name);
if (sleepTimer != null) {
// DEBUGGING ->
logInfo(logPrefix, "Old timer canceled")
// <- DEBUGGING
sleepTimer.cancel()
}
// Turn off the sleep timer, if the new state of the number item is set to 0, e.g. via UI.
if(_sleepTimerItem.state as Number == 0) {
logInfo(logPrefix, "Set timer value to 0 --> Timer turned off.")
return
}
// Create the "createSleepTimer" function, which creates a respective sleep timer.
// NOTE: I hate warnings, so I use Object as second generic argument instead of
// Functions$Function2<Integer, Functions$Function2, Timer>. As a consequence the
// argument _crealteSleepTimerFunction has to be casted to
// Functions$Function2<Integer, Functions$Function2, Timer>.
// This is a way to suppress the warnings ;-)
val Functions$Function2<Integer, Object, Timer> createSleepTimer =
[ _remainingTime, _createSleepTimerFunction |
// DEBUGGING ->
logInfo(logPrefix, "Create timer using " + _remainingTime + " as remaining time.")
// <- DEBUGGING
val Functions$Function2<Integer, Object, Timer> castedCreateSleepTimerFunction =
_createSleepTimerFunction as Functions$Function2<Integer, Object, Timer>
if (_remainingTime <= 0) {
// Apply the timer action and update the sleepTimerMap.
_sleepTimerMap.put(_sleepTimerItem.name, null)
// DEBUGGING ->
logInfo(logPrefix, "Execute timer expired action.")
// <- DEBUGGING
if (_timerExpiredAction != null) {
_timerExpiredAction.apply()
} else {
// DEBUGGING ->
logInfo(logPrefix, "No timer expired action specified.")
// <- DEBUGGING
}
return null;
} else {
return createTimer(now.plusSeconds(_sleepTimerUpdateInterval), [|
// Update the remaining time.
var int updatedRemainingTime = _remainingTime - 1
// Execute the interval expired action.
// DEBUGGING ->
logInfo(logPrefix, "Execute interval expired action.")
// <- DEBUGGING
if (_intervalExpiredAction != null) {
_intervalExpiredAction.apply(updatedRemainingTime)
} else {
// Default interval expired action.
_sleepTimerItem.postUpdate(updatedRemainingTime)
}
// DEBUGGING ->
logInfo(logPrefix, "RemainingTime: " + updatedRemainingTime)
// <- DEBUGGING
_sleepTimerMap.put(
_sleepTimerItem.name,
castedCreateSleepTimerFunction.apply(updatedRemainingTime, castedCreateSleepTimerFunction)
)
])
}
]
// Start recursive timer creation.
val int remainingSleepTime = (_sleepTimerItem.state as Number).intValue
_sleepTimerMap.put(_sleepTimerItem.name, createSleepTimer.apply(remainingSleepTime, createSleepTimer))
]
Number timer_number_item
// NOTE: minValue must be 0. Other values are not supported.
Setpoint item=timer_number_item label="My Timer [%d]" step=10 minValue=0 maxValue=500
rule "My SleepTimer rule"
when
Item timer_number_item received command
then
// the increase/decrease value interval of the timer which MUST correspond to the step-attribute
// of your Setpoint (in this case 10)
val int timerValueInterval = 10
// The update interval in seconds, i.e. in this case every 3 seconds the timer value is decremented by 1.
val int timerUpdateIntervalSec = 3
// Calculate the new value for the SleepTimer and post it as update.
var int newValue = getNextSleepTimerValue.apply(timer_number_item, timerValueInterval, gOldSleepTimerValues)
timer_number_item.postUpdate(newValue)
// Create the timer.
startSleepTimer.apply(timer_number_item, timerUpdateIntervalSec, gSleepTimers,
[ _remainingTime |
// Update routine, which is called, if the timer is updated, in this case every 3 seconds.
// The argument _remainingTime is specifies the remaining time of the timer ;-)
// Here, we just update the number item.
timer_number_item.postUpdate(_remainingTime)
]
, [|
// This is the timer expiration action. E.g. you can turn off a switch or just log it on the console.
logInfo("MySleepTimer", "Timer expired.")
])
end