diff --git a/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy b/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy index faade50..3a7d663 100644 --- a/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy +++ b/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy @@ -5,7 +5,6 @@ import java.text.DecimalFormat import groovy.json.JsonSlurper private getApiUrl() { "https://api.netatmo.com" } -private getVendorName() { "netatmo" } private getVendorAuthPath() { "${apiUrl}/oauth2/authorize?" } private getVendorTokenPath(){ "${apiUrl}/oauth2/token" } private getVendorIcon() { "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1%402x.png" } @@ -14,14 +13,13 @@ private getClientSecret() { appSettings.clientSecret } private getServerUrl() { appSettings.serverUrl } private getShardUrl() { return getApiServerUrl() } private getCallbackUrl() { "${serverUrl}/oauth/callback" } -private getBuildRedirectUrl() { "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${shardUrl}" } +private getBuildRedirectUrl() { "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${atomicState.accessToken}&apiServerUrl=${shardUrl}" } -// Automatically generated. Make future change here. definition( name: "Netatmo (Connect)", namespace: "dianoga", author: "Brian Steere", - description: "Netatmo Integration", + description: "Integrate your Netatmo devices with SmartThings", category: "SmartThings Labs", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1.png", iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1%402x.png", @@ -44,15 +42,16 @@ mappings { } def authPage() { - log.debug "In authPage" + // log.debug "running authPage()" def description def uninstallAllowed = false def oauthTokenProvided = false - if (!state.accessToken) { - log.debug "About to create access token." - state.accessToken = createAccessToken() + // If an access token doesn't exist, create one + if (!atomicState.accessToken) { + atomicState.accessToken = createAccessToken() + log.debug "Created access token" } if (canInstallLabs()) { @@ -60,36 +59,32 @@ def authPage() { def redirectUrl = getBuildRedirectUrl() // log.debug "Redirect url = ${redirectUrl}" - if (state.authToken) { - description = "Tap 'Next' to proceed" + if (atomicState.authToken) { + description = "Tap 'Next' to select devices" uninstallAllowed = true oauthTokenProvided = true } else { - description = "Click to enter Credentials." + description = "Tap to enter credentials" } if (!oauthTokenProvided) { - log.debug "Show the login page" + log.debug "Showing the login page" return dynamicPage(name: "Credentials", title: "Authorize Connection", nextPage:"listDevices", uninstall: uninstallAllowed, install:false) { section() { - paragraph "Tap below to log in to the netatmo and authorize SmartThings access." - href url:redirectUrl, style:"embedded", required:false, title:"Connect to ${getVendorName()}", description:description + paragraph "Tap below to login to Netatmo and authorize SmartThings access" + href url:redirectUrl, style:"embedded", required:false, title:"Connect to Netatmo", description:description } } } else { - log.debug "Show the devices page" - return dynamicPage(name: "Credentials", title: "Credentials Accepted!", nextPage:"listDevices", uninstall: uninstallAllowed, install:false) { + log.debug "Showing the devices page" + return dynamicPage(name: "Credentials", title: "Connected", nextPage:"listDevices", uninstall: uninstallAllowed, install:false) { section() { - input(name:"Devices", style:"embedded", required:false, title:"${getVendorName()} is now connected to SmartThings!", description:description) + input(name:"Devices", style:"embedded", required:false, title:"Netatmo is connected to SmartThings", description:description) } } } } else { - def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date. - -To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub".""" - - + def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date. To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub".""" return dynamicPage(name:"Credentials", title:"Upgrade needed!", nextPage:"", install:false, uninstall: true) { section { paragraph "$upgradeNeeded" @@ -100,15 +95,15 @@ To update your Hub, access Location Settings in the Main Menu (tap the gear next } def oauthInitUrl() { - log.debug "In oauthInitUrl" + // log.debug "runing oauthInitUrl()" - state.oauthInitState = UUID.randomUUID().toString() + atomicState.oauthInitState = UUID.randomUUID().toString() def oauthParams = [ response_type: "code", client_id: getClientId(), client_secret: getClientSecret(), - state: state.oauthInitState, + state: atomicState.oauthInitState, redirect_uri: getCallbackUrl(), scope: "read_station" ] @@ -119,78 +114,72 @@ def oauthInitUrl() { } def callback() { - // log.debug "callback()>> params: $params, params.code ${params.code}" + // log.debug "running callback()" def code = params.code def oauthState = params.state - if (oauthState == state.oauthInitState) { + if (oauthState == atomicState.oauthInitState) { def tokenParams = [ + grant_type: "authorization_code", client_secret: getClientSecret(), client_id : getClientId(), - grant_type: "authorization_code", - redirect_uri: getCallbackUrl(), code: code, - scope: "read_station" + scope: "read_station", + redirect_uri: getCallbackUrl() ] // log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}" def tokenUrl = getVendorTokenPath() - def params = [ + def requestTokenParams = [ uri: tokenUrl, - contentType: 'application/x-www-form-urlencoded', + requestContentType: 'application/x-www-form-urlencoded', body: tokenParams ] - - // log.debug "PARAMS: ${params}" + + // log.debug "PARAMS: ${requestTokenParams}" try { - httpPost(params) { resp -> - - def slurper = new JsonSlurper() - - resp.data.each { key, value -> - def data = slurper.parseText(key) - log.debug "Data: $data" - state.refreshToken = data.refresh_token - state.authToken = data.access_token - //state.accessToken = data.access_token - state.tokenExpires = now() + (data.expires_in * 1000) - // log.debug "swapped token: $resp.data" - } + httpPost(requestTokenParams) { resp -> + //log.debug "Data: ${resp.data}" + atomicState.refreshToken = resp.data.refresh_token + atomicState.authToken = resp.data.access_token + // resp.data.expires_in is in milliseconds so we need to convert it to seconds + atomicState.tokenExpires = now() + (resp.data.expires_in * 1000) } - } catch (Exception e) { - log.debug "callback: Call failed $e" + } catch (e) { + log.debug "callback() failed: $e" } - // Handle success and failure here, and render stuff accordingly - if (state.authToken) { + // If we successfully got an authToken run sucess(), else fail() + if (atomicState.authToken) { success() } else { fail() } } else { - log.error "callback() failed oauthState != state.oauthInitState" + log.error "callback() failed oauthState != atomicState.oauthInitState" } } def success() { - log.debug "in success" + log.debug "OAuth flow succeeded" def message = """ -
We have located your """ + getVendorName() + """ account.
-Tap 'Done' to continue to Devices.
+Success!
+Tap 'Done' to continue
""" connectionStatus(message) } def fail() { - log.debug "in fail" + log.debug "OAuth flow failed" + atomicState.authToken = null def message = """ -The connection could not be established!
-Click 'Done' to return to the menu.
+Error
+Tap 'Done' to return
""" connectionStatus(message) } @@ -202,13 +191,12 @@ def connectionStatus(message, redirectUrl = null) { """ } - def html = """ -