//Infrastructure for SmartThings Application //Importing Libraries import groovy.transform.Field import groovy.json.JsonSlurper //Importing Classes 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.Phrase import appTouch.Touched import NfcTouch.NfcTouch import AeonKeyFob.AeonKeyFob import AeonKeyFob.AeonKeyFobs import MusicPlayer.MusicPlayer import MusicPlayer.MusicPlayers import MotionSensor.MotionSensor import MotionSensor.MotionSensors import ImageCapture.ImageCapture import ImageCapture.ImageCaptures import SmokeDetector.SmokeDetector import SmokeDetector.SmokeDetectors import Alarm.Alarm import Alarm.Alarms import SpeechSynthesis.SpeechSynthesis import SpeechSynthesis.SpeechSynthesises import AccelerationSensor.AccelerationSensor import AccelerationSensor.AccelerationSensors import Battery.Battery import Battery.Batteries import BeaconSensor.BeaconSensor import BeaconSensor.BeaconSensors import CarbonMonoxideDetector.CarbonMonoxideDetector import CarbonMonoxideDetector.CarbonMonoxideDetectors import ColorControl.ColorControl import ColorControl.ColorControls import EnergyMeter.EnergyMeter import EnergyMeter.EnergyMeters import IlluminanceMeasurement.IlluminanceMeasurement import IlluminanceMeasurement.IlluminanceMeasurements import PowerMeter.PowerMeter import PowerMeter.PowerMeters import RelativeHumidityMeasurement.RelativeHumidityMeasurement import RelativeHumidityMeasurement.RelativeHumidityMeasurements import RelaySwitch.RelaySwitch import RelaySwitch.RelaySwitches import SleepSensor.SleepSensor import SleepSensor.SleepSensors import StepSensor.StepSensor import StepSensor.StepSensors import SwitchLevel.SwitchLevel import SwitchLevel.SwitchLevels import TemperatureMeasurement.TemperatureMeasurement import TemperatureMeasurement.TemperatureMeasurements import WaterSensor.WaterSensor import WaterSensor.WaterSensors import Valve.Valve import Valve.Valves import MobilePresence.MobilePresence import MobilePresence.MobilePresences import Event.Event import AtomicState.AtomicState import Timer.SimulatedTimer //JPF's Verify API import gov.nasa.jpf.vm.Verify //Global eventHandler ///////////////////////////////////////////////////////////////////// def eventHandler(LinkedHashMap eventDataMap) { def value = eventDataMap["value"] def name = eventDataMap["name"] def deviceId = eventDataMap["deviceId"] def descriptionText = eventDataMap["descriptionText"] def displayed = eventDataMap["displayed"] def linkText = eventDataMap["linkText"] def isStateChange = eventDataMap["isStateChange"] def unit = eventDataMap["unit"] def data = eventDataMap["data"] for (int i = 0;i < app2.eventList.size();i++) { if (app2.eventList[i] == name) { def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data) app2.functionList[i](event) } } for (int i = 0;i < app1.eventList.size();i++) { if (app1.eventList[i] == name) { def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data) app1.functionList[i](event) } } } //GlobalVariables for both Apps //Create a global variable for send event @Field def sendEvent = {eventDataMap -> eventHandler(eventDataMap) } //Object for location @Field def locationObject = new LocationVar(sendEvent) //Object for touch to call function @Field def appObject = new Touched(sendEvent, 0) //Create a global list for events //@Field def evt = [] //Global Object for class AtomicState! @Field def atomicState = new AtomicState() //Global Object for class Touch Sensor! @Field def touchSensorObject = new NfcTouch(sendEvent, 1) //Global Object for class switch! @Field def switchObject = new Switches(sendEvent, 1) //Global Object for class lock! @Field def lockObject = new Locks(sendEvent, 1) //Global Object for class door control! @Field def doorControlObject = new DoorControls(sendEvent, 1) //Global Object for class contact sensor! @Field def contactObject = new ContactSensors(sendEvent, 1) //Global Object for class presence sensor! @Field def presenceSensorObject = new PresenceSensors(sendEvent, 1) //Global Object for class thermostat! @Field def thermostatObject = new Thermostats(sendEvent, 1) //Global Object for class aeon key fob! @Field def aeonKeyFobObject = new AeonKeyFobs(sendEvent, 1) //Global Object for class music player! @Field def musicPlayerObject = new MusicPlayers(sendEvent, 1) //Global Object for class motion sensor! @Field def motionSensorObject = new MotionSensors(sendEvent, 1) //Global Object for class image capture! @Field def imageCaptureObject = new ImageCaptures(sendEvent, 1) //Global Object for class smoke detector! @Field def smokeDetectorObject = new SmokeDetectors(sendEvent, 1) //Global Object for class alarm! @Field def alarmObject = new Alarms(sendEvent, 1) //Global Object for class speech synthesis! @Field def speechSynthesisObject = new SpeechSynthesises(sendEvent, 1) //Global Object for class acceleration sensor! @Field def accelerationSensorObject = new AccelerationSensors(sendEvent, 1) //Global Object for class Battery! @Field def batteryObject = new Batteries(sendEvent, 1) //Global Object for class beacon sensor! @Field def beaconSensorObject = new BeaconSensors(sendEvent, 1) //Global Object for class carbon monoxide! @Field def carbonMonoxideDetectorObject = new CarbonMonoxideDetectors(sendEvent, 1) //Global Object for class color control! @Field def colorControlObject = new ColorControls(sendEvent, 1) //Global Object for class energy meter! @Field def energyMeterObject = new EnergyMeters(sendEvent, 1) //Global Object for class illuminance measurement! @Field def illuminanceMeasurementObject = new IlluminanceMeasurements(sendEvent, 1) //Global Object for class power meter! @Field def powerMeterObject = new PowerMeters(sendEvent, 1) //Global Object for class humidity measurement! @Field def humidityMeasurementObject = new RelativeHumidityMeasurements(sendEvent, 1) //Global Object for class relay switch! @Field def relaySwitchObject = new RelaySwitches(sendEvent, 1) //Global Object for class sleep sensor! @Field def sleepSensorObject = new SleepSensors(sendEvent, 1) //Global Object for class step sensor! @Field def stepSensorObject = new StepSensors(sendEvent, 1) //Global Object for class switch level! @Field def switchLevelObject = new SwitchLevels(sendEvent, 1) //Global Object for class temperature measurement! @Field def temperatureMeasurementObject = new TemperatureMeasurements(sendEvent, 1) //Global Object for class water sensor! @Field def waterSensorObject = new WaterSensors(sendEvent, 1) //Global Object for class valves! @Field def valveObject = new Valves(sendEvent, 1) //Global Object for class mobile presence! @Field def mobilePresenceObject = new MobilePresences(sendEvent, 1) //Application #1 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 = 48 //Global variable for number! def secondsLater = 55 //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 atomicState = obj.atomicState lock1 = obj.lockObject contact = obj.contactObject //Global variable for settings! settings = [app:app, lock1:lock1, contact:contact, minutesLater:minutesLater, secondsLater:secondsLater, recipients:recipients, phoneNumber:phoneNumber] } //Global variables for each app //Global variable for state[mode] def state = [home:[],away:[],night:[]] //Create a global logger object for methods def log = new Logger() //Create a global variable for Functions in Subscribe method def functionList = [] //Create a global variable for Objects in Subscribe method def objectList = [] //Create a global variable for Events in Subscribe method def eventList = [] //Create a global list for function schedulers def timersFuncList = [] //Create a global list for timer schedulers def timersList = [] //Create a global variable for settings def settings //Zip code def zipCode = 92617 //Methods ///////////////////////////////////////////////////////////////////// def setLocationMode(String mode) { location.mode = mode } ///////////////////////////////////////////////////////////////////// ////subscribe(obj, func) def subscribe(Object obj, Closure FunctionToCall) { if (obj == app) { objectList.add(obj) eventList.add("Touched") functionList.add(FunctionToCall) } else if (obj == location) { objectList.add(obj) eventList.add("Location") functionList.add(FunctionToCall) } } ////subscribe(obj, event, func) def subscribe(Object obj, String event, Closure FunctionToCall) { objectList.add(obj) eventList.add(event) functionList.add(FunctionToCall) } ////subscribe(obj, event, func, data) def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) { objectList.add(obj) eventList.add(event) functionList.add(FunctionToCall) } ///////////////////////////////////////////////////////////////////// ////runIn(time, func) def runIn(int seconds, Closure functionToCall) { if (timersFuncList.contains(functionToCall)) { timersList[timersFuncList.indexOf(functionToCall)].cancel() def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall) } else { timersFuncList.add(functionToCall) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall) } } def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) { runIn(seconds, functionToCall) } def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) { runIn(seconds, nameOfFunction) } def runIn(int seconds, String nameOfFunction) { timersFuncList.add(nameOfFunction) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) { "$nameOfFunction"() } } ///////////////////////////////////////////////////////////////////// ////unschedule(func) def unschedule(Closure functionToUnschedule) { for (int i = 0;i < timersFuncList.size();i++) { if (timersFuncList[i] == functionToUnschedule) { if (timersList != null) timersList[i].cancel() } } } def unschedule() { for (int i = 0;i < timersFuncList.size();i++) { if (timersList != null) timersList[i].cancel() } } ///////////////////////////////////////////////////////////////////// ////sendNotificationToContacts(text, recipients) def sendNotificationToContacts(String text, String recipients) { for (int i = 0;i < recipients.size();i++) { for (int j = 0;j < location.contacts.size();j++) { if (recipients[i] == location.contacts[j]) { println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString()) } } } } ///////////////////////////////////////////////////////////////////// ////sendSms(phone, text) def sendSms(long phoneNumber, String text) { println("Sending \""+text+"\" to "+phoneNumber.toString()) } def sendSMS(long phoneNumber, String text) { println("Sending \""+text+"\" to "+phoneNumber.toString()) } ///////////////////////////////////////////////////////////////////// ////sendPush(text) def sendPush(String text) { println(text) } ///////////////////////////////////////////////////////////////////// ////schedule(time, nameOfFunction as String) def schedule(String time, String nameOfFunction) { def _inputTime = time.split(':') Date date = new Date() def _currentTime = date.format("HH:mm:ss").split(':') //Convert input time and current time to minutes def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2]) def delay if (inputTime < currentTime) { delay = 24*60*60-inputTime+currentTime } else { delay = inputTime-currentTime } timersFuncList.add(nameOfFunction) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) { "$nameOfFunction"() } } ////schedule(time, nameOfFunction as Closure) def schedule(String time, Closure nameOfFunction) { def _inputTime = time.split(':') Date date = new Date() def _currentTime = date.format("HH:mm:ss").split(':') //Convert input time and current time to minutes def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2]) def delay if (inputTime < currentTime) { delay = 24*60*60-inputTime+currentTime } else { delay = inputTime-currentTime } if (timersFuncList.contains(nameOfFunction)) { timersList[timersFuncList.indexOf(nameOfFunction)].cancel() def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction) } else { timersFuncList.add(nameOfFunction) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction) } } ///////////////////////////////////////////////////////////////////// def now() { return System.currentTimeMillis() } ///////////////////////////////////////////////////////////////////// def getTemperatureScale() { return 'F' //Celsius for now } ///////////////////////////////////////////////////////////////////// def getSunriseAndSunset(LinkedHashMap metaData) { def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]] return sunRiseSetInfo } ///////////////////////////////////////////////////////////////////// def httpPostJson(LinkedHashMap metaData, Closure inputData) { inputData(metaData) } ///////////////////////////////////////////////////////////////////// def runEvery15Minutes(Closure inputData) { inputData() } ///////////////////////////////////////////////////////////////////// def timeToday(String time, Object timeZone) { def timeOfDay = new Date() def _inputTime = time.split(':') def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415 timeOfDay.time = inputTime return timeOfDay } ///////////////////////////////////////////////////////////////////// def sendNotification(String text, LinkedHashMap metaData) { println("Sending \""+text+"\" to "+metaData.phone.toString()) } ///////////////////////////////////////////////////////////////////// def canSchedule() { return true } def 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!") } } } } //Application #2 class App2 { def reference def location def app def atomicState //Extracted objects for App2 //Global variable for time! def starting = "15:00" //Global variable for time! def ending = "15:00" //Object for class beacon sensor! def beacons //Object for class mobile presence! def phones //Global variable for enum! def arrivalPhrase = "Good Night!" //Object for class switch! def arrivalOnSwitches //Object for class switch! def arrivalOffSwitches //Object for class lock! def arrivalLocks //Global variable for enum! def departPhrase = "Good Night!" //Object for class switch! def departOnSwitches //Object for class switch! def departOffSwitches //Object for class lock! def departLocks //Global variable for boolean! def pushNotification = "0" //Global variable for phone! def phone = 9495379373 //Global variable for enum! def days = "Monday" //Global variable for mode! def modes = "away" //Extracted objects for functions for App2 //Global Object for functions in subscribe method! def mainPage = this.&mainPage //Global Object for functions in subscribe method! def installed = this.&installed //Global Object for functions in subscribe method! def updated = this.&updated //Global Object for functions in subscribe method! def initialize = this.&initialize //Global Object for functions in subscribe method! def beaconHandler = this.&beaconHandler //Global Object for functions in subscribe method! def arriveActions = this.&arriveActions //Global Object for functions in subscribe method! def departActions = this.&departActions //Global Object for functions in subscribe method! def prefix = this.&prefix //Global Object for functions in subscribe method! def listPhrases = this.&listPhrases //Global Object for functions in subscribe method! def executePhrase = this.&executePhrase //Global Object for functions in subscribe method! def getBeaconName = this.&getBeaconName //Global Object for functions in subscribe method! def getPhoneName = this.&getPhoneName //Global Object for functions in subscribe method! def hideOptionsSection = this.&hideOptionsSection //Global Object for functions in subscribe method! def getAllOk = this.&getAllOk //Global Object for functions in subscribe method! def getModeOk = this.&getModeOk //Global Object for functions in subscribe method! def getDaysOk = this.&getDaysOk //Global Object for functions in subscribe method! def getTimeOk = this.&getTimeOk //Global Object for functions in subscribe method! def hhmm = this.&hhmm //Global Object for functions in subscribe method! def timeIntervalLabel = this.&timeIntervalLabel //Global Object for functions in subscribe method! def list = this.&list App2(Object obj) { reference = obj location = obj.locationObject app = obj.appObject atomicState = obj.atomicState beacons = obj.beaconSensorObject phones = obj.mobilePresenceObject arrivalOnSwitches = obj.switchObject arrivalOffSwitches = obj.switchObject arrivalLocks = obj.lockObject departOnSwitches = obj.switchObject departOffSwitches = obj.switchObject departLocks = obj.lockObject //Global variable for settings! settings = [app:app, starting:starting, ending:ending, beacons:beacons, phones:phones, arrivalPhrase:arrivalPhrase, arrivalOnSwitches:arrivalOnSwitches, arrivalOffSwitches:arrivalOffSwitches, arrivalLocks:arrivalLocks, departPhrase:departPhrase, departOnSwitches:departOnSwitches, departOffSwitches:departOffSwitches, departLocks:departLocks, pushNotification:pushNotification, phone:phone, days:days, modes:modes] } //Global variables for each app //Global variable for state[mode] def state = [home:[],away:[],night:[]] //Create a global logger object for methods def log = new Logger() //Create a global variable for Functions in Subscribe method def functionList = [] //Create a global variable for Objects in Subscribe method def objectList = [] //Create a global variable for Events in Subscribe method def eventList = [] //Create a global list for function schedulers def timersFuncList = [] //Create a global list for timer schedulers def timersList = [] //Create a global variable for settings def settings //Zip code def zipCode = 92617 //Methods ///////////////////////////////////////////////////////////////////// def setLocationMode(String mode) { location.mode = mode } ///////////////////////////////////////////////////////////////////// ////subscribe(obj, func) def subscribe(Object obj, Closure FunctionToCall) { if (obj == app) { objectList.add(obj) eventList.add("Touched") functionList.add(FunctionToCall) } else if (obj == location) { objectList.add(obj) eventList.add("Location") functionList.add(FunctionToCall) } } ////subscribe(obj, event, func) def subscribe(Object obj, String event, Closure FunctionToCall) { objectList.add(obj) eventList.add(event) functionList.add(FunctionToCall) } ////subscribe(obj, event, func, data) def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) { objectList.add(obj) eventList.add(event) functionList.add(FunctionToCall) } ///////////////////////////////////////////////////////////////////// ////runIn(time, func) def runIn(int seconds, Closure functionToCall) { if (timersFuncList.contains(functionToCall)) { timersList[timersFuncList.indexOf(functionToCall)].cancel() def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall) } else { timersFuncList.add(functionToCall) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall) } } def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) { runIn(seconds, functionToCall) } def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) { runIn(seconds, nameOfFunction) } def runIn(int seconds, String nameOfFunction) { timersFuncList.add(nameOfFunction) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) { "$nameOfFunction"() } } ///////////////////////////////////////////////////////////////////// ////unschedule(func) def unschedule(Closure functionToUnschedule) { for (int i = 0;i < timersFuncList.size();i++) { if (timersFuncList[i] == functionToUnschedule) { if (timersList != null) timersList[i].cancel() } } } def unschedule() { for (int i = 0;i < timersFuncList.size();i++) { if (timersList != null) timersList[i].cancel() } } ///////////////////////////////////////////////////////////////////// ////sendNotificationToContacts(text, recipients) def sendNotificationToContacts(String text, String recipients) { for (int i = 0;i < recipients.size();i++) { for (int j = 0;j < location.contacts.size();j++) { if (recipients[i] == location.contacts[j]) { println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString()) } } } } ///////////////////////////////////////////////////////////////////// ////sendSms(phone, text) def sendSms(long phoneNumber, String text) { println("Sending \""+text+"\" to "+phoneNumber.toString()) } def sendSMS(long phoneNumber, String text) { println("Sending \""+text+"\" to "+phoneNumber.toString()) } ///////////////////////////////////////////////////////////////////// ////sendPush(text) def sendPush(String text) { println(text) } ///////////////////////////////////////////////////////////////////// ////schedule(time, nameOfFunction as String) def schedule(String time, String nameOfFunction) { def _inputTime = time.split(':') Date date = new Date() def _currentTime = date.format("HH:mm:ss").split(':') //Convert input time and current time to minutes def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2]) def delay if (inputTime < currentTime) { delay = 24*60*60-inputTime+currentTime } else { delay = inputTime-currentTime } timersFuncList.add(nameOfFunction) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) { "$nameOfFunction"() } } ////schedule(time, nameOfFunction as Closure) def schedule(String time, Closure nameOfFunction) { def _inputTime = time.split(':') Date date = new Date() def _currentTime = date.format("HH:mm:ss").split(':') //Convert input time and current time to minutes def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2]) def delay if (inputTime < currentTime) { delay = 24*60*60-inputTime+currentTime } else { delay = inputTime-currentTime } if (timersFuncList.contains(nameOfFunction)) { timersList[timersFuncList.indexOf(nameOfFunction)].cancel() def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction) } else { timersFuncList.add(nameOfFunction) timersList.add(new SimulatedTimer()) def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction) } } ///////////////////////////////////////////////////////////////////// def now() { return System.currentTimeMillis() } ///////////////////////////////////////////////////////////////////// def getTemperatureScale() { return 'F' //Celsius for now } ///////////////////////////////////////////////////////////////////// def getSunriseAndSunset(LinkedHashMap metaData) { def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]] return sunRiseSetInfo } ///////////////////////////////////////////////////////////////////// def httpPostJson(LinkedHashMap metaData, Closure inputData) { inputData(metaData) } ///////////////////////////////////////////////////////////////////// def runEvery15Minutes(Closure inputData) { inputData() } ///////////////////////////////////////////////////////////////////// def timeToday(String time, Object timeZone) { def timeOfDay = new Date() def _inputTime = time.split(':') def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415 timeOfDay.time = inputTime return timeOfDay } ///////////////////////////////////////////////////////////////////// def sendNotification(String text, LinkedHashMap metaData) { println("Sending \""+text+"\" to "+metaData.phone.toString()) } ///////////////////////////////////////////////////////////////////// def canSchedule() { return true } def mainPage() { dynamicPage(name: "mainPage", install: true, uninstall: true) { section("Where do you want to watch?") { input name: "beacons", type: "capability.beacon", title: "Select your beacon(s)", multiple: true, required: true } section("Who do you want to watch for?") { input name: "phones", type: "device.mobilePresence", title: "Select your phone(s)", multiple: true, required: true } section("What do you want to do on arrival?") { input name: "arrivalPhrase", type: "enum", title: "Execute a phrase", options: listPhrases(), required: false input "arrivalOnSwitches", "capability.switch", title: "Turn on some switches", multiple: true, required: false input "arrivalOffSwitches", "capability.switch", title: "Turn off some switches", multiple: true, required: false input "arrivalLocks", "capability.lock", title: "Unlock the door", multiple: true, required: false } section("What do you want to do on departure?") { input name: "departPhrase", type: "enum", title: "Execute a phrase", options: listPhrases(), required: false input "departOnSwitches", "capability.switch", title: "Turn on some switches", multiple: true, required: false input "departOffSwitches", "capability.switch", title: "Turn off some switches", multiple: true, required: false input "departLocks", "capability.lock", title: "Lock the door", multiple: true, required: false } section("Do you want to be notified?") { input "pushNotification", "bool", title: "Send a push notification" input "phone", "phone", title: "Send a text message", description: "Tap to enter phone number", required: false } section { label title: "Give your automation a name", description: "e.g. Goodnight Home, Wake Up" } def timeLabel = timeIntervalLabel() section(title: "More options", hidden: hideOptionsSection(), hideable: true) { href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete" input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false, options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] input "modes", "mode", title: "Only when mode is", multiple: true, required: false } } } // Lifecycle management def installed() { log.debug " Installed with settings: ${settings}" initialize() } def updated() { log.debug " Updated with settings: ${settings}" unsubscribe() initialize() } def initialize() { subscribe(beacons, "presence", beaconHandler) } // Event handlers def beaconHandler(evt) { log.debug " 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 " data: $data - phones: " + phones*.deviceNetworkId def beaconName = getBeaconName(evt) // removed logging of device names. can be added back for debugging //log.debug " beaconName: $beaconName" def phoneName = getPhoneName(data) // removed logging of device names. can be added back for debugging //log.debug " 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 " msg: $msg" if (pushNotification || phone) { def options = [ method: (pushNotification && phone) ? "both" : (pushNotification ? "push" : "sms"), phone: phone ] sendNotification(msg, options) } } } } // Helpers private arriveActions(msg) { if (arrivalPhrase || arrivalOnSwitches || arrivalOffSwitches || arrivalLocks) msg += ", so" if (arrivalPhrase) { log.debug " executing: $arrivalPhrase" executePhrase(arrivalPhrase) msg += " ${prefix('executed')} $arrivalPhrase." } if (arrivalOnSwitches) { log.debug " turning on: $arrivalOnSwitches" arrivalOnSwitches.on() msg += " ${prefix('turned')} ${list(arrivalOnSwitches)} on." } if (arrivalOffSwitches) { log.debug " turning off: $arrivalOffSwitches" arrivalOffSwitches.off() msg += " ${prefix('turned')} ${list(arrivalOffSwitches)} off." } if (arrivalLocks) { log.debug " unlocking: $arrivalLocks" arrivalLocks.unlock() msg += " ${prefix('unlocked')} ${list(arrivalLocks)}." } msg } private departActions(msg) { if (departPhrase || departOnSwitches || departOffSwitches || departLocks) msg += ", so" if (departPhrase) { log.debug " executing: $departPhrase" executePhrase(departPhrase) msg += " ${prefix('executed')} $departPhrase." } if (departOnSwitches) { log.debug " turning on: $departOnSwitches" departOnSwitches.on() msg += " ${prefix('turned')} ${list(departOnSwitches)} on." } if (departOffSwitches) { log.debug " turning off: $departOffSwitches" departOffSwitches.off() msg += " ${prefix('turned')} ${list(departOffSwitches)} off." } if (departLocks) { log.debug " 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 " executed phrase: $phraseName" } } private getBeaconName(evt) { def beaconName = beacons.find { b -> b.id == evt.deviceId } return beaconName } private getPhoneName(data) { def phoneName = phones.find { phone -> // Work around DNI bug in data def pParts = phone.deviceNetworkId.split('\\|') def dParts = data.dni.split('\\|') pParts[0] == dParts[0] } return phoneName } private hideOptionsSection() { (starting || ending || days || modes) ? false : true } private getAllOk() { modeOk && daysOk && timeOk } private getModeOk() { def result = !modes || modes.contains(location.mode) log.trace " modeOk = $result" result } private getDaysOk() { def result = true if (days) { def df = new java.text.SimpleDateFormat("EEEE") if (location.timeZone) { df.setTimeZone(location.timeZone) } else { df.setTimeZone(TimeZone.getTimeZone("America/New_York")) } def day = df.format(new Date()) result = days.contains(day) } log.trace " daysOk = $result" result } private getTimeOk() { def result = true if (starting && ending) { def currTime = now() def start = timeToday(starting, location?.timeZone).time def stop = timeToday(ending, location?.timeZone).time result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start } log.trace " timeOk = $result" result } private hhmm(time, fmt = "h:mm a") { def t = timeToday(time, location.timeZone) def f = new java.text.SimpleDateFormat(fmt) f.setTimeZone(location.timeZone ?: timeZone(time)) f.format(t) } private timeIntervalLabel() { (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : "" } private list(Object names) { return names[0] } } @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() } 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 } }