Ok - I need some help again! So I decided to go back to this device handler to finally get the last bits working perfectly.
I finally have it reporting absolutely everything correctly:
All button pushes - handled
All button long holds - registers the start and end
Battery
Wake up notifications
configuration
Now that I do - I wanted to see if anyone has a smartapp - or knows how I can modify the button controller smart app so that when the button hold starts, it triggers lights to start dimming up or down and a variable rate (decided in the code) and when the button is released it stops the process?
Anyone done anything like this?
Thank you!
For anyone who wants the code for their own use (I haven’t seen any other wall controllers on smartthings community that can be made to 1. Look so nice (loads of faceplate and rocker switches compatible) and 2. actually handle the hold state correctly (FINALLY!) - see below:
/**
* Copyright 2015 AdamV
*
* 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.
*
*/
metadata {
definition (name: "Secure Wall Controller", namespace: "Z-Wave.me", author: "AdamV") {
capability "Actuator"
capability "Button"
capability "Contact Sensor"
capability "Battery"
capability "Configuration"
capability "Refresh"
fingerprint deviceId: "0x1801", inClusters: "0x5E, 0x70, 0x85, 0x2D, 0x8E, 0x80, 0x84, 0x8F, 0x5A, 0x59, 0x5B, 0x73, 0x86, 0x72, 0xEF, 0x20, 0x5B, 0x26, 0x27, 0x2B, 0x60"
}
simulator {
status "button 1 pushed": "command: 9881, payload: 00 5B 03 DE 00 01"
status "button 1 held": "command: 98C1, payload: 00 5B 03 E4 02 01"
status "button 2 pushed": "command: 9881, payload: 00 5B 03 E0 00 02"
status "button 2 held": "command: 98C1, payload: 00 5B 03 E6 02 01"
status "button 3 pushed": "command: 9881, 00 5B 03 E1 00 05"
status "button 3 held": "command: 98C1, payload: 00 5B 03 E8 02 01"
status "button 4 pushed": "command: 9881, payload: 00 5B 03 E2 00 06"
status "button 4 held": "command: 98C1, payload: 00 5B 03 EA 02 01"
status "wakeup": "command: 8003, payload: FF"
}
tiles {
standardTile("button", "device.button", width: 2, height: 2) {
state "default", label: "", icon: "st.Home.home30", backgroundColor: "#ffffff"
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
tileAttribute ("device.battery", key: "PRIMARY_CONTROL"){
state "battery", label:'${currentValue}% battery', unit:""
}
}
standardTile("configure", "device.button", width: 2, height: 2) {
state "default", label: "configure", backgroundColor: "#ffffff", action: "configure"
}
main "button"
details(["button", "battery", "configure"])
}
}
def parse(String description) {
def results = []
log.debug(“RAW command: $description”)
if (description.startsWith(“Err”)) {
log.debug(“An error has occurred”)
}
else {
def cmd = zwave.parse(description.replace("98C1", "9881"), [0x98: 1, 0x20: 1, 0x84: 1, 0x80: 1, 0x60: 3, 0x2B: 1, 0x26: 1])
log.debug "Parsed Command: $cmd"
if (cmd) {
results = zwaveEvent(cmd)
}
// if(cmd) results += zwaveEvent(cmd)
// if(!results) results = [ descriptionText: cmd, displayed: false ]
}
/*
def BUTTON_IDs = [ 1, 2, 5, 6 ]
// Split string by comma xter, will return an array.
String [] sections = description.split( “,” )
// log.debug(“sections: $sections”)
// Remove all whitespace from beginning and end of each section string.
for (def i = 0; i < sections.length; i++) {
sections[ i ] = sections[ i ].trim()
}
// log.debug("Trimmed sections: $sections")
// Fetch the command and payload strings from the sections array.
String command = sections[ 1 ]
String payload = sections[ 2 ]
// log.debug( "Command: $command" )
// log.debug( "Payload: $payload" )
// Get the command ID and payload ID from these strings.
String commandID = command[-4..-1]
String payloadID = payload[-2..-1]
// log.debug( "CommandID: $commandID" )
// log.debug( "PayloadID: $payloadID" )
// Coerce the payloadID to a Number from a string (using base 10 as radix)
Integer payloadIDint = payloadID.toInteger()
// log.debug(payloadIDint)
*/
/*
// Determine which button was pressed.
if ( commandID == "9881" && payloadIDint == BUTTON_IDs[ 0 ]) {
Integer button = 1
results = createEvent(name: "button", value: "pushed", data: [buttonNumber: button], descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
log.debug( "Button $button was pushed" )
}
else if ( commandID == “9881” && payloadIDint == BUTTON_IDs[ 1 ]) {
Integer button = 2
results = createEvent(name: “button”, value: “pushed”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was pushed” )
}
else if ( commandID == “9881” && payloadIDint == BUTTON_IDs[ 2 ]) {
Integer button = 3
results = createEvent(name: “button”, value: “pushed”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was pushed” )
}
else if ( commandID == “9881” && payloadIDint == BUTTON_IDs[ 3 ]) {
Integer button = 4
results = createEvent(name: “button”, value: “pushed”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was pushed” )
}
else if ( commandID == “98C1” && payloadIDint == BUTTON_IDs[ 0 ]) {
Integer button = 1
results = createEvent(name: “button”, value: “held”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was held”, isStateChange: true)
log.debug( “Button $button was held” )
}
else if ( commandID == “98C1” && payloadIDint == BUTTON_IDs[ 1 ]) {
Integer button = 2
results = createEvent(name: “button”, value: “held”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was held” )
}
else if ( commandID == “98C1” && payloadIDint == BUTTON_IDs[ 2 ]) {
Integer button = 4
results = createEvent(name: “button”, value: “held”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was held” )
}
else if ( commandID == “98C1” && payloadIDint == BUTTON_IDs[ 3 ]) {
Integer button = 4
results = createEvent(name: “button”, value: “held”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was held” )
}
else {
log.debug( “Commands and Button ID combinations unaccounted for happened” )
}
return results
// return commandID
*/
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x98: 1, 0x20: 1])
// can specify command class versions here like in zwave.parse
if (encapsulatedCommand) {
log.debug(encapsulatedCommand)
return zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) {
log.debug( "keyAttributes: $cmd.keyAttributes")
log.debug( "sceneNumber: $cmd.sceneNumber")
log.debug( "sequenceNumber: $cmd.sequenceNumber")
// log.debug( “payload: $cmd.payload”)
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
[ createEvent(descriptionText: "${device.displayName} woke up"),
response(zwave.wakeUpV1.wakeUpNoMoreInformation()) ]
}
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelGet cmd) {
log.debug "Multilevel get: $cmd"
}
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) {
log.debug "Multilevel report: $cmd"
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) { // Special value for low battery alert
map.value = 1
map.descriptionText = "${device.displayName} has a low battery"
map.isStateChange = true
} else {
map.value = cmd.batteryLevel
log.debug ("Battery: $cmd.batteryLevel")
}
// Store time of last battery update so we don't ask every wakeup, see WakeUpNotification handler
state.lastbatt = new Date().time
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd){
log.debug "basic event: $cmd.value"
}
def zwaveEvent(physicalgraph.zwave.commands.sceneactivationv1.SceneActivationSet cmd) {
log.debug( "Dimming Duration: $cmd.dimmingDuration")
log.debug( “Button code: $cmd.sceneId”)
if ( cmd.sceneId == 11 ) {
Integer button = 1
sendEvent(name: “button”, value: “pushed”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was pushed” )
}
else if ( cmd.sceneId == 13 ) {
Integer button = 1
sendEvent(name: “button”, value: “hold start”, data: [buttonNumber: button], descriptionText: “Button $button is closed”)
log.debug( “Button $button Hold start” )
}
else if ( cmd.sceneId == 15 ) {
Integer button = 1
sendEvent(name: “button”, value: “hold stop”, data: [buttonNumber: button], descriptionText: “Button $button is open”)
log.debug( “Button $button Hold stop” )
}
else if ( cmd.sceneId == 21 ) {
Integer button = 2
sendEvent(name: “button”, value: “pushed”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was pushed” )
}
else if ( cmd.sceneId == 23 ) {
Integer button = 2
sendEvent(name: “button”, value: “hold start”, data: [buttonNumber: button], descriptionText: “Button $button is closed”)
log.debug( “Button $button Hold start” )
}
else if ( cmd.sceneId == 25 ) {
Integer button = 2
sendEvent(name: “button”, value: “hold stop”, data: [buttonNumber: button], descriptionText: “Button $button is open”)
log.debug( “Button $button Hold stop” )
}
else if ( cmd.sceneId == 12 ) {
Integer button = 3
sendEvent(name: “button”, value: “pushed”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was pushed” )
}
else if ( cmd.sceneId == 14 ) {
Integer button = 3
sendEvent(name: “button”, value: “hold start”, data: [buttonNumber: button], descriptionText: “Button $button is closed”)
log.debug( “Button $button Hold start” )
}
else if ( cmd.sceneId == 16 ) {
Integer button = 3
sendEvent(name: “button”, value: “hold stop”, data: [buttonNumber: button], descriptionText: “Button $button is open”)
log.debug( “Button $button Hold stop” )
}
else if ( cmd.sceneId == 22 ) {
Integer button = 4
sendEvent(name: “button”, value: “pushed”, data: [buttonNumber: button], descriptionText: “$device.displayName button $button was pushed”, isStateChange: true)
log.debug( “Button $button was pushed” )
}
else if ( cmd.sceneId == 24 ) {
Integer button = 4
sendEvent(name: “button”, value: “hold start”, data: [buttonNumber: button], descriptionText: “Button $button is closed”)
log.debug( “Button $button Hold start” )
}
else if ( cmd.sceneId == 26 ) {
Integer button = 4
sendEvent(name: “button”, value: “hold stop”, data: [buttonNumber: button], descriptionText: “Button $button is open”)
log.debug( “Button $button Hold stop” )
}
else {
log.debug( “Commands and Button ID combinations unaccounted for happened” )
}
}
/*
def zwaveEvent(physicalgraph.zwave.Command cmd) {
[ descriptionText: "$device.displayName: $cmd", linkText:device.displayName, displayed: false ]
log.debug "command event: $cmd"
}
*/
/* def configure() {
def cmds = configurationCmds()
log.debug("Sending configuration: $cmds")
return cmds
}
def configurationCmds() {
//zwave.configurationV1.configurationReport().format()
//zwave.associationGrpInfoV1.associationGroupCommandListGet().format()
//zwave.associationCommandConfigurationV1.commandConfigurationGet().format()
//zwave.configurationV1.configurationSet(parameterNumber:11, size:14, scaledConfigurationValue:8).format()
def cmds = []
def hubId = zwaveHubNodeId
(11..14).each { button ->
cmds << zwave.configurationV1.configurationSet(parameterNumber: button, scaledConfigurationValue: 4).format()
}
/* (1..4).each { button ->
cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40, configurationValue: [hubId, (button-1)*40 + 1, 0, 0]).format()
cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40 + 20, configurationValue: [hubId, (button-1)*40 + 21, 0, 0]).format()
}*/
/*
cmds
}
*/
def configure() {
def commands = [ ]
// for (def i = 1; i <= 2; i++) {
// commands << zwave.associationV1.associationSet(groupingIdentifier: i, nodeId: zwaveHubNodeId).format()
// commands << zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:i, sceneId:i).format()
// commands << zwave.configurationV1.configurationSet(parameterNumber:i, size:1, scaledConfigurationValue:2).format()
//}
for (def i = 11; i <= 12; i++) {
commands << zwave.associationV1.associationSet(groupingIdentifier: 2, nodeId: zwaveHubNodeId).format()
// commands << zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:i, sceneId:i).format()
commands << zwave.configurationV1.configurationSet(parameterNumber:i, size:1, scaledConfigurationValue:4).format()
}
for (def i = 13; i <= 14; i++) {
commands << zwave.associationV1.associationSet(groupingIdentifier: 3, nodeId: zwaveHubNodeId).format()
// commands << zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:i, sceneId:i).format()
commands << zwave.configurationV1.configurationSet(parameterNumber:i, size:1, scaledConfigurationValue:4).format()
}
log.debug(“Sending configuration: ${commands}”)
delayBetween(commands, 1250)
}