Update Light_Rule.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                         send("AwayFromHome> person not present at home")
165                 }
166                 log.debug "checking if everyone is away  and quiet at home"
167                 if (residentsHaveBeenQuiet()) {
168
169                         if (everyoneIsAway()) {
170                                 if (detailedNotif) {
171                                         send("AwayFromHome>Quiet at home...")
172                                 }
173                                 runIn(delay, "takeActions")
174                         } else {
175                                 log.debug "Not everyone is away, doing nothing"
176                                 if (detailedNotif) {
177                                         send("AwayFromHome>Not everyone is away, doing nothing..")
178                                 }
179                         }
180                 } else {
181
182                         log.debug "Things are not quiet at home, doing nothing"
183                         if (detailedNotif) {
184                                 send("AwayFromHome>Things are not quiet at home...")
185                         }
186                 }
187         } else {
188                 log.debug "Still present; doing nothing"
189         }
190 }
191
192 def appTouch(evt) {
193         log.debug ("ecobeeAwayFromHome>location.mode= $location.mode, givenClimate\7f=${givenClimateName}, about to takeAction")
194
195         takeActions() 
196 }
197
198
199 def takeActions() {
200         Integer thresholdMinutes = 2 // check that the security alarm is close in a 2-minute delay
201         Integer delay = 60 * thresholdMinutes
202         def msg, minHeatTemp, minCoolTemp
203
204         def scale = getTemperatureScale()
205         if (scale == 'C') {
206                 minHeatTemp = givenHeatTemp ?: 14 // by default, 14°C is the minimum heat temp
207                 minCoolTemp = givenCoolTemp ?: 27 // by default, 27°C is the minimum cool temp
208         } else {
209                 minHeatTemp = givenHeatTemp ?: 60 // by default, 60°F is the minimum heat temp
210                 minCoolTemp = givenCoolTemp ?: 80 // by default, 80°F is the minimum cool temp
211         }
212         //  Making sure everybody is away and no motion at home
213
214         if (everyoneIsAway() && residentsHaveBeenQuiet()) {
215                 send("AwayFromHome>Nobody is at home, and it's quiet, about to take actions")
216                 if (alarmSwitch?.currentContact == "open") {
217                         alarmSwitch.on() // arm the alarm system
218                         if (detailedNotif) {
219                                 log.debug "alarm is not set, arm it..."
220                                 send(msg)
221                         }
222                 }
223                 if ((givenClimateName != null) && (givenClimateName != "")) {
224                         ecobee.each {
225                                 it.setClimate('', givenClimateName) // Set to the climateName
226                         }                
227                 } else {
228
229                         // Set heating and cooling points at ecobee
230                         ecobee.each {
231                                 it.setHold('', minCoolTemp, minHeatTemp, null, null)
232                         }               
233                 }
234
235                 msg = "AwayFromHome>${ecobee} thermostats' settings are now lower"
236                 if (detailedNotif ) {
237                         log.info msg
238                         send(msg)
239                 }
240
241                 locks?.lock() // lock the locks                 
242                 msg = "AwayFromHome>Locked the locks"
243                 if ((locks) && (detailedNotif)) {
244                         log.info msg
245                         send(msg)
246                 }
247
248                 switches?.off() // turn off the lights          
249                 msg = "AwayFromHome>Switched off all switches"
250                 if ((switches) && (detailedNotif)) {
251                         log.info msg
252                         send(msg)
253                 }
254
255
256                 cameras?.alarmOn() // arm the cameras
257                 msg = "AwayFromHome>cameras are now armed"
258                 if ((cameras) && (detailedNotif)) {
259                         log.info msg
260                         send(msg)
261                 }
262                 if (alarmSwitch) {
263                         runIn(delay, "checkAlarmSystem", [overwrite: false]) // check that the alarm system is armed
264                 }
265         }
266
267
268 }
269
270 private checkAlarmSystem() {
271         if (alarmSwitch.currentContact == "open") {
272                 if (detailedNotif) {
273                         send("AwayFromHome>alarm still not activated,repeat...")
274                 }
275                 alarmSwitch.on() // try to arm the alarm system again
276         }
277
278
279 }
280
281 private everyoneIsAway() {
282         def result = true
283         for (person in people) {
284                 if (person.currentPresence == "present") {
285                         result = false
286                         break
287                 }
288         }
289         log.debug "everyoneIsAway: $result"
290         return result
291 }
292
293 private getPerson(evt) {
294         people.find {
295                 evt.deviceId == it.id
296         }
297 }
298
299 private send(msg) {
300         if (sendPushMessage != "No") {
301                 log.debug("sending push message")
302                 sendPush(msg)
303         }
304
305         if (phone) {
306                 log.debug("sending text message")
307                 sendSms(phone, msg)
308         }
309
310         log.debug msg
311 }