Dusk-to-Dawn Dimming Motion Light - RC1

I have a professional need to become familiar with how ST and the ST community works so I bought some stuff and looked for an opportunity to do something and this is what I found. I was surprised not to find a simple way to implement a dusk-to-dawn motion light in ST that does what non-connected motion lights that you buy at the hardware store do. So, I hijacked this thread, and ended up writing my own solution starting with some code @pstuart started, borrowing from Sunrise/Sunset, taking some suggestions from @kgofswg1973 and adding some of my own code (copied below). I’d like now to share this with others.

Description: Turn lights on at dusk at a dimmed level, brighten when motion is sensed temporarily. Turn off at dawn. Adjustable dim level, bright level, bright time, dawn offset, dusk offset. Dusk and dawn times based on location (either zip code or hub location)

I would be delighted if anyone would like to try this and provide feedback/suggestions. I would like to know if anyone else thinks this should be included as one of the standard SmartApps. I know there are other ways to do this leveraging ‘modes’ and such but I think this is much easier. I’m new to ST myself and I still haven’t been able to get my head completely around modes and how to use them to make my life easier.

Also, I’m not sure how to share this with you other than copy/paste into the post. I selected the ‘share’ option when I set this up but I don’t see it when I navigate the shared apps. I’m not sure what I’m doing wrong here.

I’d like to have an icon for this. I’ve read where @Ben was offering to assign icons to apps. Seems like a light bulb with stars/moon behind would make a good icon for this.

Finally, other than making noise here, I don’t see that there is a process to promote an app from the community into the mainstream. If there is a ‘submission process’ that I just haven’t found, please point me in the right direction. I realize ST has to carefully curate what gets promoted to the mainline but if there is a process, I would like to know how get that started.

EDIT: There is a process. I will explore this once I make any final edits.

Finally (really, this time), if no one cares, I’m OK with that too. I’ve got what I needed from the experience already.

The Code. Dusk-to-Dawn Dimming Motion Light - RC1 [EDIT: this not current, please continue down thread for improved versions]

   /**
 *  Dusk-to-Dawn Dimming Motion Light - RC1
 *
 *  Written by Aaron Herzon and based on code by Patrick Stuart and SmartThings with contributions from the SmartThings community
 * 
 *
 *  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 Dimming Motion Light - RC1",
    namespace: "AaronZON",
    author: "Aaron Herzon",
    description: "Turn lights on at dusk at a dimmed level, brighten when motion is sensed temporarily.  Turn off at dawn.  Adjustable dim level, bright level, bright time, down offest, dusk offset.  Dusk and dawn times based on location (either zip code or hub location)",
    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")
    //todo:  replace icon with something appropriate.  Maybe a lightbulb with the moon and stars


preferences {
	section("Select Motion Sensor(s) you want to Use") {
        input "motions", "capability.motionSensor", title: "Motion Detectors (leave blank for just dusk-to-dawn function)", required: false, multiple: true
	}
    section("Select Dimmers you want to Use") {
        input "switches", "capability.switchLevel", title: "Dimmer Switches", required: false, multiple: true
	}
    section ("Set Bright and Dim Levels and Bright Time") {
		input "DimLevelStr", "enum", title: "Dimmed Level %", required: true, 
        	options: ["10","15","20","30","50","75"], defaultValue: "20"
        
        input "BrightLevelStr", "enum", title: "Bright Level %", required: true, 
        	options: ["100","75","50"], defaultValue: "100"
        
        input "DelayMinStr", "enum", title: "Bright time, minutes", required: true, 
        	options: ["1","3","5","10","15","30","60"], defaultValue: "5"
        	}
    section ("Zip code (optional, defaults to location coordinates)...") {
		input "zipCode", "text", title: "Enter 5-digit ZIP code", required: false
	}
    section ("Sunrise offset (optional)...") {
		input "sunriseOffsetValue", "text", title: "Offset amount in the format HH:MM", required: false
		input "sunriseOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
	}
	section ("Sunset offset (optional)...") {
		input "sunsetOffsetValue", "text", title: "Offset amount in the format HH:MM", required: false
		input "sunsetOffsetDir", "enum", title: "Before or After", required: false, options: ["Before","After"]
	}
}
def installed() {
	log.debug "Installed with settings: ${settings} DelayMin: $DelayMin"

	initialize()
}

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

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

def initialize() {
	state.DimLevel = DimLevelStr as Integer
    state.BrightLevel = BrightLevelStr as Integer
    state.DelayMin = DelayMinStr as Integer
    
    subscribe(location, "position", locationPositionChange)
	subscribe(location, "sunriseTime", sunriseSunsetTimeHandler)
	subscribe(location, "sunsetTime", sunriseSunsetTimeHandler)
    subscribe(motions, "motion.active", handleMotionEvent)
    subscribe(motions, "motion.inactive", handleEndMotionEvent)
	
    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)) {   //before midnight, after sunset
	        sunsetHandler()
            log.info "Sun is already down, run sunsetHandler"
	    }
	    else 
        {	if (riseTime.after(now)) {  //after midnight, before sunset
        	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(30)  //starts at 30 (arbitrary) to make sure lights start (my LEDs won't start at below 20% but run fine)
    state.sunPosition = "down"
    runIn(3, dimOrOffafterBright)  //after initial light-off, runs handler to set to selected dim level after 3 seconds
}


def handleMotionEvent(evt) {
	log.debug "Motion detected . . . ."
    
    if (state.sunPosition == "down") {
    	switches?.setLevel(state.BrightLevel)
        state.Level = state.BrightLevel
    	log.debug ". . . set the dimmers to level $state.BrightLevel"
    }
    else 
    {
    	log.debug ". . . but sun is up, so do nothing"
        log.debug state
    }
}

def handleEndMotionEvent(evt) {
	log.debug "Motion stopped . . . ."

    if (state.sunPosition == "down") {
    	switches?.setLevel(state.BrightLevel)  //does nothing unless sun went down during active motion
        state.Level = state.BrightLevel
    	log.debug ". . . set the dimmers to level $state.BrightLevel if not already there"
        runIn((state.DelayMin*60), dimOrOffafterBright)  //delay is number of minutes entered in preferences x 60 to get seconds
    }
    else 
    {
    	log.debug ". . . but sun is up, so do nothing"
        log.debug state
    }
}

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(state.DimLevel)
        state.Level = state.DimLevel
    	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
}
4 Likes

Glad to see my code being reused and enhanced. Too bad I don’t seem to see any credit in the source code and a copyright.

Open source is open, but not without attribution to contributions and sources. A small mention would go along way, especially if you are trying to find ways to make this more than a hobby.

I wrote the basic version more as a test to help people integrate devices and code. So glad you could run with it…

On a separate topic, if you feel the smartApp is publishable, then submit it via the developer tools / IDE for review and official publication via the smartApps on the device app.

The review process is pretty backed up, which is why I put my code out on github for others, like yourself to build upon. Just do me a favor and properly attribute the source of the concept and code.

2 Likes

@pstuart - Thanks for calling me out on this. I really hadn’t paid attention to any of the automatically generated language in the header. I revised the header and edited the original post in this thread. I’ve named @pstuart as the source for some of the code and SmartThings (listed as author of Sunset/Sunrise). I also noted contribution from the community. I’m not sure how far I need to go in crediting individuals from the community who have helped me along the way. Clearly Patrick deserves a mention as he is the one who started this.

Also, I removed the ‘copyright’. I don’t what to make any claim to the ownership of this code. I’m happy for anyone to do whatever they want with this. If upstream contributors have a problem with this, please let me know.

Thanks also, Patrick, for pointing out the submittal process. Once I get some feedback, if any, and make any final suggested changes, I will try the submittal process and see how that goes.

BTW, my ‘professional interest’ is not in being a developer. I have other ST interests related to my regular gig and I’m mainly using this project as a way to explore and understand the platform, community, and processes. I suspect this might be my first and only attempt at ‘publication’. I’m having a blast doing this so I suspect I will end up hanging around in the community, trying to contribute, and working on projects that interest me . . . more or less as a hobbyist.

Well, that’s the problem. If you use someone else’s copyrighted work, you can’t just remove the copyright.

I only want to do what’s right. I’m not a professional developer so I have no experience in this area. I have reached out to @pstuart but haven’t heard back. There’s not a lot of code left from his original version but did start with his and I agree he should be credited. As stated, my interest is learning about the platform, community, and process. I have no interest in owning any aspect of this. The only benefit I seek is knowledge and experience.

Understood. That’s how we all learn - by making mistakes :smile:

I’ve been trying to do something similar at the device level, but using individual luminance threshold states to preset dimming levels, vs the more static dawn/dusk approach. Might you consider offering a luminosity subscription option as well?
Here in the PNW it’s a more accurate correlation with interior lighting requirements than dawn/dusk.

So, @Mike_Maxwell, are you saying turn lights on at some dimmed level if the sun is down OR luminance is below some value? I had actually thought about this to deal with overcast skies near dawn or dust and/or during storms. I can certainly take a look at this. I don’t have anything with a luminance measurement capability and the IDE simulator has proven to be a bit hit-n-miss. If I take a shot at this, can you test? I also thought I saw some code somewhere that seem to suggest that there was a lux property in the Weather Underground API but I haven’t found anything concrete on that (haven’t looked very hard either).

Anyway, give me a little tighter spec on what you are thinking about and I can see if I can make it happen in ‘my’ app or if I need to defer to others. Also, please note that I’ve intended this to be a simple solution for outdoor lighting applications. For indoor, besides sun state, luminance, and motion, you might want also consider bedtime and presence and leverage modes and such. I’m not sure I’m willing/able to go this far with this but please share your thoughts.

Also, I’m going to try to level up with the community (see kerfuffle above in this thread) before I do any more work on this.

Peace

Exactly, sundown/rise or current lux level, the app should have the ability to set the lux cutoff via preferences, or a selection list like 50, 100, 200, 400, those were the values I was playing with < 50 is dark, 100-200 is that predawn/dusk state > 400 is daylight full on
Sure I’ll test it, as I’ve given up doing this at the dimmer device level, I was trying preset the dimmer state prior to physical device turn on…, not gonna happen…

What device/device type are you using for the source of the luminance level?

Aeon multi…

Just wanted to say I think this will be a popular smartapp. I actually modified the Smart Night Light - Dimmable on my own install to do just this (and to get my feet wet in the code). Oddly enough I had the same concerns regarding GPL/attributes/etc, and haven’t had the time to look up the rules yet.

1 Like

Folks, learn github. Learn to fork projects and commit code. Problems take care of themselves. Howevere, if you take someone else’s code, concept, etc. And change it a bit but it still does mostly the exact thing, and claim copyright then you aren’t really following the spirit of open source.

Collaborate… That is the way this community grows.

Wish the idea had better integration with github, it was promised months ago.

Anyway, I appreciate the effort you have gone through to remedy the situation. But if you are looking for rules, there aren’t many.

Just trial and error.

@pstuart - thanks for your understanding. I will proceed with the project with revised header info. Unless you say otherwise, I will assume you are OK with me proceeding without any copyright claim. Have you reviewed the code? It has changed a lot since you started it. If you are willing and have time, I would appreciate your review and comment.

@c4v3man - thanks for your comments. We shall see.

@Mike_Maxwell - I will take a look at adding your suggested feature (adding a luminance trigger) when I have some time. Maybe as soon as today.


In order to keep this thread about a proposed SmartApp, I will edit out the following from this post or, if anyone thinks this has not already been beat to death here and wants to have a discussion about it, I will move these comments to a new thread. If you want to see this as a new thread, please PM me rather than post.

I did look into this and make an account a week ago or so and understand the main concepts at a high level. I did not go any further since it seems like the only way to get code back into ST is to copy/paste right now. Since there are a lot of beginners here, I think a good solution might be to add git-like features to the ST IDE and create a ‘watered down’ git experience. This might be better than to expect newbs, such as myself, to know and understand git. Adding some basic git-like features to the IDE and a little more hand-holding from ST would keep the barrier to entry low and promote higher quality participation. Just my perspective . . . .

Not intended. . . . When you ‘create from form’, the IDE automatically generates the language claiming copyright for the person filling out the form. A new user has only to assume that this is somehow a requirement of ST or just SOP - this is the mistake I make. It might be better to assume authors do not want to claim a copyright and omit this language. Or, perhaps, make a check block on the form for this and explain the meaning behind a help button or pop-up. As @pstuart pointed out, git takes care of a lot of this, as I understand it.

Word.

. . . . a recurring theme

1 Like

We/I use an internal implementation of git at work, which is quite a PITA to initially get set up at the workstation level. So from that POV, I resisted getting a public repository setup.
I finally did so this morning, and all I can say is that it’s a night and day difference , it’s literally a trip over the box deal.
From a NOOB code consumer standpoint, there’s really nothing to learn, a publisher posts a repository link in this forum, and I believe the code gets automagically formatted. In any event clicking the code page sends the user straight to the publishers repository, couldn’t be easier actually.
Yea, IDE <-> gitHub was promised and would be nice, but the cut and past isn’t that big of a deal, I find the IDE editor a bit cranky anyway, so I do my editing against my local git repo using notepad++…

I did do a diff on the code, that’s not really the point. The original ask was to provide this SmartApp. I started an app, based off the sunset code smartthings publishes, then made it work with a device.

I’m glad you are taking this and running. I hope you submit for publication and its available to all as a published smartapp.

I don’t have time to support end users of anything I code these days, thus the reason I put what I have done out on github. Hopefully it inspires others to do the same.

Ultimately, until developers can actually make money off this solution, it will just be the device guys who want to sell more hardware that will pay for development.

An app store would go along way. Until then, code collaboration / forks, etc will have to be the way things get built.

The issue I ultimately have is posting code to this forum. It shouldn’t be done. It should be linked to a repo. Many reasons, but mostly organization and code consistency… But there is one big reason, this thread could be deleted, and code would be lost.

Maybe we can turn this into a training session on community development and the do’s and don’ts… I would love to see some of the community developers help more with projects and see more code of what others are doing. Since the docs, well, err, lack, um any good methods of actually coding, we need to fill in the voids.

Anyway, sorry for hijacking this thread, but I appreciate your willingness to do the right thing, whatever that is :slight_smile: Time will tell.

I’ll leave you with this quick story…

I once talked a stunt woman who did all of a famous actresses stunt work. I asked her how she felt when in a recent interview that actress said she did all her own stunt work. The stunt woman responded with, I guess I did a heck of a job if no one knows it was me. She still works in the stunt business. Of course, getting paid.

1 Like

Inspired by @pstuart, I have posted the code to my newly created GitHub repo. I don’t know how this works yet so feel free to let me know. [EDIT: this not current, please continue down thread for improved versions]

@Mike_Maxwell - can you share a snippet from the event log for this this sensor? I’m trying to understand the frequency and criteria for the luminance events. If you can directly answer that, skip the log sample.

Left unprovoked these report every 10 minutes, I have additional polling that runs every 5.

@Mike_Maxwell - Here is some code to try. Caveats (edited 12/11):

  • The code assumes that if the light level is above the threshold, the sun must be up even if, according to the clock, it is after sunset and before sunrise.
  • If you select offsets before sunset or after sunrise, if it the light level is above the threshold, the dimmer will turn off (being above light level threshold has priority over rise/set timing)
  • NEW! The code bypasses the feature I have that turns on the lights to 30%, then dims to the selected level. I added this when I discovered that my porch light would operate at 10% but would not turn on. No problem if you turn on at 30% and ramp down to 10% Weird but repeatable. I have this in the sunset handler which happens once per day. I cannot use the same method for the luminance trigger since it would fire every 10 minutes.

Please give this a go and let me know what happens! [EDIT: this not current, please continue down thread for improved versions]