Do Device Handlers Officially Support the "updated()" method?

@geko @zcapr17 @slagle @Tyler

I am seeing some interesting behavior as well. First, I never knew there was an “updated()” method that was called in a Device Handler. I know it works in a SmartApp, but the docs do not mention it for a Device Handler. So, as a test, I added an updated() method to one of my Device Handlers and I was pleasantly surprised that it does indeed get called when I finish editing preferences.

However, even though it was called, and it ran my code based on live logging debug statements, the code did not actually do what it was supposed to do. In the following snippet of code, I am using a user entered preference called “mac”. I need to use this MAC address as the Device’s deviceNetworkId to allow LAN based packets sent through the hub to be routed to the correct Device in the cloud. Pressing the “Configure” tile in the ST Phone app runs the “configure()” method below which works great. But this requires the user to enter and save preferences, and then remember to click the configure tile. I thought it would be simple to move the code to the “updated()” method instead, thereby ensuring the deviceNetworkId would always be kept up to date when a user saves preferences. Unfortunately, the device’s deviceNetworkId is never changed when done so in the “updated()” method.

Does anyone have any idea why? Any suggestions on how to fix this? Also, it appears that the “updated()” method is called twice immediately after clicking “Done” to save preferences (based on Live Logging debug statements.) Is “updated()” a truly supported method for a Device Handler?

def configure() {
	log.debug "Executing 'configure'"
    if(device.deviceNetworkId!=settings.mac) {
    	log.debug "setting device network id"
    	device.deviceNetworkId = settings.mac
    }
}

def updated() {
	log.debug "Executing 'updated'"
    if(device.deviceNetworkId!=settings.mac) {
    	log.debug "setting device network id"
    	device.deviceNetworkId = settings.mac
    }
}

Some good questions. Could it be that device.deviceNetworkId is read-only? See: What Happened to deviceNetworkId?

To solve the problem of updated() running twice, I usually do something like this:

/**
 *  updated()
 *
 *  Runs when the user hits "Done" from Settings page.
 *
 *  Note: Weirdly, update() seems to be called twice. So execution is aborted if there was a previous execution
 *  within two seconds. See: https://community.smartthings.com/t/updated-being-called-twice/62912
 **/
def updated() {
	log.trace "updated():"

	def cmds = []

	if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 2000) {
		state.updatedLastRanAt = now()

		// do stuff...
		
		return response(cmds)
	}
	else {
		log.trace "updated(): Ran within last 2 seconds so aborting."
	}
}

Follow this thread to see if we ever get an answer as to why it runs twice… Updated() being called twice?

I’m pretty sure updated() is a supported method for device handlers as it’s the main way to process settings after they’ve been changed.

2 Likes

@zcapr17 Thanks for the information! Very helpful information in those threads. The deviceNetworkID is not read-only, as my configure() method example has been working fine. Reading through the threads you linked, I did discover a few things and I have made some changes that seem to work. Definitely feels like a workaround, but at least everything now behaves as I desire.

Here’s the revised code. Thanks again for the logic to avoid the double updates! I had to use “runIn()” to execute the logic to set the deviceNetworkID a few seconds later. So either it is a race condition, or you cannot successfully set the deviceNetworkID from within the calling context of the updated() routine.

def updateDeviceNetworkID() {
	log.debug "Executing 'updateDeviceNetworkID'"
    if(device.deviceNetworkId!=mac) {
    	log.debug "setting deviceNetworkID = ${mac}"
        device.setDeviceNetworkId("${mac}")
	}
}

def updated() {
	if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 5000) {
		state.updatedLastRanAt = now()
		log.debug "Executing 'updated'"
        //updateDeviceNetworkID()
    	runIn(3, updateDeviceNetworkID)
	}
	else {
		log.trace "updated(): Ran within last 5 seconds so aborting."
	}
}

Also looks like this has a been an issue for a long time now… @geko had this same issue back in August of 2015. I read through the thread below, but I never saw where it was resolved without a user written workaround.