Turn off after X minutes unless a switch is pressed


(Matthew Rogers) #1

I’m trying to figure out how to achieve the following:

  • Turn on subset of lights (call them B, C, D) to dim setting X when motion is detected at N
  • Turn off B, C, D lights after 2 minutes, UNLESS…
  • Switch A is turned on, and when Switch A is turned on put B, C, and D at 100% brightness.

Basically, I need dim path lighting if someone is just passing through an area, but if they tap one switch, I want everything to come up to 100%.

There doesn’t seem to be any way of doing this at the moment. I’m guessing this will require a custom app, kind of a hybrid of Big Switch and turn on/off with motion?


(Tim Slagle) #2

Yep, get your code skills out :stuck_out_tongue:

If you need help let me know


(George Sudarkoff) #3

Yes, you’ll have to write some code. I actually find it an interesting use case, so I might take a shot at it tonight. I currently do it a bit differently: turn the lights on, red color, 10% in there’s motion and the mode is Night, turn off when motion stops. And to override the color/level, I turn the light off and back on again. It just resets the Hue to soft white, 100%.

In your SmartApp, you’ll need to register for two events: motion and switch. In motion handler, set a flag (e.g. state.motionActivated = true, or something like that) and schedule another event to run in 2 minutes that would turn the lights off UNLESS the flag is false. In switch handler, reset the flag to false and change the light level. See how to turn the lights off on timer here: https://gist.github.com/sudarkoff/dc5072f09bc42c2e1591


#4

@rogersmj, @tslagle13, @sudarkoff,

Have you been able to create this app?

I’ve been looking for basically the same functionality:

If in mode X, if motion is detected, turn on lights A B and C to 10% unless A B and C are already on, in which case do nothing. After no motion detected, turn A B and C off, unless they were already on.


(Bruce) #5

Here is a simple version of this app. It’s simple because it only has the logic for a single motion sensor. It can be mode restricted if that is set during installation.

So if in the right mode(s), if motion is detected, turn on some dimmers to a selected level unless any of them were already on, in which case do nothing. After motion stops, and after a selected number of minutes, turn off the dimmers unless they were on before the motion began.

This doesn’t address the OPs case of turning everything on bright if a switch is pressed, but that could be done simply.

definition(
    name: "Conditional Motion OnOff",
    namespace: "",
    author: "",
    description: "Turn on some lights with motion if not already on",
    category: "My Apps",
    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 the motion sensor...") {
		input "motion1", "capability.motionSensor", title: "Which?", required: true, multiple: false
	}
    
    section("Select the switches to control...") {
    	input "switches", "capability.switchLevel", title: "Which?", required: true, multiple: true
    }
    
    section("Select the dimmer level...") {
    	input "level", "number", title: "Level?", required: true
    }
    
    section("Select the number of minutes after to turn off") {
    	input "minutes", "number", title: "Minutes?", required: true
    }
}

def installed() {
//	log.debug "Installed with settings: ${settings}"

	initialize()
}

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

	unsubscribe()
	initialize()
}

def initialize() {
	subscribe(motion1, "motion.active", motionOnHandler)
    subscribe(motion1, "motion.inactive", motionOffHandler)
}

def motionOnHandler(evt) {
    state.switchOff = true
	for(mySwitch in switches) state.switchOff = state.switchOff && (mySwitch.currentValue("switch") == "off")
    if(state.switchOff) switches.setLevel(level)
}

def motionOffHandler(evt) {
if(state.switchOff) {if(minutes>0) runIn(minutes*60,switchesOff) else switches.off()}
}

def switchesOff() {
	switches.off()
}

(Tim Slagle) #6

Been super busy guys sorry :frowning:

I’ll try and take a look at this this weekend.


(Mcvoss) #7

How hard would this be just to have it work between sunset and sunrise, that would be great for my porch lights.


(Bruce) #8

Easy peasy. Just use a mode restriction. Have your system automatically switch to Evening mode at sunset. Have the app only run in Evening and Night modes (which ends at sunrise).


(Matthew Rogers) #9

Hey Bruce, I know this is a month late but I just wanted to say thanks for putting that together. This is just what I was looking for. Your code makes sense, and it’s helped me learn how to do some other things I’ve been toying with.

As for part “c” of my original use case, I took care of that with virtual switches.


(Bruce) #10

Hey Matthew, Glad to hear this! ST is pretty straight forward once you get over the learning curve of simple apps. Keep going…


(Matthew Rogers) #11

I wanted to share the modifications I’ve made, as well as a problem I’m having.

I added additional “classes” of switches, so there are now three:

  1. Switches to dim to a certain level when motion is detected
  2. Switches to turn on (without dimming) when motion is detected
  3. Switches to monitor, which will disable the motionOff when turned on but are NOT turned on when motion is detected

I would like the motionOff handler to get disabled anytime anyone presses any of the switches in any of the three groups after the lights have already been turned on with motion. So if someone walks into the room and lights B, C, and D are turned on, I can tap switch B to keep the lights on and disable the auto-off. I want this because the whole room isn’t quite covered by the motion sensor, and even if you’re in sight of the motion sensor but you’re (for example) being a lump on the couch watching TV, it will think no one’s there and trigger motion Off.

The problem with this idea is that it seems my switches (Evolves) don’t send any events if they’re already on and you tap the “on” part of the paddle.

Here’s my code, adapted from @bravenel 's above:

definition(
    name: "Conditional Motion OnOff",
    namespace: "rogersmj",
    author: "Matt Rogers",
    description: "Turn on some lights with motion, only if they're ALL currently off.",
    category: "My Apps",
    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 the motion sensor...") {
		input "motion1", "capability.motionSensor", title: "Which?", required: true, multiple: false
	}

    section("Select the switches to set to a certain dimmer level...") {
    	input "dimmer_switches", "capability.switchLevel", title: "Which?", required: false, multiple: true
    }

    section("Select the dimmer level...") {
    	input "level", "number", title: "Level?", required: false
    }
    
    section("Select the switches to turn on without setting dimmer level...") {
    	input "switches", "capability.switch", title: "Which?", required: false, multiple: true
    }
    
    section("And disable the motion trigger if any of these switches are on, but don't turn them on with motion...") {
    	input "watch_switches", "capability.switch", title: "Which?", required: false, multiple: true
    }

    section("Turn off this many minutes after motion stops...") {
    	input "minutes", "number", title: "Minutes?", required: true
    }
}

def installed() {
//	log.debug "Installed with settings: ${settings}"

	initialize()
}

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

	unsubscribe()
	initialize()
}

def initialize() {
	subscribe(motion1, "motion.active", motionOnHandler)
    subscribe(motion1, "motion.inactive", motionOffHandler)
}

def motionOnHandler(evt) {
    state.switchOff = true
	for(mySwitch in switches) state.switchOff = state.switchOff && (mySwitch.currentValue("switch") == "off")
    for(mySwitch in dimmer_switches) state.switchOff = state.switchOff && (mySwitch.currentValue("switch") == "off")
    for(mySwitch in watch_switches) state.switchOff = state.switchOff && (mySwitch.currentValue("switch") == "off")
    if(state.switchOff) {
    	if(level) dimmer_switches.setLevel(level)
        switches.on()
        
        //Now we're in "auto" mode, so we need to start listening to these switches. If any are pressed,
        //we need to disable the auto-off.
        subscribe(switches, "switch.on", manualPressCheck)
        subscribe(dimmer_switches, "switch.on", manualPressCheck)
        subscribe(watch_switches, "switch.on", manualPressCheck)
    }
}

def motionOffHandler(evt) {
	if(state.switchOff) {
    	if(minutes>0) {
        	runIn(minutes*60,switchesOff)
        } else {
        	switchesOff()
        }
    }
}

def switchesOff() {
	switches.off()
    dimmer_switches.off()
    
}

def manualPressCheck(evt) {
	//This gets called when one of the listened-to switches gets turned on after the motion sensor already
    //turned them on.
    
    //If the event was physical (someone actually touched the switch), this will 
    //prevent the motionOff event from doing anything
    if(evt.isPhysical()) {
    	state.switchOff = false
    
        //And we can go ahead and stop listening to these switches now.
        unsubscribe(switches)
        unsubscribe(dimmer_switches)
        unsubscribe(watch_switches)
    }
	
}

(Bruce) #12

Yes, this won’t work the way you hope. Turning on something that’s already on doesn’t do anything, doesn’t generate an event you can act on. You could turn it off, but that blows your use case. There aren’t good solutions for this. You would have to use something other than a conventional dimmer to trigger this.


(Ray) #13

How about instead of full on. When tap turn light down to let say 95%? Will that generate an event? I like the idea of this app as well.


(Matthew Rogers) #14

The problem seems to be that if a light is already on, NO event is sent if you tap the switch’s “on” button. So the app has no knowledge that someone tapped the switch and can’t take any action at all.

My above app works great if I turn on one of the “monitor” switches that isn’t already on…then the motionOff gets disabled, as it should.


(Bruce) #15

You might be able to get this to work. You’d have to subscribe to the setLevel event from the dimmer. Then the handler for that could do whatever, including putting that dimmer back to 100% if that’s what you wanted. However, there will probably be a lag between dimming the dimmer and the app firing, due to the dimmer not reporting instantly.


(Ray) #16

I looked over the code and I noticed something funny. I am not a coder and this is way over my head. here is something I think might work but don’t know how to code it. So instead of full bright with motion. how about setlevel to 95% brightness for motion for X amount of time and when press the ON button. Turn light to 100%. there is a beta app superState by @Mike_Maxwell could work for this.


#17

Thanks bravenel,

The script works pretty good. One thing i did notice and I wasn’t sure if you had any thoughts. I have a motion sensor in my kitchen that turns on the kitchen lights. I noticed that the sensor turns on the kitchen perfectly, but if motion in the kitchen stays constant and the sensor is never allowed to cycle (detect no motion then detect motion) it will turn off the lights after the specified amount of time. The only way to prevent this is to allow the sensor to go into a no motion state and then back on again or else turn the lights on manually.


(Bruce) #18

It’s been quite a while since I looked at this…

It looks as though that code is going to turn the lights off after x minutes no matter what, which is probably not what you want. Try adding this line to motionOnHandler:

unschedule()

Let me know if that fixes your problem or not. We will get this straightened out for you…