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
63 //if (!activities || !state.refreshCount)
\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
69 // Request activities every 5th attempt
\r
70 // if((refreshCount % 5) == 0)
\r
72 //agent.getAllActivities()
\r
75 // dynamicPage(name: "control", title: "Updating list of Simple Control activities", nextPage: "", refreshInterval: refreshInterval, install: false, uninstall: true) {
\r
77 // paragraph "Retrieving activities from Simple Sync"
\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
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
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
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
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
127 section([mobileOnly:true]) {
\r
128 label title: "Assign a name", required: false
\r
129 mode title: "Set for specific mode(s)"
\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
144 private ifUnset(Map options, String name, String capability) {
\r
145 if (!settings[name]) {
\r
146 input(options, name, capability)
\r
150 private ifSet(Map options, String name, String capability) {
\r
151 if (settings[name]) {
\r
152 input(options, name, capability)
\r
157 subscribeToEvents()
\r
163 subscribeToEvents()
\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
180 if (triggerModes) {
\r
181 subscribe(location, modeChangeHandler)
\r
185 schedule(timeOfDay, scheduledTimeHandler)
\r
189 def eventHandler(evt) {
\r
191 def lastTime = state[frequencyKey(evt)]
\r
192 if (oncePerDayOk(lastTime)) {
\r
194 if (lastTime == null || now() - lastTime >= frequency * 60000) {
\r
198 log.debug "Not taking action because $frequency minutes have not elapsed since last action"
\r
206 log.debug "Not taking action because it was already taken today"
\r
211 def modeChangeHandler(evt) {
\r
212 log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
\r
213 if (evt.value in triggerModes) {
\r
218 def scheduledTimeHandler() {
\r
222 def appTouchHandler(evt) {
\r
226 private startActivity(evt) {
\r
227 //agent.startActivity(activity)
\r
230 state.lastActionTimeStamp = now()
\r
234 private frequencyKey(evt) {
\r
235 //evt.deviceId ?: evt.value
\r
236 "lastActionTimeStamp"
\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
245 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
\r
250 private oncePerDayOk(Long lastTime) {
\r
253 result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
\r
254 log.trace "oncePerDayOk = $result"
\r
259 // TODO - centralize somehow
\r
260 private getAllOk() {
\r
261 modeOk && daysOk && timeOk
\r
264 private getModeOk() {
\r
265 def result = !modes || modes.contains(location.mode)
\r
266 log.trace "modeOk = $result"
\r
270 private getDaysOk() {
\r
273 def df = new java.text.SimpleDateFormat("EEEE")
\r
274 if (location.timeZone) {
\r
275 df.setTimeZone(location.timeZone)
\r
278 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
\r
280 def day = df.format(new Date())
\r
281 result = days.contains(day)
\r
283 log.trace "daysOk = $result"
\r
287 private getTimeOk() {
\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
295 log.trace "timeOk = $result"
\r
299 private hhmm(time, fmt = "h:mm a")
\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
307 private timeIntervalLabel()
\r
309 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
\r