Checking in all the SmartThings apps; both official and third-party.
[smartapps.git] / third-party / NotifyIfLeftUnlocked.groovy
diff --git a/third-party/NotifyIfLeftUnlocked.groovy b/third-party/NotifyIfLeftUnlocked.groovy
new file mode 100755 (executable)
index 0000000..86096f4
--- /dev/null
@@ -0,0 +1,110 @@
+/**
+ *  Notify If Left Unlocked
+ *
+ *  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:
+ *
+ *      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.
+ *
+ */
+
+definition(
+    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 {
+    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
+    }
+    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()
+{
+    initialize()
+}
+
+def updated()
+{
+    unsubscribe()
+    initialize()
+}
+
+def initialize()
+{
+    log.trace "Initializing with: ${settings}"
+    subscribe(aLock, "lock", lockHandler)
+}
+
+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 {
+        log.debug "Starting the countdown for ${duration} minutes..."
+        state.retries = 0
+        runIn(duration * 60, notifyUnlocked)
+    }
+}
+
+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()
+        }
+        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 {
+                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.")
+            }
+        }
+    }
+}
+
+def sendMessage(msg) {
+    if (pushNotification) {
+        sendPush(msg)
+    }
+    if (phoneNumber) {
+        sendSMS(phoneNumber, msg)
+    }
+}
+