Getting an event from a lock for ONLY when the lock is unlocked by a code

And I'm struggling with this... I'll expand more with code if needed but basically in an app that subscribes like this:

subscribe(myLock, "lastCodeName", lockHandler, ["filterEvents": false])

First...oddly, that subscribe gets triggered with LOCK and UNLOCK events. (I really feel like this is where the problem lies)

Here's the handler:

def lockHandler(evt) {
    lastName = myLock.currentValue("lastCodeName")
    if (evt.value == "unlocked") {
        debuglog "Last Name: $lastName"


Here's the problem... even if I manually unlock the door (thumb turn) or using a virtual lock device just hitting the "unlock" button vs the "unlock with code" button. This is triggering the routine.

What I'm trying to do is have a routine that is ONLY triggered when the door is unlocked with a CODE. Then I just want the last code name.

SIDE NOTE: The built in "Notifications" app does NOT recognize the last code name. AKA this fails:

or should I say NEVER triggers when any code is entered on the lock.

You sir need to look at Nyckelharpa;

or better yet, maybe @arnb can chime in on what I'm missing...if his code can do it...apparently so can mine!

I would like helping, however I have no experience with Smart Locks.

Should anything in my code be useful to you, feel free to use it.

1 Like

@bravenel is this possibly something you can help shed some light on?

My Event Engine can do this. You can select a certain code or all codes. Feel free to take a look at the code and use what you want.

In this example, the light will turn on if a selected user inputs their code. Light turns back off when door is locked.


not to knock event engine.. great app, i am using it.. but rule machine rule also works.. I have the following rule and dont get the alert when manually unlocked. At least for kwikset locks

Why don't you simply subscribe separately to the lock being unlocked? Then sort out in your code what to do.

That's the difficulty. I can not figure out how to determine if that unlock event was a code or the turning of the knob. If it was a turning of the knob I still have "lastCodeName" in there which is accurate to the point that WAS the last code that unlocked the door...but not necessarily who or what just unlocked it.

I think this may help. This originally was posted by @rvrolyk and I used it and updated it for my Schlage Lock and this combo works perfect.

My updated code for my Schlage lock is below. I added the (evt.type == 'digital' && (evt.descriptionText.endsWith('unlocked [digital]') comments to track the difference between a manual and digital unlock event. I then use this Virtual Switch in my If logic in my rule. You will need to see how your Yale lock reports the "Digital" event to see if it is the same as the Schlage or not. I am not a developer, I was just able to tweak this to make it work for me.

Installation Notes:
You must create a Virtual Switch for both the Virtual Lock and Virtual Unlock switches
even if you are not going to use one or the other. The app will not function correctly
if one of the switches are not created.

Schlage Lock Events
Lock name = Front Door Lock. (Varies per installation)

Locked using keypad Schlage button or code.
Lock event: lock : Front Door Lock was locked by keypad [physical]

Locked using inside dial.
Lock event: lock : Front Door Lock was locked by thumb turn [physical]

Locked using automation.
Lock event: lock : Front Door Lock was locked [digital]

Unlocked using code.
Unlock event: lock : Front Door Lock was unlocked by John

Unlocked using inside dial.
Unlock event: lock : Front Door Lock was unlocked by thumb turn [physical]

Unlocked using automation.
Unlock event: lock : Front Door Lock was unlocked [digital]

name: "Manual Lock Events Monitor",
namespace: "chipworkz",
author: "chipworkz",
original author: "Russ Vrolyk",
description: "Tracks when a lock is manually controlled and updates the virtual switches",
category: "Convenience",
iconUrl: "",
iconX2Url: "",
iconX3Url: "")

def lock = [
name: "lock",
type: "capability.lock",
title: "Lock",
description: "Select the lock to monitor.",
required: true,
multiple: false

def virtualLockedSwitch = [
name: "virtualLockedSwitch",
type: "capability.switch",
title: "Virtual Lock Switch",
description: "Virtual switch that should be turned on when lock is manually locked if needed.",
required: true,
multiple: false

def virtualUnlockedSwitch = [
name: "virtualUnlockedSwitch",
type: "capability.switch",
title: "Virtual Unlock Switch",
description: "Virtual switch that should be turned on when lock is manually unlocked.",
required: true,
multiple: false

def enableLogging = [
name: "enableLogging",
type: "bool",
title: "Enable debug Logging?",
defaultValue: false,
required: true

preferences {
page(name: "mainPage", title: "Schlage Lock Events Monitor:", install: true, uninstall: true) {
section("") {
input lock
input virtualLockedSwitch
input virtualUnlockedSwitch
label title: "Assign an app name", required: false
section ("Advanced Settings") {
input enableLogging

def installed() { "Installed with settings: ${settings}"

def updated() { "Updated with settings: ${settings}"

def initialize() {
log "Lock status: ${lock.displayName} " + lockStatus()
subscribe(lock, "lock.locked", lockHandler)
subscribe(lock, "lock.unlocked", unlockHandler)

def lockStatus() {
return lock.currentValue("lock")

def lockHandler(evt) {
log("Lock event: ${} : ${evt.descriptionText}")
if (evt.type == 'physical' && (evt.descriptionText.endsWith('thumb turn [physical]') || evt.descriptionText.endsWith('keypad [physical]')) || (evt.type == 'digital' && (evt.descriptionText.endsWith('locked [digital]')))) {
log "${lock.displayName} was locked manually"
if (virtualUnlockedSwitch) {
} else {
log "${lock.displayName} was locked"

def unlockHandler(evt) {
log("Unlock event: ${} : ${evt.descriptionText}")
if (evt.type == 'physical' && evt.descriptionText.endsWith('thumb turn [physical]') || (evt.type == 'digital' && (evt.descriptionText.endsWith('unlocked [digital]')))) {
log "${lock.displayName} was unlocked manually"
if (virtualUnlockedSwitch) {
} else {
log "${lock.displayName} was unlocked"

def log(msg) {
if (enableLogging) {
log.debug msg

Ahhhh, you've got me much closer. Testing with this I'm seeing a "physical" unlock with a PIN and a "digital" unlock when it's manual. Does that jive with how yours is?

It seems that I can make this work. Is this different for different locks?

This seems to work for me:

That debug statement only get triggered if it's unlocked WITH a code. Seems weird that's called "physical" but....

I added my Schlage events to my code for reference. This is how mine reports the events. This is the only lock I have so I cannot say what Yale or other locks do.

1 Like

Thanks, this helped me get there!

HOWEVER... I will make note... even in your code:

subscribe(lock, "lock.unlocked", unlockHandler)

My handler is getting ALL events regardless of what I'm putting for the subscription. So in the example above.. I'm still seeing the lock events also. Something is amiss.

Yes my code is for both lock and unlock events. The virtual switch gets triggered and I am only using the unlock switch in my rule. I don't do anything with the locked virtual switch.

Lock Monitor2

update..spent some time tonight figuring this out...and finally got it.

I got the proper "unlocked" events now from the subscription. Not sure what I missed the first time but I AM only getting unlocked events now with this:

subscribe(lock, "lock.unlocked", lockHandler)

Second, I do NOT get an evt.type I'm guessing this is a driver that part needed to be removed. My if statement looks like this:

if (evt.descriptionText.endsWith('thumbturn [physical]') || evt.descriptionText.endsWith('command [digital]')) {
    infolog "$lock.displayName was unlocked manually"
} else {
    infolog "$lock.displayName was unlocked by CODE: $lastName"
    sendPushMessage.deviceNotification("$lock.displayName was unlocked by: $lastName")

Notice "thumbturn" in my case did NOT have a space like yours did. But updating this here for others in the future. Is there not a more standard way to handle these events for ALL locks? At least the digital/physical/thumbturn/code entered parts? Understand some locks will have different capabilities... either it finally worked out now and FINALLY have a way to notify when a door gets unlocked ONLY by a code.

1 Like

More info for those that might come across this. @bptworld helped me and ultimately came up with code that hopefully covers all lock drivers...or at least most of them? Really think there needs to be more standardization there @mike.maxwell but...

here is the check that Bryan came up with that seems to be working for multiple lock types:

if (evt.descriptionText.contains("unlocked by")) { //lock was unlocked by code}

How the other built in apps are handling this I don't know...but I do know some of them don't work with HubConnect and this method will.

His event log(kwikset):


as you can see mine puts [physical] with a valid code, his does not. His doesn't use "thumbturn" it uses "manual" for someone turning the knob. I'm sure there's some other driver out there that uses "unlocked by manual" or "unlocked by thumbturn" or "unlocked by ..." FWIW you are going to have to test. No wonder why I've been having problems with Rule Machine and Notifications...

1 Like

the whole point of the lastCodeName event was so rules didn't have to parse through the description text.
description text is an optional attribute and its content is not defined.
physical/digital is also an optional attribute, and whilst we strive to make it accurate, it isn't always the case, devices in general do not make the distinction in the events they send to us, so we have to sort this out, usually with state flags ect...

the lastCodeName event should only be generated when a lock is unlocked by a pin code, is that not the case?

If not, let me know which lock drivers are in arrears...

That's the case. "Yale Zigbee Lock"

Subscribe to just the lastCodeName events but my simple routine with a debug log shows this:

This could REALLY explain why I've been having issues with RM and Notifications/etc.

ok, not sure I'm following the issue here.
This log shows in general display text if text logging is enabled in the driver, the actual events committed to the database are shown by clicking the events button in the driver...

Are you saying that unlocked by JR and Kyleigh are not generating lastCodeName events in the device events log?

Sorry let me better explain, first here are the actual events from the lock:

What I showed above was that I was writing an app. I wanted to only act on when an actual code was punched into the lock. No other events.

What am I missing about how to determine when ONLY the code has been entered? As you can see above in this thread I don't look alone in having to parse the description to get the desired result.