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