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
47 def activities = agent.latestValue('activities')
\r
49 if (!activities || !state.refreshCount)
\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
55 // Request activities every 5th attempt
\r
56 if((refreshCount % 5) == 0)
\r
58 agent.getAllActivities()
\r
61 dynamicPage(name: "control", title: "Updating list of Simple Control activities", nextPage: "", refreshInterval: refreshInterval, install: false, uninstall: true) {
\r
63 paragraph "Retrieving activities from Simple Sync"
\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
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
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
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
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
113 section([mobileOnly:true]) {
\r
114 label title: "Assign a name", required: false
\r
115 mode title: "Set for specific mode(s)"
\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
130 private ifUnset(Map options, String name, String capability) {
\r
131 if (!settings[name]) {
\r
132 input(options, name, capability)
\r
136 private ifSet(Map options, String name, String capability) {
\r
137 if (settings[name]) {
\r
138 input(options, name, capability)
\r
143 subscribeToEvents()
\r
149 subscribeToEvents()
\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
166 if (triggerModes) {
\r
167 subscribe(location, modeChangeHandler)
\r
171 schedule(timeOfDay, scheduledTimeHandler)
\r
175 def eventHandler(evt) {
\r
177 def lastTime = state[frequencyKey(evt)]
\r
178 if (oncePerDayOk(lastTime)) {
\r
180 if (lastTime == null || now() - lastTime >= frequency * 60000) {
\r
184 log.debug "Not taking action because $frequency minutes have not elapsed since last action"
\r
192 log.debug "Not taking action because it was already taken today"
\r
197 def modeChangeHandler(evt) {
\r
198 log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
\r
199 if (evt.value in triggerModes) {
\r
204 def scheduledTimeHandler() {
\r
208 def appTouchHandler(evt) {
\r
212 private startActivity(evt) {
\r
213 agent.startActivity(activity)
\r
216 state.lastActionTimeStamp = now()
\r
220 private frequencyKey(evt) {
\r
221 //evt.deviceId ?: evt.value
\r
222 "lastActionTimeStamp"
\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
231 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
\r
236 private oncePerDayOk(Long lastTime) {
\r
239 result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
\r
240 log.trace "oncePerDayOk = $result"
\r
245 // TODO - centralize somehow
\r
246 private getAllOk() {
\r
247 modeOk && daysOk && timeOk
\r
250 private getModeOk() {
\r
251 def result = !modes || modes.contains(location.mode)
\r
252 log.trace "modeOk = $result"
\r
256 private getDaysOk() {
\r
259 def df = new java.text.SimpleDateFormat("EEEE")
\r
260 if (location.timeZone) {
\r
261 df.setTimeZone(location.timeZone)
\r
264 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
\r
266 def day = df.format(new Date())
\r
267 result = days.contains(day)
\r
269 log.trace "daysOk = $result"
\r
273 private getTimeOk() {
\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
281 log.trace "timeOk = $result"
\r
285 private hhmm(time, fmt = "h:mm a")
\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
293 private timeIntervalLabel()
\r
295 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
\r