1 //Infrastructure for SmartThings Application
3 import groovy.transform.Field
4 import groovy.json.JsonSlurper
7 import ContactSensor.ContactSensor
8 import ContactSensor.ContactSensors
9 import DoorControl.DoorControl
10 import DoorControl.DoorControls
13 import Thermostat.Thermostat
14 import Thermostat.Thermostats
16 import Switch.Switches
17 import PresenceSensor.PresenceSensor
18 import PresenceSensor.PresenceSensors
20 import Location.LocationVar
21 import Location.Phrase
22 import appTouch.Touched
23 import NfcTouch.NfcTouch
24 import AeonKeyFob.AeonKeyFob
25 import AeonKeyFob.AeonKeyFobs
26 import MusicPlayer.MusicPlayer
27 import MusicPlayer.MusicPlayers
28 import MotionSensor.MotionSensor
29 import MotionSensor.MotionSensors
30 import ImageCapture.ImageCapture
31 import ImageCapture.ImageCaptures
32 import SmokeDetector.SmokeDetector
33 import SmokeDetector.SmokeDetectors
36 import SpeechSynthesis.SpeechSynthesis
37 import SpeechSynthesis.SpeechSynthesises
38 import AccelerationSensor.AccelerationSensor
39 import AccelerationSensor.AccelerationSensors
40 import Battery.Battery
41 import Battery.Batteries
42 import BeaconSensor.BeaconSensor
43 import BeaconSensor.BeaconSensors
44 import CarbonMonoxideDetector.CarbonMonoxideDetector
45 import CarbonMonoxideDetector.CarbonMonoxideDetectors
46 import ColorControl.ColorControl
47 import ColorControl.ColorControls
48 import EnergyMeter.EnergyMeter
49 import EnergyMeter.EnergyMeters
50 import IlluminanceMeasurement.IlluminanceMeasurement
51 import IlluminanceMeasurement.IlluminanceMeasurements
52 import PowerMeter.PowerMeter
53 import PowerMeter.PowerMeters
54 import RelativeHumidityMeasurement.RelativeHumidityMeasurement
55 import RelativeHumidityMeasurement.RelativeHumidityMeasurements
56 import RelaySwitch.RelaySwitch
57 import RelaySwitch.RelaySwitches
58 import SleepSensor.SleepSensor
59 import SleepSensor.SleepSensors
60 import StepSensor.StepSensor
61 import StepSensor.StepSensors
62 import SwitchLevel.SwitchLevel
63 import SwitchLevel.SwitchLevels
64 import TemperatureMeasurement.TemperatureMeasurement
65 import TemperatureMeasurement.TemperatureMeasurements
66 import WaterSensor.WaterSensor
67 import WaterSensor.WaterSensors
70 import MobilePresence.MobilePresence
71 import MobilePresence.MobilePresences
73 import AtomicState.AtomicState
74 import Timer.SimulatedTimer
77 import gov.nasa.jpf.vm.Verify
80 /////////////////////////////////////////////////////////////////////
81 def eventHandler(LinkedHashMap eventDataMap) {
82 def value = eventDataMap["value"]
83 def name = eventDataMap["name"]
84 def deviceId = eventDataMap["deviceId"]
85 def descriptionText = eventDataMap["descriptionText"]
86 def displayed = eventDataMap["displayed"]
87 def linkText = eventDataMap["linkText"]
88 def isStateChange = eventDataMap["isStateChange"]
89 def unit = eventDataMap["unit"]
90 def data = eventDataMap["data"]
92 for (int i = 0;i < app2.eventList.size();i++) {
93 if (app2.eventList[i] == name) {
94 def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
95 app2.functionList[i](event)
99 for (int i = 0;i < app1.eventList.size();i++) {
100 if (app1.eventList[i] == name) {
101 def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
102 app1.functionList[i](event)
107 //GlobalVariables for both Apps
108 //Create a global variable for send event
109 @Field def sendEvent = {eventDataMap ->
110 eventHandler(eventDataMap)
112 //Object for location
113 @Field def locationObject = new LocationVar(sendEvent)
114 //Object for touch to call function
115 @Field def appObject = new Touched(sendEvent, 0)
116 //Create a global list for events
117 //@Field def evt = []
118 //Global Object for class AtomicState!
119 @Field def atomicState = new AtomicState()
120 //Global Object for class Touch Sensor!
121 @Field def touchSensorObject = new NfcTouch(sendEvent, 1)
122 //Global Object for class switch!
123 @Field def switchObject = new Switches(sendEvent, 1)
124 //Global Object for class lock!
125 @Field def lockObject = new Locks(sendEvent, 1)
126 //Global Object for class door control!
127 @Field def doorControlObject = new DoorControls(sendEvent, 1)
128 //Global Object for class contact sensor!
129 @Field def contactObject = new ContactSensors(sendEvent, 1)
130 //Global Object for class presence sensor!
131 @Field def presenceSensorObject = new PresenceSensors(sendEvent, 1)
132 //Global Object for class thermostat!
133 @Field def thermostatObject = new Thermostats(sendEvent, 1)
134 //Global Object for class aeon key fob!
135 @Field def aeonKeyFobObject = new AeonKeyFobs(sendEvent, 1)
136 //Global Object for class music player!
137 @Field def musicPlayerObject = new MusicPlayers(sendEvent, 1)
138 //Global Object for class motion sensor!
139 @Field def motionSensorObject = new MotionSensors(sendEvent, 1)
140 //Global Object for class image capture!
141 @Field def imageCaptureObject = new ImageCaptures(sendEvent, 1)
142 //Global Object for class smoke detector!
143 @Field def smokeDetectorObject = new SmokeDetectors(sendEvent, 1)
144 //Global Object for class alarm!
145 @Field def alarmObject = new Alarms(sendEvent, 1)
146 //Global Object for class speech synthesis!
147 @Field def speechSynthesisObject = new SpeechSynthesises(sendEvent, 1)
148 //Global Object for class acceleration sensor!
149 @Field def accelerationSensorObject = new AccelerationSensors(sendEvent, 1)
150 //Global Object for class Battery!
151 @Field def batteryObject = new Batteries(sendEvent, 1)
152 //Global Object for class beacon sensor!
153 @Field def beaconSensorObject = new BeaconSensors(sendEvent, 1)
154 //Global Object for class carbon monoxide!
155 @Field def carbonMonoxideDetectorObject = new CarbonMonoxideDetectors(sendEvent, 1)
156 //Global Object for class color control!
157 @Field def colorControlObject = new ColorControls(sendEvent, 1)
158 //Global Object for class energy meter!
159 @Field def energyMeterObject = new EnergyMeters(sendEvent, 1)
160 //Global Object for class illuminance measurement!
161 @Field def illuminanceMeasurementObject = new IlluminanceMeasurements(sendEvent, 1)
162 //Global Object for class power meter!
163 @Field def powerMeterObject = new PowerMeters(sendEvent, 1)
164 //Global Object for class humidity measurement!
165 @Field def humidityMeasurementObject = new RelativeHumidityMeasurements(sendEvent, 1)
166 //Global Object for class relay switch!
167 @Field def relaySwitchObject = new RelaySwitches(sendEvent, 1)
168 //Global Object for class sleep sensor!
169 @Field def sleepSensorObject = new SleepSensors(sendEvent, 1)
170 //Global Object for class step sensor!
171 @Field def stepSensorObject = new StepSensors(sendEvent, 1)
172 //Global Object for class switch level!
173 @Field def switchLevelObject = new SwitchLevels(sendEvent, 1)
174 //Global Object for class temperature measurement!
175 @Field def temperatureMeasurementObject = new TemperatureMeasurements(sendEvent, 1)
176 //Global Object for class water sensor!
177 @Field def waterSensorObject = new WaterSensors(sendEvent, 1)
178 //Global Object for class valves!
179 @Field def valveObject = new Valves(sendEvent, 1)
180 //Global Object for class mobile presence!
181 @Field def mobilePresenceObject = new MobilePresences(sendEvent, 1)
190 //Extracted objects for App1
191 //Object for class lock!
193 //Object for class contactSensor!
195 //Global variable for number!
196 def minutesLater = 48
197 //Global variable for number!
198 def secondsLater = 55
199 //Global variable for contact!
200 def recipients = "AJ"
201 //Global variable for phone!
202 def phoneNumber = 9495379373
204 //Extracted objects for functions for App1
205 //Global Object for functions in subscribe method!
206 def installed = this.&installed
207 //Global Object for functions in subscribe method!
208 def updated = this.&updated
209 //Global Object for functions in subscribe method!
210 def initialize = this.&initialize
211 //Global Object for functions in subscribe method!
212 def lockDoor = this.&lockDoor
213 //Global Object for functions in subscribe method!
214 def unlockDoor = this.&unlockDoor
215 //Global Object for functions in subscribe method!
216 def doorHandler = this.&doorHandler
220 location = obj.locationObject
222 atomicState = obj.atomicState
223 lock1 = obj.lockObject
224 contact = obj.contactObject
225 //Global variable for settings!
226 settings = [app:app, lock1:lock1, contact:contact, minutesLater:minutesLater, secondsLater:secondsLater, recipients:recipients, phoneNumber:phoneNumber]
228 //Global variables for each app
229 //Global variable for state[mode]
230 def state = [home:[],away:[],night:[]]
231 //Create a global logger object for methods
232 def log = new Logger()
233 //Create a global variable for Functions in Subscribe method
234 def functionList = []
235 //Create a global variable for Objects in Subscribe method
237 //Create a global variable for Events in Subscribe method
239 //Create a global list for function schedulers
240 def timersFuncList = []
241 //Create a global list for timer schedulers
243 //Create a global variable for settings
249 /////////////////////////////////////////////////////////////////////
250 def setLocationMode(String mode) {
254 /////////////////////////////////////////////////////////////////////
255 ////subscribe(obj, func)
256 def subscribe(Object obj, Closure FunctionToCall) {
259 eventList.add("Touched")
260 functionList.add(FunctionToCall)
261 } else if (obj == location) {
263 eventList.add("Location")
264 functionList.add(FunctionToCall)
267 ////subscribe(obj, event, func)
268 def subscribe(Object obj, String event, Closure FunctionToCall) {
271 functionList.add(FunctionToCall)
273 ////subscribe(obj, event, func, data)
274 def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
277 functionList.add(FunctionToCall)
279 /////////////////////////////////////////////////////////////////////
280 ////runIn(time, func)
281 def runIn(int seconds, Closure functionToCall) {
282 if (timersFuncList.contains(functionToCall)) {
283 timersList[timersFuncList.indexOf(functionToCall)].cancel()
284 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
286 timersFuncList.add(functionToCall)
287 timersList.add(new SimulatedTimer())
288 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
292 def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
293 runIn(seconds, functionToCall)
296 def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
297 runIn(seconds, nameOfFunction)
300 def runIn(int seconds, String nameOfFunction) {
301 timersFuncList.add(nameOfFunction)
302 timersList.add(new SimulatedTimer())
303 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
307 /////////////////////////////////////////////////////////////////////
309 def unschedule(Closure functionToUnschedule) {
310 for (int i = 0;i < timersFuncList.size();i++) {
311 if (timersFuncList[i] == functionToUnschedule) {
312 if (timersList != null)
313 timersList[i].cancel()
320 for (int i = 0;i < timersFuncList.size();i++) {
321 if (timersList != null)
322 timersList[i].cancel()
325 /////////////////////////////////////////////////////////////////////
326 ////sendNotificationToContacts(text, recipients)
327 def sendNotificationToContacts(String text, String recipients) {
328 for (int i = 0;i < recipients.size();i++) {
329 for (int j = 0;j < location.contacts.size();j++) {
330 if (recipients[i] == location.contacts[j]) {
331 println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
336 /////////////////////////////////////////////////////////////////////
337 ////sendSms(phone, text)
338 def sendSms(long phoneNumber, String text) {
339 println("Sending \""+text+"\" to "+phoneNumber.toString())
342 def sendSMS(long phoneNumber, String text) {
343 println("Sending \""+text+"\" to "+phoneNumber.toString())
345 /////////////////////////////////////////////////////////////////////
347 def sendPush(String text) {
350 /////////////////////////////////////////////////////////////////////
351 ////schedule(time, nameOfFunction as String)
352 def schedule(String time, String nameOfFunction) {
353 def _inputTime = time.split(':')
354 Date date = new Date()
355 def _currentTime = date.format("HH:mm:ss").split(':')
357 //Convert input time and current time to minutes
358 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
359 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
362 if (inputTime < currentTime) {
363 delay = 24*60*60-inputTime+currentTime
365 delay = inputTime-currentTime
368 timersFuncList.add(nameOfFunction)
369 timersList.add(new SimulatedTimer())
370 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
374 ////schedule(time, nameOfFunction as Closure)
375 def schedule(String time, Closure nameOfFunction) {
376 def _inputTime = time.split(':')
377 Date date = new Date()
378 def _currentTime = date.format("HH:mm:ss").split(':')
380 //Convert input time and current time to minutes
381 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
382 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
385 if (inputTime < currentTime) {
386 delay = 24*60*60-inputTime+currentTime
388 delay = inputTime-currentTime
391 if (timersFuncList.contains(nameOfFunction)) {
392 timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
393 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
395 timersFuncList.add(nameOfFunction)
396 timersList.add(new SimulatedTimer())
397 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
400 /////////////////////////////////////////////////////////////////////
402 return System.currentTimeMillis()
404 /////////////////////////////////////////////////////////////////////
405 def getTemperatureScale() {
406 return 'F' //Celsius for now
409 /////////////////////////////////////////////////////////////////////
410 def getSunriseAndSunset(LinkedHashMap metaData) {
411 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
412 return sunRiseSetInfo
414 /////////////////////////////////////////////////////////////////////
415 def httpPostJson(LinkedHashMap metaData, Closure inputData) {
418 /////////////////////////////////////////////////////////////////////
419 def runEvery15Minutes(Closure inputData) {
422 /////////////////////////////////////////////////////////////////////
423 def timeToday(String time, Object timeZone) {
424 def timeOfDay = new Date()
425 def _inputTime = time.split(':')
426 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
427 timeOfDay.time = inputTime
430 /////////////////////////////////////////////////////////////////////
431 def sendNotification(String text, LinkedHashMap metaData) {
432 println("Sending \""+text+"\" to "+metaData.phone.toString())
434 /////////////////////////////////////////////////////////////////////
450 log.debug "Settings: ${settings}"
451 subscribe(lock1, "lock", doorHandler, [filterEvents: false])
452 subscribe(lock1, "unlock", doorHandler, [filterEvents: false])
453 subscribe(contact, "contact.open", doorHandler)
454 subscribe(contact, "contact.closed", doorHandler)
458 log.debug "Locking the door."
460 if(location.contactBookEnabled) {
462 log.debug ( "Sending Push Notification..." )
463 sendNotificationToContacts( "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!", recipients)
467 log.debug("Sending text message...")
468 sendSms( phoneNumber, "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!")
473 log.debug "Unlocking the door."
475 if(location.contactBookEnabled) {
477 log.debug ( "Sending Push Notification..." )
478 sendNotificationToContacts( "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!", recipients)
482 log.debug("Sending text message...")
483 sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!")
487 def doorHandler(evt){
488 if ((contact.latestValue("contact") == "open") && (evt.value == "locked")) { // If the door is open and a person locks the door then...
489 //def delay = (secondsLater) // runIn uses seconds
490 runIn( secondsLater, unlockDoor ) // ...schedule (in minutes) to unlock... We don't want the door to be closed while the lock is engaged.
492 else if ((contact.latestValue("contact") == "open") && (evt.value == "unlocked")) { // If the door is open and a person unlocks it then...
493 unschedule( unlockDoor ) // ...we don't need to unlock it later.
495 else if ((contact.latestValue("contact") == "closed") && (evt.value == "locked")) { // If the door is closed and a person manually locks it then...
496 unschedule( lockDoor ) // ...we don't need to lock it later.
498 else if ((contact.latestValue("contact") == "closed") && (evt.value == "unlocked")) { // If the door is closed and a person unlocks it then...
499 //def delay = (minutesLater * 60) // runIn uses seconds
500 runIn( (minutesLater * 60), lockDoor ) // ...schedule (in minutes) to lock.
502 else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "open")) { // If a person opens an unlocked door...
503 unschedule( lockDoor ) // ...we don't need to lock it later.
505 else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "closed")) { // If a person closes an unlocked door...
506 //def delay = (minutesLater * 60) // runIn uses seconds
507 runIn( (minutesLater * 60), lockDoor ) // ...schedule (in minutes) to lock.
509 else { //Opening or Closing door when locked (in case you have a handle lock)
510 log.debug "Unlocking the door."
512 if(location.contactBookEnabled) {
514 log.debug ( "Sending Push Notification..." )
515 sendNotificationToContacts( "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!", recipients)
519 log.debug("Sending text message...")
520 sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!")
534 //Extracted objects for App2
535 //Global variable for time!
536 def starting = "15:00"
537 //Global variable for time!
539 //Object for class beacon sensor!
541 //Object for class mobile presence!
543 //Global variable for enum!
544 def arrivalPhrase = "Good Night!"
545 //Object for class switch!
546 def arrivalOnSwitches
547 //Object for class switch!
548 def arrivalOffSwitches
549 //Object for class lock!
551 //Global variable for enum!
552 def departPhrase = "Good Night!"
553 //Object for class switch!
555 //Object for class switch!
556 def departOffSwitches
557 //Object for class lock!
559 //Global variable for boolean!
560 def pushNotification = "0"
561 //Global variable for phone!
562 def phone = 9495379373
563 //Global variable for enum!
565 //Global variable for mode!
568 //Extracted objects for functions for App2
569 //Global Object for functions in subscribe method!
570 def mainPage = this.&mainPage
571 //Global Object for functions in subscribe method!
572 def installed = this.&installed
573 //Global Object for functions in subscribe method!
574 def updated = this.&updated
575 //Global Object for functions in subscribe method!
576 def initialize = this.&initialize
577 //Global Object for functions in subscribe method!
578 def beaconHandler = this.&beaconHandler
579 //Global Object for functions in subscribe method!
580 def arriveActions = this.&arriveActions
581 //Global Object for functions in subscribe method!
582 def departActions = this.&departActions
583 //Global Object for functions in subscribe method!
584 def prefix = this.&prefix
585 //Global Object for functions in subscribe method!
586 def listPhrases = this.&listPhrases
587 //Global Object for functions in subscribe method!
588 def executePhrase = this.&executePhrase
589 //Global Object for functions in subscribe method!
590 def getBeaconName = this.&getBeaconName
591 //Global Object for functions in subscribe method!
592 def getPhoneName = this.&getPhoneName
593 //Global Object for functions in subscribe method!
594 def hideOptionsSection = this.&hideOptionsSection
595 //Global Object for functions in subscribe method!
596 def getAllOk = this.&getAllOk
597 //Global Object for functions in subscribe method!
598 def getModeOk = this.&getModeOk
599 //Global Object for functions in subscribe method!
600 def getDaysOk = this.&getDaysOk
601 //Global Object for functions in subscribe method!
602 def getTimeOk = this.&getTimeOk
603 //Global Object for functions in subscribe method!
604 def hhmm = this.&hhmm
605 //Global Object for functions in subscribe method!
606 def timeIntervalLabel = this.&timeIntervalLabel
607 //Global Object for functions in subscribe method!
608 def list = this.&list
612 location = obj.locationObject
614 atomicState = obj.atomicState
615 beacons = obj.beaconSensorObject
616 phones = obj.mobilePresenceObject
617 arrivalOnSwitches = obj.switchObject
618 arrivalOffSwitches = obj.switchObject
619 arrivalLocks = obj.lockObject
620 departOnSwitches = obj.switchObject
621 departOffSwitches = obj.switchObject
622 departLocks = obj.lockObject
623 //Global variable for settings!
624 settings = [app:app, starting:starting, ending:ending, beacons:beacons, phones:phones, arrivalPhrase:arrivalPhrase, arrivalOnSwitches:arrivalOnSwitches, arrivalOffSwitches:arrivalOffSwitches, arrivalLocks:arrivalLocks, departPhrase:departPhrase, departOnSwitches:departOnSwitches, departOffSwitches:departOffSwitches, departLocks:departLocks, pushNotification:pushNotification, phone:phone, days:days, modes:modes]
626 //Global variables for each app
627 //Global variable for state[mode]
628 def state = [home:[],away:[],night:[]]
629 //Create a global logger object for methods
630 def log = new Logger()
631 //Create a global variable for Functions in Subscribe method
632 def functionList = []
633 //Create a global variable for Objects in Subscribe method
635 //Create a global variable for Events in Subscribe method
637 //Create a global list for function schedulers
638 def timersFuncList = []
639 //Create a global list for timer schedulers
641 //Create a global variable for settings
647 /////////////////////////////////////////////////////////////////////
648 def setLocationMode(String mode) {
652 /////////////////////////////////////////////////////////////////////
653 ////subscribe(obj, func)
654 def subscribe(Object obj, Closure FunctionToCall) {
657 eventList.add("Touched")
658 functionList.add(FunctionToCall)
659 } else if (obj == location) {
661 eventList.add("Location")
662 functionList.add(FunctionToCall)
665 ////subscribe(obj, event, func)
666 def subscribe(Object obj, String event, Closure FunctionToCall) {
669 functionList.add(FunctionToCall)
671 ////subscribe(obj, event, func, data)
672 def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
675 functionList.add(FunctionToCall)
677 /////////////////////////////////////////////////////////////////////
678 ////runIn(time, func)
679 def runIn(int seconds, Closure functionToCall) {
680 if (timersFuncList.contains(functionToCall)) {
681 timersList[timersFuncList.indexOf(functionToCall)].cancel()
682 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
684 timersFuncList.add(functionToCall)
685 timersList.add(new SimulatedTimer())
686 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
690 def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
691 runIn(seconds, functionToCall)
694 def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
695 runIn(seconds, nameOfFunction)
698 def runIn(int seconds, String nameOfFunction) {
699 timersFuncList.add(nameOfFunction)
700 timersList.add(new SimulatedTimer())
701 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
705 /////////////////////////////////////////////////////////////////////
707 def unschedule(Closure functionToUnschedule) {
708 for (int i = 0;i < timersFuncList.size();i++) {
709 if (timersFuncList[i] == functionToUnschedule) {
710 if (timersList != null)
711 timersList[i].cancel()
718 for (int i = 0;i < timersFuncList.size();i++) {
719 if (timersList != null)
720 timersList[i].cancel()
723 /////////////////////////////////////////////////////////////////////
724 ////sendNotificationToContacts(text, recipients)
725 def sendNotificationToContacts(String text, String recipients) {
726 for (int i = 0;i < recipients.size();i++) {
727 for (int j = 0;j < location.contacts.size();j++) {
728 if (recipients[i] == location.contacts[j]) {
729 println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
734 /////////////////////////////////////////////////////////////////////
735 ////sendSms(phone, text)
736 def sendSms(long phoneNumber, String text) {
737 println("Sending \""+text+"\" to "+phoneNumber.toString())
740 def sendSMS(long phoneNumber, String text) {
741 println("Sending \""+text+"\" to "+phoneNumber.toString())
743 /////////////////////////////////////////////////////////////////////
745 def sendPush(String text) {
748 /////////////////////////////////////////////////////////////////////
749 ////schedule(time, nameOfFunction as String)
750 def schedule(String time, String nameOfFunction) {
751 def _inputTime = time.split(':')
752 Date date = new Date()
753 def _currentTime = date.format("HH:mm:ss").split(':')
755 //Convert input time and current time to minutes
756 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
757 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
760 if (inputTime < currentTime) {
761 delay = 24*60*60-inputTime+currentTime
763 delay = inputTime-currentTime
766 timersFuncList.add(nameOfFunction)
767 timersList.add(new SimulatedTimer())
768 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
772 ////schedule(time, nameOfFunction as Closure)
773 def schedule(String time, Closure nameOfFunction) {
774 def _inputTime = time.split(':')
775 Date date = new Date()
776 def _currentTime = date.format("HH:mm:ss").split(':')
778 //Convert input time and current time to minutes
779 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
780 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
783 if (inputTime < currentTime) {
784 delay = 24*60*60-inputTime+currentTime
786 delay = inputTime-currentTime
789 if (timersFuncList.contains(nameOfFunction)) {
790 timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
791 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
793 timersFuncList.add(nameOfFunction)
794 timersList.add(new SimulatedTimer())
795 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
798 /////////////////////////////////////////////////////////////////////
800 return System.currentTimeMillis()
802 /////////////////////////////////////////////////////////////////////
803 def getTemperatureScale() {
804 return 'F' //Celsius for now
807 /////////////////////////////////////////////////////////////////////
808 def getSunriseAndSunset(LinkedHashMap metaData) {
809 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
810 return sunRiseSetInfo
812 /////////////////////////////////////////////////////////////////////
813 def httpPostJson(LinkedHashMap metaData, Closure inputData) {
816 /////////////////////////////////////////////////////////////////////
817 def runEvery15Minutes(Closure inputData) {
820 /////////////////////////////////////////////////////////////////////
821 def timeToday(String time, Object timeZone) {
822 def timeOfDay = new Date()
823 def _inputTime = time.split(':')
824 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
825 timeOfDay.time = inputTime
828 /////////////////////////////////////////////////////////////////////
829 def sendNotification(String text, LinkedHashMap metaData) {
830 println("Sending \""+text+"\" to "+metaData.phone.toString())
832 /////////////////////////////////////////////////////////////////////
838 dynamicPage(name: "mainPage", install: true, uninstall: true) {
840 section("Where do you want to watch?") {
841 input name: "beacons", type: "capability.beacon", title: "Select your beacon(s)",
842 multiple: true, required: true
845 section("Who do you want to watch for?") {
846 input name: "phones", type: "device.mobilePresence", title: "Select your phone(s)",
847 multiple: true, required: true
850 section("What do you want to do on arrival?") {
851 input name: "arrivalPhrase", type: "enum", title: "Execute a phrase",
852 options: listPhrases(), required: false
853 input "arrivalOnSwitches", "capability.switch", title: "Turn on some switches",
854 multiple: true, required: false
855 input "arrivalOffSwitches", "capability.switch", title: "Turn off some switches",
856 multiple: true, required: false
857 input "arrivalLocks", "capability.lock", title: "Unlock the door",
858 multiple: true, required: false
861 section("What do you want to do on departure?") {
862 input name: "departPhrase", type: "enum", title: "Execute a phrase",
863 options: listPhrases(), required: false
864 input "departOnSwitches", "capability.switch", title: "Turn on some switches",
865 multiple: true, required: false
866 input "departOffSwitches", "capability.switch", title: "Turn off some switches",
867 multiple: true, required: false
868 input "departLocks", "capability.lock", title: "Lock the door",
869 multiple: true, required: false
872 section("Do you want to be notified?") {
873 input "pushNotification", "bool", title: "Send a push notification"
874 input "phone", "phone", title: "Send a text message", description: "Tap to enter phone number",
879 label title: "Give your automation a name", description: "e.g. Goodnight Home, Wake Up"
882 def timeLabel = timeIntervalLabel()
883 section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
884 href "timeIntervalInput", title: "Only during a certain time",
885 description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
887 input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
888 options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
890 input "modes", "mode", title: "Only when mode is", multiple: true, required: false
895 // Lifecycle management
897 log.debug "<beacon-control> Installed with settings: ${settings}"
902 log.debug "<beacon-control> Updated with settings: ${settings}"
908 subscribe(beacons, "presence", beaconHandler)
912 def beaconHandler(evt) {
913 log.debug "<beacon-control> beaconHandler: $evt"
916 def data = new groovy.json.JsonSlurper().parseText(evt.data)
917 // removed logging of device names. can be added back for debugging
918 //log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
920 def beaconName = getBeaconName(evt)
921 // removed logging of device names. can be added back for debugging
922 //log.debug "<beacon-control> beaconName: $beaconName"
924 def phoneName = getPhoneName(data)
925 // removed logging of device names. can be added back for debugging
926 //log.debug "<beacon-control> phoneName: $phoneName"
927 if (phoneName != null) {
928 def action = data.presence == "1" ? "arrived" : "left"
929 def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"
931 if (action == "arrived") {
932 msg = arriveActions(msg)
934 else if (action == "left") {
935 msg = departActions(msg)
937 log.debug "<beacon-control> msg: $msg"
939 if (pushNotification || phone) {
941 method: (pushNotification && phone) ? "both" : (pushNotification ? "push" : "sms"),
944 sendNotification(msg, options)
951 private arriveActions(msg) {
952 if (arrivalPhrase || arrivalOnSwitches || arrivalOffSwitches || arrivalLocks) msg += ", so"
955 log.debug "<beacon-control> executing: $arrivalPhrase"
956 executePhrase(arrivalPhrase)
957 msg += " ${prefix('executed')} $arrivalPhrase."
959 if (arrivalOnSwitches) {
960 log.debug "<beacon-control> turning on: $arrivalOnSwitches"
961 arrivalOnSwitches.on()
962 msg += " ${prefix('turned')} ${list(arrivalOnSwitches)} on."
964 if (arrivalOffSwitches) {
965 log.debug "<beacon-control> turning off: $arrivalOffSwitches"
966 arrivalOffSwitches.off()
967 msg += " ${prefix('turned')} ${list(arrivalOffSwitches)} off."
970 log.debug "<beacon-control> unlocking: $arrivalLocks"
971 arrivalLocks.unlock()
972 msg += " ${prefix('unlocked')} ${list(arrivalLocks)}."
977 private departActions(msg) {
978 if (departPhrase || departOnSwitches || departOffSwitches || departLocks) msg += ", so"
981 log.debug "<beacon-control> executing: $departPhrase"
982 executePhrase(departPhrase)
983 msg += " ${prefix('executed')} $departPhrase."
985 if (departOnSwitches) {
986 log.debug "<beacon-control> turning on: $departOnSwitches"
987 departOnSwitches.on()
988 msg += " ${prefix('turned')} ${list(departOnSwitches)} on."
990 if (departOffSwitches) {
991 log.debug "<beacon-control> turning off: $departOffSwitches"
992 departOffSwitches.off()
993 msg += " ${prefix('turned')} ${list(departOffSwitches)} off."
996 log.debug "<beacon-control> unlocking: $departLocks"
998 msg += " ${prefix('locked')} ${list(departLocks)}."
1003 private prefix(word) {
1005 def index = settings.prefixIndex == null ? 0 : settings.prefixIndex + 1
1011 result = "I also $word"
1014 result = "And I $word"
1017 result = "And $word"
1021 settings.prefixIndex = index
1022 log.trace "prefix($word'): $result"
1026 private listPhrases() {
1027 location.helloHome.getPhrases().label
1030 private executePhrase(phraseName) {
1032 location.helloHome.execute(phraseName)
1033 log.debug "<beacon-control> executed phrase: $phraseName"
1037 private getBeaconName(evt) {
1038 def beaconName = beacons.find { b -> b.id == evt.deviceId }
1042 private getPhoneName(data) {
1043 def phoneName = phones.find { phone ->
1044 // Work around DNI bug in data
1045 def pParts = phone.deviceNetworkId.split('\\|')
1046 def dParts = data.dni.split('\\|')
1047 pParts[0] == dParts[0]
1052 private hideOptionsSection() {
1053 (starting || ending || days || modes) ? false : true
1056 private getAllOk() {
1057 modeOk && daysOk && timeOk
1060 private getModeOk() {
1061 def result = !modes || modes.contains(location.mode)
1062 log.trace "<beacon-control> modeOk = $result"
1066 private getDaysOk() {
1069 def df = new java.text.SimpleDateFormat("EEEE")
1070 if (location.timeZone) {
1071 df.setTimeZone(location.timeZone)
1074 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
1076 def day = df.format(new Date())
1077 result = days.contains(day)
1079 log.trace "<beacon-control> daysOk = $result"
1083 private getTimeOk() {
1085 if (starting && ending) {
1086 def currTime = now()
1087 def start = timeToday(starting, location?.timeZone).time
1088 def stop = timeToday(ending, location?.timeZone).time
1089 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
1091 log.trace "<beacon-control> timeOk = $result"
1095 private hhmm(time, fmt = "h:mm a") {
1096 def t = timeToday(time, location.timeZone)
1097 def f = new java.text.SimpleDateFormat(fmt)
1098 f.setTimeZone(location.timeZone ?: timeZone(time))
1102 private timeIntervalLabel() {
1103 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
1106 private list(Object names) {
1113 def initOrder = Verify.getBoolean()
1115 app1 = new App1(this)
1116 app2 = new App2(this)
1118 app2 = new App2(this)
1119 app1 = new App1(this)
1122 def installOrder = Verify.getBoolean()
1132 def eventNumber = Verify.getInt(0,4)
1133 switch(eventNumber) {
1135 lockObject.setValue([name: "lock", value: "locked", deviceId: "lockID0", descriptionText: "",
1136 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1139 lockObject.setValue([name: "unlock", value: "unlocked ", deviceId: "lockID0", descriptionText: "",
1140 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1143 contactObject.setValue([name: "contact.open", value: "open", deviceId: "contactSensorID0", descriptionText: "",
1144 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1147 contactObject.setValue([name: "contact.closed", value: "closed", deviceId: "contactSensorID0", descriptionText: "",
1148 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1151 def event = Verify.getInt(0,1)
1153 presenceSensorObject.setValue([name: "presence", value: "present", deviceId: "presenceSensorID0", descriptionText: "",
1154 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"1","dni":"mobile0"}'])
1156 presenceSensorObject.setValue([name: "presence", value: "not present", deviceId: "presenceSensorID0", descriptionText: "",
1157 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"0","dni":"mobile0"}'])