Update Hue-Party-Mode.groovy
[smartapps.git] / official / smart-nightlight.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  *  Smart Nightlight
14  *
15  *  Author: SmartThings
16  *
17  */
18 definition(
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"
26 )
27
28 preferences {
29         section("Control these lights..."){
30                 input "lights", "capability.switch", multiple: true
31         }
32         section("Turning on when it's dark and there's movement..."){
33                 input "motionSensor", "capability.motionSensor", title: "Where?"
34         }
35         section("And then off when it's light or there's been no movement for..."){
36                 input "delayMinutes", "number", title: "Minutes?"
37         }
38         section("Using either on this light sensor (optional) or the local sunrise and sunset"){
39                 input "lightSensor", "capability.illuminanceMeasurement", required: false
40         }
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"]
44         }
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"]
48         }
49         section ("Zip code (optional, defaults to location coordinates when location services are enabled)...") {
50                 input "zipCode1", "text", title: "Zip code", required: false
51         }
52 }
53
54 def installed() {
55         initialize()
56 }
57
58 def updated() {
59         unsubscribe()
60         unschedule()
61         initialize()
62 }
63
64 def initialize() {
65         subscribe(motionSensor, "motion", motionHandler)
66         if (lightSensor) {
67                 subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
68         }
69         else {
70                 subscribe(location, "position", locationPositionChange)
71                 subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
72                 subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
73                 //astroCheck()
74         }
75 }
76
77 def locationPositionChange(evt) {
78         log.trace "locationChange()"
79         //astroCheck()
80 }
81
82 def sunriseSunsetTimeHandler(evt) {
83         state.lastSunriseSunsetEvent = now()
84         log.debug "SmartNightlight.sunriseSunsetTimeHandler($app.id)"
85         //astroCheck()
86 }
87
88 def motionHandler(evt) {
89         log.debug "$evt.name: $evt.value"
90         if (evt.value == "active") {
91                 if (enabled()) {
92                         log.debug "turning on lights due to motion"
93                         lights.on()
94                         state.lastStatus = "on"
95                 }
96                 state.motionStopTime = null
97         }
98         else {
99                 state.motionStopTime = now()
100                 if(delayMinutes) {
101                         runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: false])
102                 } else {
103                         turnOffMotionAfterDelay()
104                 }
105         }
106 }
107
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) {
112                 lights.off()
113                 state.lastStatus = "off"
114         }
115         else if (state.motionStopTime) {
116                 if (lastStatus != "off") {
117                         def elapsed = now() - state.motionStopTime
118                         if (elapsed >= ((delayMinutes ?: 0) * 60000L) - 2000) {
119                                 lights.off()
120                                 state.lastStatus = "off"
121                         }
122                 }
123         }
124         else if (lastStatus != "on" && evt.integerValue < 30){
125                 lights.on()
126                 state.lastStatus = "on"
127         }
128 }
129
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"
137                         lights.off()
138                         state.lastStatus = "off"
139                 }
140         }
141 }
142
143 def scheduleCheck() {
144         log.debug "In scheduleCheck - skipping"
145         //turnOffMotionAfterDelay()
146 }
147
148 def astroCheck() {
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)"
153 }
154
155 private enabled() {
156         def result
157         if (lightSensor) {
158                 result = lightSensor.currentIlluminance?.toInteger() < 30
159         }
160         else {
161                 def t = now()
162                 result = t < state.riseTime || t > state.setTime
163         }
164         result
165 }
166
167 private getSunriseOffset() {
168         sunriseOffsetValue ? (sunriseOffsetDir == "Before" ? "-$sunriseOffsetValue" : sunriseOffsetValue) : null
169 }
170
171 private getSunsetOffset() {
172         sunsetOffsetValue ? (sunsetOffsetDir == "Before" ? "-$sunsetOffsetValue" : sunsetOffsetValue) : null
173 }
174