How do I concatenate two device lists for use in an input

Trying to generate a device multi select input for several device types: I don't think that's possible so I think I'll have to build the list myself for presentation via an input.

Suppose I have a multi select device input list using type:'Virtual Contact Sensor' and another with type: 'Virtual Motion Sensor' held in settings?.contact and settings?.motion - how could I concatenate those two 'device object' lists to create another that can then be used in another multi select input ?
along the lines of...

newList.push(settings?.contact)
newList.push(settings?.motion)

So I could then do a new multi select input of 'devices'

input (name:combinedList , type: "enum", ... options: newList)

I am not sure how to get the input list to display the device names but to create a device list of 'enabled devices'.

image

I suppose I could create two lists , one with the names and one with the device objects, or look the device up by name but that seems messy.. there must be a neater way.. ?

Well, I know it is possible because certain apps do it. For example, the maker API just has one big list of all of your devices. I was just looking to try and do this on Sunday and couldn't figure it out either. Eagerly awaiting any possible solutions to this one.

As a fall-back, you can always do two lists, one using the actuator capability and one using the sensor capability. Everything should fall into one of those two capabilities. Of course, that's only a workaround.

Glad someone else is interested .. I worked out how to get a list of all devices using capability.* but I just want an input listing all the 'official virtual devices' which comprise 22 different types:

This is how I do it in Life360 Tracker.

To combine the two (or more) lists...

state.allPlaces = [state.lPlaces, state.myPlacesList].flatten().findAll{it}

I then do this to strip out all the junk...

state.thePlaces = "${state.allPlaces}".replace("[","").replace("]","").replace("]","").replace(" ,", ",").replace(", ", ",")

And then finally...

state.values = "${state.thePlaces}".split(",")
input "trackSpecific", "enum", title:"Life360 Places", options: state.values, multiple:true, required:true, submitOnChange:true

Hope this helps

Thanks, this looks interesting Bryan - let me digest.

At the moment the list returned from input is of device objects - this is going to provide just a list of deviceNames I guess but I could always lookup the device again to get a device object

Not an answer to @kevin merging question BUT, to get the whole list, feel free to look at the Input statement of this app:

GetAttributesApp

34%20PM

In general, a list of under a hundred devices might not be too bad.. but for those that have 500+ devices, that list is completely unwieldy :smiley:

I would really like to do a select on type: Virtual * or \Virtual :wink:

@kevin Two thoughts/ideas:

  1. My understanding of the input "capability.foo" is part of the "security" model where you're permitting apps access to devices based on the user input. So they have to go through the input at one point. So at very least you'll have to a capability.* then a 2nd filtered list following it.
  2. Don't use device names or labels. Those are not unique, use the device id which is unique. Event though it's confusing I could have 50 devices labeled "Lights" but only one with id 487.

Should be easy with the enum input....now I feel like trying to do it. @bptworld has a good working solution, but I don't like all the search and replaces....I wonder...

1 Like

@kevin Is this what you want?

Edit: How to use this:

  1. Go to all devices, and select all.
  2. Feel free to collapse the list of all devices (should store this probably, you can use hide: true to make hidden by default)
  3. The put text into the filter and it'll create a filtered listed based on device name or label.

Turns out the options list can be a map. It displays the values, and returns the key. So I store the device id as the key, and a nice name as the value.

You then get a nice filtered list of deviceIds. I added a function that looks up the object. (I don't like the function, but it works)

Anyways, there's another way to do it. Was fun to put it together and I'll probably use it myself. Fun part of course is, you can filter on anything you want.

3 Likes

Oooh thanks @asj this is really helpful.. I'm going to need to filter on device type but that should be easily adapted. I already have a 'select all' input so I have all the devices - I'll just get the user to click the 'select all'.

Haven't looked yet but is the second list you create a list of device objects ? Something I can do a lookup (by networkDeviceId) into to get the matching device object returned (and hence find the deviceType) and so that I can then issue commands to the device ?

Otherwise I should be able to do that against the 'all devices' list still .. but I think that would require a long loop search and performance is key (I'm reacting to incoming MQTT messages)

Should read better - you have a device object lookup function - that's all ideal @asj - thanks so much for your help here.

Kevin

Hmm, can you actually use this? (does it work on hubitat?)

https://docs.smartthings.com/en/latest/smartapp-developers-guide/preferences-and-settings.html#device-specific-inputs

Sorry pressed for time, I have a busy day, will be back later tonight. I have 1 map I create, the list is:

fitlerList = [  deviceId: deviceName, deviceId2: deviceName2, etc ]

The all device is already a list of device objects. So I can loop that and compare deviceIud to what's in alldevices. I don't create this, it's part of the input from settings.

If you're worried about load and interating the list constantly, you can add a @Field map to the device, look at my other hubitat github apps for examples. The map would have the key be the deviceid and the value the object. You try doing a lookup in the map for deviceId to get the object, if the lookup fails then you iterate the list and update the map. The field will be free'd when the hub needs ram, so it'll empty now and then, but if you're doing a good bit of traffic then you'll have a pretty good hit rate based on my experience.

I can explain later tonight/tomorrow if need be.

Thanks Andrew, I see now you iterate the full device list too... that is something I'm trying to optimise so..

I'm going to look into this option - as it's likely exactly what I need. Will read up and may chat tomorrow if I'm stuck. Off to your Github now..

I've thought about this and approached it differently to avoid the repeated lookup loops each time an MQTT message arrives.

Using your code I also create another atomicState map. This one has my 'key' and then the 'value' is the actual device object. I get this by looking up the device in allDevices when the user first enables the device. Thus it's immediately available next time.

I'm still going to research the @Field usage too as my Groovy language learning continues because I would really like my filtered list to have device objects available via input. But I still have that issue then of not displaying object names in the dropdown instead seeing [object, Object] . This might be because it should be a map with an object/displayname basis i.e. with the device object as the key if that's workable.

I have one question - when I create a long map where the values are device objects is that taking a lot of space or am I right assuming the value is just a pointer to the device object ?

Thanks again Andrew

I'm guessing the map should just hold references to the object, and not full/deep copies. So you should be fine. Couple of things to keep in mind:

  1. state can only hold serializable types since it's stored in a db. So things like strings, numbers, etc. But not objects. Just something to keep in mind.
  2. Not sure about putting an object into a map as a key. I'm not familiar enough with Java/Groovy, but being a map that imposes certain restrictions regardless of the language, and that's the key has to be hashable. Not sure objects, especially the InstalledDeviceWrapper (or what ever it is) is hashable, or if groovy can use anything but strings. Anyways, this is beyond my groovy knowledge.

I have found that once I retrieve the device object via state map (atomicState actually) then although I can read it's attributes I can't execute any methods on the object so I'm back to trying to make that second filtered list return just one settings containing all the filtered devices that I can iterate over.

I've a lot to do still in my MQTT app because I'd really like a Xmas availability of beta1 so at the moment I am iterating allDevices - I'll try and look back when I get a moment and optimise this just to 'filtered' devices which for me is all devices using one of the 22 Hubitat virtual drivers.

A pointer could be serialized but maybe it's not that simple

@chuck.schwer.

Is it possible to have two device selector inputs and combine (merge) the settings variables used into another to use in another ‘enum’ type input ? I am trying to identify all enabled selections from your 22 different virtual drivers and then combine these devices into another list that I can create a subset from, or even a single select.

I had hoped for a capability ‘virtual’ which would make this easy but the feedback from Hubitat is no that won’t happen.

I am not aware of any way to do that.

Can I make a multi select input selector that contains all my child devices (not just a list of names) ?