Thanks for posting! It is great to get examples for stuff like this. Over all the code is pretty good but I see some areas for improvement.
You don’t need to import joda’s DateTime. It gets imported automatically for you.
There is no reason to initialize a Map with null values. When the Map doesn’t contain a given key, it returns null anyway. When you set a key with a null value it removes that key and that value from the Map. So
val java.util.Map<String, Timer> sprinklerTimers = newHashMap("1"->null,"2"->null,"3"->null,"4"->null,"5"->null,"6"->null,"7"->null,"8"->null)
is equivalent to
val java.util.Map<String, Timer> sprinklerTimers = newHashMap
in all respects.
Since you imported Functions, you do not need to use the full package name for Functions$Function4. And if you import Map you won’t have to use it’s full package name either.
Functions$Function4
<Switch,
Map<String, Timer>,
...
Since you already defined the types of the arguments in the <SwitchItems, ...>
section, there is no reason to repeat those types here. Or because you define the types here, there is no reason to use the <SwichItems...>
part. Choose one or the other. Using both is redundant.
Either
val Functions$Function4
<SwitchItem,
Map<String, Timer>,
String,
Number,
Boolean>
timerLogic = [
relayItem,
timers,
timerKey,
timeout |
or
val Functions$Function4
timerLogic = [
SwitchItem relayItem,
Map<String,Timer> timers,
String timerKey,
Number timeout |
Note, this second version assumes OH 2.3 Release. Prior versions will generate a warning in the logs.
You can collapse your “Zone X timer” Rules down to one Rule.
First, put the sprinklerValveX Items into a Group, let’s call it sprinklerValves
rule "Zone timer"
when
Member of sprinklerValves changed to ON // presumably we don't want to set the timer again when it changes to OFF
then
val num = triggeringItem.name.substring(triggeringItem.name.length() - 1));
timerLogic.apply(triggeringItem, sprinklerTimers, num, 30)
end
Of course, now that there is only one Rule, there really is no need for the lambda any more.
rule "Zone timer"
when
Member of sprinklerValves changed to ON // presumably we don't want to set the timer again when it changes to OFF
then
logInfo("Logger",triggeringItem.name + " changed state") // note that the semicolons are not required except with the `return;`
timers.get(triggeringItem.name)?.cancel // why not just use the full Item name as the key?
timers.put(triggeringItem.name, createTimer(now.plusMinutes(30), [ |
logInfo("Logger",triggeringItem.name + " turning off")
triggeringItem.sendCommand(OFF)
timers.remove(triggeringItem.name)
])) // I'm not a fan of putting the lambda outside the parens, it's an argument to the method call so don't hide that fact
end
But what if I want to have variable times for each zone? There are several approaches you can use. One is you can populate a Map with the Times desired. A more flexible approach would be to put the runtime in an Item so you can change it on your sitemap. We have use Design Pattern: Associated Items for this. The key is to name the Items so we can easily construct its name based on the valve’s Item name. Let’s say we append “_Runtime” to the valve name for the Number Item’s name that stores the amount of time for the zone to Run.
NOTE: There isn’t a good way to give the Item an initial value. See Design Pattern: Encoding and Accessing Values in Rules for details on how to boot strap the values stored in the _Runtime Items.
Group:Switch sprinklerValves
Switch sprinklerValve1 (sprinklerValves) { http=">[ON:GET:http://192.168.10.142:8000/gpio?pin=1?status=1] >[OFF:GET:http://192.168.10.142:8000/gpio?pin=1?status=0] <[sprinklerCache:5000:JSONPATH($.Relay1)]" }
Number sprinklerValve1_Runtime "Valve 1's Runtime [%d]"
Switch sprinklerValve2 (sprinklerValves) { http=">[ON:GET:http://192.168.10.142:8000/gpio?pin=2?status=1] >[OFF:GET:http://192.168.10.142:8000/gpio?pin=2?status=0] <[sprinklerCache:5000:JSONPATH($.Relay2)]" }
Number sprinklerValve2_Runtime "Valve 2's Runtime [%d]"
...
You can use Setpoint on the sitemap so you can adjust the Runtime for each valve from your sitemap. You can even set a min ana a max
sitemap mySitemap label="My home automation" {
Frame label="Sprinkler" {
Switch item=sprinklerValve1
Setpoint item=sprinklerValve1_Runtime minValue=1 maxValue=59
...
I’ll use the Item Registry example from Associated Items DP (I’ll post the full set of Rules).
import org.eclipse.smarthome.model.script.ScriptServiceUtil
import org.eclipse.xtext.xbase.lib.Functions
import java.util.Map
val Map<String, Timer> sprinklerTimers = newHashMap
rule "Zone 1" when Time cron "0 0 8 1/4 * ?" then sprinklerValve1.sendCommand(ON) end
rule "Zone 2" when Time cron "0 0 9 1/4 * ?" then sprinklerValve2.sendCommand(ON) end
rule "Zone 3" when Time cron "0 0 8 2/4 * ?" then sprinklerValve3.sendCommand(ON) end
rule "Zone 4" when Time cron "0 0 9 2/4 * ?" then sprinklerValve4.sendCommand(ON) end
rule "Zone 5" when Time cron "0 0 8 3/4 * ?" then sprinklerValve5.sendCommand(ON) end
rule "Zone 6" when Time cron "0 0 9 3/4 * ?" then sprinklerValve6.sendCommand(ON) end
rule "Zone 7" when Time cron "0 0 8 4/4 * ?" then sprinklerValve7.sendCommand(ON) end
rule "Zone 8" when Time cron "0 0 9 4/4 * ?" then sprinklerValve8.sendCommand(ON) end
rule "Zone timer"
when
Member of sprinklerValves changed to ON
then
logInfo("Logger",triggeringItem.name + " changed state")
timers.get(triggeringItem.name)?.cancel
// Get the runtime for this valve
val runtime = (ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"_Runtime").state as Number).intValue
timers.put(triggeringItem.name, createTimer(now.plusMinutes(runtime), [ |
logInfo("Logger",triggeringItem.name + " turning off")
triggeringItem.sendCommand(OFF)
timers.remove(triggeringItem.name)
]))
end