/**
- * NFC Tag Toggle
+ * Notify If Left Unlocked
*
- * Copyright 2014 SmartThings
+ * Copyright 2014 George Sudarkoff
*
* 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:
* for the specific language governing permissions and limitations under the License.
*
*/
-
+
definition(
- name: "NFC Tag Toggle",
- namespace: "smartthings",
- author: "SmartThings",
- description: "Allows toggling of a switch, lock, or garage door based on an NFC Tag touch event",
- category: "SmartThings Internal",
- iconUrl: "https://s3.amazonaws.com/smartapp-icons/Developers/nfc-tag-executor.png",
- iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Developers/nfc-tag-executor@2x.png",
- iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Developers/nfc-tag-executor@2x.png")
+ name: "Notify If Left Unlocked",
+ namespace: "com.sudarkoff",
+ author: "George Sudarkoff",
+ description: "Send a push or SMS notification (and lock, if it's closed) if a door is left unlocked for a period of time.",
+ category: "Safety & Security",
+ iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
+ iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
preferences {
- page(name: "pageOne", title: "Device selection", uninstall: true, nextPage: "pageTwo") {
- section("Select an NFC tag") {
- input "tag", "capability.touchSensor", title: "NFC Tag"
- }
- section("Select devices to control") {
- input "switch1", "capability.switch", title: "Light or switch", required: false, multiple: true
- input "lock", "capability.lock", title: "Lock", required: false, multiple: true
- input "garageDoor", "capability.doorControl", title: "Garage door controller", required: false, multiple: true
- }
+ section("If this lock...") {
+ input "aLock", "capability.lock", multiple: false, required: true
+ input "openSensor", "capability.contactSensor", title: "Open/close sensor (optional)", multiple: false, required: false
}
-
- page(name: "pageTwo", title: "Master devices", install: true, uninstall: true)
-}
-
-def pageTwo() {
- dynamicPage(name: "pageTwo") {
- section("If set, the state of these devices will be toggled each time the tag is touched, " +
- "e.g. a light that's on will be turned off and one that's off will be turned on, " +
- "other devices of the same type will be set to the same state as their master device. " +
- "If no master is designated then the majority of devices of the same type will be used " +
- "to determine whether to turn on or off the devices.") {
-
- if (switch1 || masterSwitch) {
- input "masterSwitch", "enum", title: "Master switch", options: switch1.collect{[(it.id): it.displayName]}, required: false
- }
- if (lock || masterLock) {
- input "masterLock", "enum", title: "Master lock", options: lock.collect{[(it.id): it.displayName]}, required: false
- }
- if (garageDoor || masterDoor) {
- input "masterDoor", "enum", title: "Master door", options: garageDoor.collect{[(it.id): it.displayName]}, required: false
- }
- }
- section([mobileOnly:true]) {
- label title: "Assign a name", required: false
- mode title: "Set for specific mode(s)", required: false
- }
+ section("Left unlocked for...") {
+ input "duration", "number", title: "How many minutes?", required: true
+ }
+ section("Notify me...") {
+ input "pushNotification", "bool", title: "Push notification"
+ input "phoneNumber", "phone", title: "Phone number (optional)", required: false
+ input "lockIfClosed", "bool", title: "Lock the door if it's closed?"
}
}
-def installed() {
- log.debug "Installed with settings: ${settings}"
-
- initialize()
+def installed()
+{
+ initialize()
}
-def updated() {
- log.debug "Updated with settings: ${settings}"
-
- unsubscribe()
- initialize()
+def updated()
+{
+ unsubscribe()
+ initialize()
}
-def initialize() {
- subscribe tag, "nfcTouch", touchHandler
- subscribe app, touchHandler
+def initialize()
+{
+ log.trace "Initializing with: ${settings}"
+ subscribe(aLock, "lock", lockHandler)
}
-private currentStatus(devices, master, attribute) {
- log.trace "currentStatus($devices, $master, $attribute)"
- def result = null
- if (master) {
- result = devices.find{it.id == master}?.currentValue(attribute)
+def lockHandler(evt)
+{
+ log.trace "${evt.name} is ${evt.value}."
+ if (evt.value == "locked") {
+ log.debug "Canceling lock check because the door is locked..."
+ unschedule(notifyUnlocked)
}
else {
- def map = [:]
- devices.each {
- def value = it.currentValue(attribute)
- map[value] = (map[value] ?: 0) + 1
- log.trace "$it.displayName: $value"
- }
- log.trace map
- result = map.collect{it}.sort{it.value}[-1].key
+ log.debug "Starting the countdown for ${duration} minutes..."
+ state.retries = 0
+ runIn(duration * 60, notifyUnlocked)
}
- log.debug "$attribute = $result"
- result
}
-def touchHandler(evt) {
- log.trace "touchHandler($evt.descriptionText)"
- if (switch1) {
- def status = currentStatus(switch1, masterSwitch, "switch")
- switch1.each {
- if (status == "on") {
- it.off()
- }
- else {
- it.on()
- }
+def notifyUnlocked()
+{
+ // if no open/close sensor specified, assume the door is closed
+ def open = openSensor?.latestValue("contact") ?: "closed"
+
+ def message = "${aLock.displayName} is left unlocked and ${open} for more than ${duration} minutes."
+ log.trace "Sending the notification: ${message}."
+ sendMessage(message)
+
+ if (lockIfClosed) {
+ if (open == "closed") {
+ log.trace "And locking the door."
+ sendMessage("Locking the ${aLock.displayName} as prescribed.")
+ aLock.lock()
}
- }
-
- if (lock) {
- def status = currentStatus(lock, masterLock, "lock")
- lock.each {
- if (status == "locked") {
- lock.unlock()
+ else {
+ if (state.retries++ < 3) {
+ log.trace "Door is open, can't lock. Rescheduling the check."
+ sendMessage("Can't lock the ${aLock.displayName} because the door is open. Will try again in ${duration} minutes.")
+ runIn(duration * 60, notifyUnlocked)
}
else {
- lock.lock()
+ log.trace "The door is still open after ${state.retries} retries, giving up."
+ sendMessage("Unable to lock the ${aLock.displayName} after ${state.retries} retries, giving up.")
}
}
}
-
- if (garageDoor) {
- def status = currentStatus(garageDoor, masterDoor, "status")
- garageDoor.each {
- if (status == "open") {
- it.close()
- }
- else {
- it.open()
- }
- }
+}
+
+def sendMessage(msg) {
+ if (pushNotification) {
+ sendPush(msg)
+ }
+ if (phoneNumber) {
+ sendSMS(phoneNumber, msg)
}
}
+