1 //Infrastructure for SmartThings Application
3 import groovy.transform.Field
4 import groovy.json.JsonSlurper
7 import ContactSensor.ContactSensor
8 import ContactSensor.ContactSensors
9 import DoorControl.DoorControl
10 import DoorControl.DoorControls
13 import Thermostat.Thermostat
14 import Thermostat.Thermostats
16 import Switch.Switches
17 import PresenceSensor.PresenceSensor
18 import PresenceSensor.PresenceSensors
20 import Location.LocationVar
21 import Location.Phrase
22 import appTouch.Touched
23 import NfcTouch.NfcTouch
24 import AeonKeyFob.AeonKeyFob
25 import AeonKeyFob.AeonKeyFobs
26 import MusicPlayer.MusicPlayer
27 import MusicPlayer.MusicPlayers
28 import MotionSensor.MotionSensor
29 import MotionSensor.MotionSensors
30 import ImageCapture.ImageCapture
31 import ImageCapture.ImageCaptures
32 import SmokeDetector.SmokeDetector
33 import SmokeDetector.SmokeDetectors
36 import SpeechSynthesis.SpeechSynthesis
37 import SpeechSynthesis.SpeechSynthesises
38 import AccelerationSensor.AccelerationSensor
39 import AccelerationSensor.AccelerationSensors
40 import Battery.Battery
41 import Battery.Batteries
42 import BeaconSensor.BeaconSensor
43 import BeaconSensor.BeaconSensors
44 import CarbonMonoxideDetector.CarbonMonoxideDetector
45 import CarbonMonoxideDetector.CarbonMonoxideDetectors
46 import ColorControl.ColorControl
47 import ColorControl.ColorControls
48 import EnergyMeter.EnergyMeter
49 import EnergyMeter.EnergyMeters
50 import IlluminanceMeasurement.IlluminanceMeasurement
51 import IlluminanceMeasurement.IlluminanceMeasurements
52 import PowerMeter.PowerMeter
53 import PowerMeter.PowerMeters
54 import RelativeHumidityMeasurement.RelativeHumidityMeasurement
55 import RelativeHumidityMeasurement.RelativeHumidityMeasurements
56 import RelaySwitch.RelaySwitch
57 import RelaySwitch.RelaySwitches
58 import SleepSensor.SleepSensor
59 import SleepSensor.SleepSensors
60 import StepSensor.StepSensor
61 import StepSensor.StepSensors
62 import SwitchLevel.SwitchLevel
63 import SwitchLevel.SwitchLevels
64 import TemperatureMeasurement.TemperatureMeasurement
65 import TemperatureMeasurement.TemperatureMeasurements
66 import WaterSensor.WaterSensor
67 import WaterSensor.WaterSensors
70 import MobilePresence.MobilePresence
71 import MobilePresence.MobilePresences
73 import AtomicState.AtomicState
74 import Timer.SimulatedTimer
77 import gov.nasa.jpf.vm.Verify
80 /////////////////////////////////////////////////////////////////////
81 def eventHandler(LinkedHashMap eventDataMap) {
82 def value = eventDataMap["value"]
83 def name = eventDataMap["name"]
84 def deviceId = eventDataMap["deviceId"]
85 def descriptionText = eventDataMap["descriptionText"]
86 def displayed = eventDataMap["displayed"]
87 def linkText = eventDataMap["linkText"]
88 def isStateChange = eventDataMap["isStateChange"]
89 def unit = eventDataMap["unit"]
90 def data = eventDataMap["data"]
92 for (int i = 0;i < app2.eventList.size();i++) {
93 if (app2.eventList[i] == name) {
94 def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
95 app2.functionList[i](event)
99 for (int i = 0;i < app1.eventList.size();i++) {
100 if (app1.eventList[i] == name) {
101 def event = new Event(value, name, deviceId, descriptionText, displayed, linkText, linkText, isStateChange, unit, data)
102 app1.functionList[i](event)
107 //GlobalVariables for both Apps
108 //Create a global variable for send event
109 @Field def sendEvent = {eventDataMap ->
110 eventHandler(eventDataMap)
112 //Object for location
113 @Field def locationObject = new LocationVar(sendEvent)
114 //Object for touch to call function
115 @Field def appObject = new Touched(sendEvent, 0)
116 //Create a global list for events
117 //@Field def evt = []
118 //Global Object for class AtomicState!
119 @Field def atomicState = new AtomicState()
120 //Global Object for class Touch Sensor!
121 @Field def touchSensorObject = new NfcTouch(sendEvent, 1)
122 //Global Object for class switch!
123 @Field def switchObject = new Switches(sendEvent, 1)
124 //Global Object for class lock!
125 @Field def lockObject = new Locks(sendEvent, 1)
126 //Global Object for class door control!
127 @Field def doorControlObject = new DoorControls(sendEvent, 1)
128 //Global Object for class contact sensor!
129 @Field def contactObject = new ContactSensors(sendEvent, 1)
130 //Global Object for class presence sensor!
131 @Field def presenceSensorObject = new PresenceSensors(sendEvent, 1)
132 //Global Object for class thermostat!
133 @Field def thermostatObject = new Thermostats(sendEvent, 1)
134 //Global Object for class aeon key fob!
135 @Field def aeonKeyFobObject = new AeonKeyFobs(sendEvent, 1)
136 //Global Object for class music player!
137 @Field def musicPlayerObject = new MusicPlayers(sendEvent, 1)
138 //Global Object for class motion sensor!
139 @Field def motionSensorObject = new MotionSensors(sendEvent, 1)
140 //Global Object for class image capture!
141 @Field def imageCaptureObject = new ImageCaptures(sendEvent, 1)
142 //Global Object for class smoke detector!
143 @Field def smokeDetectorObject = new SmokeDetectors(sendEvent, 1)
144 //Global Object for class alarm!
145 @Field def alarmObject = new Alarms(sendEvent, 1)
146 //Global Object for class speech synthesis!
147 @Field def speechSynthesisObject = new SpeechSynthesises(sendEvent, 1)
148 //Global Object for class acceleration sensor!
149 @Field def accelerationSensorObject = new AccelerationSensors(sendEvent, 1)
150 //Global Object for class Battery!
151 @Field def batteryObject = new Batteries(sendEvent, 1)
152 //Global Object for class beacon sensor!
153 @Field def beaconSensorObject = new BeaconSensors(sendEvent, 1)
154 //Global Object for class carbon monoxide!
155 @Field def carbonMonoxideDetectorObject = new CarbonMonoxideDetectors(sendEvent, 1)
156 //Global Object for class color control!
157 @Field def colorControlObject = new ColorControls(sendEvent, 1)
158 //Global Object for class energy meter!
159 @Field def energyMeterObject = new EnergyMeters(sendEvent, 1)
160 //Global Object for class illuminance measurement!
161 @Field def illuminanceMeasurementObject = new IlluminanceMeasurements(sendEvent, 1)
162 //Global Object for class power meter!
163 @Field def powerMeterObject = new PowerMeters(sendEvent, 1)
164 //Global Object for class humidity measurement!
165 @Field def humidityMeasurementObject = new RelativeHumidityMeasurements(sendEvent, 1)
166 //Global Object for class relay switch!
167 @Field def relaySwitchObject = new RelaySwitches(sendEvent, 1)
168 //Global Object for class sleep sensor!
169 @Field def sleepSensorObject = new SleepSensors(sendEvent, 1)
170 //Global Object for class step sensor!
171 @Field def stepSensorObject = new StepSensors(sendEvent, 1)
172 //Global Object for class switch level!
173 @Field def switchLevelObject = new SwitchLevels(sendEvent, 1)
174 //Global Object for class temperature measurement!
175 @Field def temperatureMeasurementObject = new TemperatureMeasurements(sendEvent, 1)
176 //Global Object for class water sensor!
177 @Field def waterSensorObject = new WaterSensors(sendEvent, 1)
178 //Global Object for class valves!
179 @Field def valveObject = new Valves(sendEvent, 1)
180 //Global Object for class mobile presence!
181 @Field def mobilePresenceObject = new MobilePresences(sendEvent, 1)
190 //Extracted objects for App1
191 //Object for class temperature measurement!
193 //Object for class thermostat!
195 //Object for class presence sensor!
197 //Object for class smoke detector!
199 //Object for class humidity measurement!
201 //Object for class water sensor!
203 //Object for class illuminance measurement!
205 //Object for class lock!
207 //Object for class contactSensor!
209 //Object for class Acceleration Sensor!
211 //Object for class Motion Sensor!
213 //Object for class presence sensor!
215 //Object for class switch!
217 //Object for class switch level!
219 //Object for class Battery!
221 //Object for class power meter!
223 //Object for class energy meter!
225 //Global variable for text!
226 def channelKey = "This is just a text!"
227 //Global variable for number!
228 def givenInterval = 75
230 //Extracted objects for functions for App1
231 //Global Object for functions in subscribe method!
232 def installed = this.&installed
233 //Global Object for functions in subscribe method!
234 def updated = this.&updated
235 //Global Object for functions in subscribe method!
236 def initialize = this.&initialize
237 //Global Object for functions in subscribe method!
238 def appTouch = this.&appTouch
239 //Global Object for functions in subscribe method!
240 def rescheduleIfNeeded = this.&rescheduleIfNeeded
241 //Global Object for functions in subscribe method!
242 def handleTemperatureEvent = this.&handleTemperatureEvent
243 //Global Object for functions in subscribe method!
244 def handleHumidityEvent = this.&handleHumidityEvent
245 //Global Object for functions in subscribe method!
246 def handleHeatingSetpointEvent = this.&handleHeatingSetpointEvent
247 //Global Object for functions in subscribe method!
248 def handleCoolingSetpointEvent = this.&handleCoolingSetpointEvent
249 //Global Object for functions in subscribe method!
250 def handleThermostatModeEvent = this.&handleThermostatModeEvent
251 //Global Object for functions in subscribe method!
252 def handleFanModeEvent = this.&handleFanModeEvent
253 //Global Object for functions in subscribe method!
254 def handleHumidifierModeEvent = this.&handleHumidifierModeEvent
255 //Global Object for functions in subscribe method!
256 def handleHumidifierLevelEvent = this.&handleHumidifierLevelEvent
257 //Global Object for functions in subscribe method!
258 def handleDehumidifierModeEvent = this.&handleDehumidifierModeEvent
259 //Global Object for functions in subscribe method!
260 def handleDehumidifierLevelEvent = this.&handleDehumidifierLevelEvent
261 //Global Object for functions in subscribe method!
262 def handleVentilatorModeEvent = this.&handleVentilatorModeEvent
263 //Global Object for functions in subscribe method!
264 def handleFanMinOnTimeEvent = this.&handleFanMinOnTimeEvent
265 //Global Object for functions in subscribe method!
266 def handleVentilatorMinOnTimeEvent = this.&handleVentilatorMinOnTimeEvent
267 //Global Object for functions in subscribe method!
268 def handleThermostatOperatingStateEvent = this.&handleThermostatOperatingStateEvent
269 //Global Object for functions in subscribe method!
270 def handleDailyStats = this.&handleDailyStats
271 //Global Object for functions in subscribe method!
272 def handleEquipmentStatusEvent = this.&handleEquipmentStatusEvent
273 //Global Object for functions in subscribe method!
274 def handleProgramNameEvent = this.&handleProgramNameEvent
275 //Global Object for functions in subscribe method!
276 def handleWaterEvent = this.&handleWaterEvent
277 //Global Object for functions in subscribe method!
278 def handleSmokeEvent = this.&handleSmokeEvent
279 //Global Object for functions in subscribe method!
280 def handleCarbonMonoxideEvent = this.&handleCarbonMonoxideEvent
281 //Global Object for functions in subscribe method!
282 def handleIlluminanceEvent = this.&handleIlluminanceEvent
283 //Global Object for functions in subscribe method!
284 def handleLockEvent = this.&handleLockEvent
285 //Global Object for functions in subscribe method!
286 def handleBatteryEvent = this.&handleBatteryEvent
287 //Global Object for functions in subscribe method!
288 def handleContactEvent = this.&handleContactEvent
289 //Global Object for functions in subscribe method!
290 def handleAccelerationEvent = this.&handleAccelerationEvent
291 //Global Object for functions in subscribe method!
292 def handleMotionEvent = this.&handleMotionEvent
293 //Global Object for functions in subscribe method!
294 def handlePresenceEvent = this.&handlePresenceEvent
295 //Global Object for functions in subscribe method!
296 def handleSwitchEvent = this.&handleSwitchEvent
297 //Global Object for functions in subscribe method!
298 def handleSetLevelEvent = this.&handleSetLevelEvent
299 //Global Object for functions in subscribe method!
300 def handlePowerEvent = this.&handlePowerEvent
301 //Global Object for functions in subscribe method!
302 def handleEnergyEvent = this.&handleEnergyEvent
303 //Global Object for functions in subscribe method!
304 def handleCostEvent = this.&handleCostEvent
305 //Global Object for functions in subscribe method!
306 def queueValue = this.&queueValue
307 //Global Object for functions in subscribe method!
308 def processQueue = this.&processQueue
312 location = obj.locationObject
314 atomicState = obj.atomicState
315 temperatures = obj.temperatureMeasurementObject
316 thermostats = obj.thermostatObject
317 automatic = obj.presenceSensorObject
318 detectors = obj.smokeDetectorObject
319 humidities = obj.humidityMeasurementObject
320 waters = obj.waterSensorObject
321 illuminances = obj.illuminanceMeasurementObject
322 locks = obj.lockObject
323 contacts = obj.contactObject
324 accelerations = obj.accelerationSensorObject
325 motions = obj.motionSensorObject
326 presence = obj.presenceSensorObject
327 switches = obj.switchObject
328 dimmerSwitches = obj.switchLevelObject
329 batteries = obj.batteryObject
330 powers = obj.powerMeterObject
331 energys = obj.energyMeterObject
332 //Global variable for settings!
333 settings = [app:app, temperatures:temperatures, thermostats:thermostats, automatic:automatic, detectors:detectors, humidities:humidities, waters:waters, illuminances:illuminances, locks:locks, contacts:contacts, accelerations:accelerations, motions:motions, presence:presence, switches:switches, dimmerSwitches:dimmerSwitches, batteries:batteries, powers:powers, energys:energys, channelKey:channelKey, givenInterval:givenInterval]
335 //Global variables for each app
336 //Global variable for state[mode]
337 def state = [home:[],away:[],night:[]]
338 //Create a global logger object for methods
339 def log = new Logger()
340 //Create a global variable for Functions in Subscribe method
341 def functionList = []
342 //Create a global variable for Objects in Subscribe method
344 //Create a global variable for Events in Subscribe method
346 //Create a global list for function schedulers
347 def timersFuncList = []
348 //Create a global list for timer schedulers
350 //Create a global variable for settings
356 /////////////////////////////////////////////////////////////////////
357 def setLocationMode(String mode) {
361 /////////////////////////////////////////////////////////////////////
362 ////subscribe(obj, func)
363 def subscribe(Object obj, Closure FunctionToCall) {
366 eventList.add("Touched")
367 functionList.add(FunctionToCall)
368 } else if (obj == location) {
370 eventList.add("Location")
371 functionList.add(FunctionToCall)
374 ////subscribe(obj, event, func)
375 def subscribe(Object obj, String event, Closure FunctionToCall) {
378 functionList.add(FunctionToCall)
380 ////subscribe(obj, event, func, data)
381 def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
384 functionList.add(FunctionToCall)
386 /////////////////////////////////////////////////////////////////////
387 ////runIn(time, func)
388 def runIn(int seconds, Closure functionToCall) {
389 if (timersFuncList.contains(functionToCall)) {
390 timersList[timersFuncList.indexOf(functionToCall)].cancel()
391 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
393 timersFuncList.add(functionToCall)
394 timersList.add(new SimulatedTimer())
395 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
399 def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
400 runIn(seconds, functionToCall)
403 def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
404 runIn(seconds, nameOfFunction)
407 def runIn(int seconds, String nameOfFunction) {
408 timersFuncList.add(nameOfFunction)
409 timersList.add(new SimulatedTimer())
410 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
414 /////////////////////////////////////////////////////////////////////
416 def unschedule(Closure functionToUnschedule) {
417 for (int i = 0;i < timersFuncList.size();i++) {
418 if (timersFuncList[i] == functionToUnschedule) {
419 if (timersList != null)
420 timersList[i].cancel()
427 for (int i = 0;i < timersFuncList.size();i++) {
428 if (timersList != null)
429 timersList[i].cancel()
432 /////////////////////////////////////////////////////////////////////
433 ////sendNotificationToContacts(text, recipients)
434 def sendNotificationToContacts(String text, String recipients) {
435 for (int i = 0;i < recipients.size();i++) {
436 for (int j = 0;j < location.contacts.size();j++) {
437 if (recipients[i] == location.contacts[j]) {
438 println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
443 /////////////////////////////////////////////////////////////////////
444 ////sendSms(phone, text)
445 def sendSms(long phoneNumber, String text) {
446 println("Sending \""+text+"\" to "+phoneNumber.toString())
449 def sendSMS(long phoneNumber, String text) {
450 println("Sending \""+text+"\" to "+phoneNumber.toString())
452 /////////////////////////////////////////////////////////////////////
454 def sendPush(String text) {
457 /////////////////////////////////////////////////////////////////////
458 ////schedule(time, nameOfFunction as String)
459 def schedule(String time, String nameOfFunction) {
460 def _inputTime = time.split(':')
461 Date date = new Date()
462 def _currentTime = date.format("HH:mm:ss").split(':')
464 //Convert input time and current time to minutes
465 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
466 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
469 if (inputTime < currentTime) {
470 delay = 24*60*60-inputTime+currentTime
472 delay = inputTime-currentTime
475 timersFuncList.add(nameOfFunction)
476 timersList.add(new SimulatedTimer())
477 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
481 ////schedule(time, nameOfFunction as Closure)
482 def schedule(String time, Closure nameOfFunction) {
483 def _inputTime = time.split(':')
484 Date date = new Date()
485 def _currentTime = date.format("HH:mm:ss").split(':')
487 //Convert input time and current time to minutes
488 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
489 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
492 if (inputTime < currentTime) {
493 delay = 24*60*60-inputTime+currentTime
495 delay = inputTime-currentTime
498 if (timersFuncList.contains(nameOfFunction)) {
499 timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
500 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
502 timersFuncList.add(nameOfFunction)
503 timersList.add(new SimulatedTimer())
504 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
507 /////////////////////////////////////////////////////////////////////
509 return System.currentTimeMillis()
511 /////////////////////////////////////////////////////////////////////
512 def getTemperatureScale() {
513 return 'F' //Celsius for now
516 /////////////////////////////////////////////////////////////////////
517 def getSunriseAndSunset(LinkedHashMap metaData) {
518 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
519 return sunRiseSetInfo
521 /////////////////////////////////////////////////////////////////////
522 def httpPostJson(LinkedHashMap metaData, Closure inputData) {
525 /////////////////////////////////////////////////////////////////////
526 def runEvery15Minutes(Closure inputData) {
529 /////////////////////////////////////////////////////////////////////
530 def timeToday(String time, Object timeZone) {
531 def timeOfDay = new Date()
532 def _inputTime = time.split(':')
533 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
534 timeOfDay.time = inputTime
537 /////////////////////////////////////////////////////////////////////
538 def sendNotification(String text, LinkedHashMap metaData) {
539 println("Sending \""+text+"\" to "+metaData.phone.toString())
541 /////////////////////////////////////////////////////////////////////
557 subscribe(temperatures, "temperature", handleTemperatureEvent)
558 subscribe(humidities, "humidity", handleHumidityEvent)
559 subscribe(waters, "water", handleWaterEvent)
560 subscribe(waters, "water", handleWaterEvent)
561 subscribe(detectors, "smoke", handleSmokeEvent)
562 subscribe(detectors, "carbonMonoxide", handleCarbonMonoxideEvent)
563 subscribe(illuminances, "illuminance", handleIlluminanceEvent)
564 subscribe(contacts, "contact", handleContactEvent)
565 subscribe(locks, "lock", handleLockEvent)
566 subscribe(accelerations, "acceleration", handleAccelerationEvent)
567 subscribe(motions, "motion", handleMotionEvent)
568 subscribe(presence, "presence", handlePresenceEvent)
569 subscribe(switches, "switch", handleSwitchEvent)
570 subscribe(dimmerSwitches, "switch", handleSwitchEvent)
571 subscribe(dimmerSwitches, "level", handleSetLevelEvent)
572 subscribe(batteries, "battery", handleBatteryEvent)
573 subscribe(powers, "power", handlePowerEvent)
574 subscribe(energys, "energy", handleEnergyEvent)
575 subscribe(energys, "cost", handleCostEvent)
576 subscribe(thermostats, "heatingSetpoint", handleHeatingSetpointEvent)
577 subscribe(thermostats, "coolingSetpoint", handleCoolingSetpointEvent)
578 subscribe(thermostats, "thermostatMode", handleThermostatModeEvent)
579 subscribe(thermostats, "fanMode", handleFanModeEvent)
580 subscribe(thermostats, "thermostatOperatingState", handleThermostatOperatingStateEvent)
581 /*subscribe(ecobees, "dehumidifierMode", handleDehumidifierModeEvent)
582 subscribe(ecobees, "equipmentStatus", handleEquipmentStatusEvent)
583 subscribe(ecobees, "dehumidifierLevel", handleDehumidifierLevelEvent)
584 subscribe(ecobees, "humidifierMode", handleHumidifierModeEvent)
585 subscribe(ecobees, "humidifierLevel", handleHumidifierLevelEvent)
586 subscribe(ecobees, "fanMinOnTime", handleFanMinOnTimeEvent)
587 subscribe(ecobees, "ventilatorMode", handleVentilatorModeEvent)
588 subscribe(ecobees, "ventilatorMinOnTime", handleVentilatorMinOnTimeEvent)
589 subscribe(ecobees, "programScheduleName", handleProgramNameEvent)
590 subscribe(ecobees, "auxHeat1RuntimeDaily", handleDailyStats)
591 subscribe(ecobees, "auxHeat2RuntimeDaily", handleDailyStats)
592 subscribe(ecobees, "auxHeat3RuntimeDaily", handleDailyStats)
593 subscribe(ecobees, "compCool1RuntimeDaily", handleDailyStats)
594 subscribe(ecobees, "compCool2RuntimeDaily", handleDailyStats)
595 subscribe(ecobees, "fanRuntimeDaily", handleDailyStats)
596 subscribe(ecobees, "humidifierRuntimeDaily", handleDailyStats)
597 subscribe(ecobees, "dehumidifierRuntimeDaily", handleDailyStats)
598 subscribe(ecobees, "ventilatorRuntimeDaily", handleDailyStats)
599 subscribe(ecobees, "presence", handlePresenceEvent)
600 subscribe(ecobees, "compCool2RuntimeDaily", handleDailyStats)*/
601 subscribe(automatic, "yesterdayTripsAvgAverageKmpl",handleDailyStats)
602 subscribe(automatic, "yesterdayTripsAvgDistanceM",handleDailyStats)
603 subscribe(automatic, "yesterdayTripsAvgDurationS",handleDailyStats)
604 subscribe(automatic, "yesterdayTotalDistanceM",handleDailyStats)
605 subscribe(automatic, "yesterdayTripsAvgFuelVolumeL",handleDailyStats)
606 subscribe(automatic, "yesterdayTotalFuelVolumeL",handleDailyStats)
607 subscribe(automatic, "yesterdayTotalDurationS:",handleDailyStats)
608 subscribe(automatic, "yesterdayTotalNbTrips",handleDailyStats)
609 subscribe(automatic, "yesterdayTotalHardAccels",handleDailyStats)
610 subscribe(automatic, "yesterdayTotalHardBrakes:",handleDailyStats)
611 subscribe(automatic, "yesterdayTripsAvgScoreSpeeding",handleDailyStats)
612 subscribe(automatic, "yesterdayTripsAvgScoreEvents",handleDailyStats)
614 atomicState.queue=queue
616 if (atomicState.queue==null) {
617 atomicState.queue = []
619 atomicState?.poll = [ last: 0, rescheduled: now() ]
621 Integer delay = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
622 log.debug "initialize>scheduling processQueue every ${delay} minutes"
624 //Subscribe to different events (ex. sunrise and sunset events) to trigger rescheduling if needed
625 subscribe(location, "sunrise", rescheduleIfNeeded)
626 subscribe(location, "sunset", rescheduleIfNeeded)
627 subscribe(location, "mode", rescheduleIfNeeded)
628 subscribe(location, "sunriseTime", rescheduleIfNeeded)
629 subscribe(location, "sunsetTime", rescheduleIfNeeded)
630 subscribe(app, appTouch)
632 //rescheduleIfNeeded()
636 rescheduleIfNeeded(evt)
639 atomicState.queue=queue
643 def rescheduleIfNeeded(evt) {
644 if (evt) log.debug("rescheduleIfNeeded>$evt.name=$evt.value")
645 Integer delay = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
646 BigDecimal currentTime = now()
647 BigDecimal lastPollTime = (currentTime - (atomicState?.poll["last"]?:0))
648 if (lastPollTime != currentTime) {
649 Double lastPollTimeInMinutes = (lastPollTime/60000).toDouble().round(1)
650 log.info "rescheduleIfNeeded>last poll was ${lastPollTimeInMinutes.toString()} minutes ago"
652 if (((atomicState?.poll["last"]?:0) + (delay * 60000) < currentTime) && canSchedule()) {
653 log.info "rescheduleIfNeeded>scheduling processQueue in ${delay} minutes.."
655 schedule("14:00", processQueue)
657 // Update rescheduled state
660 atomicState.poll["rescheduled"] = now()
664 def handleTemperatureEvent(evt) {
670 def handleHumidityEvent(evt) {
676 def handleHeatingSetpointEvent(evt) {
681 def handleCoolingSetpointEvent(evt) {
687 def handleThermostatModeEvent(evt) {
692 def handleFanModeEvent(evt) {
697 def handleHumidifierModeEvent(evt) {
702 def handleHumidifierLevelEvent(evt) {
707 def handleDehumidifierModeEvent(evt) {
712 def handleDehumidifierLevelEvent(evt) {
717 def handleVentilatorModeEvent(evt) {
722 def handleFanMinOnTimeEvent(evt) {
727 def handleVentilatorMinOnTimeEvent(evt) {
733 def handleThermostatOperatingStateEvent(evt) {
735 it == "idle" ? 0 : (it == 'fan only') ? 1 : (it == 'heating') ? 2 : 3
739 def handleDailyStats(evt) {
745 def handleEquipmentStatusEvent(evt) {
751 def handleProgramNameEvent(evt) {
757 def handleWaterEvent(evt) {
762 def handleSmokeEvent(evt) {
767 def handleCarbonMonoxideEvent(evt) {
773 def handleIlluminanceEvent(evt) {
774 log.debug ("handleIlluminanceEvent> $evt.name= $evt.value")
780 def handleLockEvent(evt) {
782 it == "locked" ? 1 : 0
786 def handleBatteryEvent(evt) {
792 def handleContactEvent(evt) {
798 def handleAccelerationEvent(evt) {
800 it == "active" ? 1 : 0
804 def handleMotionEvent(evt) {
806 it == "active" ? 1 : 0
810 def handlePresenceEvent(evt) {
812 it == "present" ? 1 : 0
816 def handleSwitchEvent(evt) {
822 def handleSetLevelEvent(evt) {
828 def handlePowerEvent(evt) {
836 def handleEnergyEvent(evt) {
843 def handleCostEvent(evt) {
851 private queueValue(evt, Closure convert) {
852 def MAX_QUEUE_SIZE=95000
853 def jsonPayload = [compId: evt.displayName, streamId: evt.name, data: convert(evt.value), time: now()]
856 queue = atomicState.queue
858 atomicState.queue = queue
859 def queue_size = queue.toString().length()
860 def last_item_in_queue = queue[queue.size() -1]
861 log.debug "queueValue>queue size in chars=${queue_size}, appending ${jsonPayload} to queue, last item in queue= $last_item_in_queue"
862 if (queue_size > MAX_QUEUE_SIZE) {
868 Integer delay = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
869 atomicState?.poll["last"] = now()
871 if (((atomicState?.poll["rescheduled"]?:0) + (delay * 60000)) < now()) {
872 log.info "processQueue>scheduling rescheduleIfNeeded() in ${delay} minutes.."
873 schedule("0 0/${delay} * * * ?", rescheduleIfNeeded)
874 // Update rescheduled state
875 atomicState?.poll["rescheduled"] = now()
878 def queue = atomicState.queue
881 def url = "https://grovestreams.com/api/feed?api_key=${channelKey}"
882 log.debug "processQueue"
884 log.debug "Events to be sent to groveStreams: ${queue}"
887 httpPutJson([uri: url, body: queue]) {response ->
888 if (response.status != 200) {
889 log.debug "GroveStreams logging failed, status = ${response.status}"
891 log.debug "GroveStreams accepted event(s)"
894 atomicState.queue = queue
897 } catch (groovyx.net.http.ResponseParseException e) {
898 // ignore error 200, bogus exception
899 if (e.statusCode != 200) {
900 log.error "Grovestreams: ${e}"
902 log.debug "GroveStreams accepted event(s)"
906 atomicState.queue = queue
909 def errorInfo = "Error sending value: ${e}"
913 atomicState.queue = queue
929 //Extracted objects for App2
930 //Object for class lock!
932 //Object for class contactSensor!
934 //Global variable for number!
936 //Global variable for boolean!
937 def pushNotification = "0"
938 //Global variable for phone!
939 def phoneNumber = 9495379373
940 //Global variable for boolean!
941 def lockIfClosed = "1"
943 //Extracted objects for functions for App2
944 //Global Object for functions in subscribe method!
945 def installed = this.&installed
946 //Global Object for functions in subscribe method!
947 def updated = this.&updated
948 //Global Object for functions in subscribe method!
949 def initialize = this.&initialize
950 //Global Object for functions in subscribe method!
951 def lockHandler = this.&lockHandler
952 //Global Object for functions in subscribe method!
953 def notifyUnlocked = this.¬ifyUnlocked
954 //Global Object for functions in subscribe method!
955 def sendMessage = this.&sendMessage
959 location = obj.locationObject
961 atomicState = obj.atomicState
962 aLock = obj.lockObject
963 openSensor = obj.contactObject
964 //Global variable for settings!
965 settings = [app:app, aLock:aLock, openSensor:openSensor, duration:duration, pushNotification:pushNotification, phoneNumber:phoneNumber, lockIfClosed:lockIfClosed]
967 //Global variables for each app
968 //Global variable for state[mode]
969 def state = [home:[],away:[],night:[]]
970 //Create a global logger object for methods
971 def log = new Logger()
972 //Create a global variable for Functions in Subscribe method
973 def functionList = []
974 //Create a global variable for Objects in Subscribe method
976 //Create a global variable for Events in Subscribe method
978 //Create a global list for function schedulers
979 def timersFuncList = []
980 //Create a global list for timer schedulers
982 //Create a global variable for settings
988 /////////////////////////////////////////////////////////////////////
989 def setLocationMode(String mode) {
993 /////////////////////////////////////////////////////////////////////
994 ////subscribe(obj, func)
995 def subscribe(Object obj, Closure FunctionToCall) {
998 eventList.add("Touched")
999 functionList.add(FunctionToCall)
1000 } else if (obj == location) {
1002 eventList.add("Location")
1003 functionList.add(FunctionToCall)
1006 ////subscribe(obj, event, func)
1007 def subscribe(Object obj, String event, Closure FunctionToCall) {
1009 eventList.add(event)
1010 functionList.add(FunctionToCall)
1012 ////subscribe(obj, event, func, data)
1013 def subscribe(Object obj, String event, Closure FunctionToCall, LinkedHashMap metaData) {
1015 eventList.add(event)
1016 functionList.add(FunctionToCall)
1018 /////////////////////////////////////////////////////////////////////
1019 ////runIn(time, func)
1020 def runIn(int seconds, Closure functionToCall) {
1021 if (timersFuncList.contains(functionToCall)) {
1022 timersList[timersFuncList.indexOf(functionToCall)].cancel()
1023 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
1025 timersFuncList.add(functionToCall)
1026 timersList.add(new SimulatedTimer())
1027 def task = timersList[timersFuncList.indexOf(functionToCall)].runAfter(1000*seconds*0, functionToCall)
1031 def runIn(int seconds, Closure functionToCall, LinkedHashMap metaData) {
1032 runIn(seconds, functionToCall)
1035 def runIn(int seconds, String nameOfFunction, LinkedHashMap metaData) {
1036 runIn(seconds, nameOfFunction)
1039 def runIn(int seconds, String nameOfFunction) {
1040 timersFuncList.add(nameOfFunction)
1041 timersList.add(new SimulatedTimer())
1042 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(seconds*1000*0) {
1046 /////////////////////////////////////////////////////////////////////
1047 ////unschedule(func)
1048 def unschedule(Closure functionToUnschedule) {
1049 for (int i = 0;i < timersFuncList.size();i++) {
1050 if (timersFuncList[i] == functionToUnschedule) {
1051 if (timersList != null)
1052 timersList[i].cancel()
1059 for (int i = 0;i < timersFuncList.size();i++) {
1060 if (timersList != null)
1061 timersList[i].cancel()
1064 /////////////////////////////////////////////////////////////////////
1065 ////sendNotificationToContacts(text, recipients)
1066 def sendNotificationToContacts(String text, String recipients) {
1067 for (int i = 0;i < recipients.size();i++) {
1068 for (int j = 0;j < location.contacts.size();j++) {
1069 if (recipients[i] == location.contacts[j]) {
1070 println("Sending \""+text+"\" to "+location.phoneNumbers[j].toString())
1075 /////////////////////////////////////////////////////////////////////
1076 ////sendSms(phone, text)
1077 def sendSms(long phoneNumber, String text) {
1078 println("Sending \""+text+"\" to "+phoneNumber.toString())
1081 def sendSMS(long phoneNumber, String text) {
1082 println("Sending \""+text+"\" to "+phoneNumber.toString())
1084 /////////////////////////////////////////////////////////////////////
1086 def sendPush(String text) {
1089 /////////////////////////////////////////////////////////////////////
1090 ////schedule(time, nameOfFunction as String)
1091 def schedule(String time, String nameOfFunction) {
1092 def _inputTime = time.split(':')
1093 Date date = new Date()
1094 def _currentTime = date.format("HH:mm:ss").split(':')
1096 //Convert input time and current time to minutes
1097 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
1098 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
1101 if (inputTime < currentTime) {
1102 delay = 24*60*60-inputTime+currentTime
1104 delay = inputTime-currentTime
1107 timersFuncList.add(nameOfFunction)
1108 timersList.add(new SimulatedTimer())
1109 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*1000*0) {
1113 ////schedule(time, nameOfFunction as Closure)
1114 def schedule(String time, Closure nameOfFunction) {
1115 def _inputTime = time.split(':')
1116 Date date = new Date()
1117 def _currentTime = date.format("HH:mm:ss").split(':')
1119 //Convert input time and current time to minutes
1120 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60
1121 def currentTime = Integer.parseInt(_currentTime[0])*3600+Integer.parseInt(_currentTime[1])*60+Integer.parseInt(_currentTime[2])
1124 if (inputTime < currentTime) {
1125 delay = 24*60*60-inputTime+currentTime
1127 delay = inputTime-currentTime
1130 if (timersFuncList.contains(nameOfFunction)) {
1131 timersList[timersFuncList.indexOf(nameOfFunction)].cancel()
1132 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
1134 timersFuncList.add(nameOfFunction)
1135 timersList.add(new SimulatedTimer())
1136 def task = timersList[timersFuncList.indexOf(nameOfFunction)].runAfter(delay*0, nameOfFunction)
1139 /////////////////////////////////////////////////////////////////////
1141 return System.currentTimeMillis()
1143 /////////////////////////////////////////////////////////////////////
1144 def getTemperatureScale() {
1145 return 'F' //Celsius for now
1148 /////////////////////////////////////////////////////////////////////
1149 def getSunriseAndSunset(LinkedHashMap metaData) {
1150 def sunRiseSetInfo = [sunrise:[time:1563800160000],sunset:[time:1563850740000]]
1151 return sunRiseSetInfo
1153 /////////////////////////////////////////////////////////////////////
1154 def httpPostJson(LinkedHashMap metaData, Closure inputData) {
1157 /////////////////////////////////////////////////////////////////////
1158 def runEvery15Minutes(Closure inputData) {
1161 /////////////////////////////////////////////////////////////////////
1162 def timeToday(String time, Object timeZone) {
1163 def timeOfDay = new Date()
1164 def _inputTime = time.split(':')
1165 def inputTime = Integer.parseInt(_inputTime[0])*3600+Integer.parseInt(_inputTime[1])*60+1564191100415
1166 timeOfDay.time = inputTime
1169 /////////////////////////////////////////////////////////////////////
1170 def sendNotification(String text, LinkedHashMap metaData) {
1171 println("Sending \""+text+"\" to "+metaData.phone.toString())
1173 /////////////////////////////////////////////////////////////////////
1191 log.trace "Initializing with: ${settings}"
1192 subscribe(aLock, "lock", lockHandler)
1195 def lockHandler(evt)
1197 log.trace "${evt.name} is ${evt.value}."
1198 if (evt.value == "locked") {
1199 log.debug "Canceling lock check because the door is locked..."
1200 unschedule(notifyUnlocked)
1203 log.debug "Starting the countdown for ${duration} minutes..."
1205 runIn(duration * 60, notifyUnlocked)
1209 def notifyUnlocked()
1211 // if no open/close sensor specified, assume the door is closed
1212 def open = openSensor?.latestValue("contact") ?: "closed"
1214 def message = "${aLock.displayName} is left unlocked and ${open} for more than ${duration} minutes."
1215 log.trace "Sending the notification: ${message}."
1216 sendMessage(message)
1219 if (open == "closed") {
1220 log.trace "And locking the door."
1221 sendMessage("Locking the ${aLock.displayName} as prescribed.")
1225 if (state.retries++ < 3) {
1226 log.trace "Door is open, can't lock. Rescheduling the check."
1227 sendMessage("Can't lock the ${aLock.displayName} because the door is open. Will try again in ${duration} minutes.")
1228 runIn(duration * 60, notifyUnlocked)
1231 log.trace "The door is still open after ${state.retries} retries, giving up."
1232 sendMessage("Unable to lock the ${aLock.displayName} after ${state.retries} retries, giving up.")
1238 def sendMessage(msg) {
1239 if (pushNotification) {
1243 sendSMS(phoneNumber, msg)
1251 def initOrder = Verify.getBoolean()
1253 app1 = new App1(this)
1254 app2 = new App2(this)
1256 app2 = new App2(this)
1257 app1 = new App1(this)
1260 def installOrder = Verify.getBoolean()
1270 def eventNumber = Verify.getInt(0,53)
1271 switch(eventNumber) {
1279 def event = Verify.getInt(0,2)
1281 smokeDetectorObject.setValue([name: "smoke", value: "clear", deviceId: "smokeDetectorID0", descriptionText: "",
1282 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1283 } else if (event == 1) {
1284 smokeDetectorObject.setValue([name: "smoke", value: "detected", deviceId: "smokeDetectorID0", descriptionText: "",
1285 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1287 smokeDetectorObject.setValue([name: "smoke", value: "tested", deviceId: "smokeDetectorID0", descriptionText: "",
1288 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1292 def event = Verify.getInt(0,2)
1294 smokeDetectorObject.setValue([name: "carbonMonoxide", value: "clear", deviceId: "smokeDetectorID0", descriptionText: "",
1295 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1296 } else if (event == 1) {
1297 smokeDetectorObject.setValue([name: "carbonMonoxide", value: "detected", deviceId: "smokeDetectorID0", descriptionText: "",
1298 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1300 smokeDetectorObject.setValue([name: "carbonMonoxide", value: "tested", deviceId: "smokeDetectorID0", descriptionText: "",
1301 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1309 lockObject.setValue([name: "lock", value: "locked", deviceId: "lockID0", descriptionText: "",
1310 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1313 def event = Verify.getInt(0,1)
1315 accelerationSensorObject.setValue([name: "acceleration", value: "active", deviceId: "accelerationSensorID0", descriptionText: "",
1316 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1318 accelerationSensorObject.setValue([name: "acceleration", value: "inactive", deviceId: "accelerationSensorID0", descriptionText: "",
1319 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1323 def event = Verify.getInt(0,1)
1325 motionSensorObject.setValue([name: "motion", value: "active", deviceId: "motionSensorID0", descriptionText: "",
1326 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1328 motionSensorObject.setValue([name: "motion", value: "inactive", deviceId: "motionSensorID0", descriptionText: "",
1329 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1333 def event = Verify.getInt(0,1)
1335 presenceSensorObject.setValue([name: "presence", value: "present", deviceId: "presenceSensorID0", descriptionText: "",
1336 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"1","dni":"mobile0"}'])
1338 presenceSensorObject.setValue([name: "presence", value: "not present", deviceId: "presenceSensorID0", descriptionText: "",
1339 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"presence":"0","dni":"mobile0"}'])
1343 def event = Verify.getInt(0,1)
1345 switchObject.setValue([name: "switch", value: "on", deviceId: "switchID0", descriptionText: "",
1346 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1348 switchObject.setValue([name: "switch", value: "off", deviceId: "switchID0", descriptionText: "",
1349 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1355 smokeDetectorObject.setValue([name: "battery", value: "5", deviceId: "smokeDetectorID0", descriptionText: "",
1356 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1369 def event = Verify.getInt(0,4)
1371 thermostatObject.setValue([name: "thermostatMode", value: "auto", deviceId: "thermostatID0", descriptionText: "",
1372 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1373 } else if (event == 1) {
1374 thermostatObject.setValue([name: "thermostatMode", value: "cool", deviceId: "thermostatID0", descriptionText: "",
1375 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1376 } else if (event == 2) {
1377 thermostatObject.setValue([name: "thermostatMode", value: "emergencyHeat", deviceId: "thermostatID0", descriptionText: "",
1378 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1379 } else if (event == 3) {
1380 thermostatObject.setValue([name: "thermostatMode", value: "heat", deviceId: "thermostatID0", descriptionText: "",
1381 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1383 thermostatObject.setValue([name: "thermostatMode", value: "off", deviceId: "thermostatID0", descriptionText: "",
1384 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1452 def event = Verify.getInt(0,2)
1454 locationObject.setValue([name: "Location", value: "home", deviceId: "locationID0", descriptionText: "",
1455 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1456 } else if (event == 1) {
1457 locationObject.setValue([name: "Location", value: "away", deviceId: "locationID0", descriptionText: "",
1458 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1460 locationObject.setValue([name: "Location", value: "night", deviceId: "locationID0", descriptionText: "",
1461 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])
1465 appObject.setValue([name: "Touched", value: "touched", deviceId: "touchedSensorID0", descriptionText: "",
1466 displayed: true, linkText: "", isStateChange: false, unit: "", data: '{"info": "info"}'])