Skip to content

Instantly share code, notes, and snippets.

@fishy
Last active October 10, 2018 06:05
Show Gist options
  • Save fishy/1961e30a10a686513efc6458ef783b15 to your computer and use it in GitHub Desktop.
Save fishy/1961e30a10a686513efc6458ef783b15 to your computer and use it in GitHub Desktop.
SmartThings smartapp for garage door automation
/**
* Presence and Garage Door
*
* Copyright 2016 Yuxuan Wang
*
* 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: "Presence and Garage Door",
namespace: "fishy",
author: "Yuxuan Wang",
description: "Use presence sensor to automate Garage Door",
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",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
preferences {
section("Garage Door") {
input "door", "capability.garageDoorControl", title: "Which One?"
}
section("Presence Sensors") {
input "cars", "capability.presenceSensor", title: "Cars?", multiple: true
}
section("Optional Contact Sensor") {
input "contact", "capability.contactSensor", title: "Contact Sensor", description: "When set, we can use this to help figuring out the garage door's state", required: false, multiple: false
input "refreshMax", "number", title: "refresh garage state for the next N minutes (default 5)", required: false
}
section("Real close threshold") {
input "seconds", "number", title: "Seconds?", required: false
input "phone", title: "Send SMS to this number?", required: false
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
state.lastClose = now()
state.shouldOpen = false
subscribe(cars, "presence", presenceHandler)
subscribe(door, "door", doorHandler)
if (contact) {
subscribe(contact, "contact", contactHandler)
}
}
def forceRefreshGarageState(data) {
def timestamp = now()
log.debug "forceRefreshGarageState: ${new Date()}, timestamp: $timestamp, stops at ${data.stopAt}"
if (timestamp >= data.stopAt) {
log.debug "stopping refreshing..."
unschedule(forceRefreshGarageState)
}
try {
door.refresh()
} catch (Exception e) {
// It will complain that door doesn't have refresh capability. It has.
// Safe to ignore.
}
}
def contactHandler(evt) {
log.debug "contactHandler: $evt.value: $evt, $settings"
def maxMin = 5
if (refreshMax) {
maxMin = refreshMax
}
if (evt.isStateChange()) {
def timestamp = now() + 60 * 1000 * maxMin
def data = [stopAt: timestamp]
runEvery1Minute(forceRefreshGarageState, [data: data])
forceRefreshGarageState(data)
}
}
def doorHandler(evt) {
log.debug "doorHandler: $evt.value: $evt, $settings"
if ("closing" == evt.value || "closed" == evt.value) {
state.lastClose = now()
}
}
def presenceHandler(evt) {
log.debug "presenceHandler: $evt.value: $evt, $state, $settings"
def doorState = door.latestValue("door")
log.debug "Current garage state: ${doorState}"
if ("present" == evt.value) {
if (state.shouldOpen) {
def msg = "Opening ${door.label ?: door.name} because your car is home."
log.debug "${msg}"
sendPush(msg)
door.open()
} else {
def msg = "NOT auto opening ${door.label ?: door.name} as we didn't auto close it earlier"
log.debug "${msg}"
if (phone) {
sendSms(phone, msg)
}
}
} else if ("not present" == evt.value) {
def shouldClose = true
def now = now()
def elapsed = now - state.lastClose
if (seconds) {
def milliSec = seconds * 1000
shouldClose = ("closed" != doorState) || (milliSec > elapsed)
}
state.shouldOpen = shouldClose
if (shouldClose) {
log.debug "Closing at ${state.lastClose}"
def msg = "Closing ${door.label ?: door.name} because your car is leaving."
log.debug "${msg}"
sendPush(msg)
door.close()
} else {
def sec = elapsed / 1000
def msg = "NOT auto closing as ${door.label ?: door.name} was ${doorState} and last closing was ${sec}s ago"
log.debug "${msg}"
if (phone) {
sendSms(phone, msg)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment