Update circadian-daylight.groovy
[smartapps.git] / official / medicine-management-temp-motion.groovy
1 /**
2  *  Medicine Management - Temp-Motion
3  *
4  *  Copyright 2016 Jim Mangione
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7  *  in compliance with the License. You may obtain a copy of the License at:
8  * 
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13  *  for the specific language governing permissions and limitations under the License.
14  *
15  * Logic: 
16  * --- If temp > threshold set, send notification
17  * --- Send in-app notification at the medicine reminder time if no motion is detected in past 60 minutes
18  * --- If motion still isn't detected 10 minutes AFTER reminder time, LED will turn RED
19  * --- ----- Once motion is detected, LED will turn back to it's original color
20  */
21 import groovy.time.TimeCategory 
22
23 definition(
24     name: "Medicine Management - Temp-Motion",
25     namespace: "MangioneImagery",
26     author: "Jim Mangione",
27     description: "This only supports devices with capabilities TemperatureMeasurement, AccelerationSensor and ColorControl (LED). Supports two use cases. First, will notifies via in-app if the fridge where meds are stored exceeds a temperature threshold set in degrees. Secondly, sends an in-app and ambient light notification if you forget to take your meds by sensing movement of the medicine box in the fridge. A reminder will be set to a single time per day. If the box isn't moved within 60 minutes of that reminder, an in-app message will be sent. If the box still isn't moved after an additional 10 minutes, then an LED light turns red until the box is moved",
28     category: "Health & Wellness",
29     iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
30     iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
31     iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
32
33
34 preferences {
35  
36      section("My Medicine in the Refrigerator"){
37                 input "deviceAccelerationSensor", "capability.accelerationSensor", required: true, multiple: false, title: "Movement"
38         input "deviceTemperatureMeasurement", "capability.temperatureMeasurement", required: true, multiple: false, title: "Temperature"
39         } 
40
41         section("Temperature Threshold"){
42         input "tempThreshold", "number", title: "Temperature Threshold"
43     }
44
45     section("Remind me to take my medicine at"){
46         input "reminderTime", "time", title: "Time"
47     }
48     
49     // NOTE: Use REAL device - virtual device causes compilation errors
50     section("My LED Light"){
51         input "deviceLight", "capability.colorControl", title: "Smart light"
52     }    
53
54 }
55
56 def installed() {
57         log.debug "Installed with settings: ${settings}"
58         
59         initialize()
60 }
61
62 def updated() {
63         log.debug "Updated with settings: ${settings}"
64
65         unsubscribe()
66         
67         initialize()
68 }
69
70 def initialize() {
71         // will notify when temp exceeds max
72     subscribe(deviceTemperatureMeasurement, "temperature", tempHandler)
73
74     // will stop LED notification incase it was set by med reminder
75     subscribe(deviceAccelerationSensor, "acceleration.active", motionHandler)
76     
77     // how many minutes to look in the past from the reminder time
78     state.minutesToCheckPriorToReminder = 60
79     
80     // Set a timer to run once a day to notify if draw wasn't opened yet
81     schedule(reminderTime, checkMotionInPast)
82 }
83
84
85 // If temp > 39 then send an app notification out.
86 def tempHandler(evt){
87         if (evt.doubleValue > tempThreshold) {
88         log.debug "Fridge temp of $evt.value exceeded threshold"
89                 sendNotification("WARNING: Fridge temp is $evt.value with threshold of $tempThreshold")
90         }
91 }
92
93 // Should turn off any LED notification once motion detected
94 def motionHandler(evt){
95     // always call out to stop any possible LED notification
96         log.debug "Medication moved. Send stop LED notification"
97     resetLEDNotification()
98 }
99
100 // If no motion detected within 60 minutes of the timer send notification out.
101 def checkMotionInPast(){
102         log.debug "Checking past 60 minutes of activity from $reminderTime"
103     
104     // check activity of sensor for past 60 minutes for any OPENED status
105     def movement = isMoved(state.minutesToCheckPriorToReminder)
106         log.debug "Motion found: $movement"
107     
108     // if there was movement, then do nothing and assume they took their meds
109     if (!movement) {    
110         sendNotification("Hi, please remember to take your meds in the fridge")
111     
112         // if no movement, send out notification and set new reminder    
113         def reminderTimePlus10 = new Date(now() + (10 * 60000))
114
115         // needs to be scheduled if draw wasn't already opened
116         runOnce(reminderTimePlus10, checkMotionAfterReminder)
117     }
118 }
119
120 // If still no movement after 10 minutes past reminder, use LED notification
121 def checkMotionAfterReminder(){
122         log.debug "Checking additional 10 minutes of activity from $reminderTime"
123     
124     // check activity of sensor for past 10 minutes for any OPENED status
125     def movement = isMoved(10)    
126     
127         log.debug "Motion found: $movement"
128         
129     // if no open activity, blink lights
130     if (!movement) {
131         log.debug "Notify LED API"
132         setLEDNotification()
133     }
134     
135 }
136
137 // Helper function for sending out an app notification
138 def sendNotification(msg){
139         log.debug "Message Sent: $msg"
140         sendPush(msg)
141 }
142
143 // Check if the accelerometer has been activated since the minutes entered
144 // Return true if active, else false.
145 def isMoved(minutes){
146     // query last X minutes of activity log    
147     def previousDateTime = new Date(now() - (minutes * 60000))
148     
149     // capture all events recorded
150     def evts = deviceAccelerationSensor.eventsSince(previousDateTime)   
151     def motion = false
152     if (evts.size() > 0) {
153         evts.each{
154             if(it.value == "active") {
155                 motion = true 
156             }
157         }
158         }
159     
160     return motion
161 }
162
163 // Saves current color and sets the light to RED
164 def setLEDNotification(){
165
166         // turn light back off when reset is called if it was originally off
167         state.ledState = deviceLight.currentValue("switch")
168
169         // set light to RED and store original color until stopped    
170     state.origColor = deviceLight.currentValue("hue")
171     deviceLight.on()
172     deviceLight.setHue(100)
173     
174     log.debug "LED set to RED. Original color stored: $state.origColor"
175
176 }
177
178 // Sets the color back to the original saved color
179 def resetLEDNotification(){
180
181     // return color to original
182     log.debug "Reset LED color to: $state.origColor"
183     deviceLight.setHue(state.origColor)
184     
185     // if the light was turned on just for the notification, turn it back off now
186     if (state.ledState == "off") {
187         deviceLight.off()
188     }
189 }