2 * Simple Sync Trigger
\r
4 * Copyright 2015 Roomie Remote, Inc.
\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
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
25 input "starting", "time", title: "Starting", required: false
\r
26 input "ending", "time", title: "Ending", required: false
\r
31 def agentSelection()
\r
35 state.refreshCount = 0
\r
38 dynamicPage(name: "agentSelection", title: "Select your Simple Sync", nextPage: "control", install: false, uninstall: true) {
\r
40 input "agent", "capability.mediaController", title: "Simple Sync", required: true, multiple: false
\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
60 def activities = agent.latestValue('activities')
\r
62 if (!activities || !state.refreshCount)
\r
64 int refreshCount = !state.refreshCount ? 0 : state.refreshCount as int
\r
65 state.refreshCount = refreshCount + 1
\r
66 def refreshInterval = refreshCount == 0 ? 2 : 4
\r
68 // Request activities every 5th attempt
\r
69 if((refreshCount % 5) == 0)
\r
71 agent.getAllActivities()
\r
74 dynamicPage(name: "control", title: "Updating list of Simple Control activities", nextPage: "", refreshInterval: refreshInterval, install: false, uninstall: true) {
\r
76 paragraph "Retrieving activities from Simple Sync"
\r
82 dynamicPage(name: "control", title: "Run a Simple Control activity when something happens", nextPage: "timeIntervalInput", install: false, uninstall: true) {
\r
83 def anythingSet = anythingSet()
\r
86 ifSet "motion", "capability.motionSensor", title: "Motion Detected", required: false, multiple: true
\r
87 ifSet "motionInactive", "capability.motionSensor", title: "Motion Stops", required: false, multiple: true
\r
88 ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
\r
89 ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
\r
90 ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
\r
91 ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
\r
92 ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
\r
93 ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
\r
94 ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
\r
95 ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
\r
96 ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
\r
97 ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
\r
100 section(anythingSet ? "Select additional triggers" : "When...", hideable: anythingSet, hidden: true){
\r
101 ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
\r
102 ifUnset "motionInactive", "capability.motionSensor", title: "Motion Stops", required: false, multiple: true
\r
103 ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
\r
104 ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
\r
105 ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
\r
106 ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
\r
107 ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
\r
108 ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
\r
109 ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
\r
110 ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
\r
111 ifUnset "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
\r
112 ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
\r
114 section("Run this activity"){
\r
115 input "activity", "enum", title: "Activity?", required: true, options: new groovy.json.JsonSlurper().parseText(activities ?: "[]").activities?.collect { ["${it.uuid}": it.name] }
\r
118 section("More options", hideable: true, hidden: true) {
\r
119 input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
\r
120 href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
\r
121 input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
\r
122 options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
\r
123 input "modes", "mode", title: "Only when mode is", multiple: true, required: false
\r
124 input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
\r
126 section([mobileOnly:true]) {
\r
127 label title: "Assign a name", required: false
\r
128 mode title: "Set for specific mode(s)"
\r
134 private anythingSet() {
\r
135 for (name in ["motion","motionInactive","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","button1","triggerModes","timeOfDay"]) {
\r
136 if (settings[name]) {
\r
143 private ifUnset(Map options, String name, String capability) {
\r
144 if (!settings[name]) {
\r
145 input(options, name, capability)
\r
149 private ifSet(Map options, String name, String capability) {
\r
150 if (settings[name]) {
\r
151 input(options, name, capability)
\r
156 subscribeToEvents()
\r
162 subscribeToEvents()
\r
165 def subscribeToEvents() {
\r
166 log.trace "subscribeToEvents()"
\r
167 subscribe(app, appTouchHandler)
\r
168 subscribe(contact, "contact.open", eventHandler)
\r
169 subscribe(contactClosed, "contact.closed", eventHandler)
\r
170 subscribe(acceleration, "acceleration.active", eventHandler)
\r
171 subscribe(motion, "motion.active", eventHandler)
\r
172 subscribe(motionInactive, "motion.inactive", eventHandler)
\r
173 subscribe(mySwitch, "switch.on", eventHandler)
\r
174 subscribe(mySwitchOff, "switch.off", eventHandler)
\r
175 subscribe(arrivalPresence, "presence.present", eventHandler)
\r
176 subscribe(departurePresence, "presence.not present", eventHandler)
\r
177 subscribe(button1, "button.pushed", eventHandler)
\r
179 if (triggerModes) {
\r
180 subscribe(location, modeChangeHandler)
\r
184 schedule(timeOfDay, scheduledTimeHandler)
\r
188 def eventHandler(evt) {
\r
190 def lastTime = state[frequencyKey(evt)]
\r
191 if (oncePerDayOk(lastTime)) {
\r
193 if (lastTime == null || now() - lastTime >= frequency * 60000) {
\r
197 log.debug "Not taking action because $frequency minutes have not elapsed since last action"
\r
205 log.debug "Not taking action because it was already taken today"
\r
210 def modeChangeHandler(evt) {
\r
211 log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
\r
212 if (evt.value in triggerModes) {
\r
217 def scheduledTimeHandler() {
\r
221 def appTouchHandler(evt) {
\r
225 private startActivity(evt) {
\r
226 agent.startActivity(activity)
\r
229 state.lastActionTimeStamp = now()
\r
233 private frequencyKey(evt) {
\r
234 //evt.deviceId ?: evt.value
\r
235 "lastActionTimeStamp"
\r
238 private dayString(Date date) {
\r
239 def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
\r
240 if (location.timeZone) {
\r
241 df.setTimeZone(location.timeZone)
\r
244 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
\r
249 private oncePerDayOk(Long lastTime) {
\r
252 result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
\r
253 log.trace "oncePerDayOk = $result"
\r
258 // TODO - centralize somehow
\r
259 private getAllOk() {
\r
260 modeOk && daysOk && timeOk
\r
263 private getModeOk() {
\r
264 def result = !modes || modes.contains(location.mode)
\r
265 log.trace "modeOk = $result"
\r
269 private getDaysOk() {
\r
272 def df = new java.text.SimpleDateFormat("EEEE")
\r
273 if (location.timeZone) {
\r
274 df.setTimeZone(location.timeZone)
\r
277 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
\r
279 def day = df.format(new Date())
\r
280 result = days.contains(day)
\r
282 log.trace "daysOk = $result"
\r
286 private getTimeOk() {
\r
288 if (starting && ending) {
\r
289 def currTime = now()
\r
290 def start = timeToday(starting).time
\r
291 def stop = timeToday(ending).time
\r
292 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
\r
294 log.trace "timeOk = $result"
\r
298 private hhmm(time, fmt = "h:mm a")
\r
300 def t = timeToday(time, location.timeZone)
\r
301 def f = new java.text.SimpleDateFormat(fmt)
\r
302 f.setTimeZone(location.timeZone ?: timeZone(time))
\r
306 private timeIntervalLabel()
\r
308 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
\r