Update speaker-weather-forecast.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         // Input variable initialization
56         falseAlarmThreshold = 0
57 }
58
59 def updated() {
60         log.debug "Updated with settings: ${settings}"
61         // commented out log statement because presence sensor label could contain user's name
62         //log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
63         unsubscribe()
64         subscribe(people, "presence", presence)
65 }
66
67 def presence(evt)
68 {
69         log.debug "evt.name: $evt.value"
70         if (evt.value == "not present") {
71                 if (location.mode != newMode) {
72                         log.debug "checking if everyone is away"
73                         if (everyoneIsAway()) {
74                                 log.debug "starting sequence"
75                                 runIn(findFalseAlarmThreshold() * 60, "takeAction", [overwrite: false])
76                         }
77                 }
78                 else {
79                         log.debug "mode is the same, not evaluating"
80                 }
81         }
82         else {
83                 log.debug "present; doing nothing"
84         }
85 }
86
87 def takeAction()
88 {
89         if (everyoneIsAway()) {
90                 def threshold = 1000 * 60 * findFalseAlarmThreshold() - 1000
91                 def awayLongEnough = people.findAll { person ->
92                         def presenceState = person.currentState("presence")
93                         if (!presenceState) {
94                                 // This device has yet to check in and has no presence state, treat it as not away long enough
95                                 return false
96                         }
97                         def elapsed = now() - presenceState.rawDateCreated.time
98                         elapsed >= threshold
99                 }
100                 log.debug "Found ${awayLongEnough.size()} out of ${people.size()} person(s) who were away long enough"
101                 if (awayLongEnough.size() == people.size()) {
102                         // TODO -- uncomment when app label is available
103                         def message = "SmartThings changed your mode to '${newMode}' because everyone left home"
104                         log.info message
105                         send(message)
106                         setLocationMode(newMode)
107                 } else {
108                         log.debug "not everyone has been away long enough; doing nothing"
109                 }
110         } else {
111         log.debug "not everyone is away; doing nothing"
112     }
113 }
114
115 private everyoneIsAway()
116 {
117         def result = true
118         for (person in people) {
119                 if (person.currentPresence == "present") {
120                         result = false
121                         break
122                 }
123         }
124         log.debug "everyoneIsAway: $result"
125         return result
126 }
127
128 private send(msg) {
129         if (location.contactBookEnabled) {
130         log.debug("sending notifications to: ${recipients?.size()}")
131                 sendNotificationToContacts(msg, recipients)
132         }
133         else  {
134                 if (sendPushMessage != "No") {
135                         log.debug("sending push message")
136                         sendPush(msg)
137                 }
138
139                 if (phone) {
140                         log.debug("sending text message")
141                         sendSms(phone, msg)
142                 }
143         }
144         log.debug msg
145 }
146
147 private findFalseAlarmThreshold() {
148         (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold : 10
149 }