Update vacation-lighting-director.groovy
[smartapps.git] / official / bon-voyage.groovy
1 /**
2  *  Copyright 2015 SmartThings
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  *  in compliance with the License. You may obtain a copy of the License at:
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
10  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
11  *  for the specific language governing permissions and limitations under the License.
12  *
13  *  Bon Voyage
14  *
15  *  Author: SmartThings
16  *  Date: 2013-03-07
17  *
18  *  Monitors a set of presence detectors and triggers a mode change when everyone has left.
19  */
20
21 definition(
22     name: "Bon Voyage",
23     namespace: "smartthings",
24     author: "SmartThings",
25     description: "Monitors a set of SmartSense Presence tags or smartphones and triggers a mode change when everyone has left.  Used in conjunction with Big Turn Off or Make It So to turn off lights, appliances, adjust the thermostat, turn on security apps, and more.",
26     category: "Mode Magic",
27     iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld.png",
28     iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld@2x.png"
29 )
30
31 preferences {
32         section("When all of these people leave home") {
33                 input "people", "capability.presenceSensor", multiple: true
34         }
35         section("Change to this mode") {
36                 input "newMode", "mode", title: "Mode?"
37         }
38         section("False alarm threshold (defaults to 10 min)") {
39                 input "falseAlarmThreshold", "decimal", title: "Number of minutes", required: false
40         }
41         section( "Notifications" ) {
42                 input("recipients", "contact", title: "Send notifications to", required: false) {
43                         input "sendPushMessage", "enum", title: "Send a push notification?", options: ["Yes", "No"], required: false
44                         input "phone", "phone", title: "Send a Text Message?", required: false
45                 }
46         }
47
48 }
49
50 def installed() {
51         log.debug "Installed with settings: ${settings}"
52         // commented out log statement because presence sensor label could contain user's name
53         //log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
54         subscribe(people, "presence", presence)
55 }
56
57 def updated() {
58         log.debug "Updated with settings: ${settings}"
59         // commented out log statement because presence sensor label could contain user's name
60         //log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
61         unsubscribe()
62         subscribe(people, "presence", presence)
63 }
64
65 def presence(evt)
66 {
67         log.debug "evt.name: $evt.value"
68         if (evt.value == "not present") {
69                 if (location.mode != newMode) {
70                         log.debug "checking if everyone is away"
71                         if (everyoneIsAway()) {
72                                 log.debug "starting sequence"
73                                 runIn(findFalseAlarmThreshold() * 60, "takeAction", [overwrite: false])
74                         }
75                 }
76                 else {
77                         log.debug "mode is the same, not evaluating"
78                 }
79         }
80         else {
81                 log.debug "present; doing nothing"
82         }
83 }
84
85 def takeAction()
86 {
87         if (everyoneIsAway()) {
88                 def threshold = 1000 * 60 * findFalseAlarmThreshold() - 1000
89                 def awayLongEnough = people.findAll { person ->
90                         def presenceState = person.currentState("presence")
91                         if (!presenceState) {
92                                 // This device has yet to check in and has no presence state, treat it as not away long enough
93                                 return false
94                         }
95                         def elapsed = now() - presenceState.rawDateCreated.time
96                         elapsed >= threshold
97                 }
98                 log.debug "Found ${awayLongEnough.size()} out of ${people.size()} person(s) who were away long enough"
99                 if (awayLongEnough.size() == people.size()) {
100                         // TODO -- uncomment when app label is available
101                         def message = "SmartThings changed your mode to '${newMode}' because everyone left home"
102                         log.info message
103                         send(message)
104                         setLocationMode(newMode)
105                 } else {
106                         log.debug "not everyone has been away long enough; doing nothing"
107                 }
108         } else {
109         log.debug "not everyone is away; doing nothing"
110     }
111 }
112
113 private everyoneIsAway()
114 {
115         def result = true
116         for (person in people) {
117                 if (person.currentPresence == "present") {
118                         result = false
119                         break
120                 }
121         }
122         log.debug "everyoneIsAway: $result"
123         return result
124 }
125
126 private send(msg) {
127         if (location.contactBookEnabled) {
128         log.debug("sending notifications to: ${recipients?.size()}")
129                 sendNotificationToContacts(msg, recipients)
130         }
131         else  {
132                 if (sendPushMessage != "No") {
133                         log.debug("sending push message")
134                         sendPush(msg)
135                 }
136
137                 if (phone) {
138                         log.debug("sending text message")
139                         sendSms(phone, msg)
140                 }
141         }
142         log.debug msg
143 }
144
145 private findFalseAlarmThreshold() {
146         (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold : 10
147 }