2 name: "Bright When Dark And/Or Bright After Sunset",
5 description: "Turn ON light(s) and/or dimmer(s) when there's movement and the room is dark with illuminance threshold and/or between sunset and sunrise. Then turn OFF after X minute(s) when the brightness of the room is above the illuminance threshold or turn OFF after X minute(s) when there is no movement.",
6 category: "Convenience",
7 iconUrl: "http://neiloseman.com/wp-content/uploads/2013/08/stockvault-bulb128619.jpg",
8 iconX2Url: "http://neiloseman.com/wp-content/uploads/2013/08/stockvault-bulb128619.jpg"
13 page(name: "timeIntervalInput", title: "Only during a certain time...")
17 input "starting", "time", title: "Starting", required: false
18 input "ending", "time", title: "Ending", required: false
22 page(name: "configurations")
30 dynamicPage(name: "configurations", title: "Configurations...", uninstall: true, nextPage: "options")
32 section(title: "Turn ON lights on movement when...")
34 input "dark", "bool", title: "It is dark?", required: true
35 input "sun", "bool", title: "Between sunset and surise?", required: true
37 section(title: "More options...", hidden: hideOptionsSection(), hideable: true)
39 def timeLabel = timeIntervalLabel()
40 href "timeIntervalInput", title: "Only during a certain time:", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
41 input "days", "enum", title: "Only on certain days of the week:", multiple: true, required: false, options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
42 input "modes", "mode", title: "Only when mode is:", multiple: true, required: false
44 section ("Assign a name")
46 label title: "Assign a name", required: false
53 if (dark == true && sun == true)
55 dynamicPage(name: "options", title: "Lights will turn ON on movement when it is dark and between sunset and sunrise...", install: true, uninstall: true)
57 section("Control these light(s)...")
59 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
61 section("Control these dimmer(s)...")
63 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
64 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
66 section("Turning ON when it's dark and there's movement...")
68 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
70 section("And then OFF when it's light or there's been no movement for...")
72 input "delayMinutes", "number", title: "Minutes?", required: false
74 section("Using this light sensor...")
76 input "lightSensor", "capability.illuminanceMeasurement",title: "Light Sensor?", multiple: false, required: true
77 input "luxLevel", "number", title: "Illuminance threshold? (default 50 lux)",defaultValue: "50", required: false
79 section ("And between sunset and sunrise...")
81 input "sunriseOffsetValue", "text", title: "Sunrise offset", required: false, description: "00:00"
82 input "sunriseOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
83 input "sunsetOffsetValue", "text", title: "Sunset offset", required: false, description: "00:00"
84 input "sunsetOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
86 section ("Zip code (optional, defaults to location coordinates when location services are enabled)...")
88 input "zipCodeText", "text", title: "Zip Code?", required: false, description: "Local Zip Code"
92 else if (dark == true && sun == false)
94 dynamicPage(name: "options", title: "Lights will turn ON on movement when it is dark...", install: true, uninstall: true)
96 section("Control these light(s)...")
98 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
100 section("Control these dimmer(s)...")
102 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
103 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
105 section("Turning ON when it's dark and there's movement...")
107 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
109 section("And then OFF when it's light or there's been no movement for...")
111 input "delayMinutes", "number", title: "Minutes?", required: false
113 section("Using this light sensor...")
115 input "lightSensor", "capability.illuminanceMeasurement",title: "Light Sensor?", multiple: false, required: true
116 input "luxLevel", "number", title: "Illuminance threshold? (default 50 lux)",defaultValue: "50", required: false
120 else if (sun == true && dark == false)
122 dynamicPage(name: "options", title: "Lights will turn ON on movement between sunset and sunrise...", install: true, uninstall: true)
124 section("Control these light(s)...")
126 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
128 section("Control these dimmer(s)...")
130 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
131 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
133 section("Turning ON there's movement...")
135 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
137 section("And then OFF there's been no movement for...")
139 input "delayMinutes", "number", title: "Minutes?", required: false
141 section ("Between sunset and sunrise...")
143 input "sunriseOffsetValue", "text", title: "Sunrise offset", required: false, description: "00:00"
144 input "sunriseOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
145 input "sunsetOffsetValue", "text", title: "Sunset offset", required: false, description: "00:00"
146 input "sunsetOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
148 section ("Zip code (optional, defaults to location coordinates when location services are enabled)...")
150 input "zipCodeText", "text", title: "Zip Code?", required: false, description: "Local Zip Code"
156 dynamicPage(name: "options", title: "Lights will turn ON on movement...", install: true, uninstall: true)
158 section("Control these light(s)...")
160 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
162 section("Control these dimmer(s)...")
164 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
165 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
167 section("Turning ON when there's movement...")
169 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
171 section("And then OFF when there's been no movement for...")
173 input "delayMinutes", "number", title: "Minutes?", required: false
181 log.debug "Installed with settings: ${settings}."
187 log.debug "Updated with settings: ${settings}."
195 subscribe(motionSensor, "motion", motionHandler)
196 if (lights != null && lights != "" && dimmers != null && dimmers != "")
198 log.debug "$lights subscribing..."
199 subscribe(lights, "switch", lightsHandler)
200 log.debug "$dimmers subscribing..."
201 subscribe(dimmers, "switch", dimmersHandler)
202 if (dark == true && lightSensor != null && lightSensor != "")
204 log.debug "$lights and $dimmers will turn ON when movement detected and when it is dark..."
205 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
209 log.debug "$lights and $dimmers will turn ON when movement detected between sunset and sunrise..."
211 subscribe(location, "position", locationPositionChange)
212 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
213 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
215 else if (dark != true && sun != true)
217 log.debug "$lights and $dimmers will turn ON when movement detected..."
220 else if (lights != null && lights != "")
222 log.debug "$lights subscribing..."
223 subscribe(lights, "switch", lightsHandler)
224 if (dark == true && lightSensor != null && lightSensor != "")
226 log.debug "$lights will turn ON when movement detected and when it is dark..."
227 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
231 log.debug "$lights will turn ON when movement detected between sunset and sunrise..."
233 subscribe(location, "position", locationPositionChange)
234 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
235 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
237 else if (dark != true && sun != true)
239 log.debug "$lights will turn ON when movement detected..."
242 else if (dimmers != null && dimmers != "")
244 log.debug "$dimmers subscribing..."
245 subscribe(dimmers, "switch", dimmersHandler)
246 if (dark == true && lightSensor != null && lightSensor != "")
248 log.debug "$dimmers will turn ON when movement detected and when it is dark..."
249 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
253 log.debug "$dimmers will turn ON when movement detected between sunset and sunrise..."
255 subscribe(location, "position", locationPositionChange)
256 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
257 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
259 else if (dark != true && sun != true)
261 log.debug "$dimmers will turn ON when movement detected..."
264 log.debug "Determinating lights and dimmers current value..."
265 if (lights != null && lights != "")
267 if (lights.currentValue("switch").toString().contains("on"))
269 state.lightsState = "on"
270 log.debug "Lights $state.lightsState."
272 else if (lights.currentValue("switch").toString().contains("off"))
274 state.lightsState = "off"
275 log.debug "Lights $state.lightsState."
282 if (dimmers != null && dimmers != "")
284 if (dimmers.currentValue("switch").toString().contains("on"))
286 state.dimmersState = "on"
287 log.debug "Dimmers $state.dimmersState."
289 else if (dimmers.currentValue("switch").toString().contains("off"))
291 state.dimmersState = "off"
292 log.debug "Dimmers $state.dimmersState."
301 def locationPositionChange(evt)
303 log.trace "locationChange()"
307 def sunriseSunsetTimeHandler(evt)
309 state.lastSunriseSunsetEvent = now()
310 log.debug "SmartNightlight.sunriseSunsetTimeHandler($app.id)"
314 def motionHandler(evt)
316 log.debug "$evt.name: $evt.value"
317 if (evt.value == "active")
319 unschedule(turnOffLights)
320 unschedule(turnOffDimmers)
321 if (dark == true && sun == true)
323 if (darkOk == true && sunOk == true)
325 log.debug "Lights and Dimmers will turn ON because $motionSensor detected motion and $lightSensor was dark or because $motionSensor detected motion between sunset and sunrise..."
326 if (lights != null && lights != "")
328 log.debug "Lights: $lights will turn ON..."
331 if (dimmers != null && dimmers != "")
333 log.debug "Dimmers: $dimmers will turn ON..."
337 else if (darkOk == true && sunOk != true)
339 log.debug "Lights and Dimmers will turn ON because $motionSensor detected motion and $lightSensor was dark..."
340 if (lights != null && lights != "")
342 log.debug "Lights: $lights will turn ON..."
345 if (dimmers != null && dimmers != "")
347 log.debug "Dimmers: $dimmers will turn ON..."
351 else if (darkOk != true && sunOk == true)
353 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion between sunset and sunrise..."
354 if (lights != null && lights != "")
356 log.debug "Lights: $lights will turn ON..."
359 if (dimmers != null && dimmers != "")
361 log.debug "Dimmers: $dimmers will turn ON..."
367 log.debug "Lights and dimmers will not turn ON because $lightSensor is too bright or because time not between sunset and surise."
370 else if (dark == true && sun != true)
374 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion and $lightSensor was dark..."
375 if (lights != null && lights != "")
377 log.debug "Lights: $lights will turn ON..."
380 if (dimmers != null && dimmers != "")
382 log.debug "Dimmers: $dimmers will turn ON..."
388 log.debug "Lights and dimmers will not turn ON because $lightSensor is too bright."
391 else if (dark != true && sun == true)
395 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion between sunset and sunrise..."
396 if (lights != null && lights != "")
398 log.debug "Lights: $lights will turn ON..."
401 if (dimmers != null && dimmers != "")
403 log.debug "Dimmers: $dimmers will turn ON..."
409 log.debug "Lights and dimmers will not turn ON because time not between sunset and surise."
412 else if (dark != true && sun != true)
414 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion..."
415 if (lights != null && lights != "")
417 log.debug "Lights: $lights will turn ON..."
420 if (dimmers != null && dimmers != "")
422 log.debug "Dimmers: $dimmers will turn ON..."
427 else if (evt.value == "inactive")
429 unschedule(turnOffLights)
430 unschedule(turnOffDimmers)
431 if (state.lightsState != "off" || state.dimmersState != "off")
433 log.debug "Lights and/or dimmers are not OFF."
436 def delay = delayMinutes * 60
437 if (dark == true && sun == true)
439 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s) after turning ON when dark or between sunset and sunrise..."
440 if (lights != null && lights != "")
442 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
443 runIn(delay, turnOffLights)
445 if (dimmers != null && dimmers != "")
447 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
448 runIn(delay, turnOffDimmers)
451 else if (dark == true && sun != true)
453 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s) after turning ON when dark..."
454 if (lights != null && lights != "")
456 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
457 runIn(delay, turnOffLights)
459 if (dimmers != null && dimmers != "")
461 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
462 runIn(delay, turnOffDimmers)
465 else if (dark != true && sun == true)
467 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s) between sunset and sunrise..."
468 if (lights != null && lights != "")
470 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
471 runIn(delay, turnOffLights)
473 if (dimmers != null && dimmers != "")
475 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
476 runIn(delay, turnOffDimmers)
479 else if (dark != true && sun != true)
481 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s)..."
482 if (lights != null && lights != "")
484 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
485 runIn(delay, turnOffLights)
487 if (dimmers != null && dimmers != "")
489 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
490 runIn(delay, turnOffDimmers)
496 log.debug "Lights and dimmers will stay ON because no turn OFF delay was set..."
499 else if (state.lightsState == "off" && state.dimmersState == "off")
501 log.debug "Lights and dimmers are already OFF and will not turn OFF in $delayMinutes minute(s)."
506 def lightsHandler(evt)
508 log.debug "Lights Handler $evt.name: $evt.value"
509 if (evt.value == "on")
511 log.debug "Lights: $lights now ON."
512 unschedule(turnOffLights)
513 state.lightsState = "on"
515 else if (evt.value == "off")
517 log.debug "Lights: $lights now OFF."
518 unschedule(turnOffLights)
519 state.lightsState = "off"
523 def dimmersHandler(evt)
525 log.debug "Dimmer Handler $evt.name: $evt.value"
526 if (evt.value == "on")
528 log.debug "Dimmers: $dimmers now ON."
529 unschedule(turnOffDimmers)
530 state.dimmersState = "on"
532 else if (evt.value == "off")
534 log.debug "Dimmers: $dimmers now OFF."
535 unschedule(turnOffDimmers)
536 state.dimmersState = "off"
540 def illuminanceHandler(evt)
542 log.debug "$evt.name: $evt.value, lastStatus lights: $state.lightsState, lastStatus dimmers: $state.dimmersState, motionStopTime: $state.motionStopTime"
543 unschedule(turnOffLights)
544 unschedule(turnOffDimmers)
545 if (evt.integerValue > 999)
547 log.debug "Lights and dimmers will turn OFF because illuminance is superior to 999 lux..."
548 if (lights != null && lights != "")
550 log.debug "Lights: $lights will turn OFF..."
553 if (dimmers != null && dimmers != "")
555 log.debug "Dimmers: $dimmers will turn OFF..."
559 else if (evt.integerValue > ((luxLevel != null && luxLevel != "") ? luxLevel : 50))
561 log.debug "Lights and dimmers will turn OFF because illuminance is superior to $luxLevel lux..."
562 if (lights != null && lights != "")
564 log.debug "Lights: $lights will turn OFF..."
567 if (dimmers != null && dimmers != "")
569 log.debug "Dimmers: $dimmers will turn OFF..."
579 if (state.lightsState != "on")
581 log.debug "Turning ON lights: $lights..."
583 state.lightsState = "on"
587 log.debug "Lights: $lights already ON."
592 log.debug "Time, days of the week or mode out of range! $lights will not turn ON."
600 if (state.dimmersState != "on")
602 log.debug "Turning ON dimmers: $dimmers..."
603 settings.dimmers?.setLevel(level)
604 state.dimmersState = "on"
608 log.debug "Dimmers: $dimmers already ON."
613 log.debug "Time, days of the week or mode out of range! $dimmers will not turn ON."
622 if (state.lightsState != "off")
624 log.debug "Turning OFF lights: $lights..."
626 state.lightsState = "on"
630 log.debug "Lights: $lights already OFF."
635 log.debug "Time, day of the week or mode out of range! $lights will not turn OFF."
643 if (state.dimmersState != "off")
645 log.debug "Turning OFF dimmers: $dimmers..."
647 state.dimmersState = "off"
651 log.debug "Dimmers: $dimmers already OFF."
656 log.debug "Time, day of the week or mode out of range! $dimmers will not turn OFF."
662 def s = getSunriseAndSunset(zipCode: zipCodeText, sunriseOffset: sunriseOffset, sunsetOffset: sunsetOffset)
663 state.riseTime = s.sunrise.time
664 state.setTime = s.sunset.time
665 log.debug "Sunrise: ${new Date(state.riseTime)}($state.riseTime), Sunset: ${new Date(state.setTime)}($state.setTime)"
671 if (dark == true && lightSensor != null && lightSensor != "")
673 result = lightSensor.currentIlluminance < ((luxLevel != null && luxLevel != "") ? luxLevel : 50)
675 log.trace "darkOk = $result"
685 result = t < state.riseTime || t > state.setTime
687 log.trace "sunOk = $result"
691 private getSunriseOffset()
693 sunriseOffsetValue ? (sunriseOffsetDir == "Before" ? "-$sunriseOffsetValue" : sunriseOffsetValue) : null
696 private getSunsetOffset()
698 sunsetOffsetValue ? (sunsetOffsetDir == "Before" ? "-$sunsetOffsetValue" : sunsetOffsetValue) : null
703 modeOk && daysOk && timeOk
708 def result = !modes || modes.contains(location.mode)
709 log.trace "modeOk = $result"
718 def df = new java.text.SimpleDateFormat("EEEE")
719 if (location.timeZone)
721 df.setTimeZone(location.timeZone)
725 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
727 def day = df.format(new Date())
728 result = days.contains(day)
730 log.trace "daysOk = $result"
737 if (starting && ending)
740 def start = timeToday(starting).time
741 def stop = timeToday(ending).time
742 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
744 log.trace "timeOk = $result"
748 private hhmm(time, fmt = "h:mm a")
750 def t = timeToday(time, location.timeZone)
751 def f = new java.text.SimpleDateFormat(fmt)
752 f.setTimeZone(location.timeZone ?: timeZone(time))
756 private hideOptionsSection()
758 (starting || ending || days || modes) ? false : true
761 private timeIntervalLabel()
763 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""