Merge branch 'master' of ssh://plrg.eecs.uci.edu/home/git/smartthings-infrastructure
[smartthings-infrastructure.git] / main.groovy
index fb7fadd284c4f1b58466c9bdec864aa22e5e1a36..b28d935b47563728dc56e7bacdcecfde7c2759a7 100644 (file)
 import groovy.transform.Field
 
 //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 Event.Event
+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 = []
+//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)
+                       evt.add(event)
+                       app2.functionList[i](event)
+               }
+       }
 
+       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)
+                       evt.add(event)
+                       app1.functionList[i](event)
+               }
+       }
+}
 
-//ExtractedObjects
-//Global Object for class switch!
-@Field def switchesoff = new switching(3)
+//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 Touch Sensor!
+@Field def touchSensorObject = new NfcTouch(sendEvent, 1)
 //Global Object for class switch!
-@Field def switcheson = new switching(3)
+@Field def switchObject = new Switches(sendEvent, 1)
 //Global Object for class lock!
-@Field def lock1 = new locking(2)
-//Global variable for mode!
-@Field def newMode = "home"
-//Global variable for number!
-@Field def waitfor = 10
-//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)
 
-//Methods
-/////////////////////////////////////////////////////////////////////
-def definition(LinkedHashMap LHM) {
-       println("IGNORE -- JUST SOME DEFINITION")
-}
+//Application #1
+class App1 {
+       def reference
+       def location
+       def app
 
-/////////////////////////////////////////////////////////////////////
-def preferences(Closure Input) {
-       println("IGNORE -- JUST SOME DEFINITION")
-}
-/////////////////////////////////////////////////////////////////////
-def setLocationMode(String mode) {
-       location.mode = mode
-}
+       //Extracted objects for App1
+       //Object for class switch!
+       def switches
+       //Object for class thermostat!
+       def thermostats
+       //Object for class lock!
+       def locks
 
-/////////////////////////////////////////////////////////////////////
-////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 appTouch = this.&appTouch
+       //Global Object for functions in subscribe method!
+       def changedLocationMode = this.&changedLocationMode
+       //Global Object for functions in subscribe method!
+       def restoreState = this.&restoreState
+       //Global Object for functions in subscribe method!
+       def saveState = this.&saveState
+       //Global Object for functions in subscribe method!
+       def getCurrentMode = this.&getCurrentMode
+
+       App1(Object obj) {
+               reference = obj
+               location = obj.locationObject
+               app = obj.appObject
+               switches = obj.switchObject
+               thermostats = obj.thermostatObject
+               locks = obj.lockObject
+               //Global variable for settings!
+               settings = [app:app, switches:switches, thermostats:thermostats, locks:locks]
+       }
+       //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*seconds*0, nameOfFunction)
+               } else {
+                       timersFuncList.add(nameOfFunction)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds*0, nameOfFunction)
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       def now() {
+               return System.currentTimeMillis()
+       }
+       /////////////////////////////////////////////////////////////////////
+       def getTemperatureScale() {
+               return 'C' //Celsius for now
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       def getSunriseAndSunset(LinkedHashMap metaData) {
+               def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
+               return sunRiseSetInfo
+       }
 
-/**
- *  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() {
+               subscribe(location, changedLocationMode)
+               subscribe(app, appTouch)
+               saveState()
+       }
+       
+       def updated() {
+               unsubscribe()
+               subscribe(location, changedLocationMode)
+               subscribe(app, appTouch)
+               saveState()
+       }
+       
+       def appTouch(evt)
+       {
+               restoreState(currentMode)
+       }
+       
+       def changedLocationMode(evt)
+       {
+               restoreState(evt.value)
+       }
+       
+       private restoreState(mode)
+       {
+               log.info "restoring state for mode '$mode'"
+               def map = state[mode] ?: [:]
+               switches?.each {
+                       def value = map[it.id]
+                       if (value?.switch == "on") {
+                               def level = value.level
+                               if (level) {
+                                       log.debug "setting $it.label level to $level"
+                                       it.setLevel(level)
+                               }
+                               else {
+                                       log.debug "turning $it.label on"
+                                       it.on()
+                               }
+                       }
+                       else if (value?.switch == "off") {
+                               log.debug "turning $it.label off"
+                               it.off()
+                       }
+               }
+       
+               thermostats?.each {
+                       def value = map[it.id]
+                       if (value?.coolingSetpoint) {
+                               log.debug "coolingSetpoint = $value.coolingSetpoint"
+                               it.setCoolingSetpoint(value.coolingSetpoint)
+                       }
+                       if (value?.heatingSetpoint) {
+                               log.debug "heatingSetpoint = $value.heatingSetpoint"
+                               it.setHeatingSetpoint(value.heatingSetpoint)
+                       }
+               }
+       
+               locks?.each {
+                       def value = map[it.id]
+                       if (value) {
+                               if (value?.locked) {
+                                       it.lock()
+                               }
+                               else {
+                                       it.unlock()
+                               }
+                       }
+               }
+       }
+       
+       
+       private saveState()
+       {
+               def mode = currentMode
+               def map = state[mode] ?: [:]
+       
+               switches?.each {
+                       map[it.id] = [switch: it.currentSwitch, level: it.currentLevel]
+               }
+       
+               thermostats?.each {
+                       map[it.id] = [coolingSetpoint: it.currentCoolingSetpoint, heatingSetpoint: it.currentHeatingSetpoint]
+               }
+       
+               locks?.each {
+                       map[it.id] = [locked: it.currentLock == "locked"]
+               }
+       
+               state[mode] = map
+               log.debug "saved state for mode ${mode}: ${state[mode]}"
+               log.debug "state: $state"
+       }
+       
+       private getCurrentMode()
+       {
+               location.mode ?: "_none_"
        }
 }
 
 
-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
 
+       //Extracted objects for App2
+       //Object for class Touch Sensor!
+       def tag
+       //Object for class switch!
+       def switch1
+       //Object for class lock!
+       def lock
+       //Object for class door control!
+       def garageDoor
+       //Global variable for enum!
+       def masterSwitch = "Yes"
+       //Global variable for enum!
+       def masterLock = "Yes"
+       //Global variable for enum!
+       def masterDoor = "No"
 
-def updated()
-{
-       log.debug "Updated with settings: ${settings}"
-       log.debug "Current mode = ${location.mode}"
-       unsubscribe()
-       subscribe(app, appTouch)
-}
+       //Extracted objects for functions for App2
+       //Global Object for functions in subscribe method!
+       def pageTwo = this.&pageTwo
+       //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 touchHandler = this.&touchHandler
+
+       App2(Object obj) {
+               reference = obj
+               location = obj.locationObject
+               app = obj.appObject
+               tag = obj.touchSensorObject
+               switch1 = obj.switchObject
+               lock = obj.lockObject
+               garageDoor = obj.doorControlObject
+               //Global variable for settings!
+               settings = [app:app, tag:tag, switch1:switch1, lock:lock, garageDoor:garageDoor, masterSwitch:masterSwitch, masterLock:masterLock, masterDoor:masterDoor]
+       }
+       //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())
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////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*seconds*0, nameOfFunction)
+               } else {
+                       timersFuncList.add(nameOfFunction)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds*0, nameOfFunction)
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       def now() {
+               return System.currentTimeMillis()
+       }
+       /////////////////////////////////////////////////////////////////////
+       def getTemperatureScale() {
+               return 'C' //Celsius for now
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       def getSunriseAndSunset(LinkedHashMap metaData) {
+               def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
+               return sunRiseSetInfo
+       }
 
-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 pageTwo() {
+               dynamicPage(name: "pageTwo") {
+               section("If set, the state of these devices will be toggled each time the tag is touched, " + 
+                       "e.g. a light that's on will be turned off and one that's off will be turned on, " +
+                       "other devices of the same type will be set to the same state as their master device. " +
+                       "If no master is designated then the majority of devices of the same type will be used " +
+                       "to determine whether to turn on or off the devices.") {
+                   
+                   if (switch1 || masterSwitch) {
+                       input "masterSwitch", "enum", title: "Master switch", options: switch1.collect{[(it.id): it.displayName]}, required: false
+                   }
+                   if (lock || masterLock) {
+                       input "masterLock", "enum", title: "Master lock", options: lock.collect{[(it.id): it.displayName]}, required: false
+                   }
+                   if (garageDoor || masterDoor) {
+                       input "masterDoor", "enum", title: "Master door", options: garageDoor.collect{[(it.id): it.displayName]}, required: false
+                   }            
+                       }
+                       section([mobileOnly:true]) {
+                               label title: "Assign a name", required: false
+                               mode title: "Set for specific mode(s)", required: false
+                       }        
+           }
+       }
+       
+       def installed() {
+               log.debug "Installed with settings: ${settings}"
+       
+               initialize()
+       }
+       
+       def updated() {
+               log.debug "Updated with settings: ${settings}"
+       
+               unsubscribe()
+               initialize()
+       }
+       
+       def initialize() {
+               subscribe tag, "nfcTouch", touchHandler
+           subscribe app, touchHandler
+       }
+       
+       private currentStatus(devices, master, attribute) {
+               log.trace "currentStatus($devices, $master, $attribute)"
+               def result = null
+               if (master) {
+               result = devices.find{it.id == master}?.currentValue(attribute)
+           }
+           else {
+               def map = [:]
+               devices.each {
+                       def value = it.currentValue(attribute)
+                   map[value] = (map[value] ?: 0) + 1
+                   log.trace "$it.displayName: $value"
+               }
+               log.trace map
+               result = map.collect{it}.sort{it.value}[-1].key
+           }
+           log.debug "$attribute = $result"
+           result
+       }
+       
+       def touchHandler(evt) {
+               log.trace "touchHandler($evt.descriptionText)"
+           if (switch1) {
+               def status = currentStatus(switch1, masterSwitch, "switch")
+               switch1.each {
+                   if (status == "on") {
+                       it.off()
+                   }
+                   else {
+                       it.on()
+                   }
+               }
+           }
+           
+           if (lock) {
+               def status = currentStatus(lock, masterLock, "lock")
+               lock.each {
+                   if (status == "locked") {
+                       lock.unlock()
+                   }
+                   else {
+                       lock.lock()
+                   }
+               }
+           }
+           
+           if (garageDoor) {
+               def status = currentStatus(garageDoor, masterDoor, "status")
+               garageDoor.each {
+                       if (status == "open") {
+                       it.close()
+                   }
+                   else {
+                       it.open()
+                   }
+               }
+           }
+       }
 }
 
-installed()
-EventHandler()
+@Field def app1 = new App1(this)
+@Field def app2 = new App2(this)
+app1.installed()
+app2.installed()
+
+appObject.setValue([name: "nfcTouch", value: "touched", deviceId: "nfcSensorID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
+appObject.setValue([name: "Touched", value: "touched", deviceId: "touchedSensorID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
+locationObject.setValue([name: "Location", value: "away", deviceId: "locationID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
+locationObject.setValue([name: "Location", value: "home", deviceId: "locationID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])
+locationObject.setValue([name: "Location", value: "night", deviceId: "locationID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: [info: "info"]])