Update gentle-wake-up.groovy
[smartapps.git] / official / simple-sync-trigger.groovy
1 /**\r
2  *  Simple Sync Trigger\r
3  *\r
4  *  Copyright 2015 Roomie Remote, Inc.\r
5  *\r
6  *      Date: 2015-09-22\r
7  */\r
8 definition(\r
9     name: "Simple Sync Trigger",\r
10     namespace: "roomieremote-ratrigger",\r
11     author: "Roomie Remote, Inc.",\r
12     description: "Trigger Simple Control activities when certain actions take place in your home.",\r
13     category: "My Apps",\r
14     iconUrl: "https://s3.amazonaws.com/roomieuser/remotes/simplesync-60.png",\r
15     iconX2Url: "https://s3.amazonaws.com/roomieuser/remotes/simplesync-120.png",\r
16     iconX3Url: "https://s3.amazonaws.com/roomieuser/remotes/simplesync-120.png")\r
17 \r
18 \r
19 preferences {\r
20         page(name: "agentSelection", title: "Select your Simple Sync")\r
21         //page(name: "refreshActivities", title: "Updating list of Simple Sync activities")\r
22     page(name: "control", title: "Run a Simple Control activity when something happens")\r
23         page(name: "timeIntervalInput", title: "Only during a certain time", install: true, uninstall: true) {\r
24                 section {\r
25                         input "starting", "time", title: "Starting", required: false\r
26                         input "ending", "time", title: "Ending", required: false\r
27                 }\r
28         }\r
29 }\r
30 \r
31 def agentSelection()\r
32 {\r
33         //if (agent)\r
34     //{\r
35         //      state.refreshCount = 0\r
36     //}\r
37     \r
38         dynamicPage(name: "agentSelection", title: "Select your Simple Sync", nextPage: "control", install: false, uninstall: true) {\r
39                 section {\r
40                         input "agent", "capability.mediaController", title: "Simple Sync", required: true, multiple: false\r
41                 }\r
42         }\r
43 }\r
44 \r
45 // input "motion", "capability.motionSensor", title: "Motion Detected", required: false, multiple: true\r
46 // input "motionInactive", "capability.motionSensor", title: "Motion Stops", required: false, multiple: true\r
47 // input "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true\r
48 // input "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true\r
49 // input "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true\r
50 // input "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true\r
51 // input "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true\r
52 // input "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true\r
53 // input "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true\r
54 // input "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production\r
55 // input "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true\r
56 // input "timeOfDay", "time", title: "At a Scheduled Time", required: false\r
57 \r
58 def control()\r
59 {\r
60         //def activities = agent.latestValue('activities')\r
61         //def activities\r
62     \r
63     //if (!activities || !state.refreshCount)\r
64     //{\r
65         //    int refreshCount = !state.refreshCount ? 0 : state.refreshCount as int\r
66         //    state.refreshCount = refreshCount + 1\r
67         //    def refreshInterval = refreshCount == 0 ? 2 : 4\r
68                 \r
69             // Request activities every 5th attempt\r
70     //  if((refreshCount % 5) == 0)\r
71     //    {\r
72             //agent.getAllActivities()\r
73         //      }\r
74         \r
75     //    dynamicPage(name: "control", title: "Updating list of Simple Control activities", nextPage: "", refreshInterval: refreshInterval, install: false, uninstall: true) {\r
76     //          section("") {\r
77     //          paragraph "Retrieving activities from Simple Sync"\r
78     //        }\r
79     //    }\r
80         //}\r
81         //else\r
82     //{\r
83         dynamicPage(name: "control", title: "Run a Simple Control activity when something happens", nextPage: "timeIntervalInput", install: false, uninstall: true) {\r
84                 def anythingSet = anythingSet()\r
85                 if (anythingSet) {\r
86                         section("When..."){\r
87                                 ifSet "motion", "capability.motionSensor", title: "Motion Detected", required: false, multiple: true\r
88                                 ifSet "motionInactive", "capability.motionSensor", title: "Motion Stops", required: false, multiple: true\r
89                                 ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true\r
90                                 ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true\r
91                                 ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true\r
92                                 ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true\r
93                                 ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true\r
94                                 ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true\r
95                                 ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true\r
96                                 ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production\r
97                                 ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true\r
98                                 ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false\r
99                         }\r
100                 }\r
101                 section(anythingSet ? "Select additional triggers" : "When...", hideable: anythingSet, hidden: true){\r
102                         ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true\r
103                         ifUnset "motionInactive", "capability.motionSensor", title: "Motion Stops", required: false, multiple: true\r
104                         ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true\r
105                         ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true\r
106                         ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true\r
107                         ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true\r
108                         ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true\r
109                         ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true\r
110                         ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true\r
111                         ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production\r
112                         ifUnset "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true\r
113                         ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false\r
114                 }\r
115                 //section("Run this activity"){\r
116             //    input "activity", "enum", title: "Activity?", required: true, options: new groovy.json.JsonSlurper().parseText(activities ?: "[]").activities?.collect { ["${it.uuid}": it.name] }\r
117                 //}\r
118         \r
119                 section("More options", hideable: true, hidden: true) {\r
120                         input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false\r
121                         //href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"\r
122                         input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,\r
123                                 options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]\r
124                         input "modes", "mode", title: "Only when mode is", multiple: true, required: false\r
125                         input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false\r
126                 }\r
127                 section([mobileOnly:true]) {\r
128                         label title: "Assign a name", required: false\r
129                         mode title: "Set for specific mode(s)"\r
130                 }\r
131         }\r
132     //}\r
133 }\r
134 \r
135 private anythingSet() {\r
136         for (name in ["motion","motionInactive","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","button1","triggerModes","timeOfDay"]) {\r
137                 if (settings[name]) {\r
138                         return true\r
139                 }\r
140         }\r
141         return false\r
142 }\r
143 \r
144 private ifUnset(Map options, String name, String capability) {\r
145         if (!settings[name]) {\r
146                 input(options, name, capability)\r
147         }\r
148 }\r
149 \r
150 private ifSet(Map options, String name, String capability) {\r
151         if (settings[name]) {\r
152                 input(options, name, capability)\r
153         }\r
154 }\r
155 \r
156 def installed() {\r
157         subscribeToEvents()\r
158 }\r
159 \r
160 def updated() {\r
161         unsubscribe()\r
162         unschedule()\r
163         subscribeToEvents()\r
164 }\r
165 \r
166 def subscribeToEvents() {\r
167         log.trace "subscribeToEvents()"\r
168         subscribe(app, appTouchHandler)\r
169         subscribe(contact, "contact.open", eventHandler)\r
170         subscribe(contactClosed, "contact.closed", eventHandler)\r
171         subscribe(acceleration, "acceleration.active", eventHandler)\r
172         subscribe(motion, "motion.active", eventHandler)\r
173         subscribe(motionInactive, "motion.inactive", eventHandler)\r
174         subscribe(mySwitch, "switch.on", eventHandler)\r
175         subscribe(mySwitchOff, "switch.off", eventHandler)\r
176         subscribe(arrivalPresence, "presence.present", eventHandler)\r
177         subscribe(departurePresence, "presence.not present", eventHandler)\r
178         subscribe(button1, "button.pushed", eventHandler)\r
179 \r
180         if (triggerModes) {\r
181                 subscribe(location, modeChangeHandler)\r
182         }\r
183 \r
184         if (timeOfDay) {\r
185                 schedule(timeOfDay, scheduledTimeHandler)\r
186         }\r
187 }\r
188 \r
189 def eventHandler(evt) {\r
190         if (allOk) {\r
191                 def lastTime = state[frequencyKey(evt)]\r
192                 if (oncePerDayOk(lastTime)) {\r
193                         if (frequency) {\r
194                                 if (lastTime == null || now() - lastTime >= frequency * 60000) {\r
195                                         startActivity(evt)\r
196                                 }\r
197                                 else {\r
198                                         log.debug "Not taking action because $frequency minutes have not elapsed since last action"\r
199                                 }\r
200                         }\r
201                         else {\r
202                                 startActivity(evt)\r
203                         }\r
204                 }\r
205                 else {\r
206                         log.debug "Not taking action because it was already taken today"\r
207                 }\r
208         }\r
209 }\r
210 \r
211 def modeChangeHandler(evt) {\r
212         log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"\r
213         if (evt.value in triggerModes) {\r
214                 eventHandler(evt)\r
215         }\r
216 }\r
217 \r
218 def scheduledTimeHandler() {\r
219         eventHandler(null)\r
220 }\r
221 \r
222 def appTouchHandler(evt) {\r
223         startActivity(evt)\r
224 }\r
225 \r
226 private startActivity(evt) {\r
227         //agent.startActivity(activity)\r
228 \r
229         if (frequency) {\r
230                 state.lastActionTimeStamp = now()\r
231         }\r
232 }\r
233 \r
234 private frequencyKey(evt) {\r
235         //evt.deviceId ?: evt.value\r
236         "lastActionTimeStamp"\r
237 }\r
238 \r
239 private dayString(Date date) {\r
240         def df = new java.text.SimpleDateFormat("yyyy-MM-dd")\r
241         if (location.timeZone) {\r
242                 df.setTimeZone(location.timeZone)\r
243         }\r
244         else {\r
245                 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))\r
246         }\r
247         df.format(date)\r
248 }\r
249 \r
250 private oncePerDayOk(Long lastTime) {\r
251         def result = true\r
252         if (oncePerDay) {\r
253                 result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true\r
254                 log.trace "oncePerDayOk = $result"\r
255         }\r
256         result\r
257 }\r
258 \r
259 // TODO - centralize somehow\r
260 private getAllOk() {\r
261         modeOk && daysOk && timeOk\r
262 }\r
263 \r
264 private getModeOk() {\r
265         def result = !modes || modes.contains(location.mode)\r
266         log.trace "modeOk = $result"\r
267         result\r
268 }\r
269 \r
270 private getDaysOk() {\r
271         def result = true\r
272         if (days) {\r
273                 def df = new java.text.SimpleDateFormat("EEEE")\r
274                 if (location.timeZone) {\r
275                         df.setTimeZone(location.timeZone)\r
276                 }\r
277                 else {\r
278                         df.setTimeZone(TimeZone.getTimeZone("America/New_York"))\r
279                 }\r
280                 def day = df.format(new Date())\r
281                 result = days.contains(day)\r
282         }\r
283         log.trace "daysOk = $result"\r
284         result\r
285 }\r
286 \r
287 private getTimeOk() {\r
288         def result = true\r
289         if (starting && ending) {\r
290                 def currTime = now()\r
291                 def start = timeToday(starting).time\r
292                 def stop = timeToday(ending).time\r
293                 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start\r
294         }\r
295         log.trace "timeOk = $result"\r
296         result\r
297 }\r
298 \r
299 private hhmm(time, fmt = "h:mm a")\r
300 {\r
301         def t = timeToday(time, location.timeZone)\r
302         def f = new java.text.SimpleDateFormat(fmt)\r
303         f.setTimeZone(location.timeZone ?: timeZone(time))\r
304         f.format(t)\r
305 }\r
306 \r
307 private timeIntervalLabel()\r
308 {\r
309         (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""\r
310 }\r