$devices Variable?


#1

1) Give a description of the problem
Trying to determine how to use the $devices variable in my pistons, to control a group of resources.

2) What is the expected behaviour?
My use case is that I’d like to keep certain groups of devices in sync with each other. In this example, all my basement lights should remain in sync - if one turns on, they should all turn on, and vice versa.

However, I’d like to be able to have multiple groups of devices in one piston, for simplicity’s sake, and have the one piston statement control whichever is triggered. Each group of pistons would be defined as variables, and the “if” statement in the execute block would say “if any device in any of these groups fires”.

3) What is happening/not happening?
I can control a device group if I specify it specifically, but it doesn’t work when using $devices

4) More Info

Non-working example:

9/1/2021, 2:46:54 PM +672ms
+14ms ╔Received event [Downstairs Lights].switch = on with a delay of 31ms, canQueue: true, calledMyself: false
+33ms ║RunTime initialize > 32 LockT > 1ms > rtDT > 17ms > pistonT > 16ms (first state access 14 15 17)
+35ms ║Runtime (6055 bytes) successfully initialized in 17ms (v0.3.113.20210825_HE)
+37ms ║╔Execution stage started
+46ms ║║Comparison (enum) on changes = true (1ms)
+48ms ║║Cancelling condition #2's schedules...
+49ms ║║Condition #2 evaluated true (8ms)
+51ms ║║Cancelling condition #1's schedules...
+52ms ║║Condition group #1 evaluated true (state changed) (11ms)
+53ms ║║Cancelling statement #3's schedules...
+58ms ║║Error reading current value for Hubitat.switch: groovy.lang.MissingMethodException: No signature of method: com.hubitat.hub.domain.Location.currentValue() is applicable for argument types: (java.lang.String, java.lang.Boolean) values: [switch, true]
+310ms ║║Error while executing physical command Hubitat.on([]): groovy.lang.MissingMethodException: No signature of method: com.hubitat.hub.domain.Location.on() is applicable for argument types: () values: [] Possible solutions: any(), is(java.lang.Object), any(groovy.lang.Closure), clone(), find(), use(Ljava.lang.Object;)
+429ms ║║Executed virtual command [Hubitat].setSwitch (372ms)
+435ms ║║Calculating (string)DEBUG: + (string)Empty device list >> (string)DEBUG: Empty device list
+439ms ║║DEBUG: Empty device list
+441ms ║║Executed virtual command log (3ms)
+446ms ║╚Execution stage complete. (410ms)
+449ms ╚Event processed successfully (437ms)

When I run the piston as above, it doesn’t work. However this one does (only diff is line 22):

|+1ms|╔Received event [Downstairs Server Room Lights].switch = on with a delay of 140ms, canQueue: false, calledMyself: true|
|---|---|
|+5ms|║RunTime initialize > 4 LockT > 0ms > rtDT > 1ms > pistonT > 0ms (first state access 3 2 2)|
|+7ms|║Runtime (6110 bytes) successfully initialized in 1ms (v0.3.113.20210825_HE)|
|+8ms|║╔Execution stage started|
|+21ms|║║Comparison (enum) on changes = true (1ms)|
|+23ms|║║Condition #2 evaluated true (12ms)|
|+24ms|║║Condition group #1 evaluated true (state did not change) (14ms)|
|+26ms|║║Cancelling statement #3's schedules...|
|+32ms|║║Skipped execution of physical command [Downstairs Lights].on([]) because it would make no change to the device. (1ms)|
|+33ms|║║Executed virtual command [Downstairs Lights].setSwitch (2ms)|
|+37ms|║║Skipped execution of physical command [Downstairs Server Room Lights].on([]) because it would make no change to the device. (1ms)|
|+38ms|║║Executed virtual command [Downstairs Server Room Lights].setSwitch (1ms)|
|+42ms|║║Skipped execution of physical command [Downstairs Stair Lights].on([]) because it would make no change to the device. (1ms)|
|+43ms|║║Executed virtual command [Downstairs Stair Lights].setSwitch (2ms)|
|+49ms|║║Calculating (string)DEBUG: + (string)Downstairs Lights, Downstairs Server Room Lights and Downstairs Stair Lights >> (string)DEBUG: Downstairs Lights, Downstairs Server Room Lights and Downstairs Stair Lights|
|+55ms|║║DEBUG: Downstairs Lights, Downstairs Server Room Lights and Downstairs Stair Lights|
|+57ms|║║Executed virtual command [Downstairs Lights, Downstairs Server Room Lights, Downstairs Stair Lights].log (3ms)|
|+62ms|║╚Execution stage complete. (54ms)|
|+65ms|╚Event processed successfully (64ms)|

Notice that in the “not working” piston, the DEBUG output I added shows “Empty Device List”, while the “working” piston has a valid list of devices. Why is it not populating in the nonworking example, and how can I (or can I at all?) make this work?


#3

Since you already have the device groups defined in variables, why do you need $devices? Just use the defined group name (variable). WC will not send a command to any devices that are already in the commanded state so including the triggering device won’t hurt anything.

FYI, not sure exactly how $devices works but it is not static in a piston. From your test cases, it appears $devices takes on the value(s) of whatever is currently selected for the surrounding ‘with’ statement. Since in your first case, you are using $devices before it can be defined, then it is empty. In the second, you use your device variable so $devices takes on that same value. Essentially, use of the $devices variable is redundant.

This is similar to how $device takes on the value of each device in a for each loop. It is dynamic and depended on where you are in the piston.


#4

Because I’d like to have 3 or 4 device array variables, then one “execute” command that says “whichever group triggered, do something to that group” without having to hard-code what “that group” is.

I can do this with individual if/then statements, but was hoping to clean things up a bit as much of the code would be duplicated for every device group I add.


#5

Wondering if this helps explain what I’m after. An example of how I could make this happen - if “save matching devices to variable” to account for the fact that my device trigger is actually lists of devices, so I could then act upon that specific list of devices.

The end goal overall is that I can have multiple device lists as triggers, but only one action that automatically acts on the device list in question.


#6

I don’t have webCoRE installed at the moment and I can’t remember the syntax (and never used it), but you can run statements for individual conditions as well as the overall result of the if. So in pseudo code you could do:

if
  any of list1 changes
      when true
          set activelist to list1
  OR
  any of list2 changes
      when true
          set activelist to list2
then
  with activelist
    ...
endif

webCoRE stops evaluating an OR condition when it gets the first true so activelist would be set to the list with the device that changes in it.


#7

Thanks for the response!

This would work great - except that when using the “save matching devices to $var”, it saves the underlying devices (1 item - the individual device in the list that triggered), not the parent “list” of devices (1 item that encompasses all 3 devices).

If you’re referring to actual if/then actions, I’ve looked through and can’t find anything to the effect of “set active list”, but it does in theory sound very much like what I’d be looking to do.


#8

You don’t need the matching device.

There is an option somewhere that presents your conditions with what is effectively individual ‘if then … else …’ blocks so you can act on the result of the conditions themselves. I think it is probably in the piston options when editing (where you enable restrictions and things like that) and actually displays as when true / when false.

I am suggesting that you define a variable for the active list (like you did with activeDeviceList in your example) and in each when true you use a set variable task to assign it the value of the device list you are testing in the condition.


#9

Ahhhh, I understand - so like this? Then I’d do different grouped IFs (with OR between) for each group of devices I’m monitoring?

Wasn’t aware of the “while true” - seems like this could unlock some neat options.


#10

That is certainly what I had in mind. It just seemed like it might work. WebCoRE defaults to only processing as many conditions in a grouped if as required to determine the result which for an OR means stopping after the first true. Having each list in its own condition in an OR group opens the when true option up.

I understand that ‘when true’ / ‘when false’ was used more with webCoRE’s predecessor (CoRE) which I’ve never used. It certainly isn’t something that has cropped up very often in the forum and even when it has it has often seemed like the wrong choice.