Commit #7: Events thread-based + new easier Extractor.py + our own Timer class
[smartthings-infrastructure.git] / main.groovy
index ac7aa3bebe71d2fef39b354df74ac7d0f60aba57..d0d579664406ff6254a92e6a19b2d419b996152a 100644 (file)
@@ -14,227 +14,608 @@ import Location.LocationVar
 import Location.Phrase
 import appTouch.Touched
 import Event.Event
+import Timer.SimulatedTimer
 
-//GlobalVariables
+//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)
+               }
+       }
+}
+
+//GlobalVariables for both Apps
 //Create a global variable for send event
-@Field def sendEvent = {eventDataMap -> eventHandler(eventDataMap)}
-//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:[]]
-//Global object for touch
-@Field def app = new Touched(sendEvent, 0)
-//Create a global logger object for methods
-@Field def log = new Logger()
-//Create a global variable for Functions in Subscribe method
-@Field def functionList = []
-//Create a global variable for Objects in Subscribe method
-@Field def objectList = []
-//Create a global variable for Events in Subscribe method
-@Field def eventList = []
-//Create a global list for function schedulers
-@Field def timersFuncList = []
-//Create a global list for timer schedulers
-@Field def timersList = []
+@Field def sendEvent = {eventDataMap -> 
+                       eventHandler(eventDataMap)
+                       }
+//Object for location
+@Field def locationObject = new LocationVar()
+//Object for touch
+@Field def appObject = new Touched(sendEvent, 0)
 //Create a global list for events
 @Field def evt = []
 
-
-//extractedObjects
-//Global Object for class switch!
-@Field def switchesoff = new Switching(sendEvent, 1)
-//Global Object for class switch!
-@Field def switcheson = new Switching(sendEvent, 1)
+//Extracted global objects for both Apps
 //Global Object for class lock!
-@Field def lock1 = new Locking(sendEvent, 2)
-//Global variable for mode!
-@Field def newMode = "away"
-//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
-
-//Methods
-/////////////////////////////////////////////////////////////////////
-def definition(LinkedHashMap metaData) {
-       println("IGNORE -- JUST SOME DEFINITION")
-}
+@Field def lockObject = new Locking(sendEvent,1)
+//Global Object for class contactSensor!
+@Field def contactObject = new Contacting(sendEvent,1)
+//Global Object for class switch!
+@Field def switchObject = new Switching(sendEvent, 1)
 
-/////////////////////////////////////////////////////////////////////
-def preferences(Closure metaData) {
-       println("IGNORE -- JUST SOME DEFINITION")
-}
-/////////////////////////////////////////////////////////////////////
-def setLocationMode(String mode) {
-       location.mode = mode
-}
+//Application #1
+class App1 {
+       def reference
+       def location
+       def app
 
-/////////////////////////////////////////////////////////////////////
-////subscribe(obj, func)
-def subscribe(Object obj, Closure FunctionToCall) {
-       objectList.add(obj)
-       eventList.add("Touched")
-       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) {
-       timersFuncList.add(functionToCall)
-       timersList.add(new Timer())
-       def task = timersList[-1].runAfter(1000*seconds, functionToCall)
-}
-/////////////////////////////////////////////////////////////////////
-////unschedule(func)
-def unschedule(Closure functionToUnschedule) {
-       for (int i = 0;i < timersFuncList.size();i++) {
-               if (timersFuncList[i] == functionToUnschedule) {
-                       timersList[i].cancel()
+       //Extracted objects for App1
+       //Object for class lock!
+       def lock1
+       //Object for class contactSensor!
+       def contact
+       //Global variable for number!
+       def minutesLater = 1
+       //Global variable for number!
+       def secondsLater = 10
+       //Global variable for contact!
+       def recipients = "AJ"
+       //Global variable for phone!
+       def phoneNumber = 9495379373
+
+       //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
+               lock1 = obj.lockObject
+               contact = obj.contactObject
+       }
+       //Global variables for each app
+       //Settings variable defined to settings on purpose
+       def settings = "Settings"
+       //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 = []
+
+       //Methods
+       /////////////////////////////////////////////////////////////////////
+       def setLocationMode(String mode) {
+               location.mode = mode
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       ////subscribe(obj, func)
+       def subscribe(Object obj, Closure FunctionToCall) {
+               objectList.add(obj)
+               eventList.add("Touched")
+               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, functionToCall)
+               } else {
+                       timersFuncList.add(functionToCall)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds, functionToCall)
                }
        }
-}
-/////////////////////////////////////////////////////////////////////
-////sendNotificationToContacts(text, recipients)
-def sendNotificationToContacts(String text, 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 \""+text+"\" to "+location.phoneNumbers[j].toString())
+       /////////////////////////////////////////////////////////////////////
+       ////unschedule(func)
+       def unschedule(Closure functionToUnschedule) {
+               for (int i = 0;i < timersFuncList.size();i++) {
+                       if (timersFuncList[i] == functionToUnschedule) {
+                               if (timersList != null)
+                                       timersList[i].cancel()
                        }
                }
        }
-}
-/////////////////////////////////////////////////////////////////////
-////sendSms(phone, text)
-def sendSms(long phoneNumber, String text) {
-       println("Sending \""+text+"\" to "+phoneNumber.toString())
-}
-/////////////////////////////////////////////////////////////////////
-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 < eventList.size();i++) {
-               if (eventList[i] == value) {
-                       evt.add(new Event())
-                       evt[-1].value = value
-                       evt[-1].name = name
-                       evt[-1].deviceId = deviceId
-                       evt[-1].descriptionText = descriptionText
-                       evt[-1].displayed = displayed
-                       evt[-1].linkText = linkText
-                       evt[-1].displayName = linkText
-                       evt[-1].isStateChange = isStateChange
-                       evt[-1].unit = unit
-                       evt[-1].data = data
-                       functionList[i](evt[-1])
+       
+       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())
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////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) {
+                       "$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, nameOfFunction)
+               } else {
+                       timersFuncList.add(nameOfFunction)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds, nameOfFunction)
                }
        }
-}
 
-/**
- *  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
 
+       //Extracted objects for App2
+       //Object for class switch!
+       def switchesoff
+       //Object for class switch!
+       def switcheson
+       //Object for class lock!
+       def lock1
+       //Global variable for mode!
+       def newMode = "away"
+       //Global variable for number!
+       def waitfor = 4
 
-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 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
+
+       App2(Object obj) {
+               reference = obj
+               location = obj.locationObject
+               app = obj.appObject
+               switchesoff = obj.switchObject
+               switcheson = obj.switchObject
+               lock1 = obj.lockObject
+       }
+       //Global variables for each app
+       //Settings variable defined to settings on purpose
+       def settings = "Settings"
+       //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 = []
+
+       //Methods
+       /////////////////////////////////////////////////////////////////////
+       def setLocationMode(String mode) {
+               location.mode = mode
+       }
+       
+       /////////////////////////////////////////////////////////////////////
+       ////subscribe(obj, func)
+       def subscribe(Object obj, Closure FunctionToCall) {
+               objectList.add(obj)
+               eventList.add("Touched")
+               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, functionToCall)
+               } else {
+                       timersFuncList.add(functionToCall)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds, functionToCall)
+               }
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////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())
+       }
+       /////////////////////////////////////////////////////////////////////
+       ////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) {
+                       "$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, nameOfFunction)
+               } else {
+                       timersFuncList.add(nameOfFunction)
+                       timersList.add(new SimulatedTimer())
+                       def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*seconds, nameOfFunction)
+               }
+       }
 
-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 installed()
+       {
+               log.debug "Installed with settings: ${settings}"
+               log.debug "Current mode = ${location.mode}"
+               subscribe(app, appTouch)
+       }
+       
+       
+       def updated()
+       {
+               log.debug "Updated with settings: ${settings}"
+               log.debug "Current mode = ${location.mode}"
+               unsubscribe()
+               subscribe(app, appTouch)
+       }
+       
+       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)
+       }
 }
 
-installed()
+@Field def app1 = new App1(this)
+@Field def app2 = new App2(this)
+app1.installed()
+app2.installed()
+
+       // Generate a random variable
+       Random random = new Random(1131)
+       int nextRandom = 10
+       
+       // Touch events
+       new Thread() {
+               @Override
+               public void run() {
+                       while(true) {
+                               appObject.setValue([name: "Touched", value: "Touched", deviceId: 0, descriptionText: "",
+                                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
+                               Thread.sleep(random.nextInt(nextRandom));       
+                               
+                       }
+               }
+       }.start()
+       
+       // Lock events
+       new Thread() {
+               
+               @Override
+               public void run() {
+                       while(true) {
+                               lockObject.setValue([name: "lock", value: "locked", deviceId: 0, descriptionText: "",
+                                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
+                               Thread.sleep(random.nextInt(nextRandom));       
+                       }
+               }
+       }.start()
+       
+       new Thread() {
+               
+               @Override
+               public void run() {
+                       while(true) {
+                               lockObject.setValue([name: "unlock", value: "unlocked", deviceId: 0, descriptionText: "",
+                                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
+                               Thread.sleep(random.nextInt(nextRandom));       
+                       }
+               }
+       }.start()
+       
+       // Contact sensor events
+       new Thread() {
+               
+               @Override
+               public void run() {
+                       while(true) {
+                               contactObject.setValue([name: "contact.open", value: "open", deviceId: 0, descriptionText: "",
+                                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
+                               Thread.sleep(random.nextInt(nextRandom));       
+                               
+                       }
+               }
+       }.start()
+       
+       new Thread() {
+               
+               @Override
+               public void run() {
+                       while(true) {
+                               contactObject.setValue([name: "contact.closed", value: "closed", deviceId: 0, descriptionText: "",
+                                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
+                               Thread.sleep(random.nextInt(nextRandom));       
+                               
+                       }
+               }
+       }.start()