Skip to content

Instantly share code, notes, and snippets.

@natdm
Created December 11, 2017 05:21
Show Gist options
  • Save natdm/d4dea47c4ef1f11892e465392124c49f to your computer and use it in GitHub Desktop.
Save natdm/d4dea47c4ef1f11892e465392124c49f to your computer and use it in GitHub Desktop.
definition(
name: "Smarter Nightlight",
author: "Nathan Hyland",
description: "Turns on lights when it's dark and motion is detected and lights are not already on. Turns lights off when it becomes light or some time after motion ceases.",
category: "Convenience",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/light_motion-outlet-luminance.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/[email protected]"
)
preferences {
section("Control this light..."){
input "lights", "capability.switchLevel", multiple: true
}
section("And dim to this level"){
input "level", "number", title: "Level?"
}
section("Turning on when it's dark and there's movement..."){
input "motionSensor", "capability.motionSensor", title: "Where?"
}
section("And then off when it's light or there's been no movement for..."){
input "delaySeconds", "number", title: "Seconds?"
}
section("Using either on this light sensor (optional) or the local sunrise and sunset"){
input "lightSensor", "capability.illuminanceMeasurement", required: false
}
section ("Sunrise offset (optional)...") {
input "sunriseOffsetValue", "text", title: "HH:MM", required: false
input "sunriseOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
}
section ("Sunset offset (optional)...") {
input "sunsetOffsetValue", "text", title: "HH:MM", required: false
input "sunsetOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
}
section ("Zip code (optional, defaults to location coordinates when location services are enabled)...") {
input "zipCode", "text", title: "Zip code", required: false
}
}
def installed() {
initialize()
}
def updated() {
unsubscribe()
unschedule()
initialize()
}
def initialize() {
subscribe(motionSensor, "motion", motionHandler)
if (lightSensor) {
subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
}
else {
subscribe(location, "position", locationPositionChange)
subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
astroCheck()
}
}
def locationPositionChange(evt) {
log.trace "locationChange()"
astroCheck()
}
def sunriseSunsetTimeHandler(evt) {
state.lastSunriseSunsetEvent = now()
log.debug "SmartNightlight.sunriseSunsetTimeHandler($app.id)"
astroCheck()
}
def motionHandler(evt) {
log.debug "$evt.name: $evt.value"
log.debug "lights.currentLevel = ${lights.currentSwitch}"
if (evt.value == "active" && lights.currentSwitch[0] == "off") {
if (enabled()) {
log.debug "turning on lights due to motion"
lights.setLevel(level)
state.lastStatus = "on"
}
state.motionStopTime = null
}
else {
state.motionStopTime = now()
if(delaySeconds) {
runIn(delaySeconds, turnOffMotionAfterDelay, [overwrite: true])
} else {
turnOffMotionAfterDelay()
}
}
}
def illuminanceHandler(evt) {
log.debug "$evt.name: $evt.value, lastStatus: $state.lastStatus, motionStopTime: $state.motionStopTime"
def lastStatus = state.lastStatus
if (lastStatus != "off" && evt.integerValue > 50) {
lights.setLevel(0)
state.lastStatus = "off"
}
else if (state.motionStopTime) {
if (lastStatus != "off") {
def elapsed = now() - state.motionStopTime
if (elapsed >= ((delaySeconds ?: 0) * 1000L) - 2000) {
lights.setLevel(0)
state.lastStatus = "off"
}
}
}
else if (lastStatus != "on" && evt.integerValue < 30 && lights.currentSwitch[0] == "off"){
lights.setLevel(level)
state.lastStatus = "on"
}
}
def turnOffMotionAfterDelay() {
log.trace "In turnOffMotionAfterDelay, state.motionStopTime = $state.motionStopTime, state.lastStatus = $state.lastStatus"
if (state.motionStopTime && state.lastStatus != "off") {
def elapsed = now() - state.motionStopTime
log.trace "elapsed = $elapsed"
if (elapsed >= ((delaySeconds ?: 0) * 1000L) - 2000) {
log.debug "Turning off lights"
lights.setLevel(0)
state.lastStatus = "off"
}
}
}
def scheduleCheck() {
log.debug "In scheduleCheck - skipping"
//turnOffMotionAfterDelay()
}
def astroCheck() {
def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: sunriseOffset, sunsetOffset: sunsetOffset)
state.riseTime = s.sunrise.time
state.setTime = s.sunset.time
log.debug "rise: ${new Date(state.riseTime)}($state.riseTime), set: ${new Date(state.setTime)}($state.setTime)"
}
private enabled() {
def result
if (lightSensor) {
result = lightSensor.currentIlluminance?.toInteger() < 30
}
else {
def t = now()
result = t < state.riseTime || t > state.setTime
}
result
}
private getSunriseOffset() {
sunriseOffsetValue ? (sunriseOffsetDir == "Before" ? "-$sunriseOffsetValue" : sunriseOffsetValue) : null
}
private getSunsetOffset() {
sunsetOffsetValue ? (sunsetOffsetDir == "Before" ? "-$sunsetOffsetValue" : sunsetOffsetValue) : null
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment