I’m trying to put together a simple device handler and smartapp for a garage controller. I was able to put together a very simple DeviceHandler that working well with my hardware. According to online doc, the reason a device needs smartapp is “SmartApps are the connecting glue between the unique protocols of such LAN or cloud devices and a Device Handler you would create for such devices. These Service Manager SmartApps discover LAN or cloud devices and then continue to maintain their connection.” So sound like I need to create this Service Manager SmartApp. Is there a pair example of SmartApp and its counter part DeviceHandler so I can see and relate? Here’s my simple Device Handler, How do I develop a simple SmartApp that would work with this DeviceHandler? TIA!
/**
- Generic HTTP Device v1.0.20160402
- 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.
*/
import groovy.json.JsonSlurper
metadata {
definition (name: “fiController”, author: “bhoang”, namespace:“bhoang”) {
capability “GarageDoorControl”
//Custom command to select which door
command "door"
attribute "doorSelect", "number"
attribute "d0Vld", "number"
attribute "d1Vld", "number"
attribute "d2Vld", "number"
}
preferences {
input("DeviceIP", "string", title:"Device IP Address", description: "Please enter your device's IP Address", required: true, displayDuringSetup: true)
input("linkCode", "string", title:"LinkCode", description: "Please enter Senclo's Link Code", required: true, displayDuringSetup: true)
input("userName", "string", title:"User Name:", description: "Enter your Senclo's user name", required: true, displayDuringSetup: true)
input("userPassword", "string", title:"User Password", description: "Enter your Senclo's user password", required: true, displayDuringSetup: true)
input("doorID", "string", title:"Door Number", description: "Enter door number to operate", required: true, displayDuringSetup: true)
}
simulator {
}
tiles(scale: 2) {
// Multi Tile:
//multiAttributeTile(name:"DeviceTrigger", type: "generic", width: 6, height: 6, canChangeIcon: true) {
multiAttributeTile(name:"fiSelect", type: "generic", width: 3, height: 3, canChangeIcon: false) {
tileAttribute("doorSelect", key: "PRIMARY_CONTROL", decoration: "flat", width: 3, height: 3) {
attributeState "1", label: '${currentValue}', action: "door", backgroundColor:"79B821"
}
tileAttribute("temperature", key: "SECONDARY_CONTROL") {
//attributeState "temperature", label:'Temperature: ${tempValue}°C'
attributeState "temperature", label:'Temperature: 70°C'
}
}
standardTile("closeButton", "device.closeButton", decoration: "flat", width: 3, height:3) {
state "closeDoor", label:'CLOSE' , action: "close", icon: "st.Transportation.transportation14", backgroundColor:"#FF2255", nextState: "closingDoor"
state "closingDoor", label:'CLOSING' , icon: "st.Transportation.transportation14", backgroundColor:"#22FF55"
}
standardTile("openButton", "device.openButton", decoration: "flat", width:3, height:3) {
state "openDoor", label:'OPEN' , action: "open", icon: "st.Transportation.transportation12", backgroundColor:"#33CCFF", nextState: "openingDoor"
state "openingDoor", label:'OPENING' , icon: "st.Transportation.transportation14", backgroundColor:"#CC33FF"
}
valueTile("d0Vld", "device.d0Vld", decoration: "flat") {
state "defaul", value: '${currentValue}'
}
valueTile("d1Vld", "device.d1Vld", decoration: "flat") {
state "default", value: '${currentValue}'
}
valueTile("d2Vld", "device.d2Vld", decoration: "flat") {
state "default", value: '${currentValue}'
}
main (["fiSelect", "temperature"])
details(["fiSelect", "openButton", "closeButton"])
}
} //end of metadata
def door() {
if(device.currentValue(‘doorSelect’)==1) {
if(device.currentValue(‘d1Vld’)==1) {
sendEvent(name: “doorSelect”, value: “2”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
} else if(device.currentValue(‘d2Vld’)==1) {
sendEvent(name: “doorSelect”, value: “2”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
} else {
sendEvent(name: “doorSelect”, value: “1”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
}
}else if(device.currentValue(‘doorSelect’)==2) {
if(device.currentValue(‘d2Vld’)==1) {
sendEvent(name: “doorSelect”, value: “3”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
} else if(device.currentValue(‘d0Vld’)==1) {
sendEvent(name: “doorSelect”, value: “1”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
} else {
sendEvent(name: “doorSelect”, value: “2”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
}
} else if(device.currentValue(‘doorSelect’)==3) {
if(device.currentValue(‘d0Vld’)==1) {
sendEvent(name: “doorSelect”, value: “1”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
} else if(device.currentValue(‘d1Vld’)==1) {
sendEvent(name: “doorSelect”, value: “2”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
} else {
sendEvent(name: “doorSelect”, value: “3”, isStateChange: true)
log.debug “doorSelect=” + device.currentValue(‘doorSelect’)
}
}
}
def open() {
runCmd(“Open”)
}
def close() {
runCmd(“Close”)
}
def runCmd(String varCommand) {
def host = DeviceIP
def hosthex = convertIPtoHex(host).toUpperCase()
def porthex = convertPortToHex(80).toUpperCase()
device.deviceNetworkId = “$hosthex:$porthex”
log.debug "The device id configured is: $device.deviceNetworkId"
log.debug "state.DSel=" + state.DSel
def dsn = 0
if (device.currentValue('doorSelect') >= 1)
dsn = device.currentValue('doorSelect') - 1
else
dsn = 0
def path = "/ST?" + "LC=" + linkCode + "&UN=" + userName + "&UP=" + userPassword + "&ID=" + dsn + "&CMD=" + varCommand
//log.debug "path is: $path"
//log.debug "Uses which method: GET"
def body = DeviceBodyText
//log.debug "body is: $body"
def headers = [:]
//ip:port
headers.put("HOST", "$host:80")
//headers.put("Content-Type", "application/x-www-form-urlencoded")
headers.put("Content-Type", "application/json")
//log.debug "The Header is $headers"
def method = "GET"
try {
def hubAction = new physicalgraph.device.HubAction(
method: method,
path: path,
body: body,
headers: headers
)
hubAction.options = [outputMsgToS3:false]
log.debug hubAction
hubAction
}
catch (Exception e) {
log.debug "Hit Exception $e on $hubAction"
}
}
def parse(String description) {
log.debug “Parsing ‘${description}’”
def msg = parseLanMessage(description)
def headersAsString = msg.header // => headers as a string
def headerMap = msg.headers // => headers as a Map
def body = msg.body // => request body as a string
def status = msg.status // => http status code of the response
def json = msg.json // => any JSON included in response body, as a data structure of lists and maps
def xml = msg.xml // => any XML included in response body, as a document tree structure
def data = msg.data // => either JSON or XML in response body (whichever is specified by content-type header in response)
sendEvent(name: "d0Vld", value: json.Door0Vld, isStateChange: true)
sendEvent(name: "d1Vld", value: json.Door1Vld, isStateChange: true)
sendEvent(name: "d2Vld", value: json.Door2Vld, isStateChange: true)
log.debug "d0Vld: " + device.currentValue('d0Vld')
log.debug "d1Vld: " + device.currentValue('d1Vld')
log.debug "d2Vld: " + device.currentValue('d2Vld')
if(status == 200)
{
if (device.currentValue('closeButton') == "closingDoor") {
sendEvent(name: "door", value: "closeDoor", isStateChange: true)
} else if (device.currentValue('openButton') == "openingDoor") {
sendEvent(name: "door", value: "openDoor", isStateChange: true)
}
} else {
//There's something wrong still return to the same state
if (device.currentValue('closeButton') == "closingDoor") {
sendEvent(name: "door", value: "closeDoor", isStateChange: true)
} else if (device.currentValue('openButton') == "openingDoor") {
sendEvent(name: "door", value: "openDoor", isStateChange: true)
}
}
}
def installed() {
log.trace "installed()"
log.info "==================== Device Handler Installed ==================="
state.installedAt = now()
sendEvent(name: “doorSelect”, value: “1”, displayed: false)
sendEvent(name: “d0Vld”, value: “1”, displayed: false)
sendEvent(name: “d1Vld”, value: “0”, displayed: false)
sendEvent(name: “d2Vld”, value: “0”, displayed: false)
sendEvent(name: “d2Vld”, value: “0”, displayed: false)
sendEvent(name: “closeButton”, value: “closeDoor”, displayed: false)
sendEvent(name: “openButton”, value: “openDoor”, displayed: false)
}
private String convertIPtoHex(ipAddress) {
String hex = ipAddress.tokenize( ‘.’ ).collect { String.format( ‘%02x’, it.toInteger() ) }.join()
//log.debug “IP address entered is $ipAddress and the converted hex code is $hex"
return hex
}
private String convertPortToHex(port) {
String hexport = port.toString().format( ‘%04x’, port.toInteger() )
//log.debug hexport
return hexport
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
private String convertHexToIP(hex) {
//log.debug(“Convert hex to ip: $hex”)
[convertHexToInt(hex[0…1]),convertHexToInt(hex[2…3]),convertHexToInt(hex[4…5]),convertHexToInt(hex[6…7])].join(”.")
}
private getHostAddress() {
def parts = device.deviceNetworkId.split(":")
//log.debug device.deviceNetworkId
def ip = convertHexToIP(parts[0])
def port = convertHexToInt(parts[1])
return ip + “:” + port
}