Finding entry in list or collection to select one of multiple devices to control


#1

1) Give a description of the problem
I want to have two list variables. The first would be strings, corresponding to the values passed from a remote hub calling piston (via URL). The second would be devices, each entry being a device on my local hub. I want a clean, straightforward way to use the calling argument to find the matching entry in the first (string) list (collection), and then use that entry number to refer to the corresponding entry in the second (devices) list.

2) What is the expected behaviour?

/* Pseudo-code, I realize this isn't necessarily completely correct syntax
define 
    string SearchList "Mirror1"; "Mirror2"; "Mirror3";
    devices DeviceList Switch1, Switch2, Switch3
    integer DeviceIndex

Do
    -- See which entry in SearchList array matches the argument we received
    DeviceIndex = LocationOf ({args.1}, SearchList);

    -- If a match was found, use the corresponding entry in DeviceList
    -- to perform some action
    If DeviceIndex <> 0 then
        With DeviceList.DeviceIndex
            -- perform some action on the device
    End If
End Do

I can do this with brute force, but I’m hoping to do something a lot more concise, a lot clearer, and a lot easier to maintain by just updating the two arrays.

Brute force method

Do
    Case 
        When {args.1} = "Mirror1"
            -- perform some actions with Switch1
        When {args.1} = "Mirror2"
            -- perform some actions with Switch2
        When {args.1} = "Mirror3"
            -- perform some actions with Switch3
    End Case
End Do

The actions I want to perform are identical for each device and are more extensive than just a line or two of code. So it’s a real nuisance to repeat almost-identical code in ever CASE condition, and error-prone when I have to update the actions.

I suppose I could iterate through the SearchList, counting as I go, until I get a match, and then use that count to reference the necessary entry in DeviceList. But I’m hoping there’s a more elegant solution.


[Resolved]Execute reusable section of code (subroutine) based on event/conditional/device state
#2

Still hunting around in the Wiki and the WebCoRE forum. I’m finding a few things that will help so I might eventually figure this out on my own, but if anyone has a working example it will very much be appreciated.


#3

This is what I ended up with. Not quite as elegant as I was hoping for, but much better than my original brute force code. A built-in function that returns the index of a matched entry in a list, and then a way to use that index to reference another list, would be great. Having to iterate through the entire list of devices every time just isn’t aesthetic.


#4

I took a look at this. I have a similar process I have done but I use the device list to create a speak text. The problem with what you want to do is that indexOf() for strings returns the position within the string of the matching text rather than the array index for a list. webcore needs a separate function for this.

Anyway, here is my brute force solution. Also not elegant but it works.


#5

Thanks, @guxdude. Your code is basically what I was hoping could be done with a pair of function calls:

    varIndex = arrayIndex(<arrayname>, <searchstring>) 
    myDevice = arrayItem(varIndex, <secondarrayname>)

I’m basically doing much the same thing, but since the device name is what I’m searching for, I just search directly through my devicelist. Using a second array, with just the search values, would be helpful if I wanted to be able use search strings that were different from the device names, in which case I’d need to use code like yours.

I suppose I could shorten the average number of iterations through the devicelist by using a “break” after I get a match, since only one device will match on name.


#6

yes, I tried exactly what you were looking for using indexOf(,) which works fine for device lists or integer lists but, unfortunately, for a string list it just returns the index of where the search string starts. Maybe if you pass an integer rather than a string you could just index directly.


#7

So close…
I’m converting things to strings to do searches without indexed looping. I’m not actually sure if the end result is more efficient, but here it is.
This is the testing piston:


And here is the piston that actually figures out what the index is.
It sets the index to -1 if it’s not in the list or 0,1,2, etc. if it is.

And here are how the results look in the log:

But, here’s the issue: I’m not sure how to return a value to the original piston. I suppose if you just need the code in one spot, even within a loop, it’d be OK. Does anyone know if and how a piston can return a value?


#8

Execute piston task and pass argument in form of variables.


#9

I can see how to do that to send the original values to the (not-really-a-function) calculation piston. I’m already doing that.
What I’m not sure of is how to send the result back to the calling piston.
…
Actually, I’m beginning to realize that I’m expecting behavior from WebCoRE that is more like a standard programming platform. (Again. You’d think I’d know better now.) I was looking at calling a piston like calling a function but it’s only roughly analogous. In the case of pistons running on the same instance (as mine are) it appears that it will wait:


However, clearly from the annotation, I can’t use a global to return the value because it won’t be updated yet.
Still open to suggestions. Maybe an HTTP POST and then GET?


#10

My “self-taught-ness” is showing here: I’ve been playing around with the idea of passing information both ways between the calling piston and answering piston. We already have the Execute Piston command that can send variables into a piston. However, I also found that doing a GET web request to the piston’s URL will do the same.
(By the way, the URLs for your pistons will always start with the same text; something like https://graph-na04-useast2.api.smartthings.com/api/token/LONGHEXSTRING/smartapps/installations/ANOTHERLONGHEXSTRING/execute/… What follows is an individual piston hex code which is the same string that you see in your browser address line following https://dashboard.webcore.co/piston/. I took the first string and assigned it to a Global Variable and simply add the individual piston hex code to call whichever piston I want to execute. I can share the code and, since it won’t reveal that all-important first part with the LONGHEXSTRINGs, I don’t have to worry about someone calling my pistons.)
Anyway, I’m still trying to suss out how to send values back to the calling piston. It would seem that you could send it as a response to the GET, but I’m not sure how to do that. Any suggestions?


#11

I think this post covers that.


#12

I could be missing something, but it looks like that shows how to send variables into a piston, which I already have working. (Though you’ve added yet another way to send data in. I do appreciate that.)
I’m trying to figure out how to send something back at the end of the called piston.
If I could figure out how to have the piston set the $result string, that would be it.
I could issue a call to the calling piston with arguments. I’ve figured out that by checking which arguments I get I could start a piston and then call it as well, but I’d prefer to have the calling piston return a value somehow.


#13

This is possible, but with multiple triggers in the original piston, it is a bit tricky to code for. I usually pass the resulting value to a third piston for my follow up actions.


#14

" I usually pass the resulting value to a third piston for my follow up actions."
That thought had occurred to me. It may simplify things considerably.


#15

It really does. It makes webCoRE function almost like the old “GOTO” command, with different outcomes being directed to different pistons etc.


#16

Yes. I can see that that would be a great opportunity to use that prefx+uniquePistonID thing I mentioned above.


#17

Believe it or not, the hardest part (for me) is naming the extra pistons in a way so I remember what does what, and they look right when listed alphabetically. (I usually keep some of them in another category called “Behind the Scenes” so they stay out of sight, out of mind)


#18

Oh. I just had another thought. I could pass in the uniquePistonID of what I want to run after the piston figures out the index so I can have one “findTheIndex” piston that will call the appropriate follow up set in the original calling piston.
…working… :slight_smile:


#19

So, here’s the “middle” piston. This is the one that figures out the index number (array element number) of a device. It’s provided with a string of device names (which is basically just the device variable cast to a string), the string name of the device you’re looking for, and the unique Piston ID string that identifies the follow-up piston you want to call when you’ve got the index value. (See above for more on that unique Piston ID string.)


Here’s the piston that calls the device Index finder:

And here’s the follow up piston that simply displays the results. Obviously you could do a lot more with this last part.

Here’s a log of the results:


#20

Another great idea (as usual).