It’s an obscure problem that crops up from time to time.
Here, “i” is a member of a list. Members of lists don’t have sendCommand() methods.`
You need to beat it into the shape of an Item -
Well I forgot to say about that … there is only one timer variable, so it can only give you a pointer to the last timer you created. So long as you never want to cancel or reschedule the other timers that you spawned, that isn’t actually a problem.
But you may as well create them anonymously to begin with, the timer= part is indeed pointless. Just - createTimer(now().plusSeconds(5), [ |
and of course remove timer=null
I can make your code throw validation errors if I declare var timer non-globally, but that still doesn’t produce a runtime error.
Certainly with the timer= stuff removed it runs just fine.
Looking more closely at your error
this is the kind of thing that gets thrown for problems with missing Items. In this case perhaps something odd about group membership?
Anyway, this works in my environment (on one of my Groups) -
gLightsU1.members.forEach[ i |
var int randomTime = (new java.util.Random).nextInt(10);
logInfo("rollershutter close", i.name + " schließen in " + randomTime + " Sekunden")
createTimer(now().plusSeconds(randomTime.intValue), [ |
i.sendCommand(100)
])
]
Usually there is a different error reported when this occurs, but it is usually not possible to update the state of a variable outside of a forEach lambda. In this case, you are trying to update the variable timer inside the forEach lambda.
And it doesn’t really make sense. Even if it worked, timer would only be pointing to the last timer created. All the other timers created would become orphaned. So either don’t bother saving the timer variable at all or use a Map to store each timer created.
Note, if you save them to a Map or a List, you can add to the Map or List inside the forEach lambda because you are not changing the varible itself.
createTimer() always creates a timer.
Keeping a handle, a pointer to it, is optional. You only need that handle if you want to do something to the timer at some time after creation - generally see if it is running, or reschedule or cancel it.
So it’s up to you.
Should the order be really random or do you just not want to close all of them at the same time to have the radio traffic under control? I do it with a timer that runs in a loop
/############################//
//Alle Rolladen verschliessen!//
//############################//
var Timer timer_Alle_Rolladen_verschliessen = null
var Counter_Alle_Rolladen_verschliessen = 0
rule "Alle Rolladen verschliessen"
when
Item Alle_Rolladen_Zu changed from OFF to ON
then
Counter_Alle_Rolladen_verschliessen = 0
timer_Alle_Rolladen_verschliessen = createTimer(now.plusMillis(100), [|
Counter_Alle_Rolladen_verschliessen += 1
if(Counter_Alle_Rolladen_verschliessen <= gBlinds.members.size) {
if(gBlinds.members.sortBy[name].get(Counter_Alle_Rolladen_verschliessen -1).state != 100) {
gBlinds.members.sortBy[name].get(Counter_Alle_Rolladen_verschliessen -1).sendCommand(100)
timer_Alle_Rolladen_verschliessen.reschedule(now.plusMillis(200))
} else {
timer_Alle_Rolladen_verschliessen.reschedule(now.plusMillis(10))
}
} else {
timer_Alle_Rolladen_verschliessen = null
}
])
end
Thanks for this. Radio traffic has not been an issue yet. I have a z-wave network and I control my roller shutters with fibaro actors. I want the rollershutter to close in a random order because I want to simulate presence.