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: "configurations")
16 page(name: "timeIntervalInput", title: "Only during a certain time...")
20 input "starting", "time", title: "Starting", required: false
21 input "ending", "time", title: "Ending", required: false
28 dynamicPage(name: "configurations", title: "Configurations...", uninstall: true, nextPage: "options")
30 section(title: "Turn ON lights on movement when...")
32 input "dark", "bool", title: "It is dark?", required: true
33 input "sun", "bool", title: "Between sunset and surise?", required: true
35 section(title: "More options...", hidden: hideOptionsSection(), hideable: true)
37 def timeLabel = timeIntervalLabel()
38 href "timeIntervalInput", title: "Only during a certain time:", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
39 input "days", "enum", title: "Only on certain days of the week:", multiple: true, required: false, options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
40 input "modes", "mode", title: "Only when mode is:", multiple: true, required: false
42 section ("Assign a name")
44 label title: "Assign a name", required: false
51 if (dark == true && sun == true)
53 dynamicPage(name: "options", title: "Lights will turn ON on movement when it is dark and between sunset and sunrise...", install: true, uninstall: true)
55 section("Control these light(s)...")
57 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
59 section("Control these dimmer(s)...")
61 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
62 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
64 section("Turning ON when it's dark and there's movement...")
66 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
68 section("And then OFF when it's light or there's been no movement for...")
70 input "delayMinutes", "number", title: "Minutes?", required: false
72 section("Using this light sensor...")
74 input "lightSensor", "capability.illuminanceMeasurement",title: "Light Sensor?", multiple: false, required: true
75 input "luxLevel", "number", title: "Illuminance threshold? (default 50 lux)",defaultValue: "50", required: false
77 section ("And between sunset and sunrise...")
79 input "sunriseOffsetValue", "text", title: "Sunrise offset", required: false, description: "00:00"
80 input "sunriseOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
81 input "sunsetOffsetValue", "text", title: "Sunset offset", required: false, description: "00:00"
82 input "sunsetOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
84 section ("Zip code (optional, defaults to location coordinates when location services are enabled)...")
86 input "zipCode", "text", title: "Zip Code?", required: false, description: "Local Zip Code"
90 else if (dark == true && sun == false)
92 dynamicPage(name: "options", title: "Lights will turn ON on movement when it is dark...", install: true, uninstall: true)
94 section("Control these light(s)...")
96 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
98 section("Control these dimmer(s)...")
100 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
101 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
103 section("Turning ON when it's dark and there's movement...")
105 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
107 section("And then OFF when it's light or there's been no movement for...")
109 input "delayMinutes", "number", title: "Minutes?", required: false
111 section("Using this light sensor...")
113 input "lightSensor", "capability.illuminanceMeasurement",title: "Light Sensor?", multiple: false, required: true
114 input "luxLevel", "number", title: "Illuminance threshold? (default 50 lux)",defaultValue: "50", required: false
118 else if (sun == true && dark == false)
120 dynamicPage(name: "options", title: "Lights will turn ON on movement between sunset and sunrise...", install: true, uninstall: true)
122 section("Control these light(s)...")
124 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
126 section("Control these dimmer(s)...")
128 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
129 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
131 section("Turning ON there's movement...")
133 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
135 section("And then OFF there's been no movement for...")
137 input "delayMinutes", "number", title: "Minutes?", required: false
139 section ("Between sunset and sunrise...")
141 input "sunriseOffsetValue", "text", title: "Sunrise offset", required: false, description: "00:00"
142 input "sunriseOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
143 input "sunsetOffsetValue", "text", title: "Sunset offset", required: false, description: "00:00"
144 input "sunsetOffsetDir", "enum", title: "Before or After", required: false, metadata: [values: ["Before","After"]]
146 section ("Zip code (optional, defaults to location coordinates when location services are enabled)...")
148 input "zipCode", "text", title: "Zip Code?", required: false, description: "Local Zip Code"
154 dynamicPage(name: "options", title: "Lights will turn ON on movement...", install: true, uninstall: true)
156 section("Control these light(s)...")
158 input "lights", "capability.switch", title: "Light(s)?", multiple: true, required: false
160 section("Control these dimmer(s)...")
162 input "dimmers", "capability.switchLevel", title: "Dimmer(s)?", multiple: true, required:false
163 input "level", "number", title: "How bright?", required:false, description: "0% to 100%"
165 section("Turning ON when there's movement...")
167 input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true, required: true
169 section("And then OFF when there's been no movement for...")
171 input "delayMinutes", "number", title: "Minutes?", required: false
179 log.debug "Installed with settings: ${settings}."
185 log.debug "Updated with settings: ${settings}."
193 subscribe(motionSensor, "motion", motionHandler)
194 if (lights != null && lights != "" && dimmers != null && dimmers != "")
196 log.debug "$lights subscribing..."
197 subscribe(lights, "switch", lightsHandler)
198 log.debug "$dimmers subscribing..."
199 subscribe(dimmers, "switch", dimmersHandler)
200 if (dark == true && lightSensor != null && lightSensor != "")
202 log.debug "$lights and $dimmers will turn ON when movement detected and when it is dark..."
203 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
207 log.debug "$lights and $dimmers will turn ON when movement detected between sunset and sunrise..."
209 subscribe(location, "position", locationPositionChange)
210 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
211 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
213 else if (dark != true && sun != true)
215 log.debug "$lights and $dimmers will turn ON when movement detected..."
218 else if (lights != null && lights != "")
220 log.debug "$lights subscribing..."
221 subscribe(lights, "switch", lightsHandler)
222 if (dark == true && lightSensor != null && lightSensor != "")
224 log.debug "$lights will turn ON when movement detected and when it is dark..."
225 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
229 log.debug "$lights will turn ON when movement detected between sunset and sunrise..."
231 subscribe(location, "position", locationPositionChange)
232 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
233 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
235 else if (dark != true && sun != true)
237 log.debug "$lights will turn ON when movement detected..."
240 else if (dimmers != null && dimmers != "")
242 log.debug "$dimmers subscribing..."
243 subscribe(dimmers, "switch", dimmersHandler)
244 if (dark == true && lightSensor != null && lightSensor != "")
246 log.debug "$dimmers will turn ON when movement detected and when it is dark..."
247 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
251 log.debug "$dimmers will turn ON when movement detected between sunset and sunrise..."
253 subscribe(location, "position", locationPositionChange)
254 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
255 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
257 else if (dark != true && sun != true)
259 log.debug "$dimmers will turn ON when movement detected..."
262 log.debug "Determinating lights and dimmers current value..."
263 if (lights != null && lights != "")
265 if (lights.currentValue("switch").toString().contains("on"))
267 state.lightsState = "on"
268 log.debug "Lights $state.lightsState."
270 else if (lights.currentValue("switch").toString().contains("off"))
272 state.lightsState = "off"
273 log.debug "Lights $state.lightsState."
280 if (dimmers != null && dimmers != "")
282 if (dimmers.currentValue("switch").toString().contains("on"))
284 state.dimmersState = "on"
285 log.debug "Dimmers $state.dimmersState."
287 else if (dimmers.currentValue("switch").toString().contains("off"))
289 state.dimmersState = "off"
290 log.debug "Dimmers $state.dimmersState."
299 def locationPositionChange(evt)
301 log.trace "locationChange()"
305 def sunriseSunsetTimeHandler(evt)
307 state.lastSunriseSunsetEvent = now()
308 log.debug "SmartNightlight.sunriseSunsetTimeHandler($app.id)"
312 def motionHandler(evt)
314 log.debug "$evt.name: $evt.value"
315 if (evt.value == "active")
317 unschedule(turnOffLights)
318 unschedule(turnOffDimmers)
319 if (dark == true && sun == true)
321 if (darkOk == true && sunOk == true)
323 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..."
324 if (lights != null && lights != "")
326 log.debug "Lights: $lights will turn ON..."
329 if (dimmers != null && dimmers != "")
331 log.debug "Dimmers: $dimmers will turn ON..."
335 else if (darkOk == true && sunOk != true)
337 log.debug "Lights and Dimmers will turn ON because $motionSensor detected motion and $lightSensor was dark..."
338 if (lights != null && lights != "")
340 log.debug "Lights: $lights will turn ON..."
343 if (dimmers != null && dimmers != "")
345 log.debug "Dimmers: $dimmers will turn ON..."
349 else if (darkOk != true && sunOk == true)
351 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion between sunset and sunrise..."
352 if (lights != null && lights != "")
354 log.debug "Lights: $lights will turn ON..."
357 if (dimmers != null && dimmers != "")
359 log.debug "Dimmers: $dimmers will turn ON..."
365 log.debug "Lights and dimmers will not turn ON because $lightSensor is too bright or because time not between sunset and surise."
368 else if (dark == true && sun != true)
372 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion and $lightSensor was dark..."
373 if (lights != null && lights != "")
375 log.debug "Lights: $lights will turn ON..."
378 if (dimmers != null && dimmers != "")
380 log.debug "Dimmers: $dimmers will turn ON..."
386 log.debug "Lights and dimmers will not turn ON because $lightSensor is too bright."
389 else if (dark != true && sun == true)
393 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion between sunset and sunrise..."
394 if (lights != null && lights != "")
396 log.debug "Lights: $lights will turn ON..."
399 if (dimmers != null && dimmers != "")
401 log.debug "Dimmers: $dimmers will turn ON..."
407 log.debug "Lights and dimmers will not turn ON because time not between sunset and surise."
410 else if (dark != true && sun != true)
412 log.debug "Lights and dimmers will turn ON because $motionSensor detected motion..."
413 if (lights != null && lights != "")
415 log.debug "Lights: $lights will turn ON..."
418 if (dimmers != null && dimmers != "")
420 log.debug "Dimmers: $dimmers will turn ON..."
425 else if (evt.value == "inactive")
427 unschedule(turnOffLights)
428 unschedule(turnOffDimmers)
429 if (state.lightsState != "off" || state.dimmersState != "off")
431 log.debug "Lights and/or dimmers are not OFF."
434 def delay = delayMinutes * 60
435 if (dark == true && sun == true)
437 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s) after turning ON when dark or between sunset and sunrise..."
438 if (lights != null && lights != "")
440 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
441 runIn(delay, turnOffLights)
443 if (dimmers != null && dimmers != "")
445 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
446 runIn(delay, turnOffDimmers)
449 else if (dark == true && sun != true)
451 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s) after turning ON when dark..."
452 if (lights != null && lights != "")
454 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
455 runIn(delay, turnOffLights)
457 if (dimmers != null && dimmers != "")
459 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
460 runIn(delay, turnOffDimmers)
463 else if (dark != true && sun == true)
465 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s) between sunset and sunrise..."
466 if (lights != null && lights != "")
468 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
469 runIn(delay, turnOffLights)
471 if (dimmers != null && dimmers != "")
473 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
474 runIn(delay, turnOffDimmers)
477 else if (dark != true && sun != true)
479 log.debug "Lights and dimmers will turn OFF in $delayMinutes minute(s)..."
480 if (lights != null && lights != "")
482 log.debug "Lights: $lights will turn OFF in $delayMinutes minute(s)..."
483 runIn(delay, turnOffLights)
485 if (dimmers != null && dimmers != "")
487 log.debug "Dimmers: $dimmers will turn OFF in $delayMinutes minute(s)..."
488 runIn(delay, turnOffDimmers)
494 log.debug "Lights and dimmers will stay ON because no turn OFF delay was set..."
497 else if (state.lightsState == "off" && state.dimmersState == "off")
499 log.debug "Lights and dimmers are already OFF and will not turn OFF in $delayMinutes minute(s)."
504 def lightsHandler(evt)
506 log.debug "Lights Handler $evt.name: $evt.value"
507 if (evt.value == "on")
509 log.debug "Lights: $lights now ON."
510 unschedule(turnOffLights)
511 state.lightsState = "on"
513 else if (evt.value == "off")
515 log.debug "Lights: $lights now OFF."
516 unschedule(turnOffLights)
517 state.lightsState = "off"
521 def dimmersHandler(evt)
523 log.debug "Dimmer Handler $evt.name: $evt.value"
524 if (evt.value == "on")
526 log.debug "Dimmers: $dimmers now ON."
527 unschedule(turnOffDimmers)
528 state.dimmersState = "on"
530 else if (evt.value == "off")
532 log.debug "Dimmers: $dimmers now OFF."
533 unschedule(turnOffDimmers)
534 state.dimmersState = "off"
538 def illuminanceHandler(evt)
540 log.debug "$evt.name: $evt.value, lastStatus lights: $state.lightsState, lastStatus dimmers: $state.dimmersState, motionStopTime: $state.motionStopTime"
541 unschedule(turnOffLights)
542 unschedule(turnOffDimmers)
543 if (evt.integerValue > 999)
545 log.debug "Lights and dimmers will turn OFF because illuminance is superior to 999 lux..."
546 if (lights != null && lights != "")
548 log.debug "Lights: $lights will turn OFF..."
551 if (dimmers != null && dimmers != "")
553 log.debug "Dimmers: $dimmers will turn OFF..."
557 else if (evt.integerValue > ((luxLevel != null && luxLevel != "") ? luxLevel : 50))
559 log.debug "Lights and dimmers will turn OFF because illuminance is superior to $luxLevel lux..."
560 if (lights != null && lights != "")
562 log.debug "Lights: $lights will turn OFF..."
565 if (dimmers != null && dimmers != "")
567 log.debug "Dimmers: $dimmers will turn OFF..."
577 if (state.lightsState != "on")
579 log.debug "Turning ON lights: $lights..."
581 state.lightsState = "on"
585 log.debug "Lights: $lights already ON."
590 log.debug "Time, days of the week or mode out of range! $lights will not turn ON."
598 if (state.dimmersState != "on")
600 log.debug "Turning ON dimmers: $dimmers..."
601 settings.dimmers?.setLevel(level)
602 state.dimmersState = "on"
606 log.debug "Dimmers: $dimmers already ON."
611 log.debug "Time, days of the week or mode out of range! $dimmers will not turn ON."
620 if (state.lightsState != "off")
622 log.debug "Turning OFF lights: $lights..."
624 state.lightsState = "on"
628 log.debug "Lights: $lights already OFF."
633 log.debug "Time, day of the week or mode out of range! $lights will not turn OFF."
641 if (state.dimmersState != "off")
643 log.debug "Turning OFF dimmers: $dimmers..."
645 state.dimmersState = "off"
649 log.debug "Dimmers: $dimmers already OFF."
654 log.debug "Time, day of the week or mode out of range! $dimmers will not turn OFF."
660 def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: sunriseOffset, sunsetOffset: sunsetOffset)
661 state.riseTime = s.sunrise.time
662 state.setTime = s.sunset.time
663 log.debug "Sunrise: ${new Date(state.riseTime)}($state.riseTime), Sunset: ${new Date(state.setTime)}($state.setTime)"
669 if (dark == true && lightSensor != null && lightSensor != "")
671 result = lightSensor.currentIlluminance < ((luxLevel != null && luxLevel != "") ? luxLevel : 50)
673 log.trace "darkOk = $result"
683 result = t < state.riseTime || t > state.setTime
685 log.trace "sunOk = $result"
689 private getSunriseOffset()
691 sunriseOffsetValue ? (sunriseOffsetDir == "Before" ? "-$sunriseOffsetValue" : sunriseOffsetValue) : null
694 private getSunsetOffset()
696 sunsetOffsetValue ? (sunsetOffsetDir == "Before" ? "-$sunsetOffsetValue" : sunsetOffsetValue) : null
701 modeOk && daysOk && timeOk
706 def result = !modes || modes.contains(location.mode)
707 log.trace "modeOk = $result"
716 def df = new java.text.SimpleDateFormat("EEEE")
717 if (location.timeZone)
719 df.setTimeZone(location.timeZone)
723 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
725 def day = df.format(new Date())
726 result = days.contains(day)
728 log.trace "daysOk = $result"
735 if (starting && ending)
738 def start = timeToday(starting).time
739 def stop = timeToday(ending).time
740 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
742 log.trace "timeOk = $result"
746 private hhmm(time, fmt = "h:mm a")
748 def t = timeToday(time, location.timeZone)
749 def f = new java.text.SimpleDateFormat(fmt)
750 f.setTimeZone(location.timeZone ?: timeZone(time))
754 private hideOptionsSection()
756 (starting || ending || days || modes) ? false : true
759 private timeIntervalLabel()
761 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""