1 //Infrastructure for SmartThings Application
3 import groovy.transform.Field
6 import ContactSensor.ContactSensor
7 import ContactSensor.ContactSensors
8 import DoorControl.DoorControl
9 import DoorControl.DoorControls
12 import Thermostat.Thermostat
13 import Thermostat.Thermostats
15 import Switch.Switches
16 import PresenceSensor.PresenceSensor
17 import PresenceSensor.PresenceSensors
19 import Location.LocationVar
20 import Location.Phrase
21 import appTouch.Touched
22 import NfcTouch.NfcTouch
23 import AeonKeyFob.AeonKeyFob
24 import AeonKeyFob.AeonKeyFobs
25 import MusicPlayer.MusicPlayer
26 import MusicPlayer.MusicPlayers
27 import MotionSensor.MotionSensor
28 import MotionSensor.MotionSensors
29 import ImageCapture.ImageCapture
30 import ImageCapture.ImageCaptures
31 import SmokeDetector.SmokeDetector
32 import SmokeDetector.SmokeDetectors
35 import SpeechSynthesis.SpeechSynthesis
36 import SpeechSynthesis.SpeechSynthesises
38 import Timer.SimulatedTimer
41 /////////////////////////////////////////////////////////////////////
42 def eventHandler(LinkedHashMap eventDataMap) {
43 def value = eventDataMap["value"]
44 def name = eventDataMap["name"]
45 def deviceId = eventDataMap["deviceId"]
46 def descriptionText = eventDataMap["descriptionText"]
47 def displayed = eventDataMap["displayed"]
48 def linkText = eventDataMap["linkText"]
49 def isStateChange = eventDataMap["isStateChange"]
50 def unit = eventDataMap["unit"]
51 def data = eventDataMap["data"]
53 for (int i = 0;i < app2.eventList.size();i++) {
54 if (app2.eventList[i] == name) {
55 def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
57 app2.functionList[i](event)
61 for (int i = 0;i < app1.eventList.size();i++) {
62 if (app1.eventList[i] == name) {
63 def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
65 app1.functionList[i](event)
70 //GlobalVariables for both Apps
71 //Create a global variable for send event
72 @Field def sendEvent = {eventDataMap ->
73 eventHandler(eventDataMap)
76 @Field def locationObject = new LocationVar(sendEvent)
77 //Object for touch to call function
78 @Field def appObject = new Touched(sendEvent, 0)
79 //Create a global list for events
81 //Global Object for class Touch Sensor!
82 @Field def touchSensorObject = new NfcTouch(sendEvent, 1)
83 //Global Object for class switch!
84 @Field def switchObject = new Switches(sendEvent, 1)
85 //Global Object for class lock!
86 @Field def lockObject = new Locks(sendEvent, 1)
87 //Global Object for class door control!
88 @Field def doorControlObject = new DoorControls(sendEvent, 1)
89 //Global Object for class contact sensor!
90 @Field def contactObject = new ContactSensors(sendEvent, 1)
91 //Global Object for class presence sensor!
92 @Field def presenceSensorObject = new PresenceSensors(sendEvent, 1)
93 //Global Object for class thermostat!
94 @Field def thermostatObject = new Thermostats(sendEvent, 1)
95 //Global Object for class aeon key fob!
96 @Field def aeonKeyFobObject = new AeonKeyFobs(sendEvent, 1)
97 //Global Object for class music player!
98 @Field def musicPlayerObject = new MusicPlayers(sendEvent, 1)
99 //Global Object for class motion sensor!
100 @Field def motionSensorObject = new MotionSensors(sendEvent, 1)
101 //Global Object for class image capture!
102 @Field def imageCaptureObject = new ImageCaptures(sendEvent, 1)
103 //Global Object for class smoke detector!
104 @Field def smokeDetectorObject = new SmokeDetectors(sendEvent, 1)
105 //Global Object for class alarm!
106 @Field def alarmObject = new Alarms(sendEvent, 1)
107 //Global Object for class speech synthesis!
108 @Field def speechSynthesisObject = new SpeechSynthesises(sendEvent, 1)
116 //Extracted objects for App1
117 //Object for class switch!
119 //Object for class thermostat!
121 //Object for class lock!
124 //Extracted objects for functions for App1
125 //Global Object for functions in subscribe method!
126 def installed = this.&installed
127 //Global Object for functions in subscribe method!
128 def updated = this.&updated
129 //Global Object for functions in subscribe method!
130 def appTouch = this.&appTouch
131 //Global Object for functions in subscribe method!
132 def changedLocationMode = this.&changedLocationMode
133 //Global Object for functions in subscribe method!
134 def restoreState = this.&restoreState
135 //Global Object for functions in subscribe method!
136 def saveState = this.&saveState
137 //Global Object for functions in subscribe method!
138 def getCurrentMode = this.&getCurrentMode
142 location = obj.locationObject
144 switches = obj.switchObject
145 thermostats = obj.thermostatObject
146 locks = obj.lockObject
147 //Global variable for settings!
148 settings = [app:app, switches:switches, thermostats:thermostats, locks:locks]
150 //Global variables for each app
151 //Global variable for state[mode]
152 def state = [home:[],away:[],night:[]]
153 //Create a global logger object for methods
154 def log = new Logger()
155 //Create a global variable for Functions in Subscribe method
156 def functionList = []
157 //Create a global variable for Objects in Subscribe method
159 //Create a global variable for Events in Subscribe method
161 //Create a global list for function schedulers
162 def timersFuncList = []
163 //Create a global list for timer schedulers
165 //Create a global variable for settings
171 /////////////////////////////////////////////////////////////////////
172 def setLocationMode(String mode) {
176 /////////////////////////////////////////////////////////////////////
177 ////subscribe(obj, func)
178 def subscribe(Object obj, Closure FunctionToCall) {
181 eventList.add("Touched")
182 functionList.add(FunctionToCall)
183 } else if (obj == location) {
185 eventList.add("Location")
186 functionList.add(FunctionToCall)
189 ////subscribe(obj, event, func)
190 def subscribe(Object obj, String event, Closure FunctionToCall) {
193 functionList.add(FunctionToCall)
195 ////subscribe(obj, event, func, data)
196 def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
199 functionList.add(FunctionToCall)
201 /////////////////////////////////////////////////////////////////////
202 ////runIn(time, func)
203 def runIn(int seconds, Closure functionToCall) {
204 if (timersFuncList.contains(functionToCall)) {
205 timersList[timersFuncList.indexOf(functionToCall)].cancel()
206 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
208 timersFuncList.add(functionToCall)
209 timersList.add(new SimulatedTimer())
210 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
214 def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
215 runIn(seconds, functionToCall)
218 def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
219 runIn(seconds, nameOfFunction)
222 def runIn(int seconds, String nameOfFunction) {
223 timersFuncList.add(nameOfFunction)
224 timersList.add(new SimulatedTimer())
225 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
229 /////////////////////////////////////////////////////////////////////
231 def unschedule(Closure functionToUnschedule) {
232 for (int i = 0;i < timersFuncList.size();i++) {
233 if (timersFuncList[i] == functionToUnschedule) {
234 if (timersList != null)
235 timersList[i].cancel()
242 for (int i = 0;i < timersFuncList.size();i++) {
243 if (timersList != null)
244 timersList[i].cancel()
247 /////////////////////////////////////////////////////////////////////
248 ////sendNotificationToContacts(text, recipients)
249 def sendNotificationToContacts(String text, String recipients) {
250 for (int i = 0;i < recipients.size();i++) {
251 for (int j = 0;j < location.contacts.size();j++) {
252 if (recipients[i] == location.contacts[j]) {
253 println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
258 /////////////////////////////////////////////////////////////////////
259 ////sendSms(phone, text)
260 def sendSms(long phoneNumber, String text) {
261 println("Sending \""+text+"\" to "+phoneNumber.toString())
264 def sendSMS(long phoneNumber, String text) {
265 println("Sending \""+text+"\" to "+phoneNumber.toString())
267 /////////////////////////////////////////////////////////////////////
269 def sendPush(String text) {
272 /////////////////////////////////////////////////////////////////////
273 ////schedule(time, nameOfFunction as String)
274 def schedule(String time, String nameOfFunction) {
275 def _inputTime = time.split(':')
276 Date date = new Date()
277 def _currentTime = date.format("HH:mm:ss").split(':')
279 //Convert input time and current time to minutes
280 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
281 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
284 if (inputTime < currentTime) {
285 delay = 24*60*60-inputTime+currentTime
287 delay = inputTime-currentTime
290 timersFuncList.add(nameOfFunction)
291 timersList.add(new SimulatedTimer())
292 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
296 ////schedule(time, nameOfFunction as Closure)
297 def schedule(String time, Closure nameOfFunction) {
298 def _inputTime = time.split(':')
299 Date date = new Date()
300 def _currentTime = date.format("HH:mm:ss").split(':')
302 //Convert input time and current time to minutes
303 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
304 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
307 if (inputTime < currentTime) {
308 delay = 24*60*60-inputTime+currentTime
310 delay = inputTime-currentTime
313 if (timersFuncList.contains(nameOfFunction)) {
314 timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
315 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds*0, nameOfFunction)
317 timersFuncList.add(nameOfFunction)
318 timersList.add(new SimulatedTimer())
319 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds*0, nameOfFunction)
322 /////////////////////////////////////////////////////////////////////
324 return System.currentTimeMillis()
326 /////////////////////////////////////////////////////////////////////
327 def getTemperatureScale() {
328 return 'C' //Celsius for now
331 /////////////////////////////////////////////////////////////////////
332 def getSunriseAndSunset(LinkedHashMap metaData) {
333 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
334 return sunRiseSetInfo
338 subscribe(location, changedLocationMode)
339 subscribe(app, appTouch)
345 subscribe(location, changedLocationMode)
346 subscribe(app, appTouch)
352 restoreState(currentMode)
355 def changedLocationMode(evt)
357 restoreState(evt.value)
360 private restoreState(mode)
362 log.info "restoring state for mode '$mode'"
363 def map = state[mode] ?: [:]
365 def value = map[it.id]
366 if (value?.switch == "on") {
367 def level = value.level
369 log.debug "setting $it.label level to $level"
373 log.debug "turning $it.label on"
377 else if (value?.switch == "off") {
378 log.debug "turning $it.label off"
384 def value = map[it.id]
385 if (value?.coolingSetpoint) {
386 log.debug "coolingSetpoint = $value.coolingSetpoint"
387 it.setCoolingSetpoint(value.coolingSetpoint)
389 if (value?.heatingSetpoint) {
390 log.debug "heatingSetpoint = $value.heatingSetpoint"
391 it.setHeatingSetpoint(value.heatingSetpoint)
396 def value = map[it.id]
411 def mode = currentMode
412 def map = state[mode] ?: [:]
415 map[it.id] = [switch: it.currentSwitch, level: it.currentLevel]
419 map[it.id] = [coolingSetpoint: it.currentCoolingSetpoint, heatingSetpoint: it.currentHeatingSetpoint]
423 map[it.id] = [locked: it.currentLock == "locked"]
427 log.debug "saved state for mode ${mode}: ${state[mode]}"
428 log.debug "state: $state"
431 private getCurrentMode()
433 location.mode ?: "_none_"
444 //Extracted objects for App2
445 //Object for class Touch Sensor!
447 //Object for class switch!
449 //Object for class lock!
451 //Object for class door control!
453 //Global variable for enum!
454 def masterSwitch = "Yes"
455 //Global variable for enum!
456 def masterLock = "Yes"
457 //Global variable for enum!
458 def masterDoor = "No"
460 //Extracted objects for functions for App2
461 //Global Object for functions in subscribe method!
462 def pageTwo = this.&pageTwo
463 //Global Object for functions in subscribe method!
464 def installed = this.&installed
465 //Global Object for functions in subscribe method!
466 def updated = this.&updated
467 //Global Object for functions in subscribe method!
468 def initialize = this.&initialize
469 //Global Object for functions in subscribe method!
470 def touchHandler = this.&touchHandler
474 location = obj.locationObject
476 tag = obj.touchSensorObject
477 switch1 = obj.switchObject
478 lock = obj.lockObject
479 garageDoor = obj.doorControlObject
480 //Global variable for settings!
481 settings = [app:app, tag:tag, switch1:switch1, lock:lock, garageDoor:garageDoor, masterSwitch:masterSwitch, masterLock:masterLock, masterDoor:masterDoor]
483 //Global variables for each app
484 //Global variable for state[mode]
485 def state = [home:[],away:[],night:[]]
486 //Create a global logger object for methods
487 def log = new Logger()
488 //Create a global variable for Functions in Subscribe method
489 def functionList = []
490 //Create a global variable for Objects in Subscribe method
492 //Create a global variable for Events in Subscribe method
494 //Create a global list for function schedulers
495 def timersFuncList = []
496 //Create a global list for timer schedulers
498 //Create a global variable for settings
504 /////////////////////////////////////////////////////////////////////
505 def setLocationMode(String mode) {
509 /////////////////////////////////////////////////////////////////////
510 ////subscribe(obj, func)
511 def subscribe(Object obj, Closure FunctionToCall) {
514 eventList.add("Touched")
515 functionList.add(FunctionToCall)
516 } else if (obj == location) {
518 eventList.add("Location")
519 functionList.add(FunctionToCall)
522 ////subscribe(obj, event, func)
523 def subscribe(Object obj, String event, Closure FunctionToCall) {
526 functionList.add(FunctionToCall)
528 ////subscribe(obj, event, func, data)
529 def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
532 functionList.add(FunctionToCall)
534 /////////////////////////////////////////////////////////////////////
535 ////runIn(time, func)
536 def runIn(int seconds, Closure functionToCall) {
537 if (timersFuncList.contains(functionToCall)) {
538 timersList[timersFuncList.indexOf(functionToCall)].cancel()
539 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
541 timersFuncList.add(functionToCall)
542 timersList.add(new SimulatedTimer())
543 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
547 def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
548 runIn(seconds, functionToCall)
551 def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
552 runIn(seconds, nameOfFunction)
555 def runIn(int seconds, String nameOfFunction) {
556 timersFuncList.add(nameOfFunction)
557 timersList.add(new SimulatedTimer())
558 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
562 /////////////////////////////////////////////////////////////////////
564 def unschedule(Closure functionToUnschedule) {
565 for (int i = 0;i < timersFuncList.size();i++) {
566 if (timersFuncList[i] == functionToUnschedule) {
567 if (timersList != null)
568 timersList[i].cancel()
575 for (int i = 0;i < timersFuncList.size();i++) {
576 if (timersList != null)
577 timersList[i].cancel()
580 /////////////////////////////////////////////////////////////////////
581 ////sendNotificationToContacts(text, recipients)
582 def sendNotificationToContacts(String text, String recipients) {
583 for (int i = 0;i < recipients.size();i++) {
584 for (int j = 0;j < location.contacts.size();j++) {
585 if (recipients[i] == location.contacts[j]) {
586 println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
591 /////////////////////////////////////////////////////////////////////
592 ////sendSms(phone, text)
593 def sendSms(long phoneNumber, String text) {
594 println("Sending \""+text+"\" to "+phoneNumber.toString())
597 def sendSMS(long phoneNumber, String text) {
598 println("Sending \""+text+"\" to "+phoneNumber.toString())
600 /////////////////////////////////////////////////////////////////////
601 ////schedule(time, nameOfFunction as String)
602 def schedule(String time, String nameOfFunction) {
603 def _inputTime = time.split(':')
604 Date date = new Date()
605 def _currentTime = date.format("HH:mm:ss").split(':')
607 //Convert input time and current time to minutes
608 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
609 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
612 if (inputTime < currentTime) {
613 delay = 24*60*60-inputTime+currentTime
615 delay = inputTime-currentTime
618 timersFuncList.add(nameOfFunction)
619 timersList.add(new SimulatedTimer())
620 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
624 ////schedule(time, nameOfFunction as Closure)
625 def schedule(String time, Closure nameOfFunction) {
626 def _inputTime = time.split(':')
627 Date date = new Date()
628 def _currentTime = date.format("HH:mm:ss").split(':')
630 //Convert input time and current time to minutes
631 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
632 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
635 if (inputTime < currentTime) {
636 delay = 24*60*60-inputTime+currentTime
638 delay = inputTime-currentTime
641 if (timersFuncList.contains(nameOfFunction)) {
642 timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
643 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds*0, nameOfFunction)
645 timersFuncList.add(nameOfFunction)
646 timersList.add(new SimulatedTimer())
647 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds*0, nameOfFunction)
650 /////////////////////////////////////////////////////////////////////
652 return System.currentTimeMillis()
654 /////////////////////////////////////////////////////////////////////
655 def getTemperatureScale() {
656 return 'C' //Celsius for now
659 /////////////////////////////////////////////////////////////////////
660 def getSunriseAndSunset(LinkedHashMap metaData) {
661 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
662 return sunRiseSetInfo
666 dynamicPage(name: "pageTwo") {
667 section("If set, the state of these devices will be toggled each time the tag is touched, " +
668 "e.g. a light that's on will be turned off and one that's off will be turned on, " +
669 "other devices of the same type will be set to the same state as their master device. " +
670 "If no master is designated then the majority of devices of the same type will be used " +
671 "to determine whether to turn on or off the devices.") {
673 if (switch1 || masterSwitch) {
674 input "masterSwitch", "enum", title: "Master switch", options: switch1.collect{[(it.id): it.displayName]}, required: false
676 if (lock || masterLock) {
677 input "masterLock", "enum", title: "Master lock", options: lock.collect{[(it.id): it.displayName]}, required: false
679 if (garageDoor || masterDoor) {
680 input "masterDoor", "enum", title: "Master door", options: garageDoor.collect{[(it.id): it.displayName]}, required: false
683 section([mobileOnly:true]) {
684 label title: "Assign a name", required: false
685 mode title: "Set for specific mode(s)", required: false
691 log.debug "Installed with settings: ${settings}"
697 log.debug "Updated with settings: ${settings}"
704 subscribe tag, "nfcTouch", touchHandler
705 subscribe app, touchHandler
708 private currentStatus(devices, master, attribute) {
709 log.trace "currentStatus($devices, $master, $attribute)"
712 result = devices.find{it.id == master}?.currentValue(attribute)
717 def value = it.currentValue(attribute)
718 map[value] = (map[value] ?: 0) + 1
719 log.trace "$it.displayName: $value"
722 result = map.collect{it}.sort{it.value}[-1].key
724 log.debug "$attribute = $result"
728 def touchHandler(evt) {
729 log.trace "touchHandler($evt.descriptionText)"
731 def status = currentStatus(switch1, masterSwitch, "switch")
733 if (status == "on") {
743 def status = currentStatus(lock, masterLock, "lock")
745 if (status == "locked") {
755 def status = currentStatus(garageDoor, masterDoor, "status")
757 if (status == "open") {
768 @Field def app1 = new App1(this)
769 @Field def app2 = new App2(this)
773 appObject.setValue([name: "Touched", value: "touched", deviceId: "touchedSensorID0", descriptionText: "",
774 displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
775 appObject.setValue([name: "nfcTouch", value: "touched", deviceId: "nfcSensorID0", descriptionText: "",
776 displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
777 locationObject.setValue([name: "Location", value: "away", deviceId: "locationID0", descriptionText: "",
778 displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
779 locationObject.setValue([name: "Location", value: "home", deviceId: "locationID0", descriptionText: "",
780 displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
781 locationObject.setValue([name: "Location", value: "night", deviceId: "locationID0", descriptionText: "",
782 displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])