Update NotifyIfLeftUnlocked.groovy
[smartapps.git] / official / hue-mood-lighting.groovy
1 /**
2  *  Copyright 2015 SmartThings
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  *  in compliance with the License. You may obtain a copy of the License at:
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
10  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
11  *  for the specific language governing permissions and limitations under the License.
12  *
13  *  Hue Mood Lighting
14  *
15  *  Author: SmartThings
16  *  *
17  *  Date: 2014-02-21
18  */
19 definition(
20     name: "Hue Mood Lighting",
21     namespace: "smartthings",
22     author: "SmartThings",
23     description: "Sets the colors and brightness level of your Philips Hue lights to match your mood.",
24     category: "SmartThings Labs",
25     iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/hue.png",
26     iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/hue@2x.png"
27 )
28
29 preferences {
30         page(name: "mainPage", title: "Adjust the color of your Hue lights to match your mood.", install: true, uninstall: true)
31         page(name: "timeIntervalInput", title: "Only during a certain time") {
32                 section {
33                         input "starting", "time", title: "Starting", required: false
34                         input "ending", "time", title: "Ending", required: false
35                 }
36         }
37 }
38
39 // input "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
40 // input "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
41 // input "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
42 // input "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
43 // input "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
44 // input "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
45 // input "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
46 // input "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
47 // input "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
48 // input "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
49 // input "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
50 // input "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
51 // input "timeOfDay", "time", title: "At a Scheduled Time", required: false
52
53 def mainPage() {
54         dynamicPage(name: "mainPage") {
55                 def anythingSet = anythingSet()
56                 if (anythingSet) {
57                         section("Set the lighting mood when..."){
58                                 ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
59                                 ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
60                                 ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
61                                 ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
62                                 ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
63                                 ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
64                                 ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
65                                 ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
66                                 ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
67                                 ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
68                                 ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
69                                 ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
70                                 ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
71                         }
72                 }
73                 section(anythingSet ? "Select additional mood lighting triggers" : "Set the lighting mood when...", hideable: anythingSet, hidden: true){
74                         ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
75                         ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
76                         ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
77                         ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
78                         ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
79                         ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
80                         ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
81                         ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
82                         ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
83                         ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
84                         ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
85                         ifUnset "triggerModes", "mode", title: "System Changes Mode", description: "Select mode(s)", required: false, multiple: true
86                         ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
87                 }
88                 section("Control these bulbs...") {
89                         input "hues", "capability.colorControl", title: "Which Hue Bulbs?", required:true, multiple:true
90                 }
91                 section("Choose light effects...")
92                         {
93                                 input "color", "enum", title: "Hue Color?", required: false, multiple:false, options: [
94                                         ["Soft White":"Soft White - Default"],
95                                         ["White":"White - Concentrate"],
96                                         ["Daylight":"Daylight - Energize"],
97                                         ["Warm White":"Warm White - Relax"],
98                                         "Red","Green","Blue","Yellow","Orange","Purple","Pink"]
99                                 input "lightLevel", "enum", title: "Light Level?", required: false, options: [10,20,30,40,50,60,70,80,90,100]
100                         }
101
102                 section("More options", hideable: true, hidden: true) {
103                         input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
104                         href "timeIntervalInput", title: "Only during a certain time"
105                         input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
106                                 options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
107                         input "modes", "mode", title: "Only when mode is", multiple: true, required: false
108                         input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
109                 }
110                 section([mobileOnly:true]) {
111                         label title: "Assign a name", required: false
112                         mode title: "Set for specific mode(s)", required: false
113                 }
114         }
115 }
116 private anythingSet() {
117         for (name in ["motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","triggerModes","timeOfDay"]) {
118                 if (settings[name]) {
119                         return true
120                 }
121         }
122         return false
123 }
124
125 private ifUnset(Map options, String name, String capability) {
126         if (!settings[name]) {
127                 input(options, name, capability)
128         }
129 }
130
131 private ifSet(Map options, String name, String capability) {
132         if (settings[name]) {
133                 input(options, name, capability)
134         }
135 }
136
137 def installed() {
138         log.debug "Installed with settings: ${settings}"
139         subscribeToEvents()
140         // Initialize input value
141         color = "Purple"
142 }
143
144 def updated() {
145         log.debug "Updated with settings: ${settings}"
146         unsubscribe()
147         unschedule()
148         subscribeToEvents()
149 }
150
151 def subscribeToEvents() {
152         subscribe(app, appTouchHandler)
153         subscribe(contact, "contact.open", eventHandler1)
154         subscribe(contactClosed, "contact.closed", eventHandler1)
155         subscribe(acceleration, "acceleration.active", eventHandler1)
156         subscribe(motion, "motion.active", eventHandler1)
157         subscribe(mySwitch, "switch.on", eventHandler1)
158         subscribe(mySwitchOff, "switch.off", eventHandler1)
159         subscribe(arrivalPresence, "presence.present", eventHandler1)
160         subscribe(departurePresence, "presence.not present", eventHandler1)
161         subscribe(smoke, "smoke.detected", eventHandler1)
162         subscribe(smoke, "smoke.tested", eventHandler1)
163         subscribe(smoke, "carbonMonoxide.detected", eventHandler1)
164         subscribe(water, "water.wet", eventHandler1)
165         subscribe(button1, "button.pushed", eventHandler1)
166
167         if (triggerModes) {
168                 subscribe(location, modeChangeHandler)
169         }
170
171         if (timeOfDay) {
172                 schedule(timeOfDay, scheduledTimeHandler)
173         }
174 }
175
176 def eventHandler1(evt=null) {
177         log.trace "Executing Mood Lighting"
178         if (allOk) {
179                 log.trace "allOk"
180                 def lastTime = state[frequencyKey(evt)]
181                 if (oncePerDayOk(lastTime)) {
182                         if (frequency) {
183                                 if (lastTime == null || now() - lastTime >= frequency * 60000) {
184                                         takeAction(evt)
185                                 }
186                         }
187                         else {
188                                 takeAction(evt)
189                         }
190                 }
191                 else {
192                         log.debug "Not taking action because it was already taken today"
193                 }
194         }
195 }
196
197 def modeChangeHandler(evt) {
198         log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
199         if (evt.value in triggerModes) {
200                 eventHandler1(evt)
201         }
202 }
203
204 def scheduledTimeHandler() {
205         log.trace "scheduledTimeHandler()"
206         eventHandler1()
207 }
208
209 def appTouchHandler(evt) {
210         takeAction(evt)
211 }
212
213 private takeAction(evt) {
214
215         if (frequency || oncePerDay) {
216                 state[frequencyKey(evt)] = now()
217         }
218
219         def hueColor = 0
220         def saturation = 100
221
222         switch(color) {
223                 case "White":
224                         hueColor = 52
225                         saturation = 19
226                         break;
227                 case "Daylight":
228                         hueColor = 53
229                         saturation = 91
230                         break;
231                 case "Soft White":
232                         hueColor = 23
233                         saturation = 56
234                         break;
235                 case "Warm White":
236                         hueColor = 20
237                         saturation = 80 //83
238                         break;
239                 case "Blue":
240                         hueColor = 70
241                         break;
242                 case "Green":
243                         hueColor = 39
244                         break;
245                 case "Yellow":
246                         hueColor = 25
247                         break;
248                 case "Orange":
249                         hueColor = 10
250                         break;
251                 case "Purple":
252                         hueColor = 75
253                         break;
254                 case "Pink":
255                         hueColor = 83
256                         break;
257                 case "Red":
258                         hueColor = 100
259                         break;
260         }
261
262         state.previous = [:]
263
264         hues.each {
265                 state.previous[it.id] = [
266                         "switch": it.currentValue("switch"),
267                         "level" : it.currentValue("level"),
268                         "hue": it.currentValue("hue"),
269                         "saturation": it.currentValue("saturation")
270                 ]
271         }
272
273         log.debug "current values = $state.previous"
274
275         def newValue = [hue: hueColor, saturation: saturation, level: lightLevel as Integer ?: 100]
276         log.debug "new value = $newValue"
277
278         hues*.setColor(newValue)
279 }
280
281 private frequencyKey(evt) {
282         "lastActionTimeStamp"
283 }
284
285 private dayString(Date date) {
286         def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
287         if (location.timeZone) {
288                 df.setTimeZone(location.timeZone)
289         }
290         else {
291                 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
292         }
293         df.format(date)
294 }
295
296
297 private oncePerDayOk(Long lastTime) {
298         //def result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
299         //log.trace "oncePerDayOk = $result - $lastTime"
300         def result = true //No time implementation in our infrastructure!
301         result
302 }
303
304 // TODO - centralize somehow
305 private getAllOk() {
306         //modeOk && daysOk && timeOk
307         return true
308 }
309
310 private getModeOk() {
311         def result = !modes || modes.contains(location.mode)
312         log.trace "modeOk = $result"
313         result
314 }
315
316 private getDaysOk() {
317         def result = true
318         if (days) {
319                 def df = new java.text.SimpleDateFormat("EEEE")
320                 if (location.timeZone) {
321                         df.setTimeZone(location.timeZone)
322                 }
323                 else {
324                         df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
325                 }
326                 def day = df.format(new Date())
327                 result = days.contains(day)
328         }
329         log.trace "daysOk = $result"
330         result
331 }
332
333 private getTimeOk() {
334         def result = true
335         if (starting && ending) {
336                 def currTime = now()
337                 def start = timeToday(starting, location?.timeZone).time
338                 def stop = timeToday(ending, location?.timeZone).time
339                 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
340         }
341         log.trace "timeOk = $result"
342         result
343 }
344
345 private hhmm(time, fmt = "h:mm a")
346 {
347         def t = timeToday(time, location.timeZone)
348         def f = new java.text.SimpleDateFormat(fmt)
349         f.setTimeZone(location.timeZone ?: timeZone(time))
350         f.format(t)
351 }
352
353 private timeIntervalLabel()
354 {
355         (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
356 }
357 // TODO - End Centralize