Outdoor Lights - turn on dimmed at dusk, full brightness for X minutes when motion detected

Hi everyone,

I’m looking for a Smart App that will do the following - I searched the developer site and couldn’t find anything:

  1. At dusk, turn on certain lights (they will be outdoor lights in my case) at a certain dimmed intensity (let’s say 20%).

  2. When motion is detected, slowly stage the dimmer up to full intensity (20% to 100% in, say, 5 seconds). The idea is that when a person walks up to or pulls into the driveway of my house in the evening they will be greater with bright lights, otherwise the house is dimly lit so it is still visible from the street.

  3. At dawn, the lights go off.

Does this exist? If not, does anyone know any service where I can pay someone to code this up? I’m thinking elance but doesn’t look like anyone deals with smart things there.

Many thanks in advance!

4 Likes

https://github.com/pstuart/smartthings/blob/master/dawn%20to%20dust%20motion%20dimmer

Not sure if this will work, but threw it together tonight…

I don’t have dimmers so not sure if it works, but works in the virtual IDE as much as I could tell for sunset (or after) but before sunrise…

Will have to wait till tomorrow to know if it turns the lights off.

Dimmers are really odd… setLevel takes a number, but in the simulator it doesn’t appear to turn the light on or off, but the level is set, so I am assuming 0 means off and any value 1-100 means on.

Hope this helps…

1 Like

Thanks for this…

I guess there is no way to slowly turn on / slowly turn off (say, over a 5 second period)?

I will be able to test further next week when home.

It may be possible, the simulator doesn’t provide for that, it appears the setLevel() accepts two values, one is probably the light level, the second could be the time to ramp to that level. Try adding your own values in those areas and see what happens.

I am curious if this works for you…

I tried it, Patrick, and it didn’t seem to work. Turned the lights off after a while and they stayed off. I’m using TCP Connected bulbs.

Brian, need more info… Did the app do anything? What was logged?

Sorry, been out of town. Will try it again this week and see what the logs say.

So, the app triggers, but it doesn’t seem to raise the dim. The bulbs all just go off about 5 seconds after motion is detected.

11:03:14 PM: trace Scheduling ‘sunsetHandler’ for InstalledSmartApp: 11d5e245-6ed8-4b3c-bbef-a59c482764ec
11:03:12 PM: trace Deleting scheduled job ‘sunsetHandler’ for InstalledSmartApp: 11d5e245-6ed8-4b3c-bbef-a59c482764ec
11:03:12 PM: debug true
11:03:09 PM: debug Motion Detected do something
11:02:57 PM: trace Scheduling ‘sunsetHandler’ for InstalledSmartApp: 11d5e245-6ed8-4b3c-bbef-a59c482764ec
11:02:56 PM: trace Deleting scheduled job ‘sunsetHandler’ for InstalledSmartApp: 11d5e245-6ed8-4b3c-bbef-a59c482764ec
11:02:56 PM: debug true
11:02:52 PM: debug Motion Detected do something

Finally had a chance to test and same thing is happening to me…

Ok, thanks for the debug logs, I rewrote it… So hard to simulate dimmers and sunset in the IDE…

Let me know if this works for you… I added some debug tracking to help figure out what’s going on.

https://github.com/pstuart/smartthings/blob/master/dawn%20to%20dust%20motion%20dimmer

2 Likes

OK, I tweaked the time for brightening to 60 seconds, and the normal dim level to 60. The brightening on motion worked just fine, but then it got stuck at full brightness. The dimming event was scheduled, but when it tried to fire it seemed to think the time was after sunrise.

I think I’m going to take a crack at tweaking this code to not work on sunset/sunrise, but during a specific mode. I really only need it in my evening and night modes. But thanks, this is really close to what I was looking for!

This is exactly what I was looking for. Please continue to post updates!

pstuart - I tried the new version you posted. It works perfectly and is insanely awesome.

Is it possible to tie this to a lux sensor instead of a zip code to see when dusk/dawn starts. That would be helpful when there is a thunderstorm and it is dark outside (lights on).

Thanks for your hard work on this… it is very much appreciated!!!

This is working for me as well (Evolve LRM-AS). I changed 20% to 10% for my taste. That’s about as far as my coding skills will take me right now. Seems like it would be handy to be able to set this value in the UI as well as the time at 100% after motion stops.

I’m a little surprised this is not already baked into the stock ST experience. Should be.

==UPDATE. Ignore this post. My observations here are not correct. I did find an issue later. . . scroll down a few posts==

@pstuart, I spoke too soon. Nothing seemed to happen at sunrise. I’m just feeling my way through right now so take this with a grain of salt. . . If I understand the code and how it is triggered, it will only respond to motion. There appears to be no subscription to sunrise and sunset events. The code looks like it evaluates sunset and sunrise but only when triggered by a motion event. I looked at ‘Sunset/Sunrise’ and found this code:

def initialize() {
subscribe(location, "position", locationPositionChange)
subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)

astroCheck()

}

I haven’t found where this is documented but I am assuming that ‘sunriseSunsetTimeHandler’ provides the triggers needed in addition to motion.

@pstuart, I spent some more time with this last night - I think I understand what is going on in the code now. I will continue to troubleshoot and report back soon.

@pstuart - OK, I think I know why this didn’t work . . . .

Looks like you have it coded to run checkSun every day at 1:00AM local time. so, state.sunMode will always be set to “SunsetMode” (except on the very first run of checkSun if it happens to be during the day).

setSunrise() should fire off as expected at sunrise and turn the lights off. However, state.sunMode is not updated in setSunrise() so it stays “SunsetMode” . . . .

So, the next time motion is captured and triggers handleMotionEvent(evt), it thinks its still in SunsetMode, lights the lights and full brightness, then runs setSunset() which dims the lights to 20%. So then you have the lights on and responding to motion during the day which is what I observed.

I think all you have to do is update state.sunMode in setSunset() and setSunRise()…

In the meantime, however, I decided to write my own - because you can never have enough flavors of dusk-to-dawn implementations - with some additional features. I used some of your code, some from Sunrise/Sunset, and some I wrote myself. When it is cleaned up and fully tested, I will post.

I had another weird issue that turned out to be a hardware issue. I revised your code to run the dimmed state at 10%. However, the LED bulbs I’m using wouldn’t light off at 10% so I had a situation where the dimmer was reporting ON at 10% with no light. The bulb will dim to 10% but won’t start. Interesting.

@AaronZON - would love to test your new code when it’s cleaned up! If you could post it that would be greatly appreciated!

OK. Here’s the code. It ran on the simulator and is running at my house.

I have NOT tested:

  • Letting ST figure out the location (I entered the zip code in preferences).
  • Sunrise and sunset offsets

I’ve noted some todo items in comments in the code

Description: This is a dusk to dawn light that uses sunrise and sunset information from ST and allows offsets. During sun=down, the dimmer is set to 20% (hard coded but I will make this a settable preference at some point). When motion is sensed, dimmer goes to 100% for 5 minutes (to also be a settable preference eventually) then returns to 20% (or 0% if the sun happened to come up in the meantime).

I used code borrowed from @pstuart (from this thread) and from the published SmartApp ‘Sunrise/Sunset’ along with some stuff I did myself.

If you try this, let me know how it works for you and if you see any issue/opportunities.

/**
 *  Dusk-to-Dawn Motion-Dimming Light - AaronZON
 *
 *  Copyright 2014 Aaron Herzon
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
definition(
    name: "Dusk-to-Dawn Light - AaronZON",
    namespace: "AaronZON",
    author: "Aaron Herzon",
    description: "Dusk to dawn light with dim/bright motion sensing ",
    category: "Safety & Security",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")


preferences {
	section("Select Motion Sensor(s) you want to Use") {
        input "motions", "capability.motionSensor", title: "Motions", required: false, multiple: true
	}
    section("Select Dimmers you want to Use") {
        input "switches", "capability.switchLevel", title: "Switches", required: false, multiple: true
	}
    section ("Zip code (optional, defaults to location coordinates)...") {
		input "zipCode", "text", 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"]
	}
}
def installed() {
	log.debug "Installed with settings: ${settings}"

	initialize()
}

def updated() {
	log.debug "Updated with settings: ${settings}"

	unsubscribe()
	//unschedule handled in astroCheck method
	initialize()
}

def initialize() {
	subscribe(location, "position", locationPositionChange)
	subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
	subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
    subscribe(motions, "motion.active", handleMotionEvent)
	initialSunPosition()
	astroCheck()
}

def locationPositionChange(evt) {
	log.trace "locationChange()"
	astroCheck()
}

def sunriseSunsetTimeHandler(evt) {
	log.trace "sunriseSunsetTimeHandler()"
	astroCheck()
}

def initialSunPosition() {  
	//Determine if sun is down at time of initializtion and run sunsetHandler() if so
    
	def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: sunriseOffset, sunsetOffset: sunsetOffset)
	def now = new Date()
    def riseTime = s.sunrise
	def setTime = s.sunset
	log.debug "riseTime: $riseTime"
    log.debug "setTime: $setTime"
    log.debug "Now: $now"
	
    	if(setTime.before(now)) {
	        sunsetHandler()
            log.info "Sun is already down, run sunsetHandler"
	    }
	    else 
        {	if (riseTime.after(now)) {
        	sunsetHandler()
            log.info "Sun is already down, run sunsetHandler"
        	}
      	} 
}

def astroCheck() {
	//query sunset and sunrise times with offsets applied, schedule handlers for sun events
    //this method lifted from Sunrise/Sunset with some mods and error corrections
    
	def s = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: sunriseOffset, sunsetOffset: sunsetOffset)
	def now = new Date()
	def riseTime = s.sunrise
	def setTime = s.sunset
	log.debug "riseTime: $riseTime"
	log.debug "setTime: $setTime"
    log.debug "Now: $now"
	
	if (state.riseTime != riseTime.time) {
		state.riseTime = riseTime.time
		
		unschedule("sunriseHandler")
		if(riseTime.before(now)) {
			state.riseTime = riseTime.next()
		}
		
        log.info "scheduling sunrise handler for $state.riseTime"  
        //todo: resolve issue with date formated as Epoch sometimes in log
		schedule(state.riseTime, sunriseHandler)
	}
   
	if (state.setTime != setTime.time) {
		state.setTime = setTime.time
		unschedule("sunsetHandler")

	    if(setTime.before(now)) {
	        state.setTime = setTime.next()
	    }
	    
        log.info "scheduling sunset handler for $state.setTime"
        //todo: resolve issue with date formated as Epoch sometimes in log
	    schedule(state.setTime, sunsetHandler)
	}
}

def sunriseHandler() {
	log.info "Executing sunrise handler"
    switches?.setLevel(0)
    state.sunPosition = "up"
}

def sunsetHandler() {
	log.info "Executing sunset handler"
	switches?.setLevel(20)  //todo:  make this a preferences input
    state.sunPosition = "down"
}


def handleMotionEvent(evt) {
	log.debug "Motion detected . . . ."
    log.debug state
    
    if (state.sunPosition == "down") {
    	switches?.setLevel(100)
        state.Level = "99"
    	log.debug ". . . set the switches to level 100"
        log.debug state
        runIn(300, dimOrOffafterBright) //todo: make bright time a preference setting
    }
    else 
    {
    	log.debug ". . . but sun is up, so do nothing"
    }
}

def dimOrOffafterBright() {   
	//Handles the case where the sun comes up during bright time
    
	log.debug "Bright delay is complete, decide to turn off or dim based on sun position and offsets"
       
    if (state.sunPosition == "down") {
    	switches?.setLevel(20)  //todo:  Make dim level selectable in preferences
        state.Level = "20"
    	log.debug "Return to dimmed states since sun is down"
        log.debug state
    }
    else 
    {
    	switches?.setLevel(0)
        state.Level = "0"
        log.debug "Turned off lights since sun came up during bright time"
        log.debug state
    }
}

private getLabel() {
	app.label ?: "SmartThings"
}

private getSunriseOffset() {
	sunriseOffsetValue ? (sunriseOffsetDir == "Before" ? "-$sunriseOffsetValue" : sunriseOffsetValue) : null
}

private getSunsetOffset() {
	sunsetOffsetValue ? (sunsetOffsetDir == "Before" ? "-$sunsetOffsetValue" : sunsetOffsetValue) : null
}
1 Like

Worked great. I tried it last night. I was able to add a few variables so I could change levels without have to change code directly. Planning to clean up in the next day or so. Very basic right now.

section (“Dim Level?”) {
input “dnumber”, “number”, required: true
}
section (“Bright Level?”) {
input “bnumber”, “number”, required: true
}
section (“Delay?”) {
input “delay”, “number”, required: true
}

switches?.setLevel(bnumber)

@AaronZON Thank you for your work on this. Also, @pstuart for starting this thread.

1 Like