Commit #8: New version of extractor with running the preferences method make things...
[smartthings-infrastructure.git] / main.groovy
1 //Infrastructure for SmartThings Application
2 //Importing Libraries
3 import groovy.transform.Field
4
5 //Importing Classes
6 import ContactSensor.ContactSensor
7 import ContactSensor.ContactSensors
8 import DoorControl.DoorControl
9 import DoorControl.DoorControls
10 import Lock.Lock
11 import Lock.Locks
12 import Thermostat.Thermostat
13 import Thermostat.Thermostats
14 import Switch.Switch
15 import Switch.Switches
16 import PresenceSensor.PresenceSensor
17 import PresenceSensor.PresenceSensors
18 import Logger.Logger
19 import Location.LocationVar
20 import Location.Phrase
21 import appTouch.Touched
22 import NfcTouch.NfcTouch
23 import Event.Event
24 import Timer.SimulatedTimer
25
26 //Global eventHandler
27 /////////////////////////////////////////////////////////////////////
28 def eventHandler(LinkedHashMap eventDataMap) {
29         def value = eventDataMap["value"]
30         def name = eventDataMap["name"]
31         def deviceId = eventDataMap["deviceId"]
32         def descriptionText = eventDataMap["descriptionText"]
33         def displayed = eventDataMap["displayed"]
34         def linkText = eventDataMap["linkText"]
35         def isStateChange = eventDataMap["isStateChange"]
36         def unit = eventDataMap["unit"]
37         def data = eventDataMap["data"]
38
39         for (int i = 0;i < app2.eventList.size();i++) {
40                 if (app2.eventList[i] == name) {
41                         def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
42                         evt.add(event)
43                         app2.functionList[i](event)
44                 }
45         }
46
47         for (int i = 0;i < app1.eventList.size();i++) {
48                 if (app1.eventList[i] == name) {
49                         def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
50                         evt.add(event)
51                         app1.functionList[i](event)
52                 }
53         }
54 }
55
56 //GlobalVariables for both Apps
57 //Create a global variable for send event
58 @Field def sendEvent = {eventDataMap -> 
59                         eventHandler(eventDataMap)
60                         }
61 //Object for location
62 @Field def locationObject = new LocationVar()
63 //Object for touch to call function
64 @Field def appObject = new Touched(sendEvent, 0)
65 //Create a global list for events
66 @Field def evt = []
67 //Global Object for class Touch Sensor!
68 @Field def touchSensorObject = new NfcTouch(sendEvent, 1)
69 //Global Object for class switch!
70 @Field def switchObject = new Switches(sendEvent, 1)
71 //Global Object for class lock!
72 @Field def lockObject = new Locks(sendEvent, 1)
73 //Global Object for class door control!
74 @Field def doorControlObject = new DoorControls(sendEvent, 1)
75 //Global Object for class contact sensor!
76 @Field def contactObject = new ContactSensors(sendEvent, 1)
77 //Global Object for class presence sensor!
78 @Field def presenceSensorObject = new PresenceSensors(sendEvent, 1)
79 //Global Object for class thermostat!
80 @Field def thermostatObject = new Thermostats(sendEvent, 1)
81
82 //Application #1
83 class App1 {
84         def reference
85         def location
86         def app
87
88         //Extracted objects for App1
89         //Object for class Touch Sensor!
90         def tag
91         //Object for class switch!
92         def switch1
93         //Object for class lock!
94         def lock
95         //Object for class door control!
96         def garageDoor
97         //Global variable for enum!
98         def masterSwitch = "40"
99         //Global variable for enum!
100         def masterLock = "20"
101         //Global variable for enum!
102         def masterDoor = "40"
103
104         //Extracted objects for functions for App1
105         //Global Object for functions in subscribe method!
106         def pageTwo = this.&pageTwo
107         //Global Object for functions in subscribe method!
108         def installed = this.&installed
109         //Global Object for functions in subscribe method!
110         def updated = this.&updated
111         //Global Object for functions in subscribe method!
112         def initialize = this.&initialize
113         //Global Object for functions in subscribe method!
114         def currentStatus = this.&currentStatus
115         //Global Object for functions in subscribe method!
116         def touchHandler = this.&touchHandler
117
118         App1(Object obj) {
119                 reference = obj
120                 location = obj.locationObject
121                 app = obj.appObject
122                 tag = obj.touchSensorObject
123                 switch1 = obj.switchObject
124                 lock = obj.lockObject
125                 garageDoor = obj.doorControlObject
126         }
127         //Global variables for each app
128         //Settings variable defined to settings on purpose
129         def settings = "Settings"
130         //Global variable for state[mode]
131         def state = [home:[],away:[],night:[]]
132         //Create a global logger object for methods
133         def log = new Logger()
134         //Create a global variable for Functions in Subscribe method
135         def functionList = []
136         //Create a global variable for Objects in Subscribe method
137         def objectList = []
138         //Create a global variable for Events in Subscribe method
139         def eventList = []
140         //Create a global list for function schedulers
141         def timersFuncList = []
142         //Create a global list for timer schedulers
143         def timersList = []
144
145         //Methods
146         /////////////////////////////////////////////////////////////////////
147         def setLocationMode(String mode) {
148                 location.mode = mode
149         }
150         
151         /////////////////////////////////////////////////////////////////////
152         ////subscribe(obj, func)
153         def subscribe(Object obj, Closure FunctionToCall) {
154                 if (obj == app) {
155                         objectList.add(obj)
156                         eventList.add("Touched")
157                         functionList.add(FunctionToCall)
158                 } else if (obj == location) {
159                         objectList.add(obj)
160                         eventList.add("Location")
161                         functionList.add(FunctionToCall)
162                 }
163         }
164         ////subscribe(obj, event, func)
165         def subscribe(Object obj, String event, Closure FunctionToCall) {
166                 objectList.add(obj)
167                 eventList.add(event)
168                 functionList.add(FunctionToCall)
169         }
170         ////subscribe(obj, event, func, data)
171         def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
172                 objectList.add(obj)     
173                 eventList.add(event)
174                 functionList.add(FunctionToCall)
175         }
176         /////////////////////////////////////////////////////////////////////
177         ////runIn(time, func)
178         def runIn(int seconds, Closure functionToCall) {
179                 if (timersFuncList.contains(functionToCall)) {
180                         timersList[timersFuncList.indexOf(functionToCall)].cancel()
181                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds, functionToCall)
182                 } else {
183                         timersFuncList.add(functionToCall)
184                         timersList.add(new SimulatedTimer())
185                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds, functionToCall)
186                 }
187         }
188         /////////////////////////////////////////////////////////////////////
189         ////unschedule(func)
190         def unschedule(Closure functionToUnschedule) {
191                 for (int i = 0;i < timersFuncList.size();i++) {
192                         if (timersFuncList[i] == functionToUnschedule) {
193                                 if (timersList != null)
194                                         timersList[i].cancel()
195                         }
196                 }
197         }
198         
199         
200         def unschedule() {
201                 for (int i = 0;i < timersFuncList.size();i++) {
202                         if (timersList != null)
203                                 timersList[i].cancel()
204                 }
205         }
206         /////////////////////////////////////////////////////////////////////
207         ////sendNotificationToContacts(text, recipients)
208         def sendNotificationToContacts(String text, String recipients) {
209                 for (int i = 0;i < recipients.size();i++) {
210                         for (int j = 0;j < location.contacts.size();j++) {
211                                 if (recipients[i] == location.contacts[j]) {
212                                         println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
213                                 }
214                         }
215                 }
216         }
217         /////////////////////////////////////////////////////////////////////
218         ////sendSms(phone, text)
219         def sendSms(long phoneNumber, String text) {
220                 println("Sending \""+text+"\" to "+phoneNumber.toString())
221         }
222         /////////////////////////////////////////////////////////////////////
223         ////sendPush(text)
224         def sendPush(String text) {
225                 println(text)
226         }
227         /////////////////////////////////////////////////////////////////////
228         ////schedule(time, nameOfFunction as String)
229         def schedule(String time, String nameOfFunction) {
230                 def _inputTime = time.split(':')
231                 Date date = new Date()  
232                 def _currentTime = date.format("HH:mm:ss").split(':')
233         
234                 //Convert input time and current time to minutes
235                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
236                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
237                 def delay
238         
239                 if (inputTime < currentTime) {
240                         delay = 24*60*60-inputTime+currentTime
241                 } else {
242                         delay = inputTime-currentTime
243                 }
244         
245                 timersFuncList.add(nameOfFunction)
246                 timersList.add(new SimulatedTimer())
247                 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000) {
248                         "$nameOfFunction"()
249                 }
250         }
251         ////schedule(time, nameOfFunction as Closure)
252         def schedule(String time, Closure nameOfFunction) {
253                 def _inputTime = time.split(':')
254                 Date date = new Date()  
255                 def _currentTime = date.format("HH:mm:ss").split(':')
256         
257                 //Convert input time and current time to minutes
258                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
259                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
260                 def delay
261         
262                 if (inputTime < currentTime) {
263                         delay = 24*60*60-inputTime+currentTime
264                 } else {
265                         delay = inputTime-currentTime
266                 }
267         
268                 if (timersFuncList.contains(nameOfFunction)) {
269                         timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
270                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds, nameOfFunction)
271                 } else {
272                         timersFuncList.add(nameOfFunction)
273                         timersList.add(new SimulatedTimer())
274                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds, nameOfFunction)
275                 }
276         }
277
278         def pageTwo() {
279                 dynamicPage(name: "pageTwo") {
280                 section("If set, the state of these devices will be toggled each time the tag is touched, " + 
281                         "e.g. a light that's on will be turned off and one that's off will be turned on, " +
282                         "other devices of the same type will be set to the same state as their master device. " +
283                         "If no master is designated then the majority of devices of the same type will be used " +
284                         "to determine whether to turn on or off the devices.") {
285                     
286                     if (switch1 || masterSwitch) {
287                         input "masterSwitch", "enum", title: "Master switch", options: switch1.collect{[(it.id): it.displayName]}, required: false
288                     }
289                     if (lock || masterLock) {
290                         input "masterLock", "enum", title: "Master lock", options: lock.collect{[(it.id): it.displayName]}, required: false
291                     }
292                     if (garageDoor || masterDoor) {
293                         input "masterDoor", "enum", title: "Master door", options: garageDoor.collect{[(it.id): it.displayName]}, required: false
294                     }            
295                         }
296                         section([mobileOnly:true]) {
297                                 label title: "Assign a name", required: false
298                                 mode title: "Set for specific mode(s)", required: false
299                         }        
300             }
301         }
302         
303         def installed() {
304                 log.debug "Installed with settings: ${settings}"
305         
306                 initialize()
307         }
308         
309         def updated() {
310                 log.debug "Updated with settings: ${settings}"
311         
312                 unsubscribe()
313                 initialize()
314         }
315         
316         def initialize() {
317                 subscribe tag, "nfcTouch", touchHandler
318             subscribe app, touchHandler
319         }
320         
321         private currentStatus(devices, master, attribute) {
322                 log.trace "currentStatus($devices, $master, $attribute)"
323                 def result = null
324                 if (master) {
325                 result = devices.find{it.id == master}?.currentValue(attribute)
326             }
327             else {
328                 def map = [:]
329                 devices.each {
330                         def value = it.currentValue(attribute)
331                     map[value] = (map[value] ?: 0) + 1
332                     log.trace "$it.displayName: $value"
333                 }
334                 log.trace map
335                 result = map.collect{it}.sort{it.value}[-1].key
336             }
337             log.debug "$attribute = $result"
338             result
339         }
340         
341         def touchHandler(evt) {
342                 log.trace "touchHandler($evt.descriptionText)"
343             if (switch1) {
344                 def status = currentStatus(switch1, masterSwitch, "switch")
345                 switch1.each {
346                     if (status == "on") {
347                         it.off()
348                     }
349                     else {
350                         it.on()
351                     }
352                 }
353             }
354             
355             if (lock) {
356                 def status = currentStatus(lock, masterLock, "lock")
357                 lock.each {
358                     if (status == "locked") {
359                         lock.unlock()
360                     }
361                     else {
362                         lock.lock()
363                     }
364                 }
365             }
366             
367             if (garageDoor) {
368                 def status = currentStatus(garageDoor, masterDoor, "status")
369                 garageDoor.each {
370                         if (status == "open") {
371                         it.close()
372                     }
373                     else {
374                         it.open()
375                     }
376                 }
377             }
378         }
379 }
380
381
382 //Application #2
383 class App2 {
384         def reference
385         def location
386         def app
387
388         //Extracted objects for App2
389         //Object for class switch!
390         def switchesoff
391         //Object for class switch!
392         def switcheson
393         //Object for class lock!
394         def lock1
395         //Global variable for mode!
396         def newMode = "home"
397         //Global variable for number!
398         def waitfor = 10
399
400         //Extracted objects for functions for App2
401         //Global Object for functions in subscribe method!
402         def installed = this.&installed
403         //Global Object for functions in subscribe method!
404         def updated = this.&updated
405         //Global Object for functions in subscribe method!
406         def appTouch = this.&appTouch
407
408         App2(Object obj) {
409                 reference = obj
410                 location = obj.locationObject
411                 app = obj.appObject
412                 switchesoff = obj.switchObject
413                 switcheson = obj.switchObject
414                 lock1 = obj.lockObject
415         }
416         //Global variables for each app
417         //Settings variable defined to settings on purpose
418         def settings = "Settings"
419         //Global variable for state[mode]
420         def state = [home:[],away:[],night:[]]
421         //Create a global logger object for methods
422         def log = new Logger()
423         //Create a global variable for Functions in Subscribe method
424         def functionList = []
425         //Create a global variable for Objects in Subscribe method
426         def objectList = []
427         //Create a global variable for Events in Subscribe method
428         def eventList = []
429         //Create a global list for function schedulers
430         def timersFuncList = []
431         //Create a global list for timer schedulers
432         def timersList = []
433
434         //Methods
435         /////////////////////////////////////////////////////////////////////
436         def setLocationMode(String mode) {
437                 location.mode = mode
438         }
439         
440         /////////////////////////////////////////////////////////////////////
441         ////subscribe(obj, func)
442         def subscribe(Object obj, Closure FunctionToCall) {
443                 if (obj == app) {
444                         objectList.add(obj)
445                         eventList.add("Touched")
446                         functionList.add(FunctionToCall)
447                 } else if (obj == location) {
448                         objectList.add(obj)
449                         eventList.add("Location")
450                         functionList.add(FunctionToCall)
451                 }
452         }
453         ////subscribe(obj, event, func)
454         def subscribe(Object obj, String event, Closure FunctionToCall) {
455                 objectList.add(obj)
456                 eventList.add(event)
457                 functionList.add(FunctionToCall)
458         }
459         ////subscribe(obj, event, func, data)
460         def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
461                 objectList.add(obj)     
462                 eventList.add(event)
463                 functionList.add(FunctionToCall)
464         }
465         /////////////////////////////////////////////////////////////////////
466         ////runIn(time, func)
467         def runIn(int seconds, Closure functionToCall) {
468                 if (timersFuncList.contains(functionToCall)) {
469                         timersList[timersFuncList.indexOf(functionToCall)].cancel()
470                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds, functionToCall)
471                 } else {
472                         timersFuncList.add(functionToCall)
473                         timersList.add(new SimulatedTimer())
474                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds, functionToCall)
475                 }
476         }
477         /////////////////////////////////////////////////////////////////////
478         ////unschedule(func)
479         def unschedule(Closure functionToUnschedule) {
480                 for (int i = 0;i < timersFuncList.size();i++) {
481                         if (timersFuncList[i] == functionToUnschedule) {
482                                 if (timersList != null)
483                                         timersList[i].cancel()
484                         }
485                 }
486         }
487         
488         
489         def unschedule() {
490                 for (int i = 0;i < timersFuncList.size();i++) {
491                         if (timersList != null)
492                                 timersList[i].cancel()
493                 }
494         }
495         /////////////////////////////////////////////////////////////////////
496         ////sendNotificationToContacts(text, recipients)
497         def sendNotificationToContacts(String text, String recipients) {
498                 for (int i = 0;i < recipients.size();i++) {
499                         for (int j = 0;j < location.contacts.size();j++) {
500                                 if (recipients[i] == location.contacts[j]) {
501                                         println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
502                                 }
503                         }
504                 }
505         }
506         /////////////////////////////////////////////////////////////////////
507         ////sendSms(phone, text)
508         def sendSms(long phoneNumber, String text) {
509                 println("Sending \""+text+"\" to "+phoneNumber.toString())
510         }
511         /////////////////////////////////////////////////////////////////////
512         ////schedule(time, nameOfFunction as String)
513         def schedule(String time, String nameOfFunction) {
514                 def _inputTime = time.split(':')
515                 Date date = new Date()  
516                 def _currentTime = date.format("HH:mm:ss").split(':')
517         
518                 //Convert input time and current time to minutes
519                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
520                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
521                 def delay
522         
523                 if (inputTime < currentTime) {
524                         delay = 24*60*60-inputTime+currentTime
525                 } else {
526                         delay = inputTime-currentTime
527                 }
528         
529                 timersFuncList.add(nameOfFunction)
530                 timersList.add(new SimulatedTimer())
531                 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000) {
532                         "$nameOfFunction"()
533                 }
534         }
535         ////schedule(time, nameOfFunction as Closure)
536         def schedule(String time, Closure nameOfFunction) {
537                 def _inputTime = time.split(':')
538                 Date date = new Date()  
539                 def _currentTime = date.format("HH:mm:ss").split(':')
540         
541                 //Convert input time and current time to minutes
542                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
543                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
544                 def delay
545         
546                 if (inputTime < currentTime) {
547                         delay = 24*60*60-inputTime+currentTime
548                 } else {
549                         delay = inputTime-currentTime
550                 }
551         
552                 if (timersFuncList.contains(nameOfFunction)) {
553                         timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
554                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds, nameOfFunction)
555                 } else {
556                         timersFuncList.add(nameOfFunction)
557                         timersList.add(new SimulatedTimer())
558                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds, nameOfFunction)
559                 }
560         }
561
562         def installed()
563         {
564                 log.debug "Installed with settings: ${settings}"
565                 log.debug "Current mode = ${location.mode}"
566                 subscribe(app, appTouch)
567         }
568         
569         
570         def updated()
571         {
572                 log.debug "Updated with settings: ${settings}"
573                 log.debug "Current mode = ${location.mode}"
574                 unsubscribe()
575                 subscribe(app, appTouch)
576         }
577         
578         def appTouch(evt) {
579                 log.debug "changeMode, location.mode = $location.mode, newMode = $newMode, location.modes = $location.modes"
580             if (location.mode != newMode) {
581                                 setLocationMode(newMode)
582                                 log.debug "Changed the mode to '${newMode}'"
583             }   else {
584                 log.debug "New mode is the same as the old mode, leaving it be"
585                 }
586             log.debug "appTouch: $evt"
587             lock1.lock()
588             switcheson.on()
589             def delay = (waitfor != null && waitfor != "") ? waitfor * 1000 : 120000
590                 switchesoff.off(delay: delay)
591         }
592 }
593
594 @Field def app1 = new App1(this)
595 @Field def app2 = new App2(this)
596 app1.installed()
597 app2.installed()
598
599 def events = [1,2,3,4,5,6,7]
600 def list = events.permutations()
601 int count = Verify.getInt(0,list.size()-1)
602 println "COUNT: " + count
603
604 list[count].each {
605   switch(it) {
606     case 1:
607       appObject.setValue([name: "Touched", value: "Touched", deviceId: 0, descriptionText: "",
608                                            displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
609       println "1"
610       break
611     case 2:
612       lockObject.setValue([name: "lock0", value: "locked", deviceId: 0, descriptionText: "",
613                                            displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
614       println "   2"
615                         break
616     case 3:
617       lockObject.setValue([name: "lock0", value: "unlocked", deviceId: 0, descriptionText: "",
618                                            displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
619       println "      3"
620       break
621     case 4:
622       contactObject.setValue([name: "contact0", value: "open", deviceId: 0, descriptionText: "",
623                                            displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
624       println "         4"
625       break
626     case 5:
627       contactObject.setValue([name: "contact0", value: "closed", deviceId: 0, descriptionText: "",
628                                            displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
629       println "            5"
630       break
631     case 6:
632       switchObject.setValue([name: "switch0", value: "on", deviceId: 0, descriptionText: "",
633                                            displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
634       println "               6"
635       break
636     case 7:
637       switchObject.setValue([name: "switch0", value: "off", deviceId: 0, descriptionText: "",
638                                            displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
639       println "                   7"
640     default:
641       break
642   }
643 }