Problem with random timer for group members

I want to close a couple of rollershutters in a random order. All rollershutters are member of the group gRollershutter.

Executing this code leads to an error:

gRollershutter.members.forEach[ i |
        var int randomTime = (new java.util.Random).nextInt(10);
        logInfo("rollershutter close", "schließen in " + randomTime + " Sekunden")
        timer = createTimer(now().plusSeconds(randomTime.intValue), [|
                i.sendCommand(100)
                timer = null
        ])
];

Error:

[INFO ] [ome.model.script.rollershutter close] - schließen in 9 Sekunden
[ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'rollershutter close': Couldn't invoke 'assignValueTo' for feature JvmVoid:  (eProxyURI: rollershutter.rules#|::0.2.3.2.0.5.2.0.0.1.0.1.7.0.1.0.2::0::/1)

Any help would be appreciated.

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 -

gRollershutter.members.forEach[ GenericItem i |

Thanks for your help @rossko57

This seems not to be the problem.

gRollershutter.members.forEach[ GenericItem i |
    timer = createTimer(now().plusSeconds(5), [|
        i.sendCommand(100)
    ]);
    timer = null
];

This leads to the same error message as stated above.

When I execute it without the timer it works like a charm, I used it before in this way:

gRollershutter.members.forEach[ i |
    i.sendCommand(100)
];

Maybe I must not declare timer = ... more than once?

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)
        ])
]

You’ve not got any imports in this rules file?

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.

OK, thanks for the hints @rlkoshak . First solution with a Map:

import java.util.Map
val Map<String, Timer> rollershutterTimers = newHashMap

[...]

gRollershutter.members.forEach[ GenericItem i |
    var int randomTime = (new java.util.Random).nextInt(10)
    logInfo("rollershutter close", i.name + " schließt in " + randomTime + " Sekunden")
    rollershutterTimers.put(i.name, createTimer(now.plusSeconds(randomTime)) [|
        i.sendCommand(100)  // close rollershutter
        rollershutterTimers.put(i.name, null) // remove timer from hashmap
    ]);
];

This way it works as expected.

Also thanks for your advice @rossko57 . Tried your solution as well. It works like a charm, too.

gRollershutter.members.forEach[ GenericItem i |
    var int randomTime = (new java.util.Random).nextInt(10)
    logInfo("rollershutter close", i.name + " schließt in " + randomTime + " Sekunden")
    createTimer(now().plusSeconds(randomTime.intValue), [ |
        i.sendCommand(100)
    ])
];

As a final question @rossko57 and @rlkoshak: which one would you say is the better approach?

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.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.