Merge branch 'master' of ssh://plrg.eecs.uci.edu/home/git/smartthings-infrastructure
[smartthings-infrastructure.git] / main.groovy
1 //Infrastructure for SmartThings Application
2 //Importing Libraries
3 import groovy.transform.Field
4 import groovy.json.JsonSlurper
5
6 //Importing Classes
7 import ContactSensor.ContactSensor
8 import ContactSensor.ContactSensors
9 import DoorControl.DoorControl
10 import DoorControl.DoorControls
11 import Lock.Lock
12 import Lock.Locks
13 import Thermostat.Thermostat
14 import Thermostat.Thermostats
15 import Switch.Switch
16 import Switch.Switches
17 import PresenceSensor.PresenceSensor
18 import PresenceSensor.PresenceSensors
19 import Logger.Logger
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
34 import Alarm.Alarm
35 import Alarm.Alarms
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
68 import Valve.Valve
69 import Valve.Valves
70 import MobilePresence.MobilePresence
71 import MobilePresence.MobilePresences
72 import Event.Event
73 import AtomicState.AtomicState
74 import Timer.SimulatedTimer
75
76 //JPF's Verify API
77 import gov.nasa.jpf.vm.Verify
78
79 //Global eventHandler
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"]
91
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)
96                 }
97         }
98
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)
103                 }
104         }
105 }
106
107 //GlobalVariables for both Apps
108 //Create a global variable for send event
109 @Field def sendEvent = {eventDataMap -> 
110                         eventHandler(eventDataMap)
111                         }
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)
182
183 //Application #1
184 class App1 {
185         def reference
186         def location
187         def app
188         def atomicState
189
190         //Extracted objects for App1
191         //Object for class temperature measurement!
192         def temperatures
193         //Object for class thermostat!
194         def thermostats
195         //Object for class presence sensor!
196         def automatic
197         //Object for class smoke detector!
198         def detectors
199         //Object for class humidity measurement!
200         def humidities
201         //Object for class water sensor!
202         def waters
203         //Object for class illuminance measurement!
204         def illuminances
205         //Object for class lock!
206         def locks
207         //Object for class contactSensor!
208         def contacts
209         //Object for class Acceleration Sensor!
210         def accelerations
211         //Object for class Motion Sensor!
212         def motions
213         //Object for class presence sensor!
214         def presence
215         //Object for class switch!
216         def switches
217         //Object for class switch level!
218         def dimmerSwitches
219         //Object for class Battery!
220         def batteries
221         //Object for class power meter!
222         def powers
223         //Object for class energy meter!
224         def energys
225         //Global variable for text!
226         def channelKey = "This is just a text!"
227         //Global variable for number!
228         def givenInterval = 75
229
230         //Extracted objects for functions for App1
231         //Global Object for functions in subscribe method!
232         def installed = this.&installed
233         //Global Object for functions in subscribe method!
234         def updated = this.&updated
235         //Global Object for functions in subscribe method!
236         def initialize = this.&initialize
237         //Global Object for functions in subscribe method!
238         def appTouch = this.&appTouch
239         //Global Object for functions in subscribe method!
240         def rescheduleIfNeeded = this.&rescheduleIfNeeded
241         //Global Object for functions in subscribe method!
242         def handleTemperatureEvent = this.&handleTemperatureEvent
243         //Global Object for functions in subscribe method!
244         def handleHumidityEvent = this.&handleHumidityEvent
245         //Global Object for functions in subscribe method!
246         def handleHeatingSetpointEvent = this.&handleHeatingSetpointEvent
247         //Global Object for functions in subscribe method!
248         def handleCoolingSetpointEvent = this.&handleCoolingSetpointEvent
249         //Global Object for functions in subscribe method!
250         def handleThermostatModeEvent = this.&handleThermostatModeEvent
251         //Global Object for functions in subscribe method!
252         def handleFanModeEvent = this.&handleFanModeEvent
253         //Global Object for functions in subscribe method!
254         def handleHumidifierModeEvent = this.&handleHumidifierModeEvent
255         //Global Object for functions in subscribe method!
256         def handleHumidifierLevelEvent = this.&handleHumidifierLevelEvent
257         //Global Object for functions in subscribe method!
258         def handleDehumidifierModeEvent = this.&handleDehumidifierModeEvent
259         //Global Object for functions in subscribe method!
260         def handleDehumidifierLevelEvent = this.&handleDehumidifierLevelEvent
261         //Global Object for functions in subscribe method!
262         def handleVentilatorModeEvent = this.&handleVentilatorModeEvent
263         //Global Object for functions in subscribe method!
264         def handleFanMinOnTimeEvent = this.&handleFanMinOnTimeEvent
265         //Global Object for functions in subscribe method!
266         def handleVentilatorMinOnTimeEvent = this.&handleVentilatorMinOnTimeEvent
267         //Global Object for functions in subscribe method!
268         def handleThermostatOperatingStateEvent = this.&handleThermostatOperatingStateEvent
269         //Global Object for functions in subscribe method!
270         def handleDailyStats = this.&handleDailyStats
271         //Global Object for functions in subscribe method!
272         def handleEquipmentStatusEvent = this.&handleEquipmentStatusEvent
273         //Global Object for functions in subscribe method!
274         def handleProgramNameEvent = this.&handleProgramNameEvent
275         //Global Object for functions in subscribe method!
276         def handleWaterEvent = this.&handleWaterEvent
277         //Global Object for functions in subscribe method!
278         def handleSmokeEvent = this.&handleSmokeEvent
279         //Global Object for functions in subscribe method!
280         def handleCarbonMonoxideEvent = this.&handleCarbonMonoxideEvent
281         //Global Object for functions in subscribe method!
282         def handleIlluminanceEvent = this.&handleIlluminanceEvent
283         //Global Object for functions in subscribe method!
284         def handleLockEvent = this.&handleLockEvent
285         //Global Object for functions in subscribe method!
286         def handleBatteryEvent = this.&handleBatteryEvent
287         //Global Object for functions in subscribe method!
288         def handleContactEvent = this.&handleContactEvent
289         //Global Object for functions in subscribe method!
290         def handleAccelerationEvent = this.&handleAccelerationEvent
291         //Global Object for functions in subscribe method!
292         def handleMotionEvent = this.&handleMotionEvent
293         //Global Object for functions in subscribe method!
294         def handlePresenceEvent = this.&handlePresenceEvent
295         //Global Object for functions in subscribe method!
296         def handleSwitchEvent = this.&handleSwitchEvent
297         //Global Object for functions in subscribe method!
298         def handleSetLevelEvent = this.&handleSetLevelEvent
299         //Global Object for functions in subscribe method!
300         def handlePowerEvent = this.&handlePowerEvent
301         //Global Object for functions in subscribe method!
302         def handleEnergyEvent = this.&handleEnergyEvent
303         //Global Object for functions in subscribe method!
304         def handleCostEvent = this.&handleCostEvent
305         //Global Object for functions in subscribe method!
306         def queueValue = this.&queueValue
307         //Global Object for functions in subscribe method!
308         def processQueue = this.&processQueue
309
310         App1(Object obj) {
311                 reference = obj
312                 location = obj.locationObject
313                 app = obj.appObject
314                 atomicState = obj.atomicState
315                 temperatures = obj.temperatureMeasurementObject
316                 thermostats = obj.thermostatObject
317                 automatic = obj.presenceSensorObject
318                 detectors = obj.smokeDetectorObject
319                 humidities = obj.humidityMeasurementObject
320                 waters = obj.waterSensorObject
321                 illuminances = obj.illuminanceMeasurementObject
322                 locks = obj.lockObject
323                 contacts = obj.contactObject
324                 accelerations = obj.accelerationSensorObject
325                 motions = obj.motionSensorObject
326                 presence = obj.presenceSensorObject
327                 switches = obj.switchObject
328                 dimmerSwitches = obj.switchLevelObject
329                 batteries = obj.batteryObject
330                 powers = obj.powerMeterObject
331                 energys = obj.energyMeterObject
332                 //Global variable for settings!
333                 settings = [app:app, temperatures:temperatures, thermostats:thermostats, automatic:automatic, detectors:detectors, humidities:humidities, waters:waters, illuminances:illuminances, locks:locks, contacts:contacts, accelerations:accelerations, motions:motions, presence:presence, switches:switches, dimmerSwitches:dimmerSwitches, batteries:batteries, powers:powers, energys:energys, channelKey:channelKey, givenInterval:givenInterval]
334         }
335         //Global variables for each app
336         //Global variable for state[mode]
337         def state = [home:[],away:[],night:[]]
338         //Create a global logger object for methods
339         def log = new Logger()
340         //Create a global variable for Functions in Subscribe method
341         def functionList = []
342         //Create a global variable for Objects in Subscribe method
343         def objectList = []
344         //Create a global variable for Events in Subscribe method
345         def eventList = []
346         //Create a global list for function schedulers
347         def timersFuncList = []
348         //Create a global list for timer schedulers
349         def timersList = []
350         //Create a global variable for settings
351         def settings
352         //Zip code
353         def zipCode = 92617
354
355         //Methods
356         /////////////////////////////////////////////////////////////////////
357         def setLocationMode(String mode) {
358                 location.mode = mode
359         }
360         
361         /////////////////////////////////////////////////////////////////////
362         ////subscribe(obj, func)
363         def subscribe(Object obj, Closure FunctionToCall) {
364                 if (obj == app) {
365                         objectList.add(obj)
366                         eventList.add("Touched")
367                         functionList.add(FunctionToCall)
368                 } else if (obj == location) {
369                         objectList.add(obj)
370                         eventList.add("Location")
371                         functionList.add(FunctionToCall)
372                 }
373         }
374         ////subscribe(obj, event, func)
375         def subscribe(Object obj, String event, Closure FunctionToCall) {
376                 objectList.add(obj)
377                 eventList.add(event)
378                 functionList.add(FunctionToCall)
379         }
380         ////subscribe(obj, event, func, data)
381         def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
382                 objectList.add(obj)     
383                 eventList.add(event)
384                 functionList.add(FunctionToCall)
385         }
386         /////////////////////////////////////////////////////////////////////
387         ////runIn(time, func)
388         def runIn(int seconds, Closure functionToCall) {
389                 if (timersFuncList.contains(functionToCall)) {
390                         timersList[timersFuncList.indexOf(functionToCall)].cancel()
391                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
392                 } else {
393                         timersFuncList.add(functionToCall)
394                         timersList.add(new SimulatedTimer())
395                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
396                 }
397         }
398         
399         def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
400                 runIn(seconds, functionToCall)
401         }
402         
403         def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
404                 runIn(seconds, nameOfFunction)
405         }
406         
407         def runIn(int seconds, String nameOfFunction) {
408                 timersFuncList.add(nameOfFunction)
409                 timersList.add(new SimulatedTimer())
410                 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
411                         "$nameOfFunction"()
412                 }
413         }
414         /////////////////////////////////////////////////////////////////////
415         ////unschedule(func)
416         def unschedule(Closure functionToUnschedule) {
417                 for (int i = 0;i < timersFuncList.size();i++) {
418                         if (timersFuncList[i] == functionToUnschedule) {
419                                 if (timersList != null)
420                                         timersList[i].cancel()
421                         }
422                 }
423         }
424         
425         
426         def unschedule() {
427                 for (int i = 0;i < timersFuncList.size();i++) {
428                         if (timersList != null)
429                                 timersList[i].cancel()
430                 }
431         }
432         /////////////////////////////////////////////////////////////////////
433         ////sendNotificationToContacts(text, recipients)
434         def sendNotificationToContacts(String text, String recipients) {
435                 for (int i = 0;i < recipients.size();i++) {
436                         for (int j = 0;j < location.contacts.size();j++) {
437                                 if (recipients[i] == location.contacts[j]) {
438                                         println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
439                                 }
440                         }
441                 }
442         }
443         /////////////////////////////////////////////////////////////////////
444         ////sendSms(phone, text)
445         def sendSms(long phoneNumber, String text) {
446                 println("Sending \""+text+"\" to "+phoneNumber.toString())
447         }
448         
449         def sendSMS(long phoneNumber, String text) {
450                 println("Sending \""+text+"\" to "+phoneNumber.toString())
451         }
452         /////////////////////////////////////////////////////////////////////
453         ////sendPush(text)
454         def sendPush(String text) {
455                 println(text)
456         }
457         /////////////////////////////////////////////////////////////////////
458         ////schedule(time, nameOfFunction as String)
459         def schedule(String time, String nameOfFunction) {
460                 def _inputTime = time.split(':')
461                 Date date = new Date()  
462                 def _currentTime = date.format("HH:mm:ss").split(':')
463         
464                 //Convert input time and current time to minutes
465                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
466                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
467                 def delay
468         
469                 if (inputTime < currentTime) {
470                         delay = 24*60*60-inputTime+currentTime
471                 } else {
472                         delay = inputTime-currentTime
473                 }
474         
475                 timersFuncList.add(nameOfFunction)
476                 timersList.add(new SimulatedTimer())
477                 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
478                         "$nameOfFunction"()
479                 }
480         }
481         ////schedule(time, nameOfFunction as Closure)
482         def schedule(String time, Closure nameOfFunction) {
483                 def _inputTime = time.split(':')
484                 Date date = new Date()  
485                 def _currentTime = date.format("HH:mm:ss").split(':')
486         
487                 //Convert input time and current time to minutes
488                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
489                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
490                 def delay
491         
492                 if (inputTime < currentTime) {
493                         delay = 24*60*60-inputTime+currentTime
494                 } else {
495                         delay = inputTime-currentTime
496                 }
497         
498                 if (timersFuncList.contains(nameOfFunction)) {
499                         timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
500                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
501                 } else {
502                         timersFuncList.add(nameOfFunction)
503                         timersList.add(new SimulatedTimer())
504                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
505                 }
506         }
507         /////////////////////////////////////////////////////////////////////
508         def now() {
509                 return System.currentTimeMillis()
510         }
511         /////////////////////////////////////////////////////////////////////
512         def getTemperatureScale() {
513                 return 'F' //Celsius for now
514         }
515         
516         /////////////////////////////////////////////////////////////////////
517         def getSunriseAndSunset(LinkedHashMap metaData) {
518                 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
519                 return sunRiseSetInfo
520         }
521         /////////////////////////////////////////////////////////////////////
522         def httpPostJson(LinkedHashMap metaData, Closure inputData) {
523                 inputData(metaData)
524         }
525         /////////////////////////////////////////////////////////////////////
526         def runEvery15Minutes(Closure inputData) {
527                 inputData()
528         }
529         /////////////////////////////////////////////////////////////////////
530         def timeToday(String time, Object timeZone) {
531                 def timeOfDay = new Date()
532                 def _inputTime = time.split(':')
533                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
534                 timeOfDay.time = inputTime
535                 return timeOfDay
536         }
537         /////////////////////////////////////////////////////////////////////
538         def sendNotification(String text, LinkedHashMap metaData) {
539                 println("Sending \""+text+"\" to "+metaData.phone.toString())
540         }
541         /////////////////////////////////////////////////////////////////////
542         def canSchedule() {
543                 return true
544         }
545
546         def installed() {
547                 initialize()
548         }
549         
550         def updated() {
551                 unsubscribe()
552                 unschedule()
553                 initialize()
554         }
555         
556         def initialize() {
557                 subscribe(temperatures, "temperature", handleTemperatureEvent)
558                 subscribe(humidities, "humidity", handleHumidityEvent)
559                 subscribe(waters, "water", handleWaterEvent)
560                 subscribe(waters, "water", handleWaterEvent)
561                 subscribe(detectors, "smoke", handleSmokeEvent)
562                 subscribe(detectors, "carbonMonoxide", handleCarbonMonoxideEvent)
563                 subscribe(illuminances, "illuminance", handleIlluminanceEvent)
564                 subscribe(contacts, "contact", handleContactEvent)
565                 subscribe(locks, "lock", handleLockEvent)
566                 subscribe(accelerations, "acceleration", handleAccelerationEvent)
567                 subscribe(motions, "motion", handleMotionEvent)
568                 subscribe(presence, "presence", handlePresenceEvent)
569                 subscribe(switches, "switch", handleSwitchEvent)
570                 subscribe(dimmerSwitches, "switch", handleSwitchEvent)
571                 subscribe(dimmerSwitches, "level", handleSetLevelEvent)
572                 subscribe(batteries, "battery", handleBatteryEvent)
573                 subscribe(powers, "power", handlePowerEvent)
574                 subscribe(energys, "energy", handleEnergyEvent)
575                 subscribe(energys, "cost", handleCostEvent)
576                 subscribe(thermostats, "heatingSetpoint", handleHeatingSetpointEvent)
577                 subscribe(thermostats, "coolingSetpoint", handleCoolingSetpointEvent)
578                 subscribe(thermostats, "thermostatMode", handleThermostatModeEvent)
579                 subscribe(thermostats, "fanMode", handleFanModeEvent)
580                 subscribe(thermostats, "thermostatOperatingState", handleThermostatOperatingStateEvent)
581                 /*subscribe(ecobees, "dehumidifierMode", handleDehumidifierModeEvent)
582                 subscribe(ecobees, "equipmentStatus", handleEquipmentStatusEvent)
583                 subscribe(ecobees, "dehumidifierLevel", handleDehumidifierLevelEvent)
584                 subscribe(ecobees, "humidifierMode", handleHumidifierModeEvent)
585                 subscribe(ecobees, "humidifierLevel", handleHumidifierLevelEvent)
586                 subscribe(ecobees, "fanMinOnTime", handleFanMinOnTimeEvent)
587                 subscribe(ecobees, "ventilatorMode", handleVentilatorModeEvent)
588                 subscribe(ecobees, "ventilatorMinOnTime", handleVentilatorMinOnTimeEvent)
589                 subscribe(ecobees, "programScheduleName", handleProgramNameEvent)
590                 subscribe(ecobees, "auxHeat1RuntimeDaily", handleDailyStats)
591                 subscribe(ecobees, "auxHeat2RuntimeDaily", handleDailyStats)
592                 subscribe(ecobees, "auxHeat3RuntimeDaily", handleDailyStats)
593                 subscribe(ecobees, "compCool1RuntimeDaily", handleDailyStats)
594                 subscribe(ecobees, "compCool2RuntimeDaily", handleDailyStats)
595                 subscribe(ecobees, "fanRuntimeDaily", handleDailyStats)
596                 subscribe(ecobees, "humidifierRuntimeDaily", handleDailyStats)
597                 subscribe(ecobees, "dehumidifierRuntimeDaily", handleDailyStats)
598                 subscribe(ecobees, "ventilatorRuntimeDaily", handleDailyStats)
599                 subscribe(ecobees, "presence", handlePresenceEvent)
600                 subscribe(ecobees, "compCool2RuntimeDaily", handleDailyStats)*/
601                 subscribe(automatic, "yesterdayTripsAvgAverageKmpl",handleDailyStats)
602                 subscribe(automatic, "yesterdayTripsAvgDistanceM",handleDailyStats)
603                 subscribe(automatic, "yesterdayTripsAvgDurationS",handleDailyStats)
604                 subscribe(automatic, "yesterdayTotalDistanceM",handleDailyStats)
605                 subscribe(automatic, "yesterdayTripsAvgFuelVolumeL",handleDailyStats)
606                 subscribe(automatic, "yesterdayTotalFuelVolumeL",handleDailyStats)
607                 subscribe(automatic, "yesterdayTotalDurationS:",handleDailyStats)
608                 subscribe(automatic, "yesterdayTotalNbTrips",handleDailyStats)
609                 subscribe(automatic, "yesterdayTotalHardAccels",handleDailyStats)
610                 subscribe(automatic, "yesterdayTotalHardBrakes:",handleDailyStats)
611                 subscribe(automatic, "yesterdayTripsAvgScoreSpeeding",handleDailyStats)
612                 subscribe(automatic, "yesterdayTripsAvgScoreEvents",handleDailyStats)
613                 def queue = []
614                 atomicState.queue=queue
615             
616                 if (atomicState.queue==null) {
617                         atomicState.queue = []
618                 }    
619                 atomicState?.poll = [ last: 0, rescheduled: now() ]
620         
621                 Integer delay  = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
622                 log.debug "initialize>scheduling processQueue every ${delay} minutes"
623         
624                 //Subscribe to different events (ex. sunrise and sunset events) to trigger rescheduling if needed
625                 subscribe(location, "sunrise", rescheduleIfNeeded)
626                 subscribe(location, "sunset", rescheduleIfNeeded)
627                 subscribe(location, "mode", rescheduleIfNeeded)
628                 subscribe(location, "sunriseTime", rescheduleIfNeeded)
629                 subscribe(location, "sunsetTime", rescheduleIfNeeded)
630                 subscribe(app, appTouch)
631         
632                 //rescheduleIfNeeded()   
633         }
634         
635         def appTouch(evt) {
636                 rescheduleIfNeeded(evt)
637                 processQueue()
638                 def queue = []
639                 atomicState.queue=queue
640         }
641         
642         
643         def rescheduleIfNeeded(evt) {
644                 if (evt) log.debug("rescheduleIfNeeded>$evt.name=$evt.value")
645                 Integer delay  = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
646                 BigDecimal currentTime = now()    
647                 BigDecimal lastPollTime = (currentTime - (atomicState?.poll["last"]?:0))  
648                 if (lastPollTime != currentTime) {    
649                         Double lastPollTimeInMinutes = (lastPollTime/60000).toDouble().round(1)      
650                         log.info "rescheduleIfNeeded>last poll was  ${lastPollTimeInMinutes.toString()} minutes ago"
651                 }
652                 if (((atomicState?.poll["last"]?:0) + (delay * 60000) < currentTime) && canSchedule()) {
653                         log.info "rescheduleIfNeeded>scheduling processQueue in ${delay} minutes.."
654                         unschedule()  
655                         schedule("14:00", processQueue)
656                 }
657                 // Update rescheduled state
658             
659                 if (!evt) {
660                         atomicState.poll["rescheduled"] = now()    
661                 }        
662         }    
663         
664         def handleTemperatureEvent(evt) {
665                 queueValue(evt) {
666                         it.toString()
667                 }
668         }
669         
670         def handleHumidityEvent(evt) {
671                 queueValue(evt) {
672                         it.toString()
673                 }
674         }
675         
676         def handleHeatingSetpointEvent(evt) {
677                 queueValue(evt) {
678                         it.toString()
679                 }
680         }
681         def handleCoolingSetpointEvent(evt) {
682                 queueValue(evt) {
683                         it.toString()
684                 }
685         }
686         
687         def handleThermostatModeEvent(evt) {
688                 queueValue(evt) {
689                         it.toString()
690                 }
691         }
692         def handleFanModeEvent(evt) {
693                 queueValue(evt) {
694                         it.toString()
695                 }
696         }
697         def handleHumidifierModeEvent(evt) {
698                 queueValue(evt) {
699                         it.toString()
700                 }
701         }
702         def handleHumidifierLevelEvent(evt) {
703                 queueValue(evt) {
704                         it.toString()
705                 }
706         }
707         def handleDehumidifierModeEvent(evt) {
708                 queueValue(evt) {
709                         it.toString()
710                 }
711         }
712         def handleDehumidifierLevelEvent(evt) {
713                 queueValue(evt) {
714                         it.toString()
715                 }
716         }
717         def handleVentilatorModeEvent(evt) {
718                 queueValue(evt) {
719                         it.toString()
720                 }
721         }
722         def handleFanMinOnTimeEvent(evt) {
723                 queueValue(evt) {
724                         it.toString()
725                 }
726         }
727         def handleVentilatorMinOnTimeEvent(evt) {
728                 queueValue(evt) {
729                         it.toString()
730                 }
731         }
732         
733         def handleThermostatOperatingStateEvent(evt) {
734                 queueValue(evt) {
735                         it == "idle" ? 0 : (it == 'fan only') ? 1 : (it == 'heating') ? 2 : 3
736                 }
737         
738         }
739         def handleDailyStats(evt) {
740                 queueValue(evt) {
741                         it.toString()
742                 }
743         
744         }
745         def handleEquipmentStatusEvent(evt) {
746                 queueValue(evt) {
747                         it.toString()
748                 }
749         }
750         
751         def handleProgramNameEvent(evt) {
752                 queueValue(evt) {
753                         it.toString()
754                 }
755         }
756         
757         def handleWaterEvent(evt) {
758                 queueValue(evt) {
759                         it.toString()
760                 }
761         }
762         def handleSmokeEvent(evt) {
763                 queueValue(evt) {
764                         it.toString()
765                 }
766         }
767         def handleCarbonMonoxideEvent(evt) {
768                 queueValue(evt) {
769                         it.toString()
770                 }
771         }
772         
773         def handleIlluminanceEvent(evt) {
774                 log.debug ("handleIlluminanceEvent> $evt.name= $evt.value")
775                 queueValue(evt) {
776                         it.toString()
777                 }
778         }
779         
780         def handleLockEvent(evt) {
781                 queueValue(evt) {
782                         it == "locked" ? 1 : 0
783                 }
784         }
785         
786         def handleBatteryEvent(evt) {
787                 queueValue(evt) {
788                         it.toString()
789                 }
790         }
791         
792         def handleContactEvent(evt) {
793                 queueValue(evt) {
794                         it == "open" ? 1 : 0
795                 }
796         }
797         
798         def handleAccelerationEvent(evt) {
799                 queueValue(evt) {
800                         it == "active" ? 1 : 0
801                 }
802         }
803         
804         def handleMotionEvent(evt) {
805                 queueValue(evt) {
806                         it == "active" ? 1 : 0
807                 }
808         }
809         
810         def handlePresenceEvent(evt) {
811                 queueValue(evt) {
812                         it == "present" ? 1 : 0
813                 }
814         }
815         
816         def handleSwitchEvent(evt) {
817                 queueValue(evt) {
818                         it == "on" ? 1 : 0
819                 }
820         }
821         
822         def handleSetLevelEvent(evt) {
823                 queueValue(evt) {
824                         it.toString()
825                 }
826         }
827         
828         def handlePowerEvent(evt) {
829                 if (evt.value) {
830                         queueValue(evt) {
831                                 it.toString()
832                         }
833                 }
834         }
835         
836         def handleEnergyEvent(evt) {
837                 if (evt.value) {
838                         queueValue(evt) {
839                                 it.toString()
840                         }
841                 }
842         }
843         def handleCostEvent(evt) {
844                 if (evt.value) {
845                         queueValue(evt) {
846                                 it.toString()
847                         }
848                 }
849         }
850         
851         private queueValue(evt, Closure convert) {
852                 def MAX_QUEUE_SIZE=95000
853                 def jsonPayload = [compId: evt.displayName, streamId: evt.name, data: convert(evt.value), time: now()]
854                 def queue
855         
856                 queue = atomicState.queue
857                 queue << jsonPayload
858                 atomicState.queue = queue    
859                 def queue_size = queue.toString().length()
860                 def last_item_in_queue = queue[queue.size() -1]    
861                 log.debug "queueValue>queue size in chars=${queue_size}, appending ${jsonPayload} to queue, last item in queue= $last_item_in_queue"
862                 if (queue_size >  MAX_QUEUE_SIZE) {
863                         processQueue()
864                 }
865         }
866         
867         def processQueue() {
868                 Integer delay  = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
869                 atomicState?.poll["last"] = now()
870         
871                 if (((atomicState?.poll["rescheduled"]?:0) + (delay * 60000)) < now()) {
872                         log.info "processQueue>scheduling rescheduleIfNeeded() in ${delay} minutes.."
873                         schedule("0 0/${delay} * * * ?", rescheduleIfNeeded)
874                         // Update rescheduled state
875                         atomicState?.poll["rescheduled"] = now()
876                 }
877         
878                 def queue = atomicState.queue
879             
880            
881                 def url = "https://grovestreams.com/api/feed?api_key=${channelKey}"
882                 log.debug "processQueue"
883                 if (queue != []) {
884                         log.debug "Events to be sent to groveStreams: ${queue}"
885         
886                         /*try {
887                                 httpPutJson([uri: url, body: queue]) {response ->
888                                         if (response.status != 200) {
889                                                 log.debug "GroveStreams logging failed, status = ${response.status}"
890                                         } else {
891                                                 log.debug "GroveStreams accepted event(s)"
892                                                 // reset the queue 
893                                                 queue =[]                         
894                                                 atomicState.queue = queue                     
895                                         }
896                                 }
897                         } catch (groovyx.net.http.ResponseParseException e) {
898                                 // ignore error 200, bogus exception
899                                 if (e.statusCode != 200) {
900                                         log.error "Grovestreams: ${e}"
901                                 } else {
902                                         log.debug "GroveStreams accepted event(s)"
903                                 }
904                                 // reset the queue 
905                                 queue =[]                         
906                                 atomicState.queue = queue                      
907                     
908                         } catch (e) {
909                                 def errorInfo = "Error sending value: ${e}"
910                                 log.error errorInfo
911                                 // reset the queue 
912                                 queue =[]                         
913                                 atomicState.queue = queue                        
914                         }*/
915                 }
916         
917         }
918         
919 }
920
921
922 //Application #2
923 class App2 {
924         def reference
925         def location
926         def app
927         def atomicState
928
929         //Extracted objects for App2
930         //Object for class lock!
931         def aLock
932         //Object for class contactSensor!
933         def openSensor
934         //Global variable for number!
935         def duration = 47
936         //Global variable for boolean!
937         def pushNotification = "0"
938         //Global variable for phone!
939         def phoneNumber = 9495379373
940         //Global variable for boolean!
941         def lockIfClosed = "1"
942
943         //Extracted objects for functions for App2
944         //Global Object for functions in subscribe method!
945         def installed = this.&installed
946         //Global Object for functions in subscribe method!
947         def updated = this.&updated
948         //Global Object for functions in subscribe method!
949         def initialize = this.&initialize
950         //Global Object for functions in subscribe method!
951         def lockHandler = this.&lockHandler
952         //Global Object for functions in subscribe method!
953         def notifyUnlocked = this.&notifyUnlocked
954         //Global Object for functions in subscribe method!
955         def sendMessage = this.&sendMessage
956
957         App2(Object obj) {
958                 reference = obj
959                 location = obj.locationObject
960                 app = obj.appObject
961                 atomicState = obj.atomicState
962                 aLock = obj.lockObject
963                 openSensor = obj.contactObject
964                 //Global variable for settings!
965                 settings = [app:app, aLock:aLock, openSensor:openSensor, duration:duration, pushNotification:pushNotification, phoneNumber:phoneNumber, lockIfClosed:lockIfClosed]
966         }
967         //Global variables for each app
968         //Global variable for state[mode]
969         def state = [home:[],away:[],night:[]]
970         //Create a global logger object for methods
971         def log = new Logger()
972         //Create a global variable for Functions in Subscribe method
973         def functionList = []
974         //Create a global variable for Objects in Subscribe method
975         def objectList = []
976         //Create a global variable for Events in Subscribe method
977         def eventList = []
978         //Create a global list for function schedulers
979         def timersFuncList = []
980         //Create a global list for timer schedulers
981         def timersList = []
982         //Create a global variable for settings
983         def settings
984         //Zip code
985         def zipCode = 92617
986
987         //Methods
988         /////////////////////////////////////////////////////////////////////
989         def setLocationMode(String mode) {
990                 location.mode = mode
991         }
992         
993         /////////////////////////////////////////////////////////////////////
994         ////subscribe(obj, func)
995         def subscribe(Object obj, Closure FunctionToCall) {
996                 if (obj == app) {
997                         objectList.add(obj)
998                         eventList.add("Touched")
999                         functionList.add(FunctionToCall)
1000                 } else if (obj == location) {
1001                         objectList.add(obj)
1002                         eventList.add("Location")
1003                         functionList.add(FunctionToCall)
1004                 }
1005         }
1006         ////subscribe(obj, event, func)
1007         def subscribe(Object obj, String event, Closure FunctionToCall) {
1008                 objectList.add(obj)
1009                 eventList.add(event)
1010                 functionList.add(FunctionToCall)
1011         }
1012         ////subscribe(obj, event, func, data)
1013         def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
1014                 objectList.add(obj)     
1015                 eventList.add(event)
1016                 functionList.add(FunctionToCall)
1017         }
1018         /////////////////////////////////////////////////////////////////////
1019         ////runIn(time, func)
1020         def runIn(int seconds, Closure functionToCall) {
1021                 if (timersFuncList.contains(functionToCall)) {
1022                         timersList[timersFuncList.indexOf(functionToCall)].cancel()
1023                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
1024                 } else {
1025                         timersFuncList.add(functionToCall)
1026                         timersList.add(new SimulatedTimer())
1027                         def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
1028                 }
1029         }
1030         
1031         def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
1032                 runIn(seconds, functionToCall)
1033         }
1034         
1035         def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
1036                 runIn(seconds, nameOfFunction)
1037         }
1038         
1039         def runIn(int seconds, String nameOfFunction) {
1040                 timersFuncList.add(nameOfFunction)
1041                 timersList.add(new SimulatedTimer())
1042                 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
1043                         "$nameOfFunction"()
1044                 }
1045         }
1046         /////////////////////////////////////////////////////////////////////
1047         ////unschedule(func)
1048         def unschedule(Closure functionToUnschedule) {
1049                 for (int i = 0;i < timersFuncList.size();i++) {
1050                         if (timersFuncList[i] == functionToUnschedule) {
1051                                 if (timersList != null)
1052                                         timersList[i].cancel()
1053                         }
1054                 }
1055         }
1056         
1057         
1058         def unschedule() {
1059                 for (int i = 0;i < timersFuncList.size();i++) {
1060                         if (timersList != null)
1061                                 timersList[i].cancel()
1062                 }
1063         }
1064         /////////////////////////////////////////////////////////////////////
1065         ////sendNotificationToContacts(text, recipients)
1066         def sendNotificationToContacts(String text, String recipients) {
1067                 for (int i = 0;i < recipients.size();i++) {
1068                         for (int j = 0;j < location.contacts.size();j++) {
1069                                 if (recipients[i] == location.contacts[j]) {
1070                                         println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
1071                                 }
1072                         }
1073                 }
1074         }
1075         /////////////////////////////////////////////////////////////////////
1076         ////sendSms(phone, text)
1077         def sendSms(long phoneNumber, String text) {
1078                 println("Sending \""+text+"\" to "+phoneNumber.toString())
1079         }
1080         
1081         def sendSMS(long phoneNumber, String text) {
1082                 println("Sending \""+text+"\" to "+phoneNumber.toString())
1083         }
1084         /////////////////////////////////////////////////////////////////////
1085         ////sendPush(text)
1086         def sendPush(String text) {
1087                 println(text)
1088         }
1089         /////////////////////////////////////////////////////////////////////
1090         ////schedule(time, nameOfFunction as String)
1091         def schedule(String time, String nameOfFunction) {
1092                 def _inputTime = time.split(':')
1093                 Date date = new Date()  
1094                 def _currentTime = date.format("HH:mm:ss").split(':')
1095         
1096                 //Convert input time and current time to minutes
1097                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
1098                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
1099                 def delay
1100         
1101                 if (inputTime < currentTime) {
1102                         delay = 24*60*60-inputTime+currentTime
1103                 } else {
1104                         delay = inputTime-currentTime
1105                 }
1106         
1107                 timersFuncList.add(nameOfFunction)
1108                 timersList.add(new SimulatedTimer())
1109                 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
1110                         "$nameOfFunction"()
1111                 }
1112         }
1113         ////schedule(time, nameOfFunction as Closure)
1114         def schedule(String time, Closure nameOfFunction) {
1115                 def _inputTime = time.split(':')
1116                 Date date = new Date()  
1117                 def _currentTime = date.format("HH:mm:ss").split(':')
1118         
1119                 //Convert input time and current time to minutes
1120                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
1121                 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
1122                 def delay
1123         
1124                 if (inputTime < currentTime) {
1125                         delay = 24*60*60-inputTime+currentTime
1126                 } else {
1127                         delay = inputTime-currentTime
1128                 }
1129         
1130                 if (timersFuncList.contains(nameOfFunction)) {
1131                         timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
1132                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
1133                 } else {
1134                         timersFuncList.add(nameOfFunction)
1135                         timersList.add(new SimulatedTimer())
1136                         def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
1137                 }
1138         }
1139         /////////////////////////////////////////////////////////////////////
1140         def now() {
1141                 return System.currentTimeMillis()
1142         }
1143         /////////////////////////////////////////////////////////////////////
1144         def getTemperatureScale() {
1145                 return 'F' //Celsius for now
1146         }
1147         
1148         /////////////////////////////////////////////////////////////////////
1149         def getSunriseAndSunset(LinkedHashMap metaData) {
1150                 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
1151                 return sunRiseSetInfo
1152         }
1153         /////////////////////////////////////////////////////////////////////
1154         def httpPostJson(LinkedHashMap metaData, Closure inputData) {
1155                 inputData(metaData)
1156         }
1157         /////////////////////////////////////////////////////////////////////
1158         def runEvery15Minutes(Closure inputData) {
1159                 inputData()
1160         }
1161         /////////////////////////////////////////////////////////////////////
1162         def timeToday(String time, Object timeZone) {
1163                 def timeOfDay = new Date()
1164                 def _inputTime = time.split(':')
1165                 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
1166                 timeOfDay.time = inputTime
1167                 return timeOfDay
1168         }
1169         /////////////////////////////////////////////////////////////////////
1170         def sendNotification(String text, LinkedHashMap metaData) {
1171                 println("Sending \""+text+"\" to "+metaData.phone.toString())
1172         }
1173         /////////////////////////////////////////////////////////////////////
1174         def canSchedule() {
1175                 return true
1176         }
1177
1178         def installed()
1179         {
1180             initialize()
1181         }
1182         
1183         def updated()
1184         {
1185             unsubscribe()
1186             initialize()
1187         }
1188         
1189         def initialize()
1190         {
1191             log.trace "Initializing with: ${settings}"
1192             subscribe(aLock, "lock", lockHandler)
1193         }
1194         
1195         def lockHandler(evt)
1196         {
1197             log.trace "${evt.name} is ${evt.value}."
1198             if (evt.value == "locked") {
1199                 log.debug "Canceling lock check because the door is locked..."
1200                 unschedule(notifyUnlocked)
1201             }
1202             else {
1203                 log.debug "Starting the countdown for ${duration} minutes..."
1204                 state.retries = 0
1205                 runIn(duration * 60, notifyUnlocked)
1206             }
1207         }
1208         
1209         def notifyUnlocked()
1210         {
1211             // if no open/close sensor specified, assume the door is closed
1212             def open = openSensor?.latestValue("contact") ?: "closed"
1213         
1214             def message = "${aLock.displayName} is left unlocked and ${open} for more than ${duration} minutes."
1215             log.trace "Sending the notification: ${message}."
1216             sendMessage(message)
1217         
1218             if (lockIfClosed) {
1219                 if (open == "closed") {
1220                     log.trace "And locking the door."
1221                     sendMessage("Locking the ${aLock.displayName} as prescribed.")
1222                     aLock.lock()
1223                 }
1224                 else {
1225                     if (state.retries++ < 3) {
1226                         log.trace "Door is open, can't lock. Rescheduling the check."
1227                         sendMessage("Can't lock the ${aLock.displayName} because the door is open. Will try again in ${duration} minutes.")
1228                         runIn(duration * 60, notifyUnlocked)
1229                     }
1230                     else {
1231                         log.trace "The door is still open after ${state.retries} retries, giving up."
1232                         sendMessage("Unable to lock the ${aLock.displayName} after ${state.retries} retries, giving up.")
1233                     }
1234                 }
1235             }
1236         }
1237         
1238         def sendMessage(msg) {
1239             if (pushNotification) {
1240                 sendPush(msg)
1241             }
1242             if (phoneNumber) {
1243                 sendSMS(phoneNumber, msg)
1244             }
1245         }
1246         
1247 }
1248
1249 @Field def app1
1250 @Field def app2
1251 def initOrder = Verify.getBoolean()
1252 if (initOrder) {
1253         app1 = new App1(this)
1254         app2 = new App2(this)
1255 } else {
1256         app2 = new App2(this)
1257         app1 = new App1(this)
1258 }
1259
1260 def installOrder = Verify.getBoolean()
1261 if (installOrder) {
1262         app1.installed()
1263         app2.installed()
1264 } else {
1265         app2.installed()
1266         app1.installed()
1267 }
1268
1269 while(true) {
1270         def eventNumber = Verify.getInt(0,53)
1271         switch(eventNumber) {
1272                 case 0:
1273                         break
1274                 case 1:
1275                         break
1276                 case 2:
1277                         break
1278                 case 3:
1279                         def event = Verify.getInt(0,2)
1280                         if (event == 0) {
1281                                         smokeDetectorObject.setValue([name: "smoke", value: "clear", deviceId: "smokeDetectorID0", descriptionText: "",
1282                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1283                         } else if (event == 1) {
1284                                         smokeDetectorObject.setValue([name: "smoke", value: "detected", deviceId: "smokeDetectorID0", descriptionText: "",
1285                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1286                         } else {
1287                                         smokeDetectorObject.setValue([name: "smoke", value: "tested", deviceId: "smokeDetectorID0", descriptionText: "",
1288                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1289                         }
1290                         break
1291                 case 4:
1292                         def event = Verify.getInt(0,2)
1293                         if (event == 0) {
1294                                         smokeDetectorObject.setValue([name: "carbonMonoxide", value: "clear", deviceId: "smokeDetectorID0", descriptionText: "",
1295                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1296                         } else if (event == 1) {
1297                                         smokeDetectorObject.setValue([name: "carbonMonoxide", value: "detected", deviceId: "smokeDetectorID0", descriptionText: "",
1298                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1299                         } else {
1300                                         smokeDetectorObject.setValue([name: "carbonMonoxide", value: "tested", deviceId: "smokeDetectorID0", descriptionText: "",
1301                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1302                         }
1303                         break
1304                 case 5:
1305                         break
1306                 case 6:
1307                         break
1308                 case 7:
1309                         lockObject.setValue([name: "lock", value: "locked", deviceId: "lockID0", descriptionText: "",
1310                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1311                         break
1312                 case 8:
1313                         def event = Verify.getInt(0,1)
1314                         if (event == 0) {
1315                                         accelerationSensorObject.setValue([name: "acceleration", value: "active", deviceId: "accelerationSensorID0", descriptionText: "",
1316                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1317                         } else {
1318                                         accelerationSensorObject.setValue([name: "acceleration", value: "inactive", deviceId: "accelerationSensorID0", descriptionText: "",
1319                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1320                         }
1321                         break
1322                 case 9:
1323                         def event = Verify.getInt(0,1)
1324                         if (event == 0) {
1325                                         motionSensorObject.setValue([name: "motion", value: "active", deviceId: "motionSensorID0", descriptionText: "",
1326                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1327                         } else {
1328                                         motionSensorObject.setValue([name: "motion", value: "inactive", deviceId: "motionSensorID0", descriptionText: "",
1329                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1330                         }
1331                         break
1332                 case 10:
1333                         def event = Verify.getInt(0,1)
1334                         if (event == 0) {
1335                                         presenceSensorObject.setValue([name: "presence", value: "present", deviceId: "presenceSensorID0", descriptionText: "",
1336                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"1","dni":"mobile0"}'])
1337                         } else {
1338                                         presenceSensorObject.setValue([name: "presence", value: "not present", deviceId: "presenceSensorID0", descriptionText: "",
1339                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"0","dni":"mobile0"}'])
1340                         }
1341                         break
1342                 case 11:
1343                         def event = Verify.getInt(0,1)
1344                         if (event == 0) {
1345                                         switchObject.setValue([name: "switch", value: "on", deviceId: "switchID0", descriptionText: "",
1346                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1347                         } else {
1348                                         switchObject.setValue([name: "switch", value: "off", deviceId: "switchID0", descriptionText: "",
1349                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1350                         }
1351                         break
1352                 case 12:
1353                         break
1354                 case 13:
1355                         smokeDetectorObject.setValue([name: "battery", value: "5", deviceId: "smokeDetectorID0", descriptionText: "",
1356                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1357                         break
1358                 case 14:
1359                         break
1360                 case 15:
1361                         break
1362                 case 16:
1363                         break
1364                 case 17:
1365                         break
1366                 case 18:
1367                         break
1368                 case 19:
1369                         def event = Verify.getInt(0,4)
1370                         if (event == 0) {
1371                                         thermostatObject.setValue([name: "thermostatMode", value: "auto", deviceId: "thermostatID0", descriptionText: "",
1372                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1373                         } else if (event == 1) {
1374                                         thermostatObject.setValue([name: "thermostatMode", value: "cool", deviceId: "thermostatID0", descriptionText: "",
1375                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1376                         } else if (event == 2) {
1377                                         thermostatObject.setValue([name: "thermostatMode", value: "emergencyHeat", deviceId: "thermostatID0", descriptionText: "",
1378                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1379                         } else if (event == 3) {
1380                                         thermostatObject.setValue([name: "thermostatMode", value: "heat", deviceId: "thermostatID0", descriptionText: "",
1381                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1382                         } else {
1383                                         thermostatObject.setValue([name: "thermostatMode", value: "off", deviceId: "thermostatID0", descriptionText: "",
1384                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1385                         }
1386                         break
1387                 case 20:
1388                         break
1389                 case 21:
1390                         break
1391                 case 22:
1392                         break
1393                 case 23:
1394                         break
1395                 case 24:
1396                         break
1397                 case 25:
1398                         break
1399                 case 26:
1400                         break
1401                 case 27:
1402                         break
1403                 case 28:
1404                         break
1405                 case 29:
1406                         break
1407                 case 30:
1408                         break
1409                 case 31:
1410                         break
1411                 case 32:
1412                         break
1413                 case 33:
1414                         break
1415                 case 34:
1416                         break
1417                 case 35:
1418                         break
1419                 case 36:
1420                         break
1421                 case 37:
1422                         break
1423                 case 38:
1424                         break
1425                 case 39:
1426                         break
1427                 case 40:
1428                         break
1429                 case 41:
1430                         break
1431                 case 42:
1432                         break
1433                 case 43:
1434                         break
1435                 case 44:
1436                         break
1437                 case 45:
1438                         break
1439                 case 46:
1440                         break
1441                 case 47:
1442                         break
1443                 case 48:
1444                         break
1445                 case 49:
1446                         break
1447                 case 50:
1448                         break
1449                 case 51:
1450                         break
1451                 case 52:
1452                         def event = Verify.getInt(0,2)
1453                         if (event == 0) {
1454                                         locationObject.setValue([name: "Location", value: "home", deviceId: "locationID0", descriptionText: "",
1455                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1456                         } else if (event == 1) {
1457                                         locationObject.setValue([name: "Location", value: "away", deviceId: "locationID0", descriptionText: "",
1458                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1459                         } else {
1460                                         locationObject.setValue([name: "Location", value: "night", deviceId: "locationID0", descriptionText: "",
1461                                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1462                         }
1463                         break
1464                 case 53:
1465                         appObject.setValue([name: "Touched", value: "touched", deviceId: "touchedSensorID0", descriptionText: "",
1466                                         displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1467                         break
1468         }
1469 }