Update BetterLaundryMonitor.groovy
[smartapps.git] / third-party / ecobeeAwayFromHome.groovy
1 /***
2  *
3  *  Copyright 2014 Yves Racine
4  *  linkedIn profile: ca.linkedin.com/pub/yves-racine-m-sc-a/0/406/4b/
5  *
6  *  Developer retains all right, title, copyright, and interest, including all copyright, patent rights, trade secret 
7  *  in the Background technology. May be subject to consulting fees under the Agreement between the Developer and the Customer. 
8  *  Developer grants a non exclusive perpetual license to use the Background technology in the Software developed for and delivered 
9  *  to Customer under this Agreement. However, the Customer shall make no commercial use of the Background technology without
10  *  Developer's written consent.
11  *
12  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
13  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
14  *
15  *  Away from Home with Ecobee Thermostat
16  *  Turn off the lights, turn on the security alarm, and lower the settings at ecobee when away from home
17  *
18  *  Software Distribution is restricted and shall be done only with Developer's written approval.
19  *
20  *  N.B. Requires MyEcobee device available at 
21  *          http://www.ecomatiqhomes.com/#!store/tc3yr 
22  */
23  
24 // Automatically generated. Make future change here.
25 definition(
26         name: "ecobeeAwayFromHome",
27         namespace: "yracine",
28         author: "Yves Racine",
29         description: "Away From Home",
30         category: "My Apps",
31         iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee.png",
32         iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee@2x.png"
33 )
34
35 preferences {
36         section("About") {
37                 paragraph "ecobeeAwayFromHome, the smartapp that sets your ecobee thermostat to 'Away' or to some specific settings when all presences leave your home"
38                 paragraph "Version 1.9.5" 
39                 paragraph "If you like this smartapp, please support the developer via PayPal and click on the Paypal link below " 
40                         href url: "https://www.paypal.me/ecomatiqhomes"
41                 paragraph "Copyright©2014 Yves Racine"
42                         href url:"http://github.com/yracine/device-type.myecobee", style:"embedded", required:false, title:"More information..."  
43                                 description: "http://github.com/yracine/device-type.myecobee/blob/master/README.md"
44         }
45         section("When all of these people leave home") {
46                 input "people", "capability.presenceSensor", multiple: true
47         }
48         section("And there is no motion at home on these sensors [optional]") {
49                 input "motions", "capability.motionSensor", title: "Where?", multiple: true, required: false
50         }
51         section("Turn off these lights") {
52                 input "switches", "capability.switch", title: "Switch", multiple: true, required: optional
53         }
54         section("And activate the alarm system [optional]") {
55                 input "alarmSwitch", "capability.contactSensor", title: "Alarm Switch", required: false
56         }
57         section("Set the ecobee thermostat(s)") {
58                 input "ecobee", "capability.thermostat", title: "Ecobee Thermostat(s)", multiple: true
59         }
60         section("Heating set Point for the thermostat [default = 60°F/14°C]") {
61                 input "givenHeatTemp", "decimal", title: "Heat Temp", required: false
62         }
63         section("Cooling set Point for the thermostat [default = 80°F/27°C]") {
64                 input "givenCoolTemp", "decimal", title: "Cool Temp", required: false
65         }
66         section("Or set the ecobee to this Climate Name (ex. Away)") {
67                 input "givenClimateName", "text", title: "Climate Name", required: false
68         }
69         section("Lock these locks [optional]") {
70                 input "locks", "capability.lock", title: "Locks?", required: false, multiple: true
71         }
72         section("Arm this(ese) camera(s) [optional]") {
73                 input "cameras", "capability.imageCapture", title: "Cameras", multiple: true, required: false
74         }
75         section("Trigger these actions when home has been quiet for [default=3 minutes]") {
76                 input "residentsQuietThreshold", "number", title: "Time in minutes", required: false
77         }
78         section("Notifications") {
79                 input "sendPushMessage", "enum", title: "Send a push notification?", metadata: [values: ["Yes", "No"]], required: false
80                 input "phone", "phone", title: "Send a Text Message?", required: false
81         }
82         section("Detailed Notifications") {
83                 input "detailedNotif", "bool", title: "Detailed Notifications?", required: false
84         }
85
86 }
87
88
89 def installed() {
90         log.debug "Installed with settings: ${settings}"
91         log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
92         initialize()
93
94 }
95
96
97
98 def updated() {
99         log.debug "Updated with settings: ${settings}"
100         log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
101         unsubscribe()
102         initialize()
103 }
104
105 private initialize() {
106         subscribe(people, "presence", presence)
107         subscribe(alarmSwitch, "contact", alarmSwitchContact)
108         if (motions != null && motions != "") {
109                 subscribe(motions, "motion", motionEvtHandler)
110         }
111         subscribe(app, appTouch)    
112 }
113
114
115 def alarmSwitchContact(evt) {
116         log.info "alarmSwitchContact, $evt.name: $evt.value"
117
118         if ((alarmSwitch.currentContact == "closed") && residentsHaveBeenQuiet() && everyoneIsAway()) {
119                 if (detailedNotif) {
120                         send("AwayFromHome>alarm system just armed, take actions")
121                 }
122                 log.debug "alarm is armed, nobody at home"
123                 takeActions()
124         }
125 }
126
127
128 def motionEvtHandler(evt) {
129         if (evt.value == "active") {
130                 state.lastIntroductionMotion = now()
131                 log.debug "Motion at home..."
132         }
133 }
134
135
136 private residentsHaveBeenQuiet() {
137
138         def threshold = residentsQuietThreshold ?: 3 // By default, the delay is 3 minutes
139         Integer delay = threshold * 60
140
141         def result = true
142         def t0 = new Date(now() - (threshold * 60 * 1000))
143         for (sensor in motions) {
144                 def recentStates = sensor.statesSince("motion", t0)
145                 if (recentStates.find {it.value == "active"}) {
146                         result = false
147                         break
148                 }
149         }
150         log.debug "residentsHaveBeenQuiet: $result"
151         return result
152 }
153
154
155 def presence(evt) {
156         def threshold = residentsQuietThreshold ?: 3 // By default, the delay is 3 minutes
157         Integer delay = threshold * 60
158
159         log.debug "$evt.name: $evt.value"
160         if (evt.value == "not present") {
161                 def person = getPerson(evt)
162                 if (detailedNotif) {
163                         send("AwayFromHome> ${person.displayName} not present at home")
164                 }
165                 log.debug "checking if everyone is away  and quiet at home"
166                 if (residentsHaveBeenQuiet()) {
167
168                         if (everyoneIsAway()) {
169                                 if (detailedNotif) {
170                                         send("AwayFromHome>Quiet at home...")
171                                 }
172                                 runIn(delay, "takeActions")
173                         } else {
174                                 log.debug "Not everyone is away, doing nothing"
175                                 if (detailedNotif) {
176                                         send("AwayFromHome>Not everyone is away, doing nothing..")
177                                 }
178                         }
179                 } else {
180
181                         log.debug "Things are not quiet at home, doing nothing"
182                         if (detailedNotif) {
183                                 send("AwayFromHome>Things are not quiet at home...")
184                         }
185                 }
186         } else {
187                 log.debug "Still present; doing nothing"
188         }
189 }
190
191 def appTouch(evt) {
192         log.debug ("ecobeeAwayFromHome>location.mode= $location.mode, givenClimate\7f=${givenClimateName}, about to takeAction")
193
194         takeActions() 
195 }
196
197
198 def takeActions() {
199         Integer thresholdMinutes = 2 // check that the security alarm is close in a 2-minute delay
200         Integer delay = 60 * thresholdMinutes
201         def msg, minHeatTemp, minCoolTemp
202
203         def scale = getTemperatureScale()
204         if (scale == 'C') {
205                 minHeatTemp = givenHeatTemp ?: 14 // by default, 14°C is the minimum heat temp
206                 minCoolTemp = givenCoolTemp ?: 27 // by default, 27°C is the minimum cool temp
207         } else {
208                 minHeatTemp = givenHeatTemp ?: 60 // by default, 60°F is the minimum heat temp
209                 minCoolTemp = givenCoolTemp ?: 80 // by default, 80°F is the minimum cool temp
210         }
211         //  Making sure everybody is away and no motion at home
212
213         if (everyoneIsAway() && residentsHaveBeenQuiet()) {
214                 send("AwayFromHome>Nobody is at home, and it's quiet, about to take actions")
215                 if (alarmSwitch?.currentContact == "open") {
216                         alarmSwitch.on() // arm the alarm system
217                         if (detailedNotif) {
218                                 log.debug "alarm is not set, arm it..."
219                                 send(msg)
220                         }
221                 }
222                 if ((givenClimateName != null) && (givenClimateName != "")) {
223                         ecobee.each {
224                                 it.setClimate('', givenClimateName) // Set to the climateName
225                         }                
226                 } else {
227
228                         // Set heating and cooling points at ecobee
229                         ecobee.each {
230                                 it.setHold('', minCoolTemp, minHeatTemp, null, null)
231                         }               
232                 }
233
234                 msg = "AwayFromHome>${ecobee} thermostats' settings are now lower"
235                 if (detailedNotif ) {
236                         log.info msg
237                         send(msg)
238                 }
239
240                 locks?.lock() // lock the locks                 
241                 msg = "AwayFromHome>Locked the locks"
242                 if ((locks) && (detailedNotif)) {
243                         log.info msg
244                         send(msg)
245                 }
246
247                 switches?.off() // turn off the lights          
248                 msg = "AwayFromHome>Switched off all switches"
249                 if ((switches) && (detailedNotif)) {
250                         log.info msg
251                         send(msg)
252                 }
253
254
255                 cameras?.alarmOn() // arm the cameras
256                 msg = "AwayFromHome>cameras are now armed"
257                 if ((cameras) && (detailedNotif)) {
258                         log.info msg
259                         send(msg)
260                 }
261                 if (alarmSwitch) {
262                         runIn(delay, "checkAlarmSystem", [overwrite: false]) // check that the alarm system is armed
263                 }
264         }
265
266
267 }
268
269 private checkAlarmSystem() {
270         if (alarmSwitch.currentContact == "open") {
271                 if (detailedNotif) {
272                         send("AwayFromHome>alarm still not activated,repeat...")
273                 }
274                 alarmSwitch.on() // try to arm the alarm system again
275         }
276
277
278 }
279
280 private everyoneIsAway() {
281         def result = true
282         for (person in people) {
283                 if (person.currentPresence == "present") {
284                         result = false
285                         break
286                 }
287         }
288         log.debug "everyoneIsAway: $result"
289         return result
290 }
291
292 private getPerson(evt) {
293         people.find {
294                 evt.deviceId == it.id
295         }
296 }
297
298 private send(msg) {
299         if (sendPushMessage != "No") {
300                 log.debug("sending push message")
301                 sendPush(msg)
302         }
303
304         if (phone) {
305                 log.debug("sending text message")
306                 sendSms(phone, msg)
307         }
308
309         log.debug msg
310 }