Door Locks Fully Supported by ST's

@coryS - any chance you can help answer my question in the post above? secondary question: am I supposed to pair my door lock AFTER I create the new device type so that the door lock automatically begins to use the new device type? or if I already have my door lock paired, is it a problem to update its device type to the new custom one? hmmm, maybe I don’t know what I’m talking about haha.

any help is greatly appreciated… just wish this functionality was baked in, but I guess they aren’t quite there yet.

thanks,

@brandon pair it like normal then go into the IDE, select the device and change it to the new type.

@coryS hmmm, I click into the door lock device (at the “My Devices” IDE page), but don’t know where to change it’s device type.

Also, what about the code issue I had above? When creating the new device type, I wasn’t sure if I was supposed to just copy all of the code here and paste it over the default code and then that’s it?

Thanks

@brandon , replace the existing code. To change the type bring up the device and hit edit, from there you should find a drop down with all the different device types. Your new one should be at the bottom.

@coryS Ok I found where to change the Device Type now, thanks!

Follow up Q regarding the code: it looks like when I paste the full code from the github page over the new Device Type default code then save it, it does away with the Capabilities, Custom Attributes, and Custom Command the instructions said to specify for it. I see the “metadata” in the default Device Type code shows these things specified, but the code on the github page doesn’t so maybe that’s why this occurs?

Are you sure I’m not supposed to just copy and paste parts of the github code over the default Device Type code in light of this?

@Brandon I’d have to look at the code, it may just be a modification you make to it. Anyway, here is the complete code I use that will work fine for ya.

/**
 * This is a custom version of the Z-Wave Lock Device Type code. This code adds support for user codes with the use of a Smartapp.
 *
 * This Device Type was designed for the Kwikset Deadbolt so might not be compatible with other locks.
 *
 * Installation
 *
 * Create a new device type (https://graph.api.smartthings.com/ide/devices)
 *    Capabilities:
 *    Configuration
 *    Battery
 *    Polling
 *    Lock
 *    Custom Attribute
 *    user1
 *    code1
 *    Custom Command
 *    usercodechange
 */
metadata {
    // Automatically generated. Make future change here.
    definition (name: "Custom Lock", author: "cory.simpson@gmail.com") {
        capability "Configuration"
        capability "Lock"
        capability "Polling"
        capability "Battery"

        attribute "user1", "string"
        attribute "code1", "string"

        command "usercodechange"
    }

    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 {
        standardTile("toggle", "device.lock", width: 2, height: 2) {
            state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking"
            state "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking"
            state "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821"
            state "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, decoration: "flat") {
            state "battery", label:'${currentValue}% battery', action:"batteryupdate", unit:""
        }
        valueTile("usercode", "device.usercode", inactiveLabel: false, decoration: "flat") {
            state "usercode", label:'${currentValue}', unit:""
        }
        standardTile("refresh", "device.lock", inactiveLabel: false, decoration: "flat") {
            state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
        }
        standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
            state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
        }

        main "toggle"
        details(["toggle", "lock", "unlock", "battery", "usercode", "configure", "refresh"])
    }
}

import physicalgraph.zwave.commands.doorlockv1.*

//parse
def parse(String description) {
    def result = null
    if (description.startsWith("Err")) {
        result = createEvent(descriptionText:description, displayed:true)
    } else {
        def cmd = zwave.parse(description, [ 0x98: 1])
        if (cmd) {
            result = zwaveEvent(cmd)
        }
    }
    log.debug "\"$description\" parsed to ${result.inspect()}"
    log.debug "Parse result $result"
    result
}

def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
    def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1,0x62: 1, 0x63: 1, 0x70: 1, 0x71: 2, 0x75: 1, 0x80:1, 0x85: 2, 0x4E: 2, 0x4C: 1, 0x8B: 1, 0x5D: 2])
    log.debug "encapsulated: $encapsulatedCommand"
    if (encapsulatedCommand) {
        zwaveEvent(encapsulatedCommand)
    }
}

def zwaveEvent(DoorLockOperationReport cmd) {
    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"
    }
    createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
    def map = null
    if (cmd.zwaveAlarmType == 6) {
        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"
                break
            case 2:
                map.descriptionText = "$device.displayName was manually unlocked"
                break
            case 5:
                if (cmd.eventParameter) {
                    map.descriptionText = "$device.displayName was locked with code ${cmd.eventParameter.first()}"
                }
                break
            case 6:
                if (cmd.eventParameter) {
                    map.descriptionText = "$device.displayName was unlocked with code ${cmd.eventParameter.first()} zwaveAlarmEvent"
                }
                break
            case 9:
                map.descriptionText = "$device.displayName was autolocked"
                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" ]
                break
            case 0xC:
                map = [ descriptionText: "$device.displayName: all user codes deleted", display: true ]
                break
            case 0xD:
                map = [ descriptionText: "$device.displayName: user code deleted", display: true ]
                break
            case 0xE:
                map = [ descriptionText: "$device.displayName: user code added", display: true ]
                break
            default:
                map = map ?: [ descriptionText: "$device.displayName: alarm event $cmd.zwaveAlarmEvent", display: false ]
                break
        }
    } else switch(cmd.alarmType) {
    case 17:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "$device.displayName Secured at Keypad – Bolt Jammed"
            break
    case 18:
            map = [ name: "lock", value: "locked" ]
            if(cmd.alarmLevel) {
                map.descriptionText = "$device.displayName Secured by User ${cmd.alarmLevel} at Keypad"
                map = [ name: "code", value: ${cmd.alarmLevel} ]
            }
            break
    case 19:
            map = [ name: "lock", value: "unlocked" ]
            if(cmd.alarmLevel) {
                map.descriptionText = "$device.displayName Un-Secured by User ${cmd.alarmLevel} at Keypad"
            }
            break
    case 21:
            map = [ name: "lock", value: "locked" ]
            map.descriptionText = "$device.displayName Secured using Keyed cylinder or inside thumb-turn"
            break
    case 22:
            map = [ name: "lock", value: "unlocked" ]
            map.descriptionText = "$device.displayName Un-Secured using Keyed cylinder or inside thumb-turn"
            break
    case 23:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "$device.displayName Secured by Controller – Bolt Jammed"
            break
    case 24:
            map = [ name: "lock", value: "locked" ]
            map.descriptionText = "$device.displayName Secured by Controller"
            break
    case 25:
            map = [ name: "lock", value: "unlocked" ]
            map.descriptionText = "$device.displayName Un-Secured by Controller"
            break
    case 26:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "$device.displayName Lock Auto Secured – Bolt Jammed"
            break
    case 27:
            map = [ name: "lock", value: "locked" ]
            map.descriptionText = "$device.displayName Lock Auto Secured"
            break
    case 32:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "All User Codes deleted from $device.displayName"
            break
    case 33:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "User ${cmd.alarmLevel} deleted from $device.displayName"
            break
    case 112:
            map = [ name: "lock", value: "unknown" ]
            if(cmd.alarmLevel) {
                map.descriptionText = "New User: ${cmd.alarmLevel} added to $device.displayName"
            }
            break
    case 161:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "Failed User Code attempt at Keypad on $device.displayName"
            break
    case 162:
            map = [ name: "lock", value: "unknown" ]
            if(cmd.alarmLevel) {
                map.descriptionText = "Attempted access by user ${cmd.alarmLevel} outside of scheduled on $device.displayName"
            }
            break
    case 167:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "Low battery level on $device.displayName"
            break
    case 168:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "Critical battery level on $device.displayName"
            break
    case 169:
            map = [ name: "lock", value: "unknown" ]
            map.descriptionText = "Battery level too low to operate $device.displayName"
            break
    default:
            map = [ displayed: false, descriptionText: "$device.displayName: $cmd" ]
    }
    createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd) {
    def result = []
    state.associationQuery = null
    if (cmd.nodeId.any { it == zwaveHubNodeId }) {
        log.debug "$device.displayName is associated to $zwaveHubNodeId"
        state.assoc = zwaveHubNodeId
    } else {
        result << response(secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)))
    }
    result
}

//battery
def batteryupdate() {
    def result = secure(zwave.batteryV1.batteryGet())
    log.debug "battery $result"
    result
}

def zwaveEvent(physicalgraph.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
    }
    createEvent(map)
}

//User
def usercodechange(user1, code1, idstatus1) {
    log.debug "Set $code1 for User $user1"
    log.debug "Set User ID Status to $idstatus1"
    secureSequence([
        zwave.userCodeV1.userCodeSet(userIdentifier: user1, userIdStatus: idstatus1, code: code1),
        zwave.userCodeV1.userCodeGet(userIdentifier: user1)
    ], 4200)
}

def zwaveEvent(physicalgraph.zwave.commands.usercodev1.UserCodeReport cmd) {
    log.debug "$cmd"
    def map = [ name: "usercode" ]
    map.value = cmd.code
    createEvent(map)
}

//Configuration
def configupdate() {
    secure(zwave.configurationV1.configurationGet(parameterNumber: 1))
}

def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
    log.debug "$cmd"
    def map = [ name: "config", value:  cmd.configurationValue]
    if (cmd.configurationValue) {
    switch(cmd.configurationValue) {
        case "[1]":
            map.descriptionText = "User $cmd.parameterNumber is Type: Owner (Default)"
            log.debug "Owner"
            break
        case "[3]":
            map.descriptionText = "User $cmd.parameterNumber is Type: Guest (Required for Year Day Schedules)"
            break
        case "[4]":
            map.descriptionText = "User $cmd.parameterNumber is Type: Worker (Required for Week Day Schedules)"
            break
        case "[255]":
            map.descriptionText = "User $cmd.parameterNumber not found"
            break
        default:
            map = [ displayed: false, descriptionText: "$device.displayName: $cmd" ]
        }
    }
    createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
    log.warn "Unexpected 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 result = [secure(zwave.doorLockV1.doorLockOperationGet())]
    if (state.assoc == zwaveHubNodeId) {
        //log.debug "$device.displayName is associated to ${state.assoc}"
    } else if (!state.associationQuery) {
        log.debug "checking association"
        result << "delay 4200"
        result << zwave.associationV1.associationGet(groupingIdentifier:2).format()  // old Schlage locks use group 2 and don't secure the Association CC
        result << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
        state.associationQuery = new Date().time
    } else if (new Date().time - state.associationQuery.toLong() > 9000) {
        log.debug "setting association"
        result << "delay 6000"
        result << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
        result << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
        result << zwave.associationV1.associationGet(groupingIdentifier:2).format()
        result << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
        state.associationQuery = new Date().time
    }
    result
}

def updated() {
}

def secure(physicalgraph.zwave.Command cmd) {
    def result = zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
    log.debug "Result $result"
    result
}

def secureSequence(commands, delay=4200) {
    delayBetween(commands.collect{ secure(it) }, delay)
}
2 Likes

@coryS Awesome, that did the trick. Thanks man!!

Unless I’m doing something wrong this doesn’t appear to work for the new Kwikset 914. I see the usercodechange command being sent to the lock by the SmartApp but the codes never work. Is there anything else I should be looking at?

@coryds Thanks for the code snippet, that helped for me too

@bigpunk6, I’m seeing some inconsistent behavior so I thought I’d throw this out here.

I entered a usercode for user 10 and it didn’t work right away. I eventually gave up until a few days later when I noticed the app was displaying the code I entered in one of the tiles. I tried the code on the keypad and it worked. Now, I am trying to program a usercode for user 11, but the code simply won’t work no matter how long I wait.

Also, and perhaps more importantly, I am unable to delete the code for user 10. Any ideas?

Lock: Kwikset 910
OS: iOS

Update: I finally gave up and tried just changing the code for user 10. It didn’t work right away. The crazy thing is, it didn’t take effect until 3-4 days later.

What could possibly cause these types of updates to not take effect for days? Anyone?

Here are the relevant log entries:

Date Source Type Name Value Displayed Text Changed
7 May 2014 14:04:06 DEVICE usercode xxxxxx Front Door Lock usercode is xxxxxx true
7 May 2014 14:04:02 DEVICE lock unknown New User: 10 added to Front Door Lock true
7 May 2014 14:04:02 DEVICE Front Door Lock is now active false
7 May 2014 14:03:58 COMMAND usercodechange usercodechange(10, xxxxxx, 1) command was sent to Front Door Lock true
7 May 2014 14:03:58 APP_COMMAND usercodechange Temp Lock Code sent usercodechange command to Front Door Lock

Remember, these actions were taken 3-4 days ago.

Ok so I think I installed the smartapp and custom device type properly. However I have a question about usability.

It seams I would add a new instance of the smart app for every code I want to set, is that correct? I’m assuming that is how this works. Lets say I did the following:

  1. Add smartapp with the following settings:
    Lock: Front Door Lock (This is the custom device type)
    User: 1
    Code: 1234
    Assign a name: UserA

  2. Click on the smartapp just added, I see that in the events list a series of actions looking like the user was added and code was set to 1234

  3. Go to “Things” click on the lock settings and I see that the code tile says “1234”

  4. Then I Add second smartapp with the following settings:
    Lock: Front Door Lock (This is the custom device type)
    User: 2
    Code: 5678
    Assign a name: UserB

  5. Click on the smartapp just added, I see that in the events list a series of actions looking like the user was added and code was set to 5678

  6. Go to “Things” click on the lock settings and I see that the code tile now says “5678”

So the question I have is were both users added meaning both codes are set and can be used at the same time or is only 5678 UserB the working code, and if I want the 1234 UserA code I have to click on the smartapp to change the code back?

I asked because it seems that only the code listed on the tile seemed to be working.

Also the there is a tile left of the tile with the lock code that just has “–” is that supposed to be battery status?

@huydnguyen No, you just open up the same app and modify it to your purpose each time you need to make a change. Unless of course you want to install multiple apps so you have a “log” of what code is setup to what user.

Any update on this, I know its been on the roadmap for 9 months at least, would really be nice to have this functionality like your competitors.

Hopefully we can get this before the buyout is official, I have a feeling once purchased it may stall any updates for awhile.

Hi - did you ever get this working with the 914? I have it working with the 914, but only for users 3-30. I defined user’s 1 and 2 locally on the lock, and if I try to change the password, it won’t let me. I even tried to delete user 2 and re-add it via the app and it’s not working. I can only make modifications to user 1 and 2 locally. I made sure to disable a master code to make sure that wasn’t the problem, but that didn’t fix it.

Perhaps this is by design?

Anyone know if the Yale Zigbee lock is compatible with Smartthings?

How did you get it working with the 914? The app is showing up for me on my iPhone and when I run the sim, it only shows a virtual lock, not my real one. Thanks

You have to add a custom device type (code is referenced above) and then add a custom smart app. Did you do both? My research is pointing to the fact the first 2 user codes stay local on the lock and cannot be controlled through zwave, so I believe it’s fully working on the 914 now.

yes, I did both, but my lock doesn’t show up in the web gui sim, and the app doesn’t show up on my iPhone. And I’ve done other apps and devices and they work fine.