Saving and modifying a capability.* input values

Hi,

A newbie here, and with my maiden message on the forum. I've had Hubitat Elevation 7 now for a few months, and quite new to groovy also. I've got some programming experience, but for the last 10+ years it's been more of a hobby than profession. Lately I've played some with python and javascript.

I'm working on a custom app (* more about it below), where I have one input to select a capability.switch -kind of device:

input(name: "switch_selector", type: "capability.switch", title: "xxx...", submitOnChange: true, multiple: false, width: 6)

My problem / question:
In my app I would like to save the value (=the devide) of switch_selector away from it, and later on (after many page loads, user clicks and perhaps app hibernations) being able to restore it back into switch_selector. In the mean time switch_selector will have other values loaded into it, user modify them, and the results saved away.

To be concrete, I've tried saving switch_selector into a state variable, and later read / load the value back from state variable. I have managed to save something in a state variable, it looks like a dump of the deviceWrapper values. However, I have not found a way to programmatically modify the value of the switch_selector in any way, at least based on what is shown in UI. I cannot even "empty" it (=unselect any potentially selected device). Instead, switch_selector appears to always remember what was the previous value. This is at least the user interface result.

Maybe saving into a state variable is wrong approach? I tried to understand the childDevice concept, but it didn't appear as the right solution.

Any help is appreciated, for example links to code of apps that have solved this or a very similar issue. My searching skills and keywords have not given me the answers...

(*) About the app, giving context to my motivation: The apps purpose is to control multiple switches. Each switch can be scheduled to turn on and off separately of the other switches. There is a compact overall view of all included switches and their schedules (rather compact in a HTML table). Purpose is to make the overall easy to comprehend on a small mobile screen. Then the schedules and switches can be edited one at a time. I do not want to have many switch selector inputs in the user interface at the same time, it is not necessary, they are not modified very often, they would take up extra space, and on a small mobile screen there is a risk of accidentally altering them, when it was not the purpose.

1 Like

Sounds like you are looking for something like:

app.updateSetting("myNewSettingName", switch_selector)

I don't think state lets you save device objects to it, though something changed with that per a request a while back, so it might, but I don't think you'd need to do that -- and I'm assuming you might want to display this "new" input somewhere so the user can see/change the device if needed (a good idea, otherwise you'll have stranded settings and references to devices that the user can't remove, as they might check when removing a device or just by checking its "In use by" list).

4 Likes

Thank you for your reply! I tried it but with no success... More specifically, I put in the following line of code:

    app.updateSetting("switch_for_plan_${state.planInEdit}", switch_selector)

where state.planInEdit evaluates to an integer value like '2', and the resulting setting name 'switch_for_plan_2' should be unique - not inputs with that name. switch_selector is the same as in my first message above. It causes the following error entry in logs:

org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack: No signature of method: user_app_digiHubster_Switch_Hour_Plan_Controller_143.updateSetting() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl, com.hubitat.app.DeviceWrapper) values: [switch_for_plan_2, V-Switch B] (updateSetting)

It appears that the DeviceWrapper intelligently displays its name ("V-Switch B") when casting into a string for log entry. I've tried that line of code both during page load and in the appButtonHandler, but the error is the same in both cases.

Anyhow, this was news to me that settings can hold also other values than just those created for inputs, so thank you for that piece of knowledge.

As a sidetrack, I'm also a bit confused when I should use state and when settings to save something, but I guess it will come in time.

So, any further tips or thoughts?

Sorry, I was going by memory and forgot the second parameter is normally something like this:

app.updateSetting("myNewSettingName", [type: "capability.switch", value: switch_selector])

This is still going by memory/un-tested, but I think it should work. :smiley:

Yes, it seems to use the displayName property.

The intent of settings is to store values from user inputs. However, updateSetting() will create a setting if it doesn't exist. This can sometimes be handy with dynamic pages. There's nothing stopping you from using it for arbitrary storage, but that use would be a bit non-conventional. I assume you might want to offer the option for the user to see or change this device in the future, which is part of the reason I'd suggest settings here.

The above might clarify that, but to add to this, state is intended to store app (or driver) data between executions. There have historically been some restrictions on what data types state can store, and I know device (DeviceWrapper) objects were not historically one, but I think something might have changed with that recently. Settings are, of course, restricted too, with the difference being that the types there are clearly defined. :slight_smile: I'm not sure state is (on SmartThings, the type had to be serializable to/from JSON, but that doesn't mean anything here).

In general, I'd recommend state for things that your app needs to store/track internally.

2 Likes

app.updateSetting("myNewSettingName", [type: "capability.switch", value: switch_selector.id])

The value for this is the device id. The one above creates a singular device setting. For a multiple device setting the value would be a list of device ids.

4 Likes

Thank You! Both! Now it works! :smiley:

I tried both switch_selector and switch_selector.id, and they both work at least when there is only one switch selected. I'll try next with multiple values allowed...

1 Like

Coming back to this... Now I briefly tested also with multiple selections allowed, and both switch_selector and switch_selector.id worked here too. And like you @bravenel said, the stored value was a single value or a list, depending on if multiple were denied or allowed.

Now it makes sense.

And thank you @bertabcd1234 for the explanations on using settings vs state. My take is that I will use

  • settings for data that was (at least originally) given by user via the inputs. I might call this the real data (TM).
  • state should be for application control logic, like when moving in the UI from one mode to another, for example from viewing mode to editing mode.

Now that I've learned that settings can store a lot more than just the active input element data, I assume I should also take care of removing unnecessary data from it. Like when user wants to remove it altogether. I guessed/found app.removeSetting("myNewSettingName"), I take it this will do the trick, right?

To conclude, I don't know which answer I should mark as the solution, but since both seem to work, I marked the first one. And if the random future reader doesn't mind reading the whole thread, he'll find out both.

Thank you!

Yeah! If your app has dynamic pages, this is how you can remove input selections that are no longer relevant. Device references are the big ones users might notice, as users will see "In use by" for the device with (possibly) no way to remove it,Cas long as your UI doesn't provide a way to get back to that setting anymore.

I'd say it's rare to need to put things in settings besides things users have selected manually (automatically disabling debug logging, so setting that input to false, is a comon pattern in drivers), but there are probably lots of interesting uses with dynamic pages or parent/child apps.

Oh, and always taken Bruce's words over mine. :slight_smile:

1 Like

Is there any way to retrieve the "type" parameter from an existing setting? When you access the settings["myNewSettingName"] you get the value but is there anything that returns the type value or a Map of both those like you pass in? I can work around it but wondering if I've missed anything (yes, I store different capabilities in the same setting depending on user selection so now I use another setting to store the type behind the scenes)

Try

getSettingType("mySetting")

2 Likes

Oh perfect! That is exactly what I was looking for :slight_smile:

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