Try this one and check out the "usedCode" entry in the event data. It's a driver modified from lock manager in smartthings for Schalge locks.
/**
*
* INSTRUCTIONS: If you scroll down a couple pages, you should find a line that looks like:
* main "toggle"
* and that followed by a line that STARTS with:
* details(["toggle",
* If you want to change the items that are available on the details page of the device (from 'things'),
* you should edit the "details" line to include whatever items you want to see (along with the order
* you want to see them in.) There's a sample "details" line commented out (starts with //) below the
* first one. That sample enables all (or mostly all) the possible items.
*
* After the first time you have the device type installed (or after you've changed it), you might have
* to forcibly terminate the mobile app before the new/changed stuff will show up. (Most mobile apps
* don't actually terminate when you exit them. How to terminate an app depends on your mobile OS.)
*
* If a toggle is showing up as "loading..." on the UI (and you haven't recently changed it), tap the
* tile and it should reload the status within 10 seconds.
* 2015-08-21 : refactor everything to bring in most of the updates from ST's base z-wave lock type. Ensure
* its compatible with Erik Thayer's "lock code manager" smart app. (https://community.smartthings.com/t/lock-code-manager/12280)
* 2015-03-07 : When the lock is locked/unlocked automatically, from the keypad, or manually, include that
* information in the map.data, usedCode. (0 for keypad, "manual" for manually, and "auto" for automatic)
* 2015-02-02 : changed state values to prevent UI confusion. (Previously, when setting one item to 'unknown',
* the UI might show ALL the items as 'unknown'.) Also added beeper toggle.
*
* This is a modification of work originally copyrighted by "SmartThings." All modifications to their work
* is released under the following terms:
*
* The original licensing applies, with the following exceptions:
* 1. These modifications may NOT be used without freely distributing all these modifications freely
* and without limitation, in source form. The distribution may be met with a link to source code
* with these modifications.
* 2. These modifications may NOT be used, directly or indirectly, for the purpose of any type of
* monetary gain. These modifications may not be used in a larger entity which is being sold,
* leased, or anything other than freely given.
* 3. To clarify 1 and 2 above, if you use these modifications, it must be a free project, and
* available to anyone with "no strings attached." (You may require a free registration on
* a free website or portal in order to distribute the modifications.)
* 4. The above listed exceptions to the original licensing do not apply to the holder of the
* copyright of the original work. The original copyright holder can use the modifications
* to hopefully improve their original work. In that event, this author transfers all claim
* and ownership of the modifications to "SmartThings."
*
* Original Copyright information:
*
* Copyright 2014 SmartThings
*
* 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
{
// Automatically generated. Make future change here.
definition (name: "Z-Wave Schlage Touchscreen Lock", namespace: "garyd9", author: "Gary D")
{
capability "Actuator"
capability "Lock"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Lock Codes"
capability "Battery"
attribute "beeperMode", "string"
attribute "vacationMode", "string" // "on", "off", "unknown"
attribute "lockLeave", "string" // "on", "off", "unknown"
attribute "alarmMode", "string" // "unknown", "Off", "Alert", "Tamper", "Kick"
attribute "alarmSensitivity", "number" // 0 is unknown, otherwise 1-5 scaled to 1-99
attribute "localControl", "string" // "on", "off", "unknown"
attribute "autoLock", "string" // "on", "off", "unknown"
attribute "pinLength", "number"
command "unlockwtimeout"
command "setBeeperMode"
command "setVacationMode"
command "setLockLeave"
command "setAlarmMode"
command "setAlarmSensitivity"
command "setLocalControl"
command "setAutoLock"
command "setPinLength"
fingerprint deviceId: "0x4003", inClusters: "0x98"
fingerprint deviceId: "0x4004", inClusters: "0x98"
}
simulator {
status "locked": "command: 9881, payload: 00 62 03 FF 00 00 FE FE"
status "unlocked": "command: 9881, payload: 00 62 03 00 00 00 FE FE"
reply "9881006201FF,delay 4200,9881006202": "command: 9881, payload: 00 62 03 FF 00 00 FE FE"
reply "988100620100,delay 4200,9881006202": "command: 9881, payload: 00 62 03 00 00 00 FE FE"
}
tiles(scale: 2) {
multiAttributeTile(name:"toggle", type: "generic", width: 6, height: 4){
tileAttribute ("device.lock", key: "PRIMARY_CONTROL") {
attributeState "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking"
attributeState "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ff0000", nextState:"locking"
attributeState "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking"
attributeState "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821"
attributeState "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
}
}
//standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat")
//{
// state "default", label:'lock', action:"lock.lock", icon:"st.locks.lock.locked", nextState:"locking"
//}
//standardTile("unlock", "device.lock", inactiveLabel: false, decoration: "flat")
//{
// state "default", label:'unlock', action:"lock.unlock", icon:"st.locks.lock.unlocked", nextState:"unlocking"
// }
valueTile("battery", "device.battery", inactiveLabel: false, canChangeBackground: true, width: 2, height: 2) {
state "battery", label:'${currentValue}% Battery', unit:"",
backgroundColors:[
[value: 19, color: "#BC2323"],
[value: 20, color: "#D04E00"],
[value: 30, color: "#D04E00"],
[value: 40, color: "#DAC400"],
[value: 41, color: "#79b821"]
]
}
standardTile("refresh", "device.lock", inactiveLabel: false, decoration: "flat", width: 2, height: 2)
{
state "default", label:' ', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("alarmMode", "device.alarmMode", inactiveLabel: true, canChangeIcon: false, decoration: "flat", width: 2, height: 2)
{
state "unknown_alarmMode", label: 'Alarm Mode\nLoading...', icon:"st.unknown.unknown.unknown", action:"setAlarmMode", nextState:"unknown_alarmMode"
state "Off_alarmMode", label: 'Alarm: Off', icon:"st.alarm.beep.beep", action:"setAlarmMode", nextState:"unknown_alarmMode"
state "Alert_alarmMode", label: 'Alert', icon:"st.alarm.beep.beep", action:"setAlarmMode", backgroundColor:"#79b821", nextState:"unknown_alarmMode"
state "Tamper_alarmMode", label: 'Tamper', icon:"st.alarm.beep.beep", action:"setAlarmMode", backgroundColor:"#79b821", nextState:"unknown_alarmMode"
state "Kick_alarmMode", label: 'Kick', icon:"st.alarm.beep.beep", action:"setAlarmMode", backgroundColor:"#79b821", nextState:"unknown_alarmMode"
}
controlTile("alarmSensitivity", "device.alarmSensitivity", "slider", height: 2, width: 2, inactiveLabel: false, range:"(1..5)")
{
state "alarmSensitivity", label:'Sensitivity', action:"setAlarmSensitivity", backgroundColor:"#ff0000"
}
standardTile("autoLock", "device.autoLock", inactiveLabel: true, canChangeIcon: false, decoration: "flat", width: 2, height: 2)
{
state "unknown_autoLock", label: 'Auto Lock\nLoading...', icon:"st.unknown.unknown.unknown", action:"setAutoLock", nextState:"unknown_autoLock"
state "off_autoLock", label: 'Auto Lock', icon:"st.presence.house.unlocked", action:"setAutoLock", nextState:"unknown_autoLock"
state "on_autoLock", label: 'Auto Lock ENABLED', icon:"st.presence.house.secured", action:"setAutoLock", nextState:"unknown_autoLock"
}
// not included in details
standardTile("vacationMode", "device.vacationMode", inactiveLabel: true, canChangeIcon: false, decoration: "flat", width: 2, height: 2)
{
state "unknown_vacationMode", label: 'Vacation\nLoading...', icon:"st.unknown.unknown.unknown", action:"setVacationMode", nextState:"unknown_vacationMode"
state "off_vacationMode", label: 'Vacation', icon:"st.Health & Wellness.health2", action:"setVacationMode", nextState:"unknown_vacationMode"
state "on_vacationMode", label: 'Vacation', icon:"st.Health & Wellness.health2", action:"setVacationMode", nextState:"unknown_vacationMode"
}
// not included in details
standardTile("lockLeave", "device.lockLeave", inactiveLabel: true, canChangeIcon: false, decoration: "flat", width: 2, height: 2)
{
state "unknown_lockLeave", label: 'Lock & Leave\nLoading...', icon:"st.unknown.unknown.unknown", action:"setLockLeave", nextState:"unknown_lockLeave"
state "off_lockLeave", label: 'Lock & Leave', icon:"st.Health & Wellness.health12", action:"setLockLeave", nextState:"unknown_lockLeave"
state "on_lockLeave", label: 'Lock & Leave ENABLED', icon:"st.Health & Wellness.health12", action:"setLockLeave", nextState:"unknown_lockLeave"
}
// not included in details
standardTile("localControl", "device.localControl", inactiveLabel: true, canChangeIcon: false)
{
state "unknown_localControl", label: 'Local Ctrl\nLoading...', icon:"st.unknown.unknown.unknown", action:"setLocalControl", nextState:"unknown_localControl"
state "off_localControl", label: 'Local Ctrl', icon:"st.Home.home3", action:"setLocalControl", nextState:"unknown_localControl"
state "on_localControl", label: 'Local Ctrl', icon:"st.Home.home3", action:"setLocalControl", backgroundColor:"#79b821", nextState:"unknown_localControl"
}
// not included in details
standardTile("beeperMode", "device.beeperMode", inactiveLabel: true, canChangeIcon: false)
{
state "unknown_beeperMode", label: 'Beeper\nLoading...', icon:"st.unknown.unknown.unknown", action:"setBeeperMode", nextState:"unknown_beeperMode"
state "off_beeperMode", label: 'Beeper', icon:"st.unknown.unknown.unknown", action:"setBeeperMode", nextState:"unknown_beeperMode"
state "on_beeperMode", label: 'Beeper', icon:"st.unknown.unknown.unknown", action:"setBeeperMode", backgroundColor:"#79b821", nextState:"unknown_beeperMode"
}
main "toggle"
details(["toggle", "lock", "unlock", "alarmMode", "alarmSensitivity", "battery", "autoLock", "lockLeave", "refresh"])
// details(["toggle", "lock", "unlock", "alarmMode", "alarmSensitivity", "battery", "autoLock", "lockLeave", "vacationMode", "beeperMode", "refresh"])
}
}
import hubitat.zwave.commands.doorlockv1.*
import hubitat.zwave.commands.usercodev1.*
def parse(String description)
{
def result = null
if (description.startsWith("Err"))
{
if (state.sec)
{
result = createEvent(descriptionText:description, displayed:false)
}
else
{
result = createEvent(
descriptionText: "This lock failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
}
else
{
def cmd = zwave.parse(description, [ 0x98: 1, 0x72: 2, 0x85: 2, 0x86: 1 ])
if (cmd)
{
result = zwaveEvent(cmd)
}
}
log.debug "\"$description\" parsed to ${result.inspect()}"
result
}
def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd)
{
def encapsulatedCommand = cmd.encapsulatedCommand([0x62: 1, 0x71: 2, 0x80: 1, 0x85: 2, 0x63: 1, 0x98: 1, 0x86: 1])
// log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand)
{
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(hubitat.zwave.commands.securityv1.NetworkKeyVerify cmd) {
createEvent(name:"secureInclusion", value:"success", descriptionText:"Secure inclusion was successful")
}
def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd)
{
state.sec = cmd.commandClassSupport.collect { String.format("%02X ", it) }.join()
if (cmd.commandClassControl)
{
state.secCon = cmd.commandClassControl.collect { String.format("%02X ", it) }.join()
}
log.debug "Security command classes: $state.sec"
createEvent(name:"secureInclusion", value:"success", descriptionText:"Lock is securely included")
}
def zwaveEvent(DoorLockOperationReport cmd)
{
def result = []
def map = [ name: "lock" ]
if (cmd.doorLockMode == 0xFF)
{
map.value = "locked"
}
else if (cmd.doorLockMode >= 0x40)
{
map.value = "unknown"
}
else if (cmd.doorLockMode & 1)
{
map.value = "unlocked with timeout"
}
else
{
map.value = "unlocked"
if (state.assoc != zwaveHubNodeId)
{
log.debug "setting association"
result << response(secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)))
result << response(zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId))
result << response(secure(zwave.associationV1.associationGet(groupingIdentifier:1)))
}
}
result ? [createEvent(map), *result] : createEvent(map)
}
def zwaveEvent(hubitat.zwave.commands.alarmv2.AlarmReport cmd)
{
def result = []
def map = null
if (cmd.zwaveAlarmType == 6) // ZWAVE_ALARM_TYPE_ACCESS_CONTROL
{
if (1 <= cmd.zwaveAlarmEvent && cmd.zwaveAlarmEvent < 10)
{
map = [ name: "lock", value: (cmd.zwaveAlarmEvent & 1) ? "locked" : "unlocked" ]
}
switch(cmd.zwaveAlarmEvent)
{
case 1:
map.descriptionText = "$device.displayName was manually locked"
map.data = [ usedCode: "manual" ]
break
case 2:
map.descriptionText = "$device.displayName was manually unlocked"
map.data = [ usedCode: "manual" ]
break
case 5:
if (cmd.eventParameter)
{
map.descriptionText = "$device.displayName was locked with code ${cmd.eventParameter.first()}"
map.data = [ usedCode: cmd.eventParameter[0] ]
}
else
{
map.descriptionText = "$device.displayName was locked with keypad"
map.data = [ usedCode: -1 ]
}
break
case 6:
if (cmd.eventParameter)
{
map.descriptionText = "$device.displayName was unlocked with code ${cmd.eventParameter.first()}"
map.data = [ usedCode: cmd.eventParameter[0] ]
}
else
{
map.descriptionText = "$device.displayName was unlocked with keypad"
map.data = [ usedCode: -1 ]
}
break
case 9:
map.descriptionText = "$device.displayName was autolocked"
map.data = [ usedCode: "auto" ]
break
case 7:
case 8:
case 0xA:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName was not locked fully" ]
break
case 0xB:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName is jammed", eventType: "ALERT", displayed: true ]
break
case 0xC:
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", displayed: true, isStateChange: true ]
allCodesDeleted()
break
case 0xD:
if (cmd.eventParameter)
{
map = [ name: "codeReport", value: cmd.eventParameter[0], data: [ code: "" ], isStateChange: true ]
map.descriptionText = "$device.displayName code ${map.value} was deleted"
map.isStateChange = (state["code$map.value"] != "")
state["code$map.value"] = ""
}
else
{
map = [ name: "codeChanged", descriptionText: "$device.displayName: user code deleted", isStateChange: true ]
}
break
case 0xE:
map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName: user code added", isStateChange: true ]
if (cmd.eventParameter)
{
map.value = cmd.eventParameter[0]
result << response(requestCode(cmd.eventParameter[0]))
}
break
case 0xF:
map = [ name: "tamper", value: "detected", descriptionText: "$device.displayName: Too many user code failures.", eventType: "ALERT", displayed: true, isStateChange: true ]
break
// map = [ name: "codeChanged", descriptionText: "$device.displayName: user code not added, duplicate", isStateChange: true ]
// break
case 0x10:
map = [ name: "tamper", value: "detected", descriptionText: "$device.displayName: keypad temporarily disabled", displayed: true ]
break
case 0x11:
map = [ descriptionText: "$device.displayName: keypad is busy" ]
break
case 0x12:
map = [ name: "codeChanged", descriptionText: "$device.displayName: program code changed", isStateChange: true ]
break
case 0x13:
map = [ name: "tamper", value: "detected", descriptionText: "$device.displayName: code entry attempt limit exceeded", displayed: true ]
break
default:
map = map ?: [ descriptionText: "$device.displayName: alarm event $cmd.zwaveAlarmEvent", displayed: false ]
break
}
}
else if (cmd.zwaveAlarmType == 7) // ZWAVE_ALARM_TYPE_BURGLAR
{
map = [ name: "tamper", value: "detected", displayed: true, isStateChange: true ]
switch (cmd.zwaveAlarmEvent)
{
case 0:
map.value = "clear"
map.descriptionText = "$device.displayName: tamper alert cleared"
break
case 1:
case 2:
map.descriptionText = "$device.displayName: intrusion attempt detected"
break
case 3:
map.descriptionText = "$device.displayName: covering removed"
break
case 4:
map.descriptionText = "$device.displayName: invalid code"
break
default:
map.descriptionText = "$device.displayName: tamper alarm $cmd.zwaveAlarmEvent"
break
}
}
else switch(cmd.alarmType)
{
// Schlage locks should be using the alarmv2 variables above or lock/unlock events
/*
case 21: // Manually locked
case 18: // Locked with keypad
case 24: // Locked by command (Kwikset 914)
case 27: // Autolocked
map = [ name: "lock", value: "locked" ]
break
case 16: // Note: for levers this means it's unlocked, for non-motorized deadbolt, it's just unsecured and might not get unlocked
case 19:
map = [ name: "lock", value: "unlocked" ]
if (cmd.alarmLevel) {
map.descriptionText = "$device.displayName was unlocked with code $cmd.alarmLevel"
map.data = [ usedCode: cmd.alarmLevel ]
}
break
case 22:
case 25: // Kwikset 914 unlocked by command
map = [ name: "lock", value: "unlocked" ]
break
*/
case 9:
case 17:
case 23:
case 26:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName bolt is jammed" ]
break
case 13:
map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName code $cmd.alarmLevel was added", isStateChange: true ]
result << response(requestCode(cmd.alarmLevel))
break
case 32:
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ]
allCodesDeleted()
case 33:
map = [ name: "codeReport", value: cmd.alarmLevel, data: [ code: "" ], isStateChange: true ]
map.descriptionText = "$device.displayName code $cmd.alarmLevel was deleted"
map.isStateChange = (state["code$cmd.alarmLevel"] != "")
state["code$cmd.alarmLevel"] = ""
break
case 112:
map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName code $cmd.alarmLevel changed", isStateChange: true ]
result << response(requestCode(cmd.alarmLevel))
break
case 130: // Yale YRD batteries replaced
map = [ descriptionText: "$device.displayName batteries replaced", isStateChange: true ]
break
case 131:
map = [ /*name: "codeChanged", value: cmd.alarmLevel,*/ descriptionText: "$device.displayName code $cmd.alarmLevel is duplicate", isStateChange: false ]
break
case 161:
if (cmd.alarmLevel == 2)
{
map = [ descriptionText: "$device.displayName front escutcheon removed", isStateChange: true ]
}
else
{
map = [ descriptionText: "$device.displayName detected failed user code attempt", isStateChange: true ]
}
break
case 167:
if (!state.lastbatt || (new Date().time) - state.lastbatt > 12*60*60*1000)
{
map = [ descriptionText: "$device.displayName: battery low", isStateChange: true ]
result << response(secure(zwave.batteryV1.batteryGet()))
}
else
{
map = [ name: "battery", value: device.currentValue("battery"), descriptionText: "$device.displayName: battery low", displayed: true ]
}
break
case 168:
map = [ name: "battery", value: 1, descriptionText: "$device.displayName: battery level critical", displayed: true ]
break
case 169:
map = [ name: "battery", value: 0, descriptionText: "$device.displayName: battery too low to operate lock", isStateChange: true ]
break
default:
map = [ displayed: false, descriptionText: "$device.displayName: alarm event $cmd.alarmType level $cmd.alarmLevel" ]
break
}
result ? [createEvent(map), *result] : createEvent(map)
}
def zwaveEvent(UserCodeReport cmd)
{
def result = []
def name = "code$cmd.userIdentifier"
def code = cmd.code
def map = [:]
if (cmd.userIdStatus == UserCodeReport.USER_ID_STATUS_OCCUPIED ||
(cmd.userIdStatus == UserCodeReport.USER_ID_STATUS_STATUS_NOT_AVAILABLE && cmd.user && code != "**********"))
{
if (code == "**********")
{ // Schlage locks send us this instead of the real code
state.blankcodes = true
code = state["set$name"] ?: decrypt(state[name]) ?: code
state.remove("set$name".toString())
}
if (!code && cmd.userIdStatus == 1)
{ // Schlage touchscreen sends blank code to notify of a changed code
map = [ name: "codeChanged", value: cmd.userIdentifier, displayed: true, isStateChange: true ]
map.descriptionText = "$device.displayName code $cmd.userIdentifier " + (state[name] ? "changed" : "was added")
code = state["set$name"] ?: decrypt(state[name]) ?: "****"
state.remove("set$name".toString())
}
else
{
map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: code ] ]
map.descriptionText = "$device.displayName code $cmd.userIdentifier is set"
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
map.isStateChange = true
}
result << createEvent(map)
}
else
{
map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: "" ] ]
if (state.blankcodes && state["reset$name"])
{ // we deleted this code so we can tell that our new code gets set
map.descriptionText = "$device.displayName code $cmd.userIdentifier was reset"
map.displayed = map.isStateChange = false
result << createEvent(map)
state["set$name"] = state["reset$name"]
result << response(setCode(cmd.userIdentifier, state["reset$name"]))
state.remove("reset$name".toString())
}
else
{
if (state[name])
{
map.descriptionText = "$device.displayName code $cmd.userIdentifier was deleted"
}
else
{
map.descriptionText = "$device.displayName code $cmd.userIdentifier is not set"
}
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
map.isStateChange = true
result << createEvent(map)
}
code = ""
}
state[name] = code ? encrypt(code) : code
if (cmd.userIdentifier == state.requestCode)
{ // reloadCodes() was called, keep requesting the codes in order
if (state.requestCode + 1 > state.codes || state.requestCode >= 30)
{
state.remove("requestCode") // done
}
else
{
state.requestCode = state.requestCode + 1 // get next
result << response(requestCode(state.requestCode))
}
}
if (cmd.userIdentifier == state.pollCode)
{
if (state.pollCode + 1 > state.codes || state.pollCode >= 30)
{
state.remove("pollCode") // done
}
else
{
state.pollCode = state.pollCode + 1
}
}
log.debug "code report parsed to ${result.inspect()}"
result
}
def zwaveEvent(UsersNumberReport cmd)
{
def result = []
state.codes = cmd.supportedUsers
if (state.requestCode && state.requestCode <= cmd.supportedUsers)
{
result << response(requestCode(state.requestCode))
}
result
}
def zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
def result = []
if (cmd.nodeId.any { it == zwaveHubNodeId })
{
state.remove("associationQuery")
log.debug "$device.displayName is associated to $zwaveHubNodeId"
result << createEvent(descriptionText: "$device.displayName is associated")
state.assoc = zwaveHubNodeId
if (cmd.groupingIdentifier == 2)
{
result << response(zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId))
}
}
else if (cmd.groupingIdentifier == 1)
{
result << response(secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)))
}
else if (cmd.groupingIdentifier == 2)
{
result << response(zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId))
}
result
}
def zwaveEvent(hubitat.zwave.commands.timev1.TimeGet cmd)
{
def result = []
def now = new Date().toCalendar()
if(location.timeZone) now.timeZone = location.timeZone
result << createEvent(descriptionText: "$device.displayName requested time update", displayed: false)
result << response(secure(zwave.timeV1.timeReport(
hourLocalTime: now.get(Calendar.HOUR_OF_DAY),
minuteLocalTime: now.get(Calendar.MINUTE),
secondLocalTime: now.get(Calendar.SECOND)))
)
result
}
def zwaveEvent(hubitat.zwave.commands.basicv1.BasicSet cmd)
{
// The old Schlage locks use group 1 for basic control - we don't want that, so unsubscribe from group 1
def result = [ createEvent(name: "lock", value: cmd.value ? "unlocked" : "locked") ]
result << response(zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId))
if (state.assoc != zwaveHubNodeId)
{
result << response(zwave.associationV1.associationGet(groupingIdentifier:2))
}
result
}
def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd)
{
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF)
{
map.value = 1
map.descriptionText = "$device.displayName has a low battery"
}
else
{
map.value = cmd.batteryLevel
}
state.lastbatt = new Date().time
createEvent(map)
}
def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd)
{
def result = []
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
log.debug "msr: $msr"
updateDataValue("MSR", msr)
result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)
result
}
def zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd)
{
def fw = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
updateDataValue("fw", fw)
if (state.MSR == "003B-6341-5044") {
updateDataValue("ver", "${cmd.applicationVersion >> 4}.${cmd.applicationVersion & 0xF}")
}
def text = "$device.displayName: firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
createEvent(descriptionText: text, isStateChange: false)
}
def zwaveEvent(hubitat.zwave.commands.applicationstatusv1.ApplicationBusy cmd)
{
def msg = cmd.status == 0 ? "try again later" :
cmd.status == 1 ? "try again in $cmd.waitTime seconds" :
cmd.status == 2 ? "request queued" : "sorry"
createEvent(displayed: true, descriptionText: "$device.displayName is busy, $msg")
}
def zwaveEvent(hubitat.zwave.commands.applicationstatusv1.ApplicationRejectedRequest cmd)
{
createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
}
def zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport cmd)
{
def result = []
def map = null // use this for config reports that are handled
// use desc/val for generic handling of config reports (it will just send a descriptionText for the acitivty stream)
def desc = null
def val = ""
switch (cmd.parameterNumber)
{
case 0x3:
map = parseBinaryConfigRpt('beeperMode', cmd.configurationValue[0], 'Beeper Mode')
break
// done: vacation mode toggle
case 0x4:
map = parseBinaryConfigRpt('vacationMode', cmd.configurationValue[0], 'Vacation Mode')
break
// done: lock and leave mode
case 0x5:
map = parseBinaryConfigRpt('lockLeave', cmd.configurationValue[0], 'Lock & Leave')
break
// these don't seem to be useful. It's just a bitmap of the code slots used.
case 0x6:
desc = "User Slot Bit Fields"
val = "${cmd.configurationValue[3]} ${cmd.configurationValue[2]} ${cmd.configurationValue[1]} ${cmd.configurationValue[0]}"
break
// done: the alarm mode of the lock.
case 0x7:
map = [ name:"alarmMode", displayed: true ]
// when getting the alarm mode, also query the sensitivity for that current alarm mode
switch (cmd.configurationValue[0])
{
case 0x00:
map.value = "Off_alarmMode"
break
case 0x01:
map.value = "Alert_alarmMode"
result << response(secure(zwave.configurationV2.configurationGet(parameterNumber: 0x08)))
break
case 0x02:
map.value = "Tamper_alarmMode"
result << response(secure(zwave.configurationV2.configurationGet(parameterNumber: 0x09)))
break
case 0x03:
map.value = "Kick_alarmMode"
result << response(secure(zwave.configurationV2.configurationGet(parameterNumber: 0x0A)))
break
default:
map.value = "unknown_alarmMode"
}
map.descriptionText = "$device.displayName Alarm Mode set to \"$map.value\""
break
// done: alarm sensitivities - one for each mode
case 0x8:
case 0x9:
case 0xA:
def whichMode = null
switch (cmd.parameterNumber)
{
case 0x8:
whichMode = "Alert"
break;
case 0x9:
whichMode = "Tamper"
break;
case 0xA:
whichMode = "Kick"
break;
}
def curAlarmMode = device.currentValue("alarmMode")
val = "${cmd.configurationValue[0]}"
// the lock has sensitivity values between 1 and 5. We set the slider's range ("1".."5") in the Tile's Definition
def modifiedValue = cmd.configurationValue[0]
map = [ descriptionText: "$device.displayName Alarm $whichMode Sensitivity set to $val", displayed: true ]
if (curAlarmMode == "${whichMode}_alarmMode")
{
map.name = "alarmSensitivity"
map.value = modifiedValue
}
else
{
log.debug "got sensitivity for $whichMode while in $curAlarmMode"
map.isStateChange = true
}
break
case 0xB:
map = parseBinaryConfigRpt('localControl', cmd.configurationValue[0], 'Local Alarm Control')
break
// how many times has the electric motor locked or unlock the device?
case 0xC:
desc = "Electronic Transition Count"
def ttl = cmd.configurationValue[3] + (cmd.configurationValue[2] * 0x100) + (cmd.configurationValue[1] * 0x10000) + (cmd.configurationValue[0] * 0x1000000)
val = "$ttl"
break
// how many times has the device been locked or unlocked manually?
case 0xD:
desc = "Mechanical Transition Count"
def ttl = cmd.configurationValue[3] + (cmd.configurationValue[2] * 0x100) + (cmd.configurationValue[1] * 0x10000) + (cmd.configurationValue[0] * 0x1000000)
val = "$ttl"
break
// how many times has there been a failure by the electric motor? (due to jamming??)
case 0xE:
desc = "Electronic Failed Count"
def ttl = cmd.configurationValue[3] + (cmd.configurationValue[2] * 0x100) + (cmd.configurationValue[1] * 0x10000) + (cmd.configurationValue[0] * 0x1000000)
val = "$ttl"
break
// done: auto lock mode
case 0xF:
map = parseBinaryConfigRpt('autoLock', cmd.configurationValue[0], 'Auto Lock')
break
// this will be useful as an attribute/command usable by a smartapp
case 0x10:
map = [ name: 'pinLength', value: cmd.configurationValue[0], displayed: true, descriptionText: "$device.displayName PIN length configured to ${cmd.configurationValue[0]} digits"]
break
// not sure what this one stores
case 0x11:
desc = "Electronic High Preload Transition Count"
def ttl = cmd.configurationValue[3] + (cmd.configurationValue[2] * 0x100) + (cmd.configurationValue[1] * 0x10000) + (cmd.configurationValue[0] * 0x1000000)
val = "$ttl"
break
// ???
case 0x12:
desc = "Bootloader Version"
val = "${cmd.configurationValue[0]}"
break
default:
desc = "Unknown parameter ${cmd.parameterNumber}"
val = "${cmd.configurationValue[0]}"
break
}
if (map)
{
result << createEvent(map)
}
else if (desc != null)
{
// generic description text
result << createEvent([ descriptionText: "$device.displayName reports \"$desc\" configured as \"$val\"", displayed: true, isStateChange: true ])
}
result
}
def parseBinaryConfigRpt(paramName, paramValue, paramDesc)
{
def map = [ name: paramName, displayed: true ]
def newVal = "on"
if (paramValue == 0)
{
newVal = "off"
}
map.value = "${newVal}_${paramName}"
map.descriptionText = "$device.displayName $paramDesc has been turned $newVal"
return map
}
def zwaveEvent(hubitat.zwave.Command cmd)
{
createEvent(displayed: false, descriptionText: "$device.displayName: $cmd")
}
def lockAndCheck(doorLockMode)
{
secureSequence([
zwave.doorLockV1.doorLockOperationSet(doorLockMode: doorLockMode),
zwave.doorLockV1.doorLockOperationGet()
], 4200)
}
def lock()
{
lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_SECURED)
}
def unlock()
{
lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED)
}
def unlockwtimeout()
{
lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED_WITH_TIMEOUT)
}
def refresh() {
// def cmds = [secure(zwave.doorLockV1.doorLockOperationGet())]
def cmds = secureSequence([
zwave.doorLockV1.doorLockOperationGet(),
// zwave.configurationV2.configurationBulkGet(numberOfParameters: 3, parameterOffset: 0x8),
// zwave.configurationV2.configurationBulkGet(numberOfParameters: 4, parameterOffset: 0x3),
// zwave.configurationV2.configurationGet(parameterNumber: 0x3), // beeper (done)
// zwave.configurationV2.configurationGet(parameterNumber: 0x4), // vacation mode (done)
// zwave.configurationV2.configurationGet(parameterNumber: 0x5), // lock and leave (done)
// zwave.configurationV2.configurationGet(parameterNumber: 0x6), // user slot bit field (not needed)
// zwave.configurationV2.configurationGet(parameterNumber: 0x7), // alarm mode (done)
// zwave.configurationV2.configurationGet(parameterNumber: 0x8), // alert alarm sensitivity (done: retrieved after alarm mode)
// zwave.configurationV2.configurationGet(parameterNumber: 0x9), // tamper alarm sensitivity (done: retrieved after alarm mode)
// zwave.configurationV2.configurationGet(parameterNumber: 0xA), // kick alarm sensititivy (done: retrieved after alarm mode)
// zwave.configurationV2.configurationGet(parameterNumber: 0xB), // local alarm control disable (done)
// zwave.configurationV2.configurationGet(parameterNumber: 0xC), // electronic transition count
// zwave.configurationV2.configurationGet(parameterNumber: 0xD), // mechanical transition count
// zwave.configurationV2.configurationGet(parameterNumber: 0xE), // electronic failure count
// zwave.configurationV2.configurationGet(parameterNumber: 0xF), // autolock (done)
// zwave.configurationV2.configurationGet(parameterNumber: 0x10), // user code pin length (done)
// zwave.configurationV2.configurationGet(parameterNumber: 0x11,) // electronic high preload transition count
// zwave.configurationV2.configurationGet(parameterNumber: 0x12,) // bootloader version
], 6000)
// go ahead and fill in any missing values
if (null == device.latestValue("pinLength"))
{
log.debug "getting pin length"
cmds << "delay 6000"
cmds << secure(zwave.configurationV2.configurationGet(parameterNumber: 0x10))
}
if (state.assoc == zwaveHubNodeId) {
log.debug "$device.displayName is associated to ${state.assoc}"
} else if (!state.associationQuery) {
log.debug "checking association"
cmds << "delay 4200"
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format() // old Schlage locks use group 2 and don't secure the Association CC
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = new Date().time
} else if (new Date().time - state.associationQuery.toLong() > 9000) {
log.debug "setting association"
cmds << "delay 6000"
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = new Date().time
}
log.debug "refresh is sending ${cmds.inspect()}, state: ${state.inspect()}"
cmds
}
def poll() {
def cmds = []
state.pinLength = null;
if (state.assoc != zwaveHubNodeId && secondsPast(state.associationQuery, 19 * 60))
{
log.debug "setting association"
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
cmds << "delay 6000"
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
cmds << "delay 6000"
state.associationQuery = new Date().time
}
else
{
// go ahead and fill in any missing values
if (null == device.latestValue("pinLength"))
{
cmds << secure(zwave.configurationV2.configurationGet(parameterNumber: 0x10))
cmds << "delay 6000"
}
// Only check lock state if it changed recently or we haven't had an update in an hour
def latest = device.currentState("lock")?.date?.time
if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60))
{
cmds << secure(zwave.doorLockV1.doorLockOperationGet())
state.lastPoll = (new Date()).time
}
else if (!state.MSR)
{
cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
}
else if (!state.fw)
{
cmds << zwave.versionV1.versionGet().format()
}
else if (!state.codes)
{
state.pollCode = 1
cmds << secure(zwave.userCodeV1.usersNumberGet())
}
else if (state.pollCode && state.pollCode <= state.codes)
{
cmds << requestCode(state.pollCode)
}
else if (!state.lastbatt || (new Date().time) - state.lastbatt > 53*60*60*1000)
{
cmds << secure(zwave.batteryV1.batteryGet())
}
else if (!state.enc)
{
encryptCodes()
state.enc = 1
}
}
reportAllCodes(state)
log.debug "poll is sending ${cmds.inspect()}"
device.activity()
cmds ?: null
}
private def encryptCodes()
{
def keys = new ArrayList(state.keySet().findAll { it.startsWith("code") })
keys.each { key ->
def match = (key =~ /^code(\d+)$/)
if (match) try
{
def keynum = match[0][1].toInteger()
if (keynum > 30 && !state[key])
{
state.remove(key)
}
else if (state[key] && !state[key].startsWith("~"))
{
log.debug "encrypting $key: ${state[key].inspect()}"
state[key] = encrypt(state[key])
}
}
catch (java.lang.NumberFormatException e) { }
}
}
def requestCode(codeNumber)
{
log.debug "getting user code $codeNumber"
secure(zwave.userCodeV1.userCodeGet(userIdentifier: codeNumber))
}
def reloadAllCodes()
{
def cmds = []
if (!state.codes)
{
state.requestCode = 1
cmds << secure(zwave.userCodeV1.usersNumberGet())
}
else
{
if(!state.requestCode) state.requestCode = 1
cmds << requestCode(codeNumber)
}
cmds
}
def setCode(codeNumber, code)
{
def strcode = code
log.debug "setting code $codeNumber to $code"
if (code instanceof String)
{
code = code.toList().findResults { if(it > ' ' && it != ',' && it != '-') it.toCharacter() as Short }
}
else
{
strcode = code.collect{ it as Character }.join()
}
if (state.blankcodes)
{
// Can't just set, we won't be able to tell if it was successful
if (state["code$codeNumber"] != "")
{
if (state["setcode$codeNumber"] != strcode)
{
state["resetcode$codeNumber"] = strcode
return deleteCode(codeNumber)
}
}
else
{
state["setcode$codeNumber"] = strcode
}
}
secureSequence([
zwave.userCodeV1.userCodeSet(userIdentifier:codeNumber, userIdStatus:1, user:code),
zwave.userCodeV1.userCodeGet(userIdentifier:codeNumber)
], 7000)
}
def deleteCode(codeNumber)
{
log.debug "deleting code $codeNumber"
secureSequence([
zwave.userCodeV1.userCodeSet(userIdentifier:codeNumber, userIdStatus:0),
zwave.userCodeV1.userCodeGet(userIdentifier:codeNumber)
], 7000)
}
def updateCodes(codeSettings)
{
log.debug "updateCodes called with: ${codeSettings.inspect()}"
if(codeSettings instanceof String) codeSettings = util.parseJson(codeSettings)
def set_cmds = []
def get_cmds = []
codeSettings.each { name, updated ->
def current = decrypt(state[name])
if (name.startsWith("code"))
{
def n = name[4..-1].toInteger()
if (updated?.size() >= 4 && updated != current)
{
log.debug "$name was $current, set to $updated"
def cmds = setCode(n, updated)
set_cmds << cmds.first()
get_cmds << cmds.last()
}
else if ((current && updated == "") || updated == "0")
{
log.debug "$name was $current, set to deleted"
def cmds = deleteCode(n)
set_cmds << cmds.first()
get_cmds << cmds.last()
}
else if (updated && updated.size() < 4)
{
log.debug "Attempt to set $name to a value that's too short"
// Entered code was too short
codeSettings["code$n"] = current
}
else
{
log.debug "$name remains unchanged."
}
}
else
log.warn("unexpected entry $name: $updated")
}
if (set_cmds)
{
return response(delayBetween(set_cmds, 2200) + ["delay 2200"] + delayBetween(get_cmds, 4200))
}
}
def getCode(codeNumber)
{
decrypt(state["code$codeNumber"])
}
def getAllCodes()
{
state.findAll { it.key.startsWith 'code' }.collectEntries {
[it.key, (it.value instanceof String && it.value.startsWith("~")) ? decrypt(it.value) : it.value]
}
}
private secure(hubitat.zwave.Command cmd)
{
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private secureSequence(commands, delay=4200)
{
delayBetween(commands.collect{ secure(it) }, delay)
}
private Boolean secondsPast(timestamp, seconds)
{
if (!(timestamp instanceof Number))
{
if (timestamp instanceof Date)
{
timestamp = timestamp.time
}
else if ((timestamp instanceof String) && timestamp.isNumber())
{
timestamp = timestamp.toLong()
}
else
{
return true
}
}
return (new Date().time - timestamp) > (seconds * 1000)
}
private allCodesDeleted() {
if (state.codes instanceof Integer)
{
(1..state.codes).each { n ->
if (state["code$n"])
{
result << createEvent(name: "codeReport", value: n, data: [ code: "" ], descriptionText: "code $n was deleted",
displayed: false, isStateChange: true)
}
state["code$n"] = ""
}
}
}
// all the on/off parameters work the same way, so make a common method
// to deal with them
//
def setOnOffParameter(paramName, paramNumber)
{
def cmds = null
def cs = device.currentValue(paramName)
// change parameter to the 'unknown' value - it will get refreshed after it is done changing
sendEvent(name: paramName, value: "unknown_${paramName}", displayed: false )
if (cs == "on_${paramName}")
{
// turn it off
cmds = secureSequence([zwave.configurationV2.configurationSet(parameterNumber: paramNumber, size: 1, configurationValue: [0])],5000)
}
else if (cs == "off_${paramName}")
{
// turn it on
cmds = secureSequence([zwave.configurationV2.configurationSet(parameterNumber: paramNumber, size: 1, configurationValue: [0xFF])],5000)
}
else
{
// it's in an unknown state, so just query it
cmds = secureSequence([zwave.configurationV2.configurationGet(parameterNumber: paramNumber)], 5000)
}
log.debug "set $paramName sending ${cmds.inspect()}"
cmds
}
def setBeeperMode()
{
setOnOffParameter("beeperMode", 0x3)
}
def setVacationMode()
{
setOnOffParameter("vacationMode", 0x4)
}
def setLockLeave()
{
setOnOffParameter("lockLeave", 0x5)
}
def setLocalControl()
{
setOnOffParameter("localControl", 0xB)
}
def setAutoLock()
{
setOnOffParameter("autoLock", 0xF)
}
def setAlarmMode(def newValue = null)
{
def cs = device.currentValue("alarmMode")
def newMode = 0x0
def cmds = null
if (newValue == null)
{
switch (cs)
{
case "Off_alarmMode":
newMode = 0x1
break
case "Alert_alarmMode":
newMode = 0x2
break
case "Tamper_alarmMode":
newMode = 0x3
break
case "Kick_alarmMode":
newMode = 0x0
break
case "unknown_alarmMode":
default:
// don't send a mode - instead request the current state
cmds = secureSequence([zwave.configurationV2.configurationGet(parameterNumber: 0x7)], 5000)
}
}
else
{
switch (newValue)
{
case "Off":
newMode = 0x0
break
case "Alert":
newMode = 0x1
break
case "Tamper":
newMode = 0x2
break
case "Kick":
newMode = 0x3
break
default:
// don't send a mode - instead request the current state
cmds = secureSequence([zwave.configurationV2.configurationGet(parameterNumber: 0x7)], 5000)
}
}
if (cmds == null)
{
// change the alarmSensitivity to the 'unknown' value - it will get refreshed after the alarm mode is done changing
sendEvent(name: 'alarmSensitivity', value: 0, displayed: false )
cmds = secureSequence([zwave.configurationV2.configurationSet(parameterNumber: 7, size: 1, configurationValue: [newMode])],5000)
}
log.debug "setAlarmMode sending ${cmds.inspect()}"
cmds
}
def setPinLength(newValue)
{
def cmds = null
if ((newValue == null) || (newValue == 0))
{
// just send a request to refresh the value
cmds = secureSequence([zwave.configurationV2.configurationGet(parameterNumber: 0x10)],5000)
}
else if (newValue <= 8)
{
sendEvent(descriptionText: "$device.displayName attempting to change PIN length to $newValue", displayed: true, isStateChange: true)
cmds = secureSequence([zwave.configurationV2.configurationSet(parameterNumber: 10, size: 1, configurationValue: [newValue])],5000)
}
else
{
sendEvent(descriptionText: "$device.displayName UNABLE to set PIN length of $newValue", displayed: true, isStateChange: true)
}
log.debug "setPinLength sending ${cmds.inspect()}"
cmds
}
def setAlarmSensitivity(newValue)
{
def cmds = null
if (newValue != null)
{
// newvalue will be between 1 and 5 inclusive as controlled by the slider's range definition
newValue = newValue.toInteger();
// there are three possible values to set. which one depends on the current alarmMode
def cs = device.currentValue("alarmMode")
def paramToSet = 0
switch(cs)
{
case "Off_alarmMode":
// do nothing. the slider should be disabled anyway
break
case "Alert_alarmMode":
// set param 8
paramToSet = 0x8
break
case "Tamper_alarmMode":
paramToSet = 0x9
break
case "Kick_alarmMode":
paramToSet = 0xA
break
case "unknown_alarmMode":
default:
sendEvent(descriptionText: "$device.displayName unable to set alarm sensitivity while alarm mode in unknown state", displayed: true, isStateChange: true)
break
}
if (paramToSet != 0)
{
// first set the attribute to 0 for UI purposes
sendEvent(name: 'alarmSensitivity', value: 0, displayed: false )
// then add the actual attribute set call
cmds = secureSequence([zwave.configurationV2.configurationSet(parameterNumber: paramToSet, size: 1, configurationValue: [newValue])],5000)
log.debug "setAlarmSensitivity sending ${cmds.inspect()}"
}
}
cmds
}
// provides compatibility with Erik Thayer's "Lock Code Manager"
def reportAllCodes(state)
{
def map = [ name: "reportAllCodes", data: [:], displayed: false, isStateChange: false, type: "physical" ]
state.each { entry ->
//iterate through all the state entries and add them to the event data to be handled by application event handlers
if ( entry.key ==~ /^code\d{1,}/ && entry.value.startsWith("~") )
{
map.data.put(entry.key, decrypt(entry.value))
}
else
{
map.data.put(entry.key, entry.value)
}
}
sendEvent(map)
}