sme minor changes
[smartthings-infrastructure.git] / main.groovy
index e406b84248a0ca0013e123ccc59bf4f78b510c4e..a1474f1fcd60a238b457107787b61fcc76bf007f 100644 (file)
 //Infrastructure for SmartThings Application
 //Importing Libraries
 import groovy.transform.Field
+import groovy.json.JsonSlurper
 
 //Importing Classes
-import ContactSensor.contacting
-import ContactSensor.contacts
-import Lock.locking
-import Lock.locks
-import Switch.switching
-import Switch.switches
-import Event.Event
+import ContactSensor.ContactSensor
+import ContactSensor.ContactSensors
+import DoorControl.DoorControl
+import DoorControl.DoorControls
+import Lock.Lock
+import Lock.Locks
+import Thermostat.Thermostat
+import Thermostat.Thermostats
+import Switch.Switch
+import Switch.Switches
+import PresenceSensor.PresenceSensor
+import PresenceSensor.PresenceSensors
 import Logger.Logger
-import Location.locationVar
+import Location.LocationVar
 import Location.Phrase
-import appTouch.Touch
+import appTouch.Touched
+import NfcTouch.NfcTouch
+import AeonKeyFob.AeonKeyFob
+import AeonKeyFob.AeonKeyFobs
+import MusicPlayer.MusicPlayer
+import MusicPlayer.MusicPlayers
+import MotionSensor.MotionSensor
+import MotionSensor.MotionSensors
+import ImageCapture.ImageCapture
+import ImageCapture.ImageCaptures
+import SmokeDetector.SmokeDetector
+import SmokeDetector.SmokeDetectors
+import Alarm.Alarm
+import Alarm.Alarms
+import SpeechSynthesis.SpeechSynthesis
+import SpeechSynthesis.SpeechSynthesises
+import AccelerationSensor.AccelerationSensor
+import AccelerationSensor.AccelerationSensors
+import Battery.Battery
+import Battery.Batteries
+import BeaconSensor.BeaconSensor
+import BeaconSensor.BeaconSensors
+import CarbonMonoxideDetector.CarbonMonoxideDetector
+import CarbonMonoxideDetector.CarbonMonoxideDetectors
+import ColorControl.ColorControl
+import ColorControl.ColorControls
+import EnergyMeter.EnergyMeter
+import EnergyMeter.EnergyMeters
+import IlluminanceMeasurement.IlluminanceMeasurement
+import IlluminanceMeasurement.IlluminanceMeasurements
+import PowerMeter.PowerMeter
+import PowerMeter.PowerMeters
+import RelativeHumidityMeasurement.RelativeHumidityMeasurement
+import RelativeHumidityMeasurement.RelativeHumidityMeasurements
+import RelaySwitch.RelaySwitch
+import RelaySwitch.RelaySwitches
+import SleepSensor.SleepSensor
+import SleepSensor.SleepSensors
+import StepSensor.StepSensor
+import StepSensor.StepSensors
+import SwitchLevel.SwitchLevel
+import SwitchLevel.SwitchLevels
+import TemperatureMeasurement.TemperatureMeasurement
+import TemperatureMeasurement.TemperatureMeasurements
+import WaterSensor.WaterSensor
+import WaterSensor.WaterSensors
+import Valve.Valve
+import Valve.Valves
+import MobilePresence.MobilePresence
+import MobilePresence.MobilePresences
+import Event.Event
+import AtomicState.AtomicState
+import Timer.SimulatedTimer
 
-//GlobalVariables
-//create a location object to change the variable inside the class
-@Field def location = new locationVar()
-//Settings variable defined to settings on purpose
-@Field def settings = "Settings"
-//Global variable for state[mode]
-@Field def state = [home:[],away:[],night:[]]
-//Create a global logger object for methods
-@Field def log = new Logger()
-//Create a global object for app
-@Field def app = new Touch(1)
-//Create a global list for objects for events on subscribe methods
-@Field def ObjectList = []
-//Create a global list for events
-@Field def EventList = []
-//Create a global list for function calls based on corresponding events
-@Field def FunctionList = []
-//Create a global list for function schedulers
-@Field def ListofTimersFunc = []
-//Create a global list for timer schedulers
-@Field def ListofTimers = []
+//JPF's Verify API
+import gov.nasa.jpf.vm.Verify
 
+//Global eventHandler
+/////////////////////////////////////////////////////////////////////
+def eventHandler(LinkedHashMap eventDataMap) {
+       def value = eventDataMap["value"]
+       def name = eventDataMap["name"]
+       def deviceId = eventDataMap["deviceId"]
+       def descriptionText = eventDataMap["descriptionText"]
+       def displayed = eventDataMap["displayed"]
+       def linkText = eventDataMap["linkText"]
+       def isStateChange = eventDataMap["isStateChange"]
+       def unit = eventDataMap["unit"]
+       def data = eventDataMap["data"]
 
+       for (int i = 0;i < app2.eventList.size();i++) {
+               if (app2.eventList[i] == name) {
+                       def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
+                       app2.functionList[i](event)
+               }
+       }
 
-//ExtractedObjects
-//Global Object for class switch!
-@Field def switchesoff = new switching(1)
+       for (int i = 0;i < app1.eventList.size();i++) {
+               if (app1.eventList[i] == name) {
+                       def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
+                       app1.functionList[i](event)
+               }
+       }
+}
+
+//GlobalVariables for both Apps
+//Create a global variable for send event
+@Field def sendEvent = {eventDataMap -> 
+                       eventHandler(eventDataMap)
+                       }
+//Object for location
+@Field def locationObject = new LocationVar(sendEvent)
+//Object for touch to call function
+@Field def appObject = new Touched(sendEvent, 0)
+//Create a global list for events
+//@Field def evt = []
+//Global Object for class AtomicState!
+@Field def atomicState = new AtomicState()
+//Global Object for class Touch Sensor!
+@Field def touchSensorObject = new NfcTouch(sendEvent, 1)
 //Global Object for class switch!
-@Field def switcheson = new switching(1)
+@Field def switchObject = new Switches(sendEvent, 1)
 //Global Object for class lock!
-@Field def lock1 = new locking(1)
-//Global variable for mode!
-@Field def newMode = "away"
-//Global variable for number!
-@Field def waitfor = 5
-//Global Object for functions in subscribe method!
-@Field def installed = this.&installed
-//Global Object for functions in subscribe method!
-@Field def updated = this.&updated
-//Global Object for functions in subscribe method!
-@Field def appTouch = this.&appTouch
+@Field def lockObject = new Locks(sendEvent, 1)
+//Global Object for class door control!
+@Field def doorControlObject = new DoorControls(sendEvent, 1)
+//Global Object for class contact sensor!
+@Field def contactObject = new ContactSensors(sendEvent, 1)
+//Global Object for class presence sensor!
+@Field def presenceSensorObject = new PresenceSensors(sendEvent, 1)
+//Global Object for class thermostat!
+@Field def thermostatObject = new Thermostats(sendEvent, 1)
+//Global Object for class aeon key fob!
+@Field def aeonKeyFobObject = new AeonKeyFobs(sendEvent, 1)
+//Global Object for class music player!
+@Field def musicPlayerObject = new MusicPlayers(sendEvent, 1)
+//Global Object for class motion sensor!
+@Field def motionSensorObject = new MotionSensors(sendEvent, 1)
+//Global Object for class image capture!
+@Field def imageCaptureObject = new ImageCaptures(sendEvent, 1)
+//Global Object for class smoke detector!
+@Field def smokeDetectorObject = new SmokeDetectors(sendEvent, 1)
+//Global Object for class alarm!
+@Field def alarmObject = new Alarms(sendEvent, 1)
+//Global Object for class speech synthesis!
+@Field def speechSynthesisObject = new SpeechSynthesises(sendEvent, 1)
+//Global Object for class acceleration sensor!
+@Field def accelerationSensorObject = new AccelerationSensors(sendEvent, 1)
+//Global Object for class Battery!
+@Field def batteryObject = new Batteries(sendEvent, 1)
+//Global Object for class beacon sensor!
+@Field def beaconSensorObject = new BeaconSensors(sendEvent, 1)
+//Global Object for class carbon monoxide!
+@Field def carbonMonoxideDetectorObject = new CarbonMonoxideDetectors(sendEvent, 1)
+//Global Object for class color control!
+@Field def colorControlObject = new ColorControls(sendEvent, 1)
+//Global Object for class energy meter!
+@Field def energyMeterObject = new EnergyMeters(sendEvent, 1)
+//Global Object for class illuminance measurement!
+@Field def illuminanceMeasurementObject = new IlluminanceMeasurements(sendEvent, 1)
+//Global Object for class power meter!
+@Field def powerMeterObject = new PowerMeters(sendEvent, 1)
+//Global Object for class humidity measurement!
+@Field def humidityMeasurementObject = new RelativeHumidityMeasurements(sendEvent, 1)
+//Global Object for class relay switch!
+@Field def relaySwitchObject = new RelaySwitches(sendEvent, 1)
+//Global Object for class sleep sensor!
+@Field def sleepSensorObject = new SleepSensors(sendEvent, 1)
+//Global Object for class step sensor!
+@Field def stepSensorObject = new StepSensors(sendEvent, 1)
+//Global Object for class switch level!
+@Field def switchLevelObject = new SwitchLevels(sendEvent, 1)
+//Global Object for class temperature measurement!
+@Field def temperatureMeasurementObject = new TemperatureMeasurements(sendEvent, 1)
+//Global Object for class water sensor!
+@Field def waterSensorObject = new WaterSensors(sendEvent, 1)
+//Global Object for class valves!
+@Field def valveObject = new Valves(sendEvent, 1)
+//Global Object for class mobile presence!
+@Field def mobilePresenceObject = new MobilePresences(sendEvent, 1)
 
-//Methods
-/////////////////////////////////////////////////////////////////////
-def definition(LinkedHashMap LHM) {
-       println("IGNORE -- JUST SOME DEFINITION")
-}
+//Application #1
+class App1 {
+       def reference
+       def location
+       def app
+       def atomicState
 
-/////////////////////////////////////////////////////////////////////
-def preferences(Closure Input) {
-       println("IGNORE -- JUST SOME DEFINITION")
-}
-/////////////////////////////////////////////////////////////////////
-def setLocationMode(String mode) {
-       location.mode = mode
-}
+       //Extracted objects for App1
+       //Object for class lock!
+       def lock1
+       //Object for class contactSensor!
+       def contact
+       //Global variable for number!
+       def minutesLater = 70
+       //Global variable for number!
+       def secondsLater = 93
+       //Global variable for contact!
+       def recipients = "AJ"
+       //Global variable for phone!
+       def phoneNumber = 9495379373
 
-/////////////////////////////////////////////////////////////////////
-////subscribe(app, func)
-def subscribe(Object Obj, Closure Input) {
-       ObjectList.add(Obj)
-       EventList.add("Touched")
-       FunctionList.add(Input)
-}
-////subscribe(obj, string, func)
-def subscribe(Object Obj, String S, Closure Input) {
-       ObjectList.add(Obj)
-       EventList.add(S)
-       FunctionList.add(Input)
-}
-////subscribe(obj, string, func, hashmap)
-def subscribe(Object Obj, String S, Closure Input, LinkedHashMap LHM) {
-       ObjectList.add(Obj)     
-       EventList.add(S)
-       FunctionList.add(Input)
-}
-/////////////////////////////////////////////////////////////////////
-def EventHandler() {
-       while(true) {
-               List evt = []
-               print "Waiting for an event...\n"
-               def EVENT = System.in.newReader().readLine()
-               SepLine = EVENT.split()
-               for (int i = 0; i < EventList.size(); i++) {
-                       if (EventList[i] == SepLine[0]) {
-                               println("The following effect: \n")
-                               evt.add(new Event())
-                               switch(SepLine[0]) { 
-                                       case "Touched":
-                                               ObjectList[i].touched = 1
-                                               evt[-1].value = "Touched"
-                                               evt[-1].linkText = "touched by user"
-                                               evt[-1].name = "TouchSensor"
-                                               evt[-1].descriptionText = "Touching"
-                                               break
-                                       case "lock":
-                                               if (SepLine[1] == "0") {
-                                                       ObjectList[i][0].lock()
-                                                       evt[-1].deviceId = 0
-                                                       evt[-1].value = "locked"
-                                                       evt[-1].linkText = "lock0"
-                                                       evt[-1].displayName = "lock0"
-                                                       evt[-1].name = "lock"
-                                                       evt[-1].descriptionText = "locking"
-                                               } else if (SepLine[1] == "1") {
-                                                       ObjectList[i][1].lock()
-                                                       evt[-1].deviceId = 1
-                                                       evt[-1].value = "locked"
-                                                       evt[-1].linkText = "lock1"
-                                                       evt[-1].displayName = "lock1"
-                                                       evt[-1].name = "lock"
-                                                       evt[-1].descriptionText = "locking"
-                                               } else if (SepLine[1] == "2") {
-                                                       ObjectList[i][2].lock()
-                                                       evt[-1].deviceId = 2
-                                                       evt[-1].value = "locked"
-                                                       evt[-1].linkText = "lock2"
-                                                       evt[-1].displayName = "lock2"
-                                                       evt[-1].name = "lock"
-                                                       evt[-1].descriptionText = "locking"
-                                               }
-                                               break
-                                       case "unlock":
-                                               if (SepLine[1] == "0") {
-                                                       ObjectList[i][0].unlock()
-                                                       evt[-1].deviceId = 0
-                                                       evt[-1].value = "unlocked"
-                                                       evt[-1].linkText = "lock0"
-                                                       evt[-1].displayName = "lock0"
-                                                       evt[-1].name = "lock"
-                                                       evt[-1].descriptionText = "unlocking"   
-                                               } else if (SepLine[1] == "1") {
-                                                       ObjectList[i][1].unlock()
-                                                       evt[-1].deviceId = 0
-                                                       evt[-1].value = "unlocked"
-                                                       evt[-1].linkText = "lock1"
-                                                       evt[-1].displayName = "lock1"
-                                                       evt[-1].name = "lock"
-                                                       evt[-1].descriptionText = "unlocking"
-                                               } else if (SepLine[1] == "2") {
-                                                       ObjectList[i][2].unlock()
-                                                       evt[-1].deviceId = 2
-                                                       evt[-1].value = "unlocked"
-                                                       evt[-1].linkText = "lock2"
-                                                       evt[-1].displayName = "lock2"
-                                                       evt[-1].name = "lock"
-                                                       evt[-1].descriptionText = "unlocking"
-                                               }
-                                               break
-                                       case "contact.open":
-                                               if (SepLine[1] == "0") {
-                                                       ObjectList[i][0].contactLatestValue = ObjectList[i].currentContact
-                                                       ObjectList[i][0].currentContact = "open"
-                                                       evt[-1].deviceId = 0
-                                                       evt[-1].value = "contact.open"
-                                                       evt[-1].linkText = "contact0"
-                                                       evt[-1].displayName = "contact0"
-                                                       evt[-1].name = "ContactSensor"
-                                                       evt[-1].descriptionText = "opening"     
-                                               } else if (SepLine[1] == "1") {
-                                                       ObjectList[i][1].contactLatestValue = ObjectList[i].currentContact
-                                                       ObjectList[i][1].currentContact = "open"
-                                                       evt[-1].deviceId = 1
-                                                       evt[-1].value = "contact.open"
-                                                       evt[-1].linkText = "contact1"
-                                                       evt[-1].displayName = "contact1"
-                                                       evt[-1].name = "ContactSensor"
-                                                       evt[-1].descriptionText = "opening"
-                                               } else if (SepLine[1] == "2") {
-                                                       ObjectList[i][2].contactLatestValue = ObjectList[i].currentContact
-                                                       ObjectList[i][2].currentContact = "open"
-                                                       evt[-1].deviceId = 2
-                                                       evt[-1].value = "contact.open"
-                                                       evt[-1].linkText = "contact2"
-                                                       evt[-1].displayName = "contact2"
-                                                       evt[-1].name = "ContactSensor"
-                                                       evt[-1].descriptionText = "opening"
-                                               }
-                                               break
-                                       case "contact.closed":
-                                               if (SepLine[1] == "0") {
-                                                       ObjectList[i][0].contactLatestValue = ObjectList[i].currentContact
-                                                       ObjectList[i][0].currentContact = "closed"
-                                                       evt[-1].deviceId = 0
-                                                       evt[-1].value = "contact.closed"
-                                                       evt[-1].linkText = "contact0"
-                                                       evt[-1].displayName = "contact0"
-                                                       evt[-1].name = "ContactSensor"
-                                                       evt[-1].descriptionText = "closing"
-                                               } else if (SepLine[1] == "1") {
-                                                       ObjectList[i][1].contactLatestValue = ObjectList[i].currentContact
-                                                       ObjectList[i][1].currentContact = "closed"
-                                                       evt[-1].deviceId = 1
-                                                       evt[-1].value = "contact.closed"
-                                                       evt[-1].linkText = "contact1"
-                                                       evt[-1].displayName = "contact1"
-                                                       evt[-1].name = "ContactSensor"
-                                                       evt[-1].descriptionText = "closing"
-                                               } else if (SepLine[1] == "2") {
-                                                       ObjectList[i][2].contactLatestValue = ObjectList[i].currentContact
-                                                       ObjectList[i][2].currentContact = "closed"
-                                                       evt[-1].deviceId = 2
-                                                       evt[-1].value = "contact.closed"
-                                                       evt[-1].linkText = "contact2"
-                                                       evt[-1].displayName = "contact2"
-                                                       evt[-1].name = "ContactSensor"
-                                                       evt[-1].descriptionText = "closing"
-                                               }
-                                               break
-                                       default:
-                                               break
-                               }
-                               FunctionList[i](evt[-1])
+       //Extracted objects for functions for App1
+       //Global Object for functions in subscribe method!
+       def installed = this.&installed
+       //Global Object for functions in subscribe method!
+       def updated = this.&updated
+       //Global Object for functions in subscribe method!
+       def initialize = this.&initialize
+       //Global Object for functions in subscribe method!
+       def lockDoor = this.&lockDoor
+       //Global Object for functions in subscribe method!
+       def unlockDoor = this.&unlockDoor
+       //Global Object for functions in subscribe method!
+       def doorHandler = this.&doorHandler
+
+       App1(Object obj) {
+               reference = obj
+               location = obj.locationObject
+               app = obj.appObject
+               atomicState = obj.atomicState
+               lock1 = obj.lockObject
+               contact = obj.contactObject
+               //Global variable for settings!
+               settings = [app:app, lock1:lock1, contact:contact, minutesLater:minutesLater, secondsLater:secondsLater, recipients:recipients, phoneNumber:phoneNumber]
+       }
+       //Global variables for each app
+       //Global variable for state[mode]
+       def state = [home:[],away:[],night:[]]
+       //Create a global logger object for methods
+       def log = new Logger()
+       //Create a global variable for Functions in Subscribe method
+       def functionList = []
+       //Create a global variable for Objects in Subscribe method
+       def objectList = []
+       //Create a global variable for Events in Subscribe method
+       def eventList = []
+       //Create a global list for function schedulers
+       def timersFuncList = []
+       //Create a global list for timer schedulers
+       def timersList = []
+       //Create a global variable for settings
+       def settings
+       //Zip code
+       def zipCode = 92617
+
+       //Methods
+       /////////////////////////////////////////////////////////////////////
+       def setLocationMode(String mode) {
+               location.mode = mode
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       ////subscribe(obj, func)
+       def subscribe(Object obj, Closure FunctionToCall) {
+               if (obj == app) {
+                       objectList.add(obj)
+                       eventList.add("Touched")
+                       functionList.add(FunctionToCall)
+               } else if (obj == location) {
+                       objectList.add(obj)
+                       eventList.add("Location")
+                       functionList.add(FunctionToCall)
+               }
+       }
+       ////subscribe(obj, event, func)
+       def subscribe(Object obj, String event, Closure FunctionToCall) {
+               objectList.add(obj)
+               eventList.add(event)
+               functionList.add(FunctionToCall)
+       }
+       ////subscribe(obj, event, func, data)
+       def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
+               objectList.add(obj)     
+               eventList.add(event)
+               functionList.add(FunctionToCall)
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////runIn(time, func)
+       def runIn(int seconds, Closure functionToCall) {
+               if (timersFuncList.contains(functionToCall)) {
+                       timersList[timersFuncList.indexOf(functionToCall)].cancel()
+                       def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
+               } else {
+                       timersFuncList.add(functionToCall)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
+               }
+       }
+       
+       def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
+               runIn(seconds, functionToCall)
+       }
+       
+       def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
+               runIn(seconds, nameOfFunction)
+       }
+       
+       def runIn(int seconds, String nameOfFunction) {
+               timersFuncList.add(nameOfFunction)
+               timersList.add(new SimulatedTimer())
+               def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
+                       "$nameOfFunction"()
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////unschedule(func)
+       def unschedule(Closure functionToUnschedule) {
+               for (int i = 0;i < timersFuncList.size();i++) {
+                       if (timersFuncList[i] == functionToUnschedule) {
+                               if (timersList != null)
+                                       timersList[i].cancel()
                        }
                }
        }
-}
-/////////////////////////////////////////////////////////////////////
-////runIn(time, func)
-def runIn(int seconds, Closure Input) {
-       ListofTimersFunc.add(Input)
-       ListofTimers.add(new Timer())
-       def task = ListofTimers[-1].runAfter(1000*seconds, Input)
-}
-/////////////////////////////////////////////////////////////////////
-////unschedule(func)
-def unschedule(Closure Input) {
-       for (int i = 0;i < ListofTimersFunc.size();i++) {
-               if (ListofTimersFunc[i] == Input) {
-                       ListofTimers[i].cancel()
+       
+       
+       def unschedule() {
+               for (int i = 0;i < timersFuncList.size();i++) {
+                       if (timersList != null)
+                               timersList[i].cancel()
                }
        }
-}
-/////////////////////////////////////////////////////////////////////
-////sendNotificationToContacts(text, recipients)
-def sendNotificationToContacts(String S, List recipients) {
-       for (int i = 0;i < recipients.size();i++) {
-               for (int j = 0;j < location.CONTACTS.size();j++) {
-                       if (recipients[i] == location.CONTACTS[j]) {
-                               println("Sending \""+S+"\" to "+location.PhoneNums[j].toString())
+       /////////////////////////////////////////////////////////////////////
+       ////sendNotificationToContacts(text, recipients)
+       def sendNotificationToContacts(String text, String recipients) {
+               for (int i = 0;i < recipients.size();i++) {
+                       for (int j = 0;j < location.contacts.size();j++) {
+                               if (recipients[i] == location.contacts[j]) {
+                                       println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
+                               }
                        }
                }
        }
-}
-/////////////////////////////////////////////////////////////////////
-////sendNotificationToContacts(text, recipients)
-def sendSms(long Phone, String S) {
-       println("Sending \""+S+"\" to "+Phone.toString())
-}
+       /////////////////////////////////////////////////////////////////////
+       ////sendSms(phone, text)
+       def sendSms(long phoneNumber, String text) {
+               println("Sending \""+text+"\" to "+phoneNumber.toString())
+       }
+       
+       def sendSMS(long phoneNumber, String text) {
+               println("Sending \""+text+"\" to "+phoneNumber.toString())
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////sendPush(text)
+       def sendPush(String text) {
+               println(text)
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////schedule(time, nameOfFunction as String)
+       def schedule(String time, String nameOfFunction) {
+               def _inputTime = time.split(':')
+               Date date = new Date()  
+               def _currentTime = date.format("HH:mm:ss").split(':')
+       
+               //Convert input time and current time to minutes
+               def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
+               def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
+               def delay
+       
+               if (inputTime < currentTime) {
+                       delay = 24*60*60-inputTime+currentTime
+               } else {
+                       delay = inputTime-currentTime
+               }
+       
+               timersFuncList.add(nameOfFunction)
+               timersList.add(new SimulatedTimer())
+               def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
+                       "$nameOfFunction"()
+               }
+       }
+       ////schedule(time, nameOfFunction as Closure)
+       def schedule(String time, Closure nameOfFunction) {
+               def _inputTime = time.split(':')
+               Date date = new Date()  
+               def _currentTime = date.format("HH:mm:ss").split(':')
+       
+               //Convert input time and current time to minutes
+               def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
+               def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
+               def delay
+       
+               if (inputTime < currentTime) {
+                       delay = 24*60*60-inputTime+currentTime
+               } else {
+                       delay = inputTime-currentTime
+               }
+       
+               if (timersFuncList.contains(nameOfFunction)) {
+                       timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
+               } else {
+                       timersFuncList.add(nameOfFunction)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       def now() {
+               return System.currentTimeMillis()
+       }
+       /////////////////////////////////////////////////////////////////////
+       def getTemperatureScale() {
+               return 'F' //Celsius for now
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       def getSunriseAndSunset(LinkedHashMap metaData) {
+               def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
+               return sunRiseSetInfo
+       }
+       /////////////////////////////////////////////////////////////////////
+       def httpPostJson(LinkedHashMap metaData, Closure inputData) {
+               inputData(metaData)
+       }
+       /////////////////////////////////////////////////////////////////////
+       def runEvery15Minutes(Closure inputData) {
+               inputData()
+       }
+       /////////////////////////////////////////////////////////////////////
+       def timeToday(String time, Object timeZone) {
+               def timeOfDay = new Date()
+               def _inputTime = time.split(':')
+               def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
+               timeOfDay.time = inputTime
+               return timeOfDay
+       }
+       /////////////////////////////////////////////////////////////////////
+       def sendNotification(String text, LinkedHashMap metaData) {
+               println("Sending \""+text+"\" to "+metaData.phone.toString())
+       }
+       /////////////////////////////////////////////////////////////////////
+       def canSchedule() {
+               return true
+       }
 
-/**
- *  Good Night House
- *
- *  Copyright 2014 Joseph Charette
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- *  in compliance with the License. You may obtain a copy of the License at:
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
- *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
- *  for the specific language governing permissions and limitations under the License.
- *
- */
-definition(
-    name: "Good Night House",
-    namespace: "charette.joseph@gmail.com",
-    author: "Joseph Charette",
-    description: "Some on, some off with delay for bedtime, Lock The Doors",
-    category: "Convenience",
-    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
-    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png"
-/**
-*  Borrowed code from
-*  Walk Gentle Into That Good Night
-*
-*  Author: oneaccttorulethehouse@gmail.com
-*  Date: 2014-02-01
- */
- )
-preferences {
-       section("When I touch the app turn these lights off…"){
-               input "switchesoff", "capability.switch", multiple: true, required:true
-       }
-    section("When I touch the app turn these lights on…"){
-               input "switcheson", "capability.switch", multiple: true, required:false
-       }
-    section("Lock theses locks...") {
-               input "lock1","capability.lock", multiple: true
-    }
-       section("And change to this mode...") {
-               input "newMode", "mode", title: "Mode?"
-       }
-   section("After so many seconds (optional)"){
-               input "waitfor", "number", title: "Off after (default 120)", required: true
+       def installed(){
+           initialize()
+       }
+       
+       def updated(){
+           unsubscribe()
+           unschedule()
+           initialize()
+       }
+       
+       def initialize(){
+           log.debug "Settings: ${settings}"
+           subscribe(lock1, "lock", doorHandler, [filterEvents: false])
+           subscribe(lock1, "unlock", doorHandler, [filterEvents: false])  
+           subscribe(contact, "contact.open", doorHandler)
+           subscribe(contact, "contact.closed", doorHandler)
+       }
+       
+       def lockDoor(){
+           log.debug "Locking the door."
+           lock1.lock()
+           if(location.contactBookEnabled) {
+               if ( recipients ) {
+                   log.debug ( "Sending Push Notification..." ) 
+                   sendNotificationToContacts( "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!", recipients)
+               }
+           }
+           if (phoneNumber) {
+               log.debug("Sending text message...")
+               sendSms( phoneNumber, "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!")
+           }
+       }
+       
+       def unlockDoor(){
+           log.debug "Unlocking the door."
+           lock1.unlock()
+           if(location.contactBookEnabled) {
+               if ( recipients ) {
+                   log.debug ( "Sending Push Notification..." ) 
+                   sendNotificationToContacts( "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!", recipients)
+               }
+           }
+           if ( phoneNumber ) {
+               log.debug("Sending text message...")
+               sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!")
+           }
+       }
+       
+       def doorHandler(evt){
+           if ((contact.latestValue("contact") == "open") && (evt.value == "locked")) { // If the door is open and a person locks the door then...  
+               //def delay = (secondsLater) // runIn uses seconds
+               runIn( secondsLater, unlockDoor )   // ...schedule (in minutes) to unlock...  We don't want the door to be closed while the lock is engaged. 
+           }
+           else if ((contact.latestValue("contact") == "open") && (evt.value == "unlocked")) { // If the door is open and a person unlocks it then...
+               unschedule( unlockDoor ) // ...we don't need to unlock it later.
+           }
+           else if ((contact.latestValue("contact") == "closed") && (evt.value == "locked")) { // If the door is closed and a person manually locks it then...
+               unschedule( lockDoor ) // ...we don't need to lock it later.
+           }   
+           else if ((contact.latestValue("contact") == "closed") && (evt.value == "unlocked")) { // If the door is closed and a person unlocks it then...
+              //def delay = (minutesLater * 60) // runIn uses seconds
+               runIn( (minutesLater * 60), lockDoor ) // ...schedule (in minutes) to lock.
+           }
+           else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "open")) { // If a person opens an unlocked door...
+               unschedule( lockDoor ) // ...we don't need to lock it later.
+           }
+           else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "closed")) { // If a person closes an unlocked door...
+               //def delay = (minutesLater * 60) // runIn uses seconds
+               runIn( (minutesLater * 60), lockDoor ) // ...schedule (in minutes) to lock.
+           }
+           else { //Opening or Closing door when locked (in case you have a handle lock)
+               log.debug "Unlocking the door."
+               lock1.unlock()
+               if(location.contactBookEnabled) {
+                   if ( recipients ) {
+                       log.debug ( "Sending Push Notification..." ) 
+                       sendNotificationToContacts( "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!", recipients)
+                   }
+               }
+               if ( phoneNumber ) {
+                   log.debug("Sending text message...")
+                   sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!")
+               }
+           }
        }
 }
 
 
-def installed()
-{
-       log.debug "Installed with settings: ${settings}"
-       log.debug "Current mode = ${location.mode}"
-       subscribe(app, appTouch)
-}
+//Application #2
+class App2 {
+       def reference
+       def location
+       def app
+       def atomicState
+
+       //Extracted objects for App2
+       //Global variable for time!
+       def starting = "15:00"
+       //Global variable for time!
+       def ending = "15:00"
+       //Object for class beacon sensor!
+       def beacons
+       //Object for class mobile presence!
+       def phones
+       //Global variable for enum!
+       def arrivalPhrase = "Good Night!"
+       //Object for class switch!
+       def arrivalOnSwitches
+       //Object for class switch!
+       def arrivalOffSwitches
+       //Object for class lock!
+       def arrivalLocks
+       //Global variable for enum!
+       def departPhrase = "Good Night!"
+       //Object for class switch!
+       def departOnSwitches
+       //Object for class switch!
+       def departOffSwitches
+       //Object for class lock!
+       def departLocks
+       //Global variable for boolean!
+       def pushNotification = "0"
+       //Global variable for phone!
+       def phone = 9495379373
+       //Global variable for enum!
+       def days = "Monday"
+       //Global variable for mode!
+       def modes = "night"
+
+       //Extracted objects for functions for App2
+       //Global Object for functions in subscribe method!
+       def mainPage = this.&mainPage
+       //Global Object for functions in subscribe method!
+       def installed = this.&installed
+       //Global Object for functions in subscribe method!
+       def updated = this.&updated
+       //Global Object for functions in subscribe method!
+       def initialize = this.&initialize
+       //Global Object for functions in subscribe method!
+       def beaconHandler = this.&beaconHandler
+       //Global Object for functions in subscribe method!
+       def arriveActions = this.&arriveActions
+       //Global Object for functions in subscribe method!
+       def departActions = this.&departActions
+       //Global Object for functions in subscribe method!
+       def prefix = this.&prefix
+       //Global Object for functions in subscribe method!
+       def listPhrases = this.&listPhrases
+       //Global Object for functions in subscribe method!
+       def executePhrase = this.&executePhrase
+       //Global Object for functions in subscribe method!
+       def getBeaconName = this.&getBeaconName
+       //Global Object for functions in subscribe method!
+       def getPhoneName = this.&getPhoneName
+       //Global Object for functions in subscribe method!
+       def hideOptionsSection = this.&hideOptionsSection
+       //Global Object for functions in subscribe method!
+       def getAllOk = this.&getAllOk
+       //Global Object for functions in subscribe method!
+       def getModeOk = this.&getModeOk
+       //Global Object for functions in subscribe method!
+       def getDaysOk = this.&getDaysOk
+       //Global Object for functions in subscribe method!
+       def getTimeOk = this.&getTimeOk
+       //Global Object for functions in subscribe method!
+       def hhmm = this.&hhmm
+       //Global Object for functions in subscribe method!
+       def timeIntervalLabel = this.&timeIntervalLabel
+       //Global Object for functions in subscribe method!
+       def list = this.&list
+
+       App2(Object obj) {
+               reference = obj
+               location = obj.locationObject
+               app = obj.appObject
+               atomicState = obj.atomicState
+               beacons = obj.beaconSensorObject
+               phones = obj.mobilePresenceObject
+               arrivalOnSwitches = obj.switchObject
+               arrivalOffSwitches = obj.switchObject
+               arrivalLocks = obj.lockObject
+               departOnSwitches = obj.switchObject
+               departOffSwitches = obj.switchObject
+               departLocks = obj.lockObject
+               //Global variable for settings!
+               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]
+       }
+       //Global variables for each app
+       //Global variable for state[mode]
+       def state = [home:[],away:[],night:[]]
+       //Create a global logger object for methods
+       def log = new Logger()
+       //Create a global variable for Functions in Subscribe method
+       def functionList = []
+       //Create a global variable for Objects in Subscribe method
+       def objectList = []
+       //Create a global variable for Events in Subscribe method
+       def eventList = []
+       //Create a global list for function schedulers
+       def timersFuncList = []
+       //Create a global list for timer schedulers
+       def timersList = []
+       //Create a global variable for settings
+       def settings
+       //Zip code
+       def zipCode = 92617
+
+       //Methods
+       /////////////////////////////////////////////////////////////////////
+       def setLocationMode(String mode) {
+               location.mode = mode
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       ////subscribe(obj, func)
+       def subscribe(Object obj, Closure FunctionToCall) {
+               if (obj == app) {
+                       objectList.add(obj)
+                       eventList.add("Touched")
+                       functionList.add(FunctionToCall)
+               } else if (obj == location) {
+                       objectList.add(obj)
+                       eventList.add("Location")
+                       functionList.add(FunctionToCall)
+               }
+       }
+       ////subscribe(obj, event, func)
+       def subscribe(Object obj, String event, Closure FunctionToCall) {
+               objectList.add(obj)
+               eventList.add(event)
+               functionList.add(FunctionToCall)
+       }
+       ////subscribe(obj, event, func, data)
+       def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
+               objectList.add(obj)     
+               eventList.add(event)
+               functionList.add(FunctionToCall)
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////runIn(time, func)
+       def runIn(int seconds, Closure functionToCall) {
+               if (timersFuncList.contains(functionToCall)) {
+                       timersList[timersFuncList.indexOf(functionToCall)].cancel()
+                       def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
+               } else {
+                       timersFuncList.add(functionToCall)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
+               }
+       }
+       
+       def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
+               runIn(seconds, functionToCall)
+       }
+       
+       def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
+               runIn(seconds, nameOfFunction)
+       }
+       
+       def runIn(int seconds, String nameOfFunction) {
+               timersFuncList.add(nameOfFunction)
+               timersList.add(new SimulatedTimer())
+               def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
+                       "$nameOfFunction"()
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////unschedule(func)
+       def unschedule(Closure functionToUnschedule) {
+               for (int i = 0;i < timersFuncList.size();i++) {
+                       if (timersFuncList[i] == functionToUnschedule) {
+                               if (timersList != null)
+                                       timersList[i].cancel()
+                       }
+               }
+       }
+       
+       
+       def unschedule() {
+               for (int i = 0;i < timersFuncList.size();i++) {
+                       if (timersList != null)
+                               timersList[i].cancel()
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////sendNotificationToContacts(text, recipients)
+       def sendNotificationToContacts(String text, String recipients) {
+               for (int i = 0;i < recipients.size();i++) {
+                       for (int j = 0;j < location.contacts.size();j++) {
+                               if (recipients[i] == location.contacts[j]) {
+                                       println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
+                               }
+                       }
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////sendSms(phone, text)
+       def sendSms(long phoneNumber, String text) {
+               println("Sending \""+text+"\" to "+phoneNumber.toString())
+       }
+       
+       def sendSMS(long phoneNumber, String text) {
+               println("Sending \""+text+"\" to "+phoneNumber.toString())
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////sendPush(text)
+       def sendPush(String text) {
+               println(text)
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////schedule(time, nameOfFunction as String)
+       def schedule(String time, String nameOfFunction) {
+               def _inputTime = time.split(':')
+               Date date = new Date()  
+               def _currentTime = date.format("HH:mm:ss").split(':')
+       
+               //Convert input time and current time to minutes
+               def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
+               def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
+               def delay
+       
+               if (inputTime < currentTime) {
+                       delay = 24*60*60-inputTime+currentTime
+               } else {
+                       delay = inputTime-currentTime
+               }
+       
+               timersFuncList.add(nameOfFunction)
+               timersList.add(new SimulatedTimer())
+               def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
+                       "$nameOfFunction"()
+               }
+       }
+       ////schedule(time, nameOfFunction as Closure)
+       def schedule(String time, Closure nameOfFunction) {
+               def _inputTime = time.split(':')
+               Date date = new Date()  
+               def _currentTime = date.format("HH:mm:ss").split(':')
+       
+               //Convert input time and current time to minutes
+               def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
+               def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
+               def delay
+       
+               if (inputTime < currentTime) {
+                       delay = 24*60*60-inputTime+currentTime
+               } else {
+                       delay = inputTime-currentTime
+               }
+       
+               if (timersFuncList.contains(nameOfFunction)) {
+                       timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
+               } else {
+                       timersFuncList.add(nameOfFunction)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       def now() {
+               return System.currentTimeMillis()
+       }
+       /////////////////////////////////////////////////////////////////////
+       def getTemperatureScale() {
+               return 'F' //Celsius for now
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       def getSunriseAndSunset(LinkedHashMap metaData) {
+               def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
+               return sunRiseSetInfo
+       }
+       /////////////////////////////////////////////////////////////////////
+       def httpPostJson(LinkedHashMap metaData, Closure inputData) {
+               inputData(metaData)
+       }
+       /////////////////////////////////////////////////////////////////////
+       def runEvery15Minutes(Closure inputData) {
+               inputData()
+       }
+       /////////////////////////////////////////////////////////////////////
+       def timeToday(String time, Object timeZone) {
+               def timeOfDay = new Date()
+               def _inputTime = time.split(':')
+               def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
+               timeOfDay.time = inputTime
+               return timeOfDay
+       }
+       /////////////////////////////////////////////////////////////////////
+       def sendNotification(String text, LinkedHashMap metaData) {
+               println("Sending \""+text+"\" to "+metaData.phone.toString())
+       }
+       /////////////////////////////////////////////////////////////////////
+       def canSchedule() {
+               return true
+       }
 
+       def mainPage() {
+               dynamicPage(name: "mainPage", install: true, uninstall: true) {
+       
+                       section("Where do you want to watch?") {
+                               input name: "beacons", type: "capability.beacon", title: "Select your beacon(s)", 
+                                       multiple: true, required: true
+                       }
+       
+                       section("Who do you want to watch for?") {
+                               input name: "phones", type: "device.mobilePresence", title: "Select your phone(s)", 
+                                       multiple: true, required: true
+                       }
+       
+                       section("What do you want to do on arrival?") {
+                               input name: "arrivalPhrase", type: "enum", title: "Execute a phrase", 
+                                       options: listPhrases(), required: false
+                               input "arrivalOnSwitches", "capability.switch", title: "Turn on some switches", 
+                                       multiple: true, required: false
+                               input "arrivalOffSwitches", "capability.switch", title: "Turn off some switches", 
+                                       multiple: true, required: false
+                               input "arrivalLocks", "capability.lock", title: "Unlock the door",
+                                       multiple: true, required: false
+                       }
+       
+                       section("What do you want to do on departure?") {
+                               input name: "departPhrase", type: "enum", title: "Execute a phrase", 
+                                       options: listPhrases(), required: false
+                               input "departOnSwitches", "capability.switch", title: "Turn on some switches", 
+                                       multiple: true, required: false
+                               input "departOffSwitches", "capability.switch", title: "Turn off some switches", 
+                                       multiple: true, required: false
+                               input "departLocks", "capability.lock", title: "Lock the door",
+                                       multiple: true, required: false
+                       }
+       
+                       section("Do you want to be notified?") {
+                               input "pushNotification", "bool", title: "Send a push notification"
+                               input "phone", "phone", title: "Send a text message", description: "Tap to enter phone number", 
+                                       required: false
+                       }
+       
+                       section {
+                               label title: "Give your automation a name", description: "e.g. Goodnight Home, Wake Up"
+                       }
+       
+                       def timeLabel = timeIntervalLabel()
+                       section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
+                               href "timeIntervalInput", title: "Only during a certain time", 
+                                       description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
+       
+                               input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
+                                       options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
+       
+                               input "modes", "mode", title: "Only when mode is", multiple: true, required: false
+                       }
+               }
+       }
+       
+       // Lifecycle management
+       def installed() {
+               log.debug "<beacon-control> Installed with settings: ${settings}"
+               initialize()
+       }
+       
+       def updated() {
+               log.debug "<beacon-control> Updated with settings: ${settings}"
+               unsubscribe()
+               initialize()
+       }
+       
+       def initialize() {
+               subscribe(beacons, "presence", beaconHandler)
+       }
+       
+       // Event handlers
+       def beaconHandler(evt) {
+               log.debug "<beacon-control> beaconHandler: $evt"
+       
+               if (allOk) {
+                       def data = new groovy.json.JsonSlurper().parseText(evt.data)
+                       // removed logging of device names. can be added back for debugging
+                       //log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
+       
+                       def beaconName = getBeaconName(evt)
+                       // removed logging of device names. can be added back for debugging
+                       //log.debug "<beacon-control> beaconName: $beaconName"
+       
+                       def phoneName = getPhoneName(data)
+                       // removed logging of device names. can be added back for debugging
+                       //log.debug "<beacon-control> phoneName: $phoneName"
+                       if (phoneName != null) {
+                   def action = data.presence == "1" ? "arrived" : "left"
+                   def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"
+       
+                   if (action == "arrived") {
+                       msg = arriveActions(msg)
+                   }
+                   else if (action == "left") {
+                       msg = departActions(msg)
+                   }
+                   log.debug "<beacon-control> msg: $msg"
+       
+                   if (pushNotification || phone) {
+                       def options = [
+                           method: (pushNotification && phone) ? "both" : (pushNotification ? "push" : "sms"),
+                           phone: phone
+                       ]
+                       sendNotification(msg, options)
+                   }
+               }
+               }
+       }
+       
+       // Helpers
+       private arriveActions(msg) {
+               if (arrivalPhrase || arrivalOnSwitches || arrivalOffSwitches || arrivalLocks) msg += ", so"
+               
+               if (arrivalPhrase) {
+                       log.debug "<beacon-control> executing: $arrivalPhrase"
+                       executePhrase(arrivalPhrase)
+                       msg += " ${prefix('executed')} $arrivalPhrase."
+               }
+               if (arrivalOnSwitches) {
+                       log.debug "<beacon-control> turning on: $arrivalOnSwitches"
+                       arrivalOnSwitches.on()
+                       msg += " ${prefix('turned')} ${list(arrivalOnSwitches)} on."
+               }
+               if (arrivalOffSwitches) {
+                       log.debug "<beacon-control> turning off: $arrivalOffSwitches"
+                       arrivalOffSwitches.off()
+                       msg += " ${prefix('turned')} ${list(arrivalOffSwitches)} off."
+               }
+               if (arrivalLocks) {
+                       log.debug "<beacon-control> unlocking: $arrivalLocks"
+                       arrivalLocks.unlock()
+                       msg += " ${prefix('unlocked')} ${list(arrivalLocks)}."
+               }
+               msg
+       }
+       
+       private departActions(msg) {
+               if (departPhrase || departOnSwitches || departOffSwitches || departLocks) msg += ", so"
+               
+               if (departPhrase) {
+                       log.debug "<beacon-control> executing: $departPhrase"
+                       executePhrase(departPhrase)
+                       msg += " ${prefix('executed')} $departPhrase."
+               }
+               if (departOnSwitches) {
+                       log.debug "<beacon-control> turning on: $departOnSwitches"
+                       departOnSwitches.on()
+                       msg += " ${prefix('turned')} ${list(departOnSwitches)} on."
+               }
+               if (departOffSwitches) {
+                       log.debug "<beacon-control> turning off: $departOffSwitches"
+                       departOffSwitches.off()
+                       msg += " ${prefix('turned')} ${list(departOffSwitches)} off."
+               }
+               if (departLocks) {
+                       log.debug "<beacon-control> unlocking: $departLocks"
+                       departLocks.lock()
+                       msg += " ${prefix('locked')} ${list(departLocks)}."
+               }
+               msg
+       }
+       
+       private prefix(word) {
+               def result
+               def index = settings.prefixIndex == null ? 0 : settings.prefixIndex + 1
+               switch (index) {
+                       case 0:
+                               result = "I $word"
+                               break
+                       case 1:
+                               result = "I also $word"
+                               break
+                       case 2:
+                               result = "And I $word"
+                               break
+                       default:
+                               result = "And $word"
+                               break
+               }
+       
+               settings.prefixIndex = index
+               log.trace "prefix($word'): $result"
+               result
+       }
+       
+       private listPhrases() {
+               location.helloHome.getPhrases().label
+       }
+       
+       private executePhrase(phraseName) {
+               if (phraseName) {
+                       location.helloHome.execute(phraseName)
+                       log.debug "<beacon-control> executed phrase: $phraseName"
+               }
+       }
+       
+       private getBeaconName(evt) {
+               def beaconName = beacons.find { b -> b.id == evt.deviceId }
+               return beaconName
+       }
+       
+       private getPhoneName(data) {    
+               def phoneName = phones.find { phone ->
+                       // Work around DNI bug in data
+                       def pParts = phone.deviceNetworkId.split('\\|')
+                       def dParts = data.dni.split('\\|')
+               pParts[0] == dParts[0]
+               }
+               return phoneName
+       }
+       
+       private hideOptionsSection() {
+               (starting || ending || days || modes) ? false : true
+       }
+       
+       private getAllOk() {
+               modeOk && daysOk && timeOk
+       }
+       
+       private getModeOk() {
+               def result = !modes || modes.contains(location.mode)
+               log.trace "<beacon-control> modeOk = $result"
+               result
+       }
+       
+       private getDaysOk() {
+               def result = true
+               if (days) {
+                       def df = new java.text.SimpleDateFormat("EEEE")
+                       if (location.timeZone) {
+                               df.setTimeZone(location.timeZone)
+                       }
+                       else {
+                               df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
+                       }
+                       def day = df.format(new Date())
+                       result = days.contains(day)
+               }
+               log.trace "<beacon-control> daysOk = $result"
+               result
+       }
+       
+       private getTimeOk() {
+               def result = true
+               if (starting && ending) {
+                       def currTime = now()
+                       def start = timeToday(starting, location?.timeZone).time
+                       def stop = timeToday(ending, location?.timeZone).time
+                       result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
+               }
+               log.trace "<beacon-control> timeOk = $result"
+               result
+       }
+       
+       private hhmm(time, fmt = "h:mm a") {
+               def t = timeToday(time, location.timeZone)
+               def f = new java.text.SimpleDateFormat(fmt)
+               f.setTimeZone(location.timeZone ?: timeZone(time))
+               f.format(t)
+       }
+       
+       private timeIntervalLabel() {
+               (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
+       }
+       
+       private list(Object names) {
+               return names[0]
+       }
+}
 
-def updated()
-{
-       log.debug "Updated with settings: ${settings}"
-       log.debug "Current mode = ${location.mode}"
-       unsubscribe()
-       subscribe(app, appTouch)
+@Field def app1
+@Field def app2
+def initOrder = Verify.getBoolean()
+if (initOrder) {
+       app1 = new App1(this)
+       app2 = new App2(this)
+} else {
+       app2 = new App2(this)
+       app1 = new App1(this)
 }
 
-def appTouch(evt) {
-       log.debug "changeMode, location.mode = $location.mode, newMode = $newMode, location.modes = $location.modes"
-    if (location.mode != newMode) {
-                       setLocationMode(newMode)
-                       log.debug "Changed the mode to '${newMode}'"
-    }  else {
-       log.debug "New mode is the same as the old mode, leaving it be"
-       }
-    log.debug "appTouch: $evt"
-    lock1.lock()
-    switcheson.on()
-    def delay = (waitfor != null && waitfor != "") ? waitfor * 1000 : 120000
-       switchesoff.off(delay: delay)
+def installOrder = Verify.getBoolean()
+if (installOrder) {
+       app1.installed()
+       app2.installed()
+} else {
+       app2.installed()
+       app1.installed()
 }
 
-installed()
-EventHandler()
+while(true) {
+       def eventNumber = Verify.getInt(0,4)
+       switch(eventNumber) {
+               case 0:
+                       lockObject.setValue([name: "lock", value: "locked", deviceId: "lockID0", descriptionText: "",
+                                       displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+                       break
+               case 1:
+                       lockObject.setValue([name: "unlock", value: "unlocked ", deviceId: "lockID0", descriptionText: "",
+                                       displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+                       break
+               case 2:
+                       contactObject.setValue([name: "contact.open", value: "open", deviceId: "contactSensorID0", descriptionText: "",
+                                       displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+                       break
+               case 3:
+                       contactObject.setValue([name: "contact.closed", value: "closed", deviceId: "contactSensorID0", descriptionText: "",
+                                       displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+                       break
+               case 4:
+                       def event = Verify.getInt(0,1)
+                       if (event == 0) {
+                                       presenceSensorObject.setValue([name: "presence", value: "present", deviceId: "presenceSensorID0", descriptionText: "",
+                                                       displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"1","dni":"mobile0"}'])
+                       } else {
+                                       presenceSensorObject.setValue([name: "presence", value: "not present", deviceId: "presenceSensorID0", descriptionText: "",
+                                                       displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"0","dni":"mobile0"}'])
+                       }
+                       break
+       }
+}