Last active
October 10, 2018 06:05
-
-
Save fishy/1961e30a10a686513efc6458ef783b15 to your computer and use it in GitHub Desktop.
SmartThings smartapp for garage door automation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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