State of light is on after mode change, can't figure out why?

Hi,

I’m using a great app that @AaronZON created that turns the lights on when motion is detected and according to a lux setting. App works great because as motion keeps getting detected, it continually extends the timer.

One weird issue though. I am also using another app from @AaronZON that essentially does the same thing, but does not use the lux sensor (for a nightlight). The former app is used in “Home” mode, the latter in “Sleeping” mode. When things quiet down around 10pm, I have smartthings change into “Sleeping” mode.

Whenever there is a mode change, the lights won’t fire with either app because the app thinks the state of the light switch is on. The app will only start firing after it goes through a full cycle of off (i.e., don’t see motion for 10 minutes, turn lights off - then the state is “reset”).

Is there any way I can get the app to check the real state of the light switch, and move forward through its rules based on that?

Code for the apps below:

App used during “Home” mode:
/

**
 *  Turn light on with motion for x minutes if it is dark based on light sensor
 *
 *   Written by 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: "Dimmer (Motion & Lux)",
    namespace: "AaronZON",
    author: "Aaron Herzon",
    description: "Turn on lights temporarily when there is motion but only if it is dark according to light sensor",
    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.  


preferences {
	section("Select Motion Sensor(s) you want to Use") {
        input "motions", "capability.motionSensor", title: "Motion Detector", required: true, multiple: true
	}
    section("Select Dimmers you want to Use") {
        input "switches", "capability.switchLevel", title: "Dimmer Switches", required: false, multiple: true
	}
    section ("Set Brightness for motion-triggered light and on time after motion stops") {

        input "BrightLevelStr", "number", title: "Motion-Sensed Level %", required: true, 
        	defaultValue: "100"

        input "DelayMinStr", "number", title: "Bright Delay After Motion Stops, minutes", required: true, 
        	defaultValue: "5"
        	}
    section ("Ignore motion if light level is above this value") {
    	input "LightMeter", "capability.illuminanceMeasurement", title: "Light Meters", required: true, multiple: false
		input "LuxSetPointStr", "number", title: "Lux level", required: true, 
        	defaultValue: "50"
           	}
}
def installed() {
	log.debug "Installed with settings: ${settings}"

	initialize()
}

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

	unsubscribe()
	initialize()
}

def initialize() {
	log.debug "initialize()"

    state.BrightLevel = BrightLevelStr as Integer
   	if (state.BrightLevel == 100) {
    	state.BrightLevel = 99
    }
    state.DelayMin = DelayMinStr
    state.LuxSetPoint = LuxSetPointStr


    subscribe(motions, "motion.active", handleMotionEvent)
    subscribe(motions, "motion.inactive", handleEndMotionEvent)

}


def handleMotionEvent(evt) {
	log.debug "Motion detected..."

    unschedule(turnLightsOff)

    if (state.lightIsOn) {
        log.debug "... light already on (do nothing)"
        }
    else {
    	def currentLux=LightMeter.currentIlluminance
        log.debug "Lux ($currentLux) Setpoint: ($state.LuxSetPoint)"
        if(currentLux < state.LuxSetPoint) {
        	switches?.setLevel(state.BrightLevel)
        	state.lightIsOn=true
        	log.debug "...below setpoint, so turn lights on"
        }
        else {
        	log.debug "above setpoint, so do nothing"
        }
	}
}

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

    if (state.lightIsOn) {
    	runIn((state.DelayMin*60), turnLightsOff)
        }

}

def turnLightsOff() {   

	log.debug "turnLightsOff()" 

    switches?.setLevel(0)
    state.lightIsOn=false
}

App used during “Sleeping” mode (as nightlight)

/**
 *  Turn light on with motion for x minutes on motion
 *
 *   Written by 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: "Nightlight (Dimmable)",
    namespace: "test",
    author: "test",
    description: "Dimmable Nightlight",
    category: "Safety & Security",
    iconUrl: "http://cdn.marketplaceimages.windowsphone.com/v8/images/c7615463-1173-4d2a-a239-9f1b956a53aa?imageType=ws_icon_small",  
    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.  


preferences {
	section("Select Motion Sensor(s) you want to Use") {
        input "motions", "capability.motionSensor", title: "Motion Detector", required: true, multiple: true
	}
    section("Select Dimmers you want to Use") {
        input "switches", "capability.switchLevel", title: "Dimmer Switches", required: false, multiple: true
	}
    section ("Set Brightness for motion-triggered light and on time after motion stops") {

        input "BrightLevelStr", "number", title: "Motion-Sensed Level %", required: true, 
        	defaultValue: "100"

        input "DelayMinStr", "number", title: "Bright Delay After Motion Stops, minutes", required: true, 
        	defaultValue: "5"
        	}
}
def installed() {
	log.debug "Installed with settings: ${settings}"

	initialize()
}

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

	unsubscribe()
	initialize()
}

def initialize() {
	log.debug "initialize()"

    state.BrightLevel = BrightLevelStr as Integer
   	if (state.BrightLevel == 100) {
    	state.BrightLevel = 99
    }
    state.DelayMin = DelayMinStr
    

    subscribe(motions, "motion.active", handleMotionEvent)
    subscribe(motions, "motion.inactive", handleEndMotionEvent)

}


def handleMotionEvent(evt) {
	log.debug "Motion detected..."

    unschedule(turnLightsOff)

    if (state.lightIsOn) {
        log.debug "... light already on (do nothing)"
        }
    else {
    	   	switches?.setLevel(state.BrightLevel)
        	state.lightIsOn=true
        	log.debug "...turning lights on"
        }
        
	}

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

    if (state.lightIsOn) {
    	runIn((state.DelayMin*60), turnLightsOff)
        }

}

def turnLightsOff() {   

	log.debug "turnLightsOff()" 

    switches?.setLevel(99)
    switches?.setLevel(0)
    state.lightIsOn=false
}

Just looking briefly at this code I can see a number of issues. There are subtleties that aren’t being handled correctly. I don’t know precisely what is going on, but this behavior is identical to a documented bug with the ST built-in “Lights Turn On Motion” app. With that app, there is a timing problem with motion turning the lights on shortly after they’ve turned off from no motion.

At any rate, there are other apps available that do what you want. These two need work…

Part of the problem is that you are using two different apps for different modes, so the state information cannot be passed between them. You need to use a single app for both modes so the state information is available irrespective of the mode change.

Thanks Bryce,

I was able to figure it out - I’ve revised the code to turnthe state light to false on a mode change… now everything works perfectly.

/**
 *  Turn light on with motion for x minutes if it is dark based on light sensor
 *
 *   Written by 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: "Dimmer (Motion & Lux)",
    namespace: "AaronZON",
    author: "Aaron Herzon",
    description: "Turn on lights temporarily when there is motion but only if it is dark according to light sensor",
    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.  


preferences {
	section("Select Motion Sensor(s) you want to Use") {
        input "motions", "capability.motionSensor", title: "Motion Detector", required: true, multiple: true
	}
    section("Select Dimmers you want to Use") {
        input "switches", "capability.switchLevel", title: "Dimmer Switches", required: false, multiple: true
	}
    section ("Set Brightness for motion-triggered light and on time after motion stops") {

        input "BrightLevelStr", "number", title: "Motion-Sensed Level %", required: true, 
        	defaultValue: "100"

        input "DelayMinStr", "number", title: "Bright Delay After Motion Stops, minutes", required: true, 
        	defaultValue: "5"
        	}
    section ("Ignore motion if light level is above this value") {
    	input "LightMeter", "capability.illuminanceMeasurement", title: "Light Meters", required: true, multiple: false
		input "LuxSetPointStr", "number", title: "Lux level", required: true, 
        	defaultValue: "50"
           	}
}
def installed() {
	log.debug "Installed with settings: ${settings}"

	initialize()
}

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

	unsubscribe()
	initialize()
}

def initialize() {
	log.debug "initialize()"

    state.BrightLevel = BrightLevelStr as Integer
   	if (state.BrightLevel == 100) {
    	state.BrightLevel = 99
    }
    state.DelayMin = DelayMinStr
    state.LuxSetPoint = LuxSetPointStr


    subscribe(motions, "motion.active", handleMotionEvent)
    subscribe(motions, "motion.inactive", handleEndMotionEvent)

//[TC] start
// mode change handler
subscribe(location, "mode", modeChangeHandler)

// shortcut for mode change handler
// subscribe(location, modeChangeHandler)
//[TC END] 

}


def handleMotionEvent(evt) {
	log.debug "Motion detected..."

    unschedule(turnLightsOff)

    if (state.lightIsOn) {
        log.debug "... light state on (do nothing)"
        }
    else {
    	def currentLux=LightMeter.currentIlluminance
        log.debug "Lux ($currentLux) Setpoint: ($state.LuxSetPoint)"
        if(currentLux < state.LuxSetPoint) {
        	switches?.setLevel(state.BrightLevel)
        	state.lightIsOn=true
        	log.debug "...below setpoint, so turn lights on"
        }
        else {
        	log.debug "above setpoint, so do nothing"
        }
	}
}

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

//[TC] on motion stopped, state should not matter    if (state.lightIsOn) {
    	runIn((state.DelayMin*60), turnLightsOff)
//[TC] on motion stopped, state should not matter        }

}

def turnLightsOff() {   

	log.debug "turnLightsOff()" 

    switches?.setLevel(0)
    state.lightIsOn=false
}
//[TC mode change handler
//

def modeChangeHandler(evt) {
	log.debug "Mode Changed... Turn state.light off"

//[NK] on motion stopped, state should not matter    if (state.lightIsOn) {
    	state.lightIsOn=false
        runIn((state.DelayMin*60), turnLightsOff)
//[NK] on motion stopped, state should not matter        }

}