Changes in classes: new concept for latest value + all types of events generated...
[smartthings-infrastructure.git] / main.groovy
index a1474f1fcd60a238b457107787b61fcc76bf007f..953f33f858966b54ff90e94eff5fe23214359b26 100644 (file)
@@ -70,7 +70,6 @@ import Valve.Valves
 import MobilePresence.MobilePresence
 import MobilePresence.MobilePresences
 import Event.Event
-import AtomicState.AtomicState
 import Timer.SimulatedTimer
 
 //JPF's Verify API
@@ -92,14 +91,24 @@ def eventHandler(LinkedHashMap eventDataMap) {
        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)
+                       if (app2.functionList[i] instanceof String) {
+                               String toCall = app2.functionList[i]
+                               app2."$toCall"(event)
+                       }
+                       else
+                               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)
-                       app1.functionList[i](event)
+                       if (app1.functionList[i] instanceof String) {
+                               String toCall = app1.functionList[i]
+                               app1."$toCall"(event)
+                       }
+                       else
+                               app1.functionList[i](event)
                }
        }
 }
@@ -115,8 +124,6 @@ def eventHandler(LinkedHashMap eventDataMap) {
 @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!
@@ -185,45 +192,51 @@ class App1 {
        def reference
        def location
        def app
-       def atomicState
 
        //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
+       //Object for class color control!
+       def master
+       //Object for class color control!
+       def slaves
+       //Global variable for boolean!
+       def randomYes = "1"
 
        //Extracted objects for functions for App1
        //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
+       def init = this.&init
+       //Global Object for functions in subscribe method!
+       def onOffHandler = this.&onOffHandler
+       //Global Object for functions in subscribe method!
+       def colorHandler = this.&colorHandler
+       //Global Object for functions in subscribe method!
+       def getRandomColorMaster = this.&getRandomColorMaster
+       //Global Object for functions in subscribe method!
+       def tempHandler = this.&tempHandler
+       //Global Object for functions in subscribe method!
+       def textAppName = this.&textAppName
+       //Global Object for functions in subscribe method!
+       def textVersion = this.&textVersion
        //Global Object for functions in subscribe method!
-       def lockDoor = this.&lockDoor
+       def textCopyright = this.&textCopyright
        //Global Object for functions in subscribe method!
-       def unlockDoor = this.&unlockDoor
+       def textLicense = this.&textLicense
        //Global Object for functions in subscribe method!
-       def doorHandler = this.&doorHandler
+       def textHelp = this.&textHelp
 
        App1(Object obj) {
                reference = obj
                location = obj.locationObject
                app = obj.appObject
-               atomicState = obj.atomicState
-               lock1 = obj.lockObject
-               contact = obj.contactObject
+               master = obj.colorControlObject
+               slaves = obj.colorControlObject
                //Global variable for settings!
-               settings = [app:app, lock1:lock1, contact:contact, minutesLater:minutesLater, secondsLater:secondsLater, recipients:recipients, phoneNumber:phoneNumber]
+               settings = [app: app, master: master, slaves: slaves, randomYes: randomYes, END: "END"]
        }
        //Global variables for each app
        //Global variable for state[mode]
@@ -244,11 +257,16 @@ class App1 {
        def settings
        //Zip code
        def zipCode = 92617
+       //atomicState variable
+       def atomicState = [version: "1.01"]
 
        //Methods
        /////////////////////////////////////////////////////////////////////
        def setLocationMode(String mode) {
-               location.mode = mode
+               location.setValue([name: "Location", value: "$mode", deviceId: "locationID0", descriptionText: "",
+                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+               location.setValue([name: "mode", value: "$mode", deviceId: "locationID0", descriptionText: "",
+                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
        }
        
        /////////////////////////////////////////////////////////////////////
@@ -270,6 +288,12 @@ class App1 {
                eventList.add(event)
                functionList.add(FunctionToCall)
        }
+       ////subscribe(obj, event, nameOfFunc)
+       def subscribe(Object obj, String event, String nameOfFunction) {
+               objectList.add(obj)
+               eventList.add(event)
+               functionList.add(nameOfFunction)
+       }
        ////subscribe(obj, event, func, data)
        def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
                objectList.add(obj)     
@@ -315,6 +339,17 @@ class App1 {
                }
        }
        
+       def unschedule(String nameOfFunctionToUnschedule) {
+               for (int i = 0;i < timersFuncList.size();i++) {
+                       if (timersFuncList[i] instanceof String) {
+                               if (timersFuncList[i] == nameOfFunctionToUnschedule) {
+                                       if (timersList != null)
+                                               timersList[i].cancel()
+                               }
+                       }
+               }
+       }
+       
        
        def unschedule() {
                for (int i = 0;i < timersFuncList.size();i++) {
@@ -333,6 +368,16 @@ class App1 {
                        }
                }
        }
+       
+       def sendNotificationToContacts(String text, String recipients, LinkedHashMap metaData) {
+               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) {
@@ -435,92 +480,161 @@ class App1 {
        def canSchedule() {
                return true
        }
+       /////////////////////////////////////////////////////////////////////
+       def createAccessToken() {
+               state.accessToken = "accessToken"
+               return state.accessToken
+       }
+       /////////////////////////////////////////////////////////////////////
+       def runOnce(Date date, Closure methodToCall) {
+               methodTocall()
+       }
 
-       def installed(){
-           initialize()
+       def mainPage() {
+               section("Master Light") {
+                       input "master", "capability.colorControl", title: "Colored Light", required: true
+               }
+               section("Lights that follow the master settings") {
+                       input "slaves", "capability.colorControl", title: "Colored Lights",  multiple: true, required: true, submitOnChange: true
+               }
+               section([mobileOnly:true], "Options") {
+                       input "randomYes", "bool",title: "When Master Turned On, Randomize Color", defaultValue: false
+                       href "pageAbout", title: "About ${textAppName()}", description: "Tap to get application version, license and instructions"
+               }
+               
+               dynamicPage(name: "mainPage", title: "", install: true, uninstall: false) {
+               def masterInList = slaves?.id?.find{it==master?.id}
+               if (masterInList) {
+                       section ("**WARNING**"){
+                               paragraph "You have included the Master Light in the Slave Group. This will cause a loop in execution. Please remove this device from the Slave Group.", image: "https://raw.githubusercontent.com/MichaelStruck/SmartThingsPublic/master/img/caution.png"
+                       }
+               }
+               
+               page(name: "pageAbout", title: "About ${textAppName()}", uninstall: true) {
+                               section {
+                                       paragraph "${textVersion()}\n${textCopyright()}\n\n${textLicense()}\n"
+                               }
+                               section("Instructions") {
+                                       paragraph textHelp()
+                               }
+                               section("Tap button below to remove application"){
+                               }
+                       }
+                       
+               }
+       }
+       
+       def installed() {   
+               init() 
        }
        
        def updated(){
-           unsubscribe()
-           unschedule()
-           initialize()
+               unsubscribe()
+           init()
+       }
+       
+       def init() {
+           subscribe(master, "switch", onOffHandler)
+           subscribe(master, "level", colorHandler)
+           subscribe(master, "hue", colorHandler)
+           subscribe(master, "saturation", colorHandler)
+           subscribe(master, "colorTemperature", tempHandler)
+       }
+       //-----------------------------------
+       def onOffHandler(evt){
+               if (slaves && master) {
+                       if (!slaves?.id.find{it==master?.id}){
+                       if (master?.currentValue("switch") == "on"){
+                           if (randomYes) getRandomColorMaster()
+                                       else slaves?.on()
+                       }
+                       else {
+                           slaves?.off()  
+                       }
+                       }
+               }
        }
        
-       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 colorHandler(evt) {
+               if (slaves && master) {
+                       if (!slaves?.id?.find{it==master?.id} && master?.currentValue("switch") == "on"){
+                               log.debug "Changing Slave units H,S,L"
+                       def dimLevel = master?.currentValue("level")
+                       def hueLevel = master?.currentValue("hue")
+                       def saturationLevel = master.currentValue("saturation")
+                               def newValue = [hue: hueLevel, saturation: saturationLevel, level: dimLevel as Integer]
+                       slaves?.setColor(newValue)
+                       try {
+                               log.debug "Changing Slave color temp"
+                               def tempLevel = master?.currentValue("colorTemperature")
+                               slaves?.setColorTemperature(tempLevel)
+                       }
+                               catch (e){
+                               log.debug "Color temp for master --"
+                       }
+                       }
+               }
        }
        
-       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 getRandomColorMaster(){
+           def hueLevel = Math.floor(Math.random() *1000)
+           def saturationLevel = Math.floor(Math.random() * 100)
+           def dimLevel = master?.currentValue("level")
+               def newValue = [hue: hueLevel, saturation: saturationLevel, level: dimLevel as Integer]
+           log.debug hueLevel
+           log.debug saturationLevel
+           master.setColor(newValue)
+           slaves?.setColor(newValue)   
+       }
+       
+       def tempHandler(evt){
+               if (slaves && master) {
+                   if (!slaves?.id?.find{it==master?.id} && master?.currentValue("switch") == "on"){
+                       if (evt.value != "--") {
+                           log.debug "Changing Slave color temp based on Master change"
+                           def tempLevel = master.currentValue("colorTemperature")
+                           slaves?.setColorTemperature(tempLevel)
+                       }
+                       }
+               }
        }
        
-       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!")
-           }
+       //Version/Copyright/Information/Help
+       
+       private def textAppName() {
+               def text = "Color Coordinator"
+       }       
+       
+       private def textVersion() {
+           def text = "Version 1.1.1 (12/13/2016)"
        }
        
-       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!")
-               }
-           }
+       private def textCopyright() {
+           def text = "Copyright Â© 2016 Michael Struck"
+       }
+       
+       private def textLicense() {
+           def text =
+                       "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"+
+                       "\n\n"+
+                       "    http://www.apache.org/licenses/LICENSE-2.0"+
+                       "\n\n"+
+                       "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."
+       }
+       
+       private def textHelp() {
+               def text =
+               "This application will allow you to control the settings of multiple colored lights with one control. " +
+               "Simply choose a master control light, and then choose the lights that will follow the settings of the master, "+
+               "including on/off conditions, hue, saturation, level and color temperature. Also includes a random color feature."
        }
+       
 }
 
 
@@ -529,99 +643,45 @@ class App2 {
        def reference
        def location
        def app
-       def atomicState
 
        //Extracted objects for App2
+       //Object for class contactSensor!
+       def deviceContactSensor
        //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"
+       def reminderTime = "15:00"
+       //Object for class color control!
+       def deviceLight
 
        //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
+       def contactHandler = this.&contactHandler
        //Global Object for functions in subscribe method!
-       def getBeaconName = this.&getBeaconName
+       def checkOpenDrawInPast = this.&checkOpenDrawInPast
        //Global Object for functions in subscribe method!
-       def getPhoneName = this.&getPhoneName
+       def checkOpenDrawAfterReminder = this.&checkOpenDrawAfterReminder
        //Global Object for functions in subscribe method!
-       def hideOptionsSection = this.&hideOptionsSection
+       def sendNotification = this.&sendNotification
        //Global Object for functions in subscribe method!
-       def getAllOk = this.&getAllOk
+       def isOpened = this.&isOpened
        //Global Object for functions in subscribe method!
-       def getModeOk = this.&getModeOk
+       def setLEDNotification = this.&setLEDNotification
        //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
+       def resetLEDNotification = this.&resetLEDNotification
 
        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
+               deviceContactSensor = obj.contactObject
+               deviceLight = obj.colorControlObject
                //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]
+               settings = [app: app, deviceContactSensor: deviceContactSensor, reminderTime: reminderTime, deviceLight: deviceLight, END: "END"]
        }
        //Global variables for each app
        //Global variable for state[mode]
@@ -642,11 +702,16 @@ class App2 {
        def settings
        //Zip code
        def zipCode = 92617
+       //atomicState variable
+       def atomicState = [version: "1.01"]
 
        //Methods
        /////////////////////////////////////////////////////////////////////
        def setLocationMode(String mode) {
-               location.mode = mode
+               location.setValue([name: "Location", value: "$mode", deviceId: "locationID0", descriptionText: "",
+                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+               location.setValue([name: "mode", value: "$mode", deviceId: "locationID0", descriptionText: "",
+                                  displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
        }
        
        /////////////////////////////////////////////////////////////////////
@@ -668,6 +733,12 @@ class App2 {
                eventList.add(event)
                functionList.add(FunctionToCall)
        }
+       ////subscribe(obj, event, nameOfFunc)
+       def subscribe(Object obj, String event, String nameOfFunction) {
+               objectList.add(obj)
+               eventList.add(event)
+               functionList.add(nameOfFunction)
+       }
        ////subscribe(obj, event, func, data)
        def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
                objectList.add(obj)     
@@ -713,6 +784,17 @@ class App2 {
                }
        }
        
+       def unschedule(String nameOfFunctionToUnschedule) {
+               for (int i = 0;i < timersFuncList.size();i++) {
+                       if (timersFuncList[i] instanceof String) {
+                               if (timersFuncList[i] == nameOfFunctionToUnschedule) {
+                                       if (timersList != null)
+                                               timersList[i].cancel()
+                               }
+                       }
+               }
+       }
+       
        
        def unschedule() {
                for (int i = 0;i < timersFuncList.size();i++) {
@@ -731,6 +813,16 @@ class App2 {
                        }
                }
        }
+       
+       def sendNotificationToContacts(String text, String recipients, LinkedHashMap metaData) {
+               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) {
@@ -833,329 +925,183 @@ class App2 {
        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
-                       }
-               }
+       /////////////////////////////////////////////////////////////////////
+       def createAccessToken() {
+               state.accessToken = "accessToken"
+               return state.accessToken
        }
-       
-       // Lifecycle management
+       /////////////////////////////////////////////////////////////////////
+       def runOnce(Date date, Closure methodToCall) {
+               methodTocall()
+       }
+
        def installed() {
-               log.debug "<beacon-control> Installed with settings: ${settings}"
+               log.debug "Installed with settings: ${settings}"
+               
                initialize()
        }
        
        def updated() {
-               log.debug "<beacon-control> Updated with settings: ${settings}"
+               log.debug "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)
-                   }
+           // will stop LED notification incase it was set by med reminder
+           subscribe(deviceContactSensor, "contact", contactHandler)
+       
+           // how many minutes to look in the past from the reminder time, for an open draw
+           state.minutesToCheckOpenDraw = 60
+           
+           // is true when LED notification is set after exceeding 10 minutes past reminder time
+           state.ledNotificationTriggered = false
+           
+           // Set a timer to run once a day to notify if draw wasn't opened yet
+           schedule(reminderTime, checkOpenDrawInPast)
+          
+       }
+       
+       // Should turn off any LED notification on OPEN state
+       def contactHandler(evt){
+               if (evt.value == "open") {
+               // if LED notification triggered, reset it.
+               log.debug "Cabinet opened"
+               if (state.ledNotificationTriggered) {
+                   resetLEDNotification()
                }
                }
        }
        
-       // 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
+       // If the draw was NOT opened within 60 minutes of the timer send notification out.
+       def checkOpenDrawInPast(){
+               log.debug "Checking past 60 minutes of activity from $reminderTime"
+           
+           // check activity of sensor for past 60 minutes for any OPENED status
+           def cabinetOpened = isOpened(state.minutesToCheckOpenDraw)
+               log.debug "Cabinet found opened: $cabinetOpened"
+           
+           // if it's opened, then do nothing and assume they took their meds
+           if (!cabinetOpened) {    
+               sendNotification("Hi, please remember to take your meds in the cabinet")
+              
+              // if no open activity, send out notification and set new reminder    
+               def reminderTimePlus10 = new Date(now() + (10 * 60000))
+       
+               // needs to be scheduled if draw wasn't already opened
+               runOnce(reminderTimePlus10, checkOpenDrawAfterReminder)
+           }
        }
        
-       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]
+       // If the draw was NOT opened after 10 minutes past reminder, use LED notification
+       def checkOpenDrawAfterReminder(){
+               log.debug "Checking additional 10 minutes of activity from $reminderTime"
+           
+           // check activity of sensor for past 10 minutes for any OPENED status
+           def cabinetOpened = isOpened(10)    
+           
+               log.debug "Cabinet found opened: $cabinetOpened"
+               
+           // if no open activity, blink lights
+           if (!cabinetOpened) {
+               log.debug "Set LED to Notification color"
+               setLEDNotification()
+           }
+           
+       }
+       
+       // Helper function for sending out an app notification
+       def sendNotification(msg){
+               log.debug "Message Sent: $msg"
+               sendPush(msg)
+       }
+       
+       // Check if the sensor has been opened since the minutes entered
+       // Return true if opened found, else false.
+       def isOpened(minutes){
+           // query last X minutes of activity log    
+           def previousDateTime = new Date(now() - (minutes * 60000))
+           
+           // capture all events recorded
+           def evts = deviceContactSensor.eventsSince(previousDateTime)   
+           def cabinetOpened = false
+           if (evts.size() > 0) {
+               evts.each{
+                   if(it.value == "open") {
+                       cabinetOpened = true 
+                   }
+               }
                }
-               return phoneName
+           
+           return cabinetOpened
        }
        
-       private hideOptionsSection() {
-               (starting || ending || days || modes) ? false : true
-       }
-       
-       private getAllOk() {
-               modeOk && daysOk && timeOk
-       }
+       // Saves current color and sets the light to RED
+       def setLEDNotification(){
        
-       private getModeOk() {
-               def result = !modes || modes.contains(location.mode)
-               log.trace "<beacon-control> modeOk = $result"
-               result
-       }
+               state.ledNotificationTriggered = true
+           
+               // turn light back off when reset is called if it was originally off
+               state.ledState = deviceLight.currentValue("switch")
        
-       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
-       }
+               // set light to RED and store original color until stopped    
+           state.origColor = deviceLight.currentValue("hue")
+           deviceLight.on()
+           deviceLight.setHue(100)
+           
+           log.debug "LED set to RED. Original color stored: $state.origColor"
        
-       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)
-       }
+       // Sets the color back to the original saved color
+       def resetLEDNotification(){
        
-       private timeIntervalLabel() {
-               (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
-       }
+               state.ledNotificationTriggered = false
+           
+           // return color to original
+           log.debug "Reset LED color to: $state.origColor"
+           if (state.origColor != null) {
+               deviceLight.setHue(state.origColor)
+           }
+           
+           // if the light was turned on just for the notification, turn it back off now
+           if (state.ledState == "off") {
+               deviceLight.off()
+           }
        
-       private list(Object names) {
-               return names[0]
        }
 }
 
 @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 installOrder = Verify.getBoolean()
-if (installOrder) {
-       app1.installed()
-       app2.installed()
-} else {
-       app2.installed()
-       app1.installed()
-}
+app1 = new App1(this)
+app2 = new App2(this)
 
-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
-       }
-}
+app1.installed()
+app2.installed()
+
+contactObject.setValue([name: "contact", value: "open", deviceId: "contactSensorID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+contactObject.setValue([name: "contact", value: "closed", deviceId: "contactSensorID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+contactObject.setValue([name: "contact", value: "open", deviceId: "contactSensorID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+
+colorControlObject.setValue([name: "hue", value: "32", deviceId: "colorControlID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+colorControlObject.setValue([name: "saturation", value: "32", deviceId: "colorControlID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+colorControlObject.setValue([name: "level", value: "32", deviceId: "colorControlID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+colorControlObject.setValue([name: "switch", value: "on", deviceId: "colorControlID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+colorControlObject.setValue([name: "switch", value: "off", deviceId: "colorControlID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+colorControlObject.setValue([name: "switch", value: "on", deviceId: "colorControlID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
+colorControlObject.setValue([name: "colorTemperature", value: "32", deviceId: "colorControlID0", descriptionText: "",
+displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])