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.
19 name: "Smart Nightlight",
20 namespace: "smartthings",
21 author: "SmartThings",
22 description: "Turns on lights when it's dark and motion is detected. Turns lights off when it becomes light or some time after motion ceases.",
23 category: "Convenience",
24 iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/light_motion-outlet-luminance.png",
25 iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/light_motion-outlet-luminance@2x.png"
29 section("Control these lights..."){
30 input "lights", "capability.switch", multiple: true
32 section("Turning on when it's dark and there's movement..."){
33 input "motionSensor", "capability.motionSensor", title: "Where?"
35 section("And then off when it's light or there's been no movement for..."){
36 input "delayMinutes", "number", title: "Minutes?"
38 section("Using either on this light sensor (optional) or the local sunrise and sunset"){
39 input "lightSensor", "capability.illuminanceMeasurement", required: false
41 section ("Sunrise offset (optional)...") {
42 input "sunriseOffsetValue", "text", title: "HH:MM", required: false
43 input "sunriseOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
45 section ("Sunset offset (optional)...") {
46 input "sunsetOffsetValue", "text", title: "HH:MM", required: false
47 input "sunsetOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
49 section ("Zip code (optional, defaults to location coordinates when location services are enabled)...") {
50 input "zipCode1", "text", title: "Zip code", required: false
65 subscribe(motionSensor, "motion", motionHandler)
67 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
70 subscribe(location, "mode", locationPositionChange)
71 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
72 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
77 def locationPositionChange(evt) {
78 log.trace "locationChange()"
82 def sunriseSunsetTimeHandler(evt) {
83 state.lastSunriseSunsetEvent = now()
84 log.debug "SmartNightlight.sunriseSunsetTimeHandler($app.id)"
88 def motionHandler(evt) {
89 log.debug "$evt.name: $evt.value"
90 if (evt.value == "active") {
92 log.debug "turning on lights due to motion"
94 state.lastStatus = "on"
96 state.motionStopTime = null
99 state.motionStopTime = now()
101 runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: false])
103 turnOffMotionAfterDelay()
108 def illuminanceHandler(evt) {
109 log.debug "$evt.name: $evt.value, lastStatus: $state.lastStatus, motionStopTime: $state.motionStopTime"
110 def lastStatus = state.lastStatus
111 if (lastStatus != "off" && evt.integerValue > 50) {
113 state.lastStatus = "off"
115 else if (state.motionStopTime) {
116 if (lastStatus != "off") {
117 def elapsed = now() - state.motionStopTime
118 if (elapsed >= ((delayMinutes ?: 0) * 60000L) - 2000) {
120 state.lastStatus = "off"
124 else if (lastStatus != "on" && evt.integerValue < 30){
126 state.lastStatus = "on"
130 def turnOffMotionAfterDelay() {
131 log.trace "In turnOffMotionAfterDelay, state.motionStopTime = $state.motionStopTime, state.lastStatus = $state.lastStatus"
132 if (state.motionStopTime && state.lastStatus != "off") {
133 def elapsed = now() - state.motionStopTime
134 log.trace "elapsed = $elapsed"
135 if (elapsed >= ((delayMinutes ?: 0) * 60000L) - 2000) {
136 log.debug "Turning off lights"
138 state.lastStatus = "off"
143 def scheduleCheck() {
144 log.debug "In scheduleCheck - skipping"
145 //turnOffMotionAfterDelay()
149 def s = getSunriseAndSunset(zipCode: zipCode1, sunriseOffset: sunriseOffset, sunsetOffset: sunsetOffset)
150 state.riseTime = s.sunrise.time
151 state.setTime = s.sunset.time
152 log.debug "rise: ${new Date(state.riseTime)}($state.riseTime), set: ${new Date(state.setTime)}($state.setTime)"
158 result = lightSensor.currentIlluminance?.toInteger() < 30
162 result = t < state.riseTime || t > state.setTime
167 private getSunriseOffset() {
168 sunriseOffsetValue ? (sunriseOffsetDir == "Before" ? "-$sunriseOffsetValue" : sunriseOffsetValue) : null
171 private getSunsetOffset() {
172 sunsetOffsetValue ? (sunsetOffsetDir == "Before" ? "-$sunsetOffsetValue" : sunsetOffsetValue) : null