Description text logging - actionable?

I am interested in knowing if an unlock or lock was performed by a person via code entry (lock), manually (unlock), or programatically (lock, unlock) by Hubitat. Descriptive Text logging exposes this, but events are generic.

dev:3032019-06-27 10:20:02.757 am infoFront Door Lock was unlocked by digital command
dev:3032019-06-27 10:20:00.530 am infoFront Door Lock was unlocked

Is there a way to grab the descriptive text logging info and use it in a rule?

Example:
If unlock = manual
then
disarm alarm
and
announce alarm is disarmed

YF

You'd need to write your own app to do this. I threw something together for my Yale for a similar reason but my use case is inverted - I wanted to arm the alarm when the lock wasn't manually locked. Your handler method can look at the event type and the description; e.g,

def lockHandler(evt) {
	if (evt.type == 'physical' && evt.descriptionText.endsWith('by manual')) {
		log "${lock.displayName} was locked manually"
	} else {
		log "${lock.displayName} was locked"
	}
}

I doubt the description format is guaranteed to remain stable but it seems reasonable as long as you're aware of that :slight_smile:

It looks like your lock has a slightly different format so maybe you'd want to change the if condition a bit but the syntax shouldn't be drastically different.

I have a Yale Assure SL so they should be the same. Pardon my "pseudo code" example.

I am not a coder, but have done some scripting. Feel like sharing your app?

YF.

Sure. I haven't used it much so I can't speak to how reliable it is but it seemed to work ok the few days it was used (we don't use the front door much and my use case is for a pet sitter). I can't remember why but I added the ability to bind two types of virtual switches - one that is on when manually locked and off when manually unlocked and the other is the opposite (on when manually unlocked).

definition(
    name: "Manual Lock Events",
    namespace: "rvrolyk",
    author: "Russ Vrolyk",
    description: "Tracks when a lock is manually controlled and updates virtual switch",
    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.",
    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: false,
    multiple: false
]

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

preferences {
	page(name: "mainPage", title: "<b>Lock to monitor:</b>", install: true, uninstall: true) {
		section("") {
			input lock
			input virtualLockedSwitch
			input virtualUnlockedSwitch
			label title: "Assign an app name", required: false
		}
		section ("<b>Advanced Settings</b>") {
			input enableLogging
		}
	}
}

def installed() {
	log.info "Installed with settings: ${settings}"
	initialize()
}

def updated() {
	log.info "Updated with settings: ${settings}"
	unsubscribe()
	initialize()
}

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.name} : ${evt.descriptionText}")
	if (evt.type == 'physical' && evt.descriptionText.endsWith('by manual')) {
		log "${lock.displayName} was locked manually"
		virtualLockedSwitch.on()
		if (virtualUnlockedSwitch) {
			virtualUnlockedSwitch.off()
		}
	} else {
		log "${lock.displayName} was locked"
	}
}

def unlockHandler(evt) {
	log("Unlock event: ${evt.name} : ${evt.descriptionText}")
	if (evt.type == 'physical' && evt.descriptionText.endsWith('by manual')) {
		log "${lock.displayName} was unlocked manually"
		virtualLockedSwitch.off()
		if (virtualUnlockedSwitch) {
			virtualUnlockedSwitch.on()
		}
	} else {
		log "${lock.displayName} was unlocked"
	}
}

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

Thanks! This gives me something to play around with.

There isn't much effort spent in unifying an events displayText attribute across all drivers, nor is there any guarantee it wouldnt get changed, cleaned up or otherwise modified when a specific driver gets updated for whatever reason. This type of change won't ever be in the realase notes since its an optional attribute whos content has no schematic definition.

Thanks Mike, that's more or less what I meant to imply :slight_smile:
I'm certainly not counting on the code I wrote to work forever and won't be surprised if/when it breaks because the description format changed. I probably should figure out some way to detect that but my use case isn't critical enough or frequent enough for me to have bothered yet.

1 Like

I know this is an old thread but I just want to thank Russ for this! I just got my hub a couple of days ago and I am rebuilding everything coming over from ST. I have a Schlage lock and setup a rule to report who unlocked the door using the LastCodeName but this also was triggered by manually unlocking the door. I updated this app code to match my lock and it works perfectly. I can now also check the status of the virtual switch in my rule so manual unlocks are no longer falsely reported as being unlocked by the user defined in the LastCodeName.

For the Schalge lock, I just had to update the 2nd part of the if statement from 'by manual' to 'thumb turn [physical]' for the statement to be true.

I an not a coder and have never done this before but I tried to use .includes instead of .endsWith and check for the word thumb but that didn't work. The change above worked so I am good with that. :grin:

Thanks again Russ!

Glad to hear it worked out for you @chipworkz! It's been working for me for a while now but as mentioned above, be advised the description format isn't guaranteed so it may break (and require looking for different strings) in the future. I'm trying to get some other code wrapped up but maybe at some point I'll clean this up and allow the string handling to be a preference and then share it via github... not sure when I'll have time but it seems like a good goal :slight_smile:

1 Like

It isn't too big of a deal if the description changes although if the lock is not being upgraded, I would be surprised if something changed the text. I am just putting this out there in case someone else out there has a Schlage lock and wants to use this. The purpose for this will vary per user but it was a GREAT starting point that you provided.

For the Schlage lock, there are several Events that can be looked at. I wanted this app to account for everything other than a user code unlocking the door. So I added some or statements to your if conditions. Now the virtual switches will be set correctly if I lock or unlock the door from inside, from the keypad outside, or using the "Lock" dashboard tile. The Dashboard tile is still technically manually locking or unlocking the door in my opinion but that event is digital instead of physical.

For my purpose I am having the virtual switches turn off after a short time otherwise when a user code is entered to unlock the door, the "Locked Manually" switch stays on and doesn't toggle off like it would if the door was manually unlocked. In the end, I will most likely only use the Unlocked switch and set the Locked switch required to false since my rule is just checking to see if the Unlocked Manually switch is off to proceed. I just wanted to update the if statements to learn and give more options in case they were needed.

I am still tweaking things and just learning this but thought I would share to give others some ideas on what can be done with your app.

def lockHandler(evt) {
log("Lock event: ${evt.name} : ${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"
virtualLockedSwitch.on()
if (virtualUnlockedSwitch) {
virtualUnlockedSwitch.off()
}
} else {
log "${lock.displayName} was locked"
}
}

def unlockHandler(evt) {
log("Unlock event: ${evt.name} : ${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"
virtualLockedSwitch.off()
if (virtualUnlockedSwitch) {
virtualUnlockedSwitch.on()
}
} else {
log "${lock.displayName} was unlocked"

Schlage Lock Events for Reference
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 or dashboard tile.
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 or dashboard tile.
Unlock event: lock : Front Door Lock was unlocked [digital]