Do you have any devices to test with currently? If so, here is the beginnings of an app that should at least demonstrate the capability get the token and device information from the local bridge.
Demonstration App
/*
* Nuki Locks
*
* Licensed Virtual 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.
*
* Change History:
*
* Date Who What
* ---- --- ----
*/
static String version() { return '0.0.1' }
import groovy.transform.Field
import java.net.URLEncoder
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
definition (
name: "Nuki Locks",
namespace: "thebearmay",
author: "Jean P. May, Jr.",
description: "Provide Interface for Nuki Locks",
category: "Utility",
importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/xxxxx.groovy",
oauth: true,
iconUrl: "",
iconX2Url: ""
)
preferences {
page name: "mainPage"
page name: "nukiSetup"
}
mappings {
path("/nStatus") {
action: [POST: "nukiStatus"]
}
}
void installed() {
if(debugEnabled) log.trace "installed()"
state?.isInstalled = true
initialize()
}
void updated(){
if(debugEnabled) log.trace "updated()"
if(!state?.isInstalled) { state?.isInstalled = true }
if(debugEnabled) runIn(1800,logsOff)
}
void initialize(){
}
void logsOff(){
app.updateSetting("debugEnabled",[value:"false",type:"bool"])
}
def mainPage(){
dynamicPage (name: "mainPage", title: "", install: true, uninstall: true) {
if (app.getInstallationState() == 'COMPLETE') {
section("Main") {
href "nukiSetup", title: "nukiSetup", required: false
}
section("Reset Application Name", hideable: true, hidden: true){
input "nameOverride", "text", title: "New Name for Application", multiple: false, required: false, submitOnChange: true, defaultValue: app.getLabel()
if(nameOverride != app.getLabel) app.updateLabel(nameOverride)
}
} else {
section("") {
paragraph title: "Click Done", "Please click Done to install app before continuing"
}
}
}
}
def nukiSetup(){
dynamicPage (name: "nukiSetup", title: "", install: false, uninstall: false) {
section("Nuki Setup", hideable: true, hidden: false){
input "nukiUrl", "text", title:"<b>URL to the local bridge:</b>",submitOnChange:true
if (nukiUrl != null) {
input "getAuth", "button", title:"Get Nuki Token"
paragraph "When issuing this API-call the bridge turns on its LED for 30 seconds. \nThe button of the bridge has to be pressed within this timeframe. \nOtherwise the bridge returns a negative success and no token."
if(state.nukiToken) paragraph "Nuki Token: $state.nukiToken"
}
if(state.nukiToken) input "getDevices", "button", title: "Discover Devices"
}
}
}
//Begin App Communication
void sendRemote(command) {
if(command != "/auth") nUri = "$nukiUrl$command?token=$state.nukiToken"
else nUri = "$nukiUrl$command"
Map requestParams =
[
uri: "$nUri",
requestContentType: 'application/json',
contentType: 'application/json',
body: []
]
if(debugEnabled) log.debug "$requestParams"
asynchttpPost("getResp", requestParams, [cmd:"${command}"])
}
void getResp(resp, data) {
try {
if(debugEnabled) log.debug "$resp.properties - ${data['cmd']} - ${resp.getStatus()}"
if(resp.getStatus() == 200 || resp.getStatus() == 207){
if(resp.data)
state.returnString = resp.data
if(data['cmd'] == "/auth")
storAuth(resp.data)
else if(data['cmd'] == "/list")
processDevices(resp.data)
else state.returnString = "{\"value\":\"Null Data Set\", \"status\":\"${resp.getStatus()}\"}"
} else
state.returnString = "{\"status\":\"${resp.getStatus()}\"}"
} catch (Exception ex) {
state.returnString = ex.message
log.error ex.message
}
state.lastStatus = resp.getStatus()
}
void jsonResponse(retData){
render (contentType: 'application/json', body: JsonOutput.toJson(retData) )
}
void nukiStatus() {
if(debugEnabled) log.debug "Nuki Callback Received"
jsonResponse(status: "acknowledged")
// send lock / unlock to correct virtual device
}
// End App Communication
void storAuth(data) {
def jSlurp = new JsonSlurper()
Map resMap = (Map)jSlurp.parseText((String)data)
state.nukiToken = resMap["token"]
//gen access token & send callback address
// /callback/add?url=urlEncoded-HE-CloudAPI w/access token
}
void processDevices(data){
def jSlurp = new JsonSlurper()
resMap = jSlurp.parseText((String)data)
state.devIdList = resMap["nukiId"]
//create virtual devices to allow lock/unlock from HE and store attributes
//deviceType = 0 or 4
//subscribe to lock attribute
}
void childDeviceHndlr(evt) {
// Handle lock events from childDevices
}
void appButtonHandler(btn) {
switch(btn) {
case "getAuth":
if(debugEnabled) log.debug "Get Auth"
sendRemote("/auth")
break
case "getDevices":
if(debugEnabled) log.debug "Get Auth"
sendRemote("/list")
break
case "getAuth":
if(debugEnabled) log.debug "Get Auth"
sendRemote("/auth")
break
default:
if(debugEnabled) log.error "Undefined button $btn pushed"
break
}
}
void intialize() {
}
void uninstalled(){
}