2 * Copyright 2015 SmartThings
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
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"
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") {
33 input "starting", "time", title: "Starting", required: false
34 input "ending", "time", title: "Ending", required: false
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
54 dynamicPage(name: "mainPage") {
55 def anythingSet = 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
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
88 section("Control these bulbs...") {
89 input "hues", "capability.colorControl", title: "Which Hue Bulbs?", required:true, multiple:true
91 section("Choose light effects...")
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]
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
110 section([mobileOnly:true]) {
111 label title: "Assign a name", required: false
112 mode title: "Set for specific mode(s)", required: false
116 private anythingSet() {
117 for (name in ["motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","triggerModes","timeOfDay"]) {
118 if (settings[name]) {
125 private ifUnset(Map options, String name, String capability) {
126 if (!settings[name]) {
127 input(options, name, capability)
131 private ifSet(Map options, String name, String capability) {
132 if (settings[name]) {
133 input(options, name, capability)
138 log.debug "Installed with settings: ${settings}"
140 // Initialize input value
145 log.debug "Updated with settings: ${settings}"
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)
168 subscribe(location, modeChangeHandler)
172 schedule(timeOfDay, scheduledTimeHandler)
176 def eventHandler1(evt=null) {
177 log.trace "Executing Mood Lighting"
180 def lastTime = state[frequencyKey(evt)]
181 if (oncePerDayOk(lastTime)) {
183 if (lastTime == null || now() - lastTime >= frequency * 60000) {
192 log.debug "Not taking action because it was already taken today"
197 def modeChangeHandler(evt) {
198 log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
199 if (evt.value in triggerModes) {
204 def scheduledTimeHandler() {
205 log.trace "scheduledTimeHandler()"
209 def appTouchHandler(evt) {
213 private takeAction(evt) {
215 if (frequency || oncePerDay) {
216 state[frequencyKey(evt)] = now()
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")
273 log.debug "current values = $state.previous"
275 def newValue = [hue: hueColor, saturation: saturation, level: lightLevel as Integer ?: 100]
276 log.debug "new value = $newValue"
278 hues*.setColor(newValue)
281 private frequencyKey(evt) {
282 "lastActionTimeStamp"
285 private dayString(Date date) {
286 def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
287 if (location.timeZone) {
288 df.setTimeZone(location.timeZone)
291 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
297 private oncePerDayOk(Long lastTime) {
298 //def result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
299 //log.trace "oncePerDayOk = $result - $lastTime"
300 result = true //No time implementation in our infrastructure!
304 // TODO - centralize somehow
306 //modeOk && daysOk && timeOk
310 private getModeOk() {
311 def result = !modes || modes.contains(location.mode)
312 log.trace "modeOk = $result"
316 private getDaysOk() {
319 def df = new java.text.SimpleDateFormat("EEEE")
320 if (location.timeZone) {
321 df.setTimeZone(location.timeZone)
324 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
326 def day = df.format(new Date())
327 result = days.contains(day)
329 log.trace "daysOk = $result"
333 private getTimeOk() {
335 if (starting && ending) {
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
341 log.trace "timeOk = $result"
345 private hhmm(time, fmt = "h:mm a")
347 def t = timeToday(time, location.timeZone)
348 def f = new java.text.SimpleDateFormat(fmt)
349 f.setTimeZone(location.timeZone ?: timeZone(time))
353 private timeIntervalLabel()
355 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
357 // TODO - End Centralize