Trouble with href and Dynamic Pages

@Jim another bug and this one’s a regression it was fixed in the last version and it back again but with a little twist.

When using dynamic pages and HREF to pass parameters to a page, it doesn’t pass the parameter for the link clicked. Here is the sample app to replicate it:

definition(
    name: "Test HREF App",
    namespace: "rboy",
    author: "rboy",
    description: "Test HREF Pages",
    category: "SmartThings Labs",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld@2x.png"
)

preferences {
    page(name: "main")
    page(name: "testPage")
}


def main() {
    dynamicPage(name: "main", title: "HREF Test", uninstall: true, install: true) {
        for (int i = 1; i <= 5; i++) {
            def priorName = settings."userNames${i}" 
            section("User #${i}") { 
                if (priorName) {
                    input name: "userNames${i}", description: "${priorName}", title: "Name", defaultValue: priorName, type: "text", multiple: false, required: false, submitOnChange: true
                } else {
                    input name: "userNames${i}", description: "Tap to set", title: "Name", type: "text", multiple: false, required: false, submitOnChange: true
                }

                def hrefParams = [
                    user: i as String, 
                    passed: true 
                ]
                log.trace "$i Params: $hrefParams"
                href(name: "testPage", params: hrefParams, title: "Click here to define actions for ${settings."userNames${i}"}", page: "testPage", description: "", required: false)
            }
        }
        
        section("Mode Change Open Door/Window/Switch Notification") {
            def modes = location.modes
            for (mode in modes) {
                // Unlock actions for each mode
                def hrefParams1 = [
                    user: mode as String, 
                    passed: true 
                ]
                href(name: "modeDoorMonitor", params: hrefParams1, title: "When switching to mode ${mode}", page: "testPage", description: "", required: false)
            }
        }
    }
}

def testPage(params) {
    //  params is broken, after doing a submitOnChange on this page, params is lost. So as a work around when this page is called with params save it to state and if the page is called with no params we know it's the bug and use the last state instead
    if (params.passed) {
        atomicState.params = params // We got something, so save it otherwise it's a page refresh for submitOnChange
    }

    def user = atomicState.params?.user ?: ""
    def name = user ? settings."userNames${user}" : ""

    log.trace "Actions Page, user:$user, name:$name, passed params: $params, saved params:$atomicState.params"

    dynamicPage(name:"testPage", title: "Setup actions for each door" + (user ? " for user $name." : ""), uninstall: false, install: false) {
        section {
            input "userOverrideUnlockActions${user}", "bool", title: "Define specific actions for $name", required: true,  submitOnChange: true
        }
    }
}

def initialize() {
}

def updated() {
}

This works fine on iOS and the previous version of the Android but with 2.1.6 try this simple test. Install the SmartApp.
Now enter 4 user names. Then click on the “Click here to define actions” for each user starting from 1 to 4. All good. Now do it random order, e.g 4 then 1 then 3 then 2 etc. You’ll find that it’s passing the wrong HREF data to the pages.

Thanks to @gjanes for bring this to our attention. This is impact all of the apps which use HREF and dynamic pages.

There is another issue as reported by @gjanes, in Android when entering data into a field, unless you click Done on the keypad it doesn’t register the data. So if you have a required input field and you type in the data and click on install/done on the SmartApp before clicking done on the keypad or changing fields it will throw an error that a field is missing (i.e. it hasn’t registered the entered data).

YOU GOT TO BE KIDDING ME!!!

I’m so fed up of these “updates”, @alex one year ago in an interview you asked me what we can do better and I said ONE big thing, track regression bugs. I’m sorry but that just hasn’t happened. I don’t know how many times ST will keep breaking this. First it was only Android, iOS was the stand up guy, now iOS too.

HREF for dynamic pages is now broken for iOS, try moving between random pages and it sends the wrong data over:
https://community.smartthings.com/t/android-2-1-6-is-out/55784/18?u=rboy

It’s almost like QA either doesn’t exist or is ineffective at the most basic tasks of tracking regression bugs.

2 Likes

This is why I refuse to update SmartThings apps for months at a time now. Every other app on my phone I trust to update when there are updates. ST scares me to update every time !!!

2 Likes

Thanks for the feedback. I am debugging this now on iOS and I am seeing the correct params being passed to the page but I am seeing something else going on.

  1. I see a lookup issue with atomicState in the way your using it. The mobile app is passing the right values but the display is in the SmartApp is pulling the wrong user
  2. A limitation in the platform seems to be that you have to do the work around with atomicState. Ideally you should be able to set params on the page and be able to keep passing them from request to save. The mobile apps do support this and I need to track down why it’s being blocked on the server side.

For now, both of these issues can be fixed in the SmartApp like below. When the HREF request is made param is passed, so you can save it and look up your user on that. When the submitOnChange from the switch happens the atomicState will be evaluated for user since params.user is not going to be present.

def testPage(params) {
    //  params is broken, after doing a submitOnChange on this page, params is lost. So as a work around when this page is called with params save it to state and if the page is called with no params we know it's the bug and use the last state instead
    if (params.passed) {
        atomicState.params = params // We got something, so save it otherwise it's a page refresh for submitOnChange
    }

    def user = ""

    // Get user from the passed in params when the page is loading, else get from the last saved to work around not having params on pages
    if (params.user) {
        log.trace "params lookup for user $params"
        user = params.user
    } else if (atomicState.params) {
        log.trace "atomicState params lookup for user $atomicState.params"
        user = atomicState.params?.user ?: ""
    }

    log.trace "Found user $user"
    def name = user ? settings."userNames${user}" : ""

    log.trace "Actions Page, user:$user, name:$name, passed params: $params, saved params:$atomicState.params"

    dynamicPage(name:"testPage", title: "Setup actions for each door" + (user ? " for user $name." : ""), uninstall: false, install: false) {
        section {
            input "userOverrideUnlockActions${user}", "bool", title: "Define specific actions for $name", required: true, submitOnChange: true
        }
    }
}

I hope this will help for now, I will work with the platform developers to see if we can pass paras: params on the dynamic page and this would all work much better for you with less work around.

1 Like

We moved this out of the mobile release threads because the issue was unrelated to the releases. It looks like this is a problem with the caching layer for SmartApp state containing stale data. We still need to dig into the root cause but in the meantime we have disabled caching for InstalledSmartApp state and it looks like the app loads the dynamic pages correctly now.

Thanks for calling this out. @RBoy @kleneau

2 Likes