This should be implementable in a single Rule.
- You can have more than one trigger for a Rule
- You are not required to have a
from
in the trigger - In this case, we can handle the open or closed in the same Rule
- Inside the Rule, you can figure out which Item triggered the Rule using triggeringItem
- Design Pattern: Expire Binding Based Timers make for simpler code so I prefer to use them. Then we can also use Design Pattern: Associated Items, but I’ll show it using regular Timers because it will be easier to handle the two different times that way.
- This would be a perfect use for Design Pattern: Looping Timers
- Put your Items into a Group and you can simplify the triggers. Then you can add all the rest of your doors and windows to that Group and reuse this same Rule for reminders on those.
import org.eclipse.smarthome.model.script.ScriptServiceUtil
import java.util.Map
val Map<String, Timers> timers = newHashMap
rule "A garage door opened"
when
Member of doorStatus changed to open or
Member of doorStatus changed to closed
then
// Get the timer
val timer = timers.get(triggeringItem.name)
// A door changed to ON and there isn't already a timer (shouldn't happen where timer !== null)
if(triggerinItem.state.toString == "open" && timer === null) {
var time = 10 // we use this as a flag to see if this is the first or second time the timer runs
timers.put(createTimer(now.plusMinutes(time.intValue), [ |
// The Timer would have been canceled by now if the door closed, no need to check
// Get the car name and email address assocaited with triggeringItem
val carName = transform("MAP", "garagedoors.map", triggeringItem.name) // see https://community.openhab.org/t/design-pattern-human-readable-names-in-messages/34117
val phone = transform("MAP", "phones.map", triggeringItem.name)
// Send the alert
logInfo("Garage Door 1", "The garage door for the " + carName + " has been left open for " + time + " minutes. Message texted to owner.")
sendmail(phone, " ", "The garage door for the " + carName + " has been left open for " + time + " minutes")
// if time == 10, this is the first time we ran, change time to 30 so next time we know it's the second
// time the timer was run and we can send the appropriate alert
if(time == 10) {
time = 30
timers.get(triggeringItem.name).reschedule(now.plusMinutes(time.intValue))
}
else {
timers.put(triggeringItem.name, null) // we are done after the second alert
}
])
} // changed to open
else {
timers.get(triggeringItem.name)?.cancel // the ? means only call cancel if the get is not null
timers.put(triggeringItem.name, null)
} // changed to closed
end
This is better. But what if we used Expire based Timers after all?
import org.eclipse.smarthome.model.script.ScriptServiceUtil
rule "A garage door changed to open or closed"
when
Member of doorStatus changed to open or
Member of doorStatus changed to closed
then
val timer1 = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"_timer_10")
val timer2 = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"_timer_30")
if(triggeringItem.state.toString == "closed") {
timer1.postUpdate(OFF)
tiemr2.postUpdate(OFF)
}
else if(triggeringItem.state.toString == "open" && timer1 == OFF && timer2 == OFF) {
timer1.sendCommand(ON)
}
// othewise ignore the event, or log a warning as this should never happen
end
rule "A timer expired"
when
Member of doorStatusTimers received command ON or
Member of doorStatusTimers received command OFF
then
// Get the appropriate information for this timer
val carName = transform("MAP", "garagedoors.map", triggeringItem.name)
val phone = transform("MAP", "phones.map", triggeringItem.name)
val time = triggeringItem.name.split("_").get(4)
// Send the alert
logInfo("Garage Door", "The garage door for the " + carName + " has been left open for " + time + " minutes. Message texted to owner.")
sendmail(phone, " ", "The garage door for the " + carName + " has been left open for " + time + " minutes")
// Schedule the next timer
if(time == "10") {
sendCommand(triggeringItem.name.replace("10", "30"))
}
end
It’s fewer lines of code and it is significantly simpler.
If you are willing to fudge on your requirements, the Rules can be made even simpler. For example, if you just want an alert every 10 minute or every 30 minutes until it closes, I can drop the lines of code in this last example by half.
For more ways to avoid duplicated code see Design Pattern: DRY, How Not to Repeat Yourself in Rules DSL.