How to pass parameters in href?

thanks @wackware!

The params parameter is how to do it. I’ll update the preferences docs.

But in the meantime, we also created an example showing this in action. Check it out: https://github.com/SmartThingsCommunity/Code/blob/master/smartapps/preferences/page-params-by-href.groovy

2 Likes

I wouldn’t expect to work in IDE.

Is there a “params” badge? :blush:

1 Like

@Jim, a few experimentally determined details:

  • The name of the params parameter matters. Adding other random parameters does case any additional data to be passed.
  • The name of the argument variable in the method signature does not matter; params from the href will map to whatever argument name is used.
  • In the scope of the method (in the Android App),
  • The argument variable information starts out with content:
    • id : The currentPageInstalledSmartAppId hash value
    • name: The name of the method
  • Params values from the calling calling href are added and will overwrite these values.
    • e.g. params[id:1, value:35] will result in [id:1, name:myMethod, value:35]
  • In the simulator, something different is happening.
    • The starting content of the argument variable has more values
      • e.g. [id:<hash>, install:, settings.undefined.value:, settings:[:], name:myMethod, versionId:, currentPageInstalledSmartAppId:<hash>]
      • The params values are not added.

Thanks,

–Mike

1 Like

Anybody seen this before?
Happens after re-loading a dynamic page from an href…
The data exists in settings, selecting “Select switches…” indeed shows nothing selected.
Re-selecting then next and done results in same thing…

I’ve seen this. I thought I just forgot to select. Selecting again worked second time.

1 Like

Unfortunatly re-purchasing my happy meal, still results in no prize…

In the IDE, at least, it doesn’t look like the dynamic pages are generated until you go back up to the previous page and reenter the page in question. Or something like that (can’t check right now). I guess it comes down to when a dynamic page is created and destroyed.

FWIW: I avoid assuming that the simulator is a realistic analogue of the mobile app. So I do all debugging in the app and ignore simulator inconsistencies.

There’s some sort of bug in this thing.
It makes no sense to me why this dynamic pages input type content list isn’t being pre-selected with the contents of the input name variable.
It would appear that “dynamic pages” really means “dynamic content”, with this single flaw, you aren’t able to create and repopulate pages on the fly based on a template.

Do I really have to create a separate page instance for every page I need, with the only difference being the page method and dynamic page names?

Just a general FYI, we updated some docs around the href element to include info about the params parameter:

http://docs.smartthings.com/en/latest/smartapp-developers-guide/preferences-and-settings.html#href

And, as mentioned earlier, we posted some example code of using params to href: https://github.com/SmartThingsCommunity/Code/blob/master/smartapps/preferences/page-params-by-href.groovy

Alright, this is a good one to keep in mind if you ever decide to put a device input section in a dynamic page.
Two whole days, I’ve spent screwing around with this problem:

input(
	//name	: "${switchID}	// doesn't work no way, no how
	//name	: switchID		// doesn't work if constructed thus, def switchID = "thisCrud${someOtherBits}"
	name	: switchID	// works: where switchID was built oldschool, IE: def switchID = "thisCrud" + someOtherVar 
	,title		: "Select switches..."
	,multiple	: true
	,required	: true
	,type		: "capability.droveMeInsane"
)

Argggg…, in other words gstrings, bad juju…

So to avoid the problem shown in the screen shot, do not directly use gString variable name construction directly within the input maps name key value assignment.

2 Likes

Mike, is it really working? The field is marked in RED, usually meaning that a required selection hasn’t been made.

BTW, I’m fighting a similar battle with this:

switches.eachWithIndex {s, i ->
    input name: "switch_cmd_$i", type: "enum", title: "Turn $s on or off?", options: [ "on", "off"], required: true
}

The previously made selections are shown, but the field is marked as RED and I can’t continue until I select the field and change the selections.

Oddly enough, I just made a change similar to yours, and now its working:

switches.eachWithIndex {s, i ->
    def switch_setting_name = "switch_cmd_" + i.toString();
    input name: switch_setting_name, type: "enum", title: "Turn $s on or off?", options: [ "on", "off"], required: true
}

Yea, that’s the bug, using a groovy gString: “${myVariable}” or “$myVariable” for a dynamic input name field, fails most of the time when the contents of the input are matched up with the selection list. I think this has to do with the late binding that gString implements.

I guess I need to do a better job describing this stuff…

Well, at least I’m having a load of fun playing with all kinds of dynamic stuff. I’m getting most of it to even work, too.

I’m building an app that creates an unlimited number of dynamic input pages, and sub pages from single page templates.
this took more than 10 minutes to work out LOL…

/**
 *  superState	1.0
 *
 *  Copyright 2015 Mike Maxwell
 *  
 *  Device (switch/dimmer/color) state capture and replay utility.
 *	- resulting scene is assigned to a child device
 *  - scene devices are editable post capture (recapture/add/delete)
 *	- option to restore each devices previous state individually (as captured when scene is turned on) when the scene is turned off 
 *
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
definition(
    name: "superState",
    namespace: "mmaxwell",
    author: "Mike Maxwell",
    description: "Device state capture, replay and edit tool",
    category: "Convenience",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/ModeMagic/Cat-ModeMagic.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/ModeMagic/Cat-ModeMagic@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/ModeMagic/Cat-ModeMagic@3x.png"
)

preferences {
    page(name: "main")
    page(name: "group",nextPage	: "main")
    page(name: "scene",nextPage	: "main")
    page(name: "delete",nextPage: "main")
}

def main(){
	def nextGroupIDX = getNextGroupIDX()
    def nextGroupID = "g${nextGroupIDX}"
    dynamicPage(name: "main", title: "superState", uninstall: true,install: true) {
        section("Device groups"){
   	        def prefGroups = getGroupMaps()
            prefGroups.each(){ prefGroup ->
            	 href(
                    name		: prefGroup.key
                    ,title		: prefGroup.value 
                    ,required	: false
                    ,params		: [groupID:prefGroup.key]
                    ,page		: "group"
                    ,description: null
                    ,state		: isGroupComplete(prefGroup.key)
                )
            }
        }
       section(){
      		//always have a link for adding a new group
            href(
            	name		: nextGroupID
            	,title		: "Add a device group..." 
                ,required	: false
                ,params		: [groupID:nextGroupID]
                ,page		: "group"
                ,description: null
            )
        }
    }
}

def group(params){
	def groupID = params.groupID
    def switchID = groupID + "dswitches" 
    def lockID = groupID + "dlocks"
    def relayID = groupID + "drelays"
    def valveID = groupID + "dvalves"
    def doorID = groupID + "ddoors"

	def nextSceneIDX = getNextSceneIDX(groupID)
    def nextSceneID = "${groupID}s${nextSceneIDX}"

   	dynamicPage(name: "group", title: getGroupPageTitle(groupID), uninstall: false,install: false) {
        section() {
            input(
            	name			: groupID
                ,title			: "Group Name"
                ,multiple		: false
                ,required		: true
                ,type			: "text"
            )
         }
        section("Scenes"){
            def prefScenes = getSceneMaps(groupID)
            prefScenes.each(){ prefScene ->
                href(
                	name		: prefScene.key
                	,title		: prefScene.value
                	,required	: false
                    ,params		: [sceneID:prefScene.key]
                    ,page		: "scene"
                    ,state		: "complete"
                    ,description: null
                )
            }
        }
         //need to skip this section if groupID.value is null
         //some bug here if we create a scene before the group is saved
         //log.debug "settings on group page:${settings}"
         if (settings[groupID]){
         	section(){
        		//always have a link for adding a new scene
        	    href(
        	        name		: nextSceneID
        	        ,title		: "Add a Scene..." 
        	        ,required	: false
        	        ,params		: [sceneID:nextSceneID]
        	        ,page		: "scene"
        	        ,description: null
            	)  
        	} 
        }
        section("Devices"){
  			input(
        	    name			: switchID
        	    ,title			: "Switches"
        	    ,multiple		: true
        	    ,required		: false
            	,type			: "capability.switch"
        	)
		}
	}
}
def scene(params){
	def sceneID = params.sceneID
	dynamicPage(name: "scene", title: getSceneTitle(sceneID), install: false) {
        section() {
     		input(
        	    name		: sceneID
                ,type		: "text"
        	    ,title		: "Name for this Scene..."
        	    ,required	: true
        	)
        }
    }
}

@Mike_Maxwell, I’m encountering another issue with this, and I’m wondering if you’ve seen the same thing:

Once you create a dynamic setting name, have you figured out a way to get rid of it (when it’s no longer needed)?

It appears that there’s simply no way to clean out the “settings” (and/or “state”) maps to get rid of stuff no longer used. Once a setting with a particular name (dynamic or otherwise) is created, it’s permanent unless the smartapp is uninstalled.

I saw that @pstuart had a similar issue when dealing with passwords (settings.remove(x) had no lasting impact.)

This seems like it could lead to a form of memory bloat when working with dynamic settings… (It’ll be fun to play with this on the new v2 hub. Eventually, the thing could be made to run out of memory storing settings that no longer are applicable.)

Well yea, “it would appear” that the settings get built/rebuilt/nuked when the pages are built/deleted.
SmartApps seem to remove the page and its contents when the relevant input fields are cleared and the app is saved (AKA done button selected)
For the app I’m writing, I’m taking all the preferences data I find after refresh then building/maintaining a map data structure using the state variable, effectively using the prefs as various keys into this map where the additional app/device specific attributes the app needs are stored.
Anything that you transfer from your preference variables into your state data structure, can be fully managed.

I’ve not figured a way of transferring a deviceList (which appears to be a list of device objects), into a state variable though…

Ha!

20 characterssss

15 characters in the documentation are worth about 3 days of development efforts!

2 Likes

Why? This is redundant since all the devices and their states are subscribed in the settings array.

You can always get a device capability status in the settings.[inputname] list and route what you want to a state variable if you want, but just seems like a waste of memory to duplicate what is already there.