Not sure if something like this exists, I love a light to come in one of my rooms when I walk in, but I hate that I can’t turn off the light without the motion triggering it back on. Is there a smartapp that would only turn it on with motion if it hasn’t seen motion for a set period of time?
The short answer to your question is that no such SmartApp exists. However, it would not be difficult to create one.
Is this the right logic?
Motion turns on a light; however, it checks first to see that it’s been at least x minutes since motion was last active. If not that long, nothing happens.
EDITED TO ADD:
If what you want is to disable the motion turning on the light after you’ve turned it off at the switch, that’s easy to do. How to do it depends on how your are doing it now. ??
Ya, I haven’t coded an app myself, so I am just starting to read lots of existing code to familiarize myself with the language. I’ve coded for other languages before at a basic level, and it doesn’t seem too out of reach… just didn’t want to jump into something if it already existed.
At the moment I don’t have a working solution, and I tend to just delete my motion based lights for that space when they become annoying. Its for a theater room, that is always dark when the shades are closed. I like to have a light turn on when I enter the room… but when I am watching something I want the lights to do what I say, and not follow motion. I’ve played with the idea of having a different mode for that, but I don’t like having to remember to take it out of that mode, and I haven’t figured out a good way to let smartthings take me out of that mode.
Seems to me the logic you describe is perfect.
Motion Detected - Turn on light - IF Motion has not been detected for X minutes. Then I could play around with the time that works for me. I have the lights in that room turn off if no motion is active for 20 minutes, and that seems to work well… so I assume I would line the time up with that.
Thanks for your thoughts on this. I assume I could edit one of the templates like Brighten Dark Places to subscribe to motion.inactive and add an IF statement. It would just require me cutting some teeth in the process.
Cutting and pasting is a pretty good way to start.
Using a mode is not unreasonable at all. However it is you put it into “movie” mode could be used to take it back out of that mode. You can use an app to remember the previous mode, and then to restore it. A Minimote is a great way to implement this kind of thing. Like this:
You enter the theater and the lights turn on with motion.
You sit down, get your food and drinks and friends all situated, and turn the lights down with a button on the Minimote. When you’re done with the movie, another button turns the lights up (and restores the previous mode, and re-enables motion on).
Where are good examples of things like this? I am having a hard time finding the way to measure time since a motion.inactive event.
In the IDE, My SmartApps, New SmartApp, then after you fill out the form and hit create, Browse SmartApp Templates in upper right hand corner. There are dozens and dozens, and there are several that do things with time so you can copy something.
Tried copying from several published templates… I can’t seem to get this code to respond to the inactive status.
preferences {
section("When there's movement...") {
input "motion1", "capability.motionSensor", title: "Where?", multiple: true
}
section("Unless there has been movement with the past"){
input "minutes1", "number", title: "Minutes?"
}
section("Turn ON light(s)..."){
input "switches", "capability.switch", multiple: true
}
}
def installed()
{
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
}
def updated()
{
unsubscribe()
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
}
def NoActivityClock() {
log.debug "NoActivityClock"
def motionState = motion1.currentState("motion")
if (motionState.value == "inactive") {
def elapsed = now() - motionState.rawDateCreated.time
def threshold = 1000 * 60 * minutes1 - 1000
log.debug "$threshold ms Threshold"
log.debug "$elapsed ms Elapsed"
log.debug "$minutes1 Minutes1"
}
}
def motionActiveHandler(evt) {
log.debug "$evt.name: $evt.value"
def motionState = motion1.currentState("motion")
if (evt.value == "active") {
if (elapsed >= threshold) {
log.debug "Motion has stayed inactive long enough since last check ($elapsed ms): turning lights on"
log.debug "turning on lights"
switches.on()
}
}
else if (elapsed <= threshold) {
log.debug "Motion is active, do nothing and wait for inactive"
}
else {
log.debug "Motion has not stayed inactive long enough since last check ($elapsed ms): doing nothing"
}
}
def motionInactiveHandler(evt) {
log.debug "$evt.name: $evt.value"
if (motionState.value == "inactive") {
NoActivityClock()
}
}
EDIT: Ok, I editing the settings in the iOS app, and I noticed it then subscribed to my events. I guess the real problem lies with not properly calling up the NoActivityClock.
Ok, so after that “break through”
preferences {
section("When there's movement...") {
input "motion1", "capability.motionSensor", title: "Where?", multiple: true
}
section("Unless there has been movement with the past"){
input "minutes1", "number", title: "Minutes?"
}
section("Turn ON light(s)..."){
input "switches", "capability.switch", multiple: true
}
}
def installed()
{
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionActiveHandler)
}
def updated()
{
unsubscribe()
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionActiveHandler)
}
def NoActivityClock() {
log.debug "NoActivityClock"
def motionState = motion1.currentState("motion")
if (motionState.value == "inactive") {
log.debug "inactive running NoActivityClock"
def elapsed = now() - motionState.rawDateCreated.time
def threshold = 1000 * 60 * minutes1 - 1000
log.debug "$threshold ms Threshold"
log.debug "$elapsed ms Elapsed"
log.debug "$minutes1 Minutes1"
}
}
def motionActiveHandler(evt) {
log.debug "$evt.name: $evt.value"
def motionState = motion1.currentState("motion")
if (evt.value == "active") {
if (elapsed >= threshold) {
log.debug "Motion has stayed inactive long enough since last check ($elapsed ms) is > ($threshold ms): turning lights on"
log.debug "turning on lights"
switches.on()
}
}
else if (evt.value == "inactive") {
// runIn(minutes1 * 60, NoActivityClock, [overwrite: false])
log.debug "else if $evt.name: $evt.value"
motionState = motion1.currentState("motion")
def elapsed = now() - motionState.rawDateCreated.time
def threshold = 1000 * 60 * minutes1 - 1000
log.debug "$threshold ms Threshold"
log.debug "$elapsed ms Elapsed"
log.debug "Minutes1 set to $minutes1"
}
}
I am still getting null values in my motionActiveHandler event. How to I get those values created in the inactive event to cross over?
I had a similar problem when using Tim Slagle’s Nightlight app. It worked great until you sat too still for the motion sensor.
My Wife wasn’t thrilled with having to stand up and wave her arms while reading. So I added an override switch to the app that basically turns the auto motion lights to manual mode.
/**
* Lamp-Proof NightLight
*
* Modified from Tim Slagle's Nightlight Smartapp
*
* Copyright 2014 Matthew Armitage and Tim Slagle
*
* 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: "Lamp-Proof NightLight with Override",
namespace: "shujin",
author: "Matthew Armitage",
description: "NightLight app set to use higher luminance threshold and have an override switch",
category: "Convenience",
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("Monitor the luminosity..."){
input "lightSensor", "capability.illuminanceMeasurement"
}
section("And control these lights..."){
input "lights", "capability.switch", multiple: true
}
section("Turning on when its dark and there's movement..."){
input "motionSensor", "capability.motionSensor", title: "Where?", multiple: true
}
section("And then off when its light or there's been no movement for..."){
input "delayMinutes", "number", title: "Minutes?"
}
section("And use this switch as an override"){
input "override", "capability.switch", required: false
}
}
def installed() {
subscribe(motionSensor, "motion", motionHandler)
subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
subscribe(override, "switch.on", onOverride)
subscribe(override, "switch.off", offOverride)
}
def updated() {
unsubscribe()
subscribe(motionSensor, "motion", motionHandler)
subscribe(lightSensor, "illuminance", illuminanceHandler, [filterEvents: false])
subscribe(override, "switch.on", onOverride)
subscribe(override, "switch.off", offOverride)
}
def onOverride(evt) {
state.overrideSet = "on"
}
def offOverride(evt) {
state.overrideSet = "off"
}
def motionHandler(evt) {
log.debug "$evt.name: $evt.value"
def overrideSet = state.overrideSet
if (evt.value == "active") {
if (lightSensor.latestValue("illuminance") < 80 && overrideSet != "on") {
lights.on()
state.lastStatus = "on"
}
state.motionStopTime = null
}
else {
state.motionStopTime = now()
}
}
def illuminanceHandler(evt) {
log.debug "$evt.name: $evt.value, lastStatus: $state.lastStatus, motionStopTime: $state.motionStopTime"
def lastStatus = state.lastStatus
def overrideSet = state.overrideSet
if (lastStatus != "on" && evt.integerValue < 30 && !motionSensor && overrideSet != "on") {
lights.on()
state.lastStatus = "on"
}
else if (lastStatus != "off" && evt.integerValue > 50 && overrideSet != "on" ) {
lights.off()
state.lastStatus = "off"
}
else if (state.motionStopTime && lastStatus != "off" && overrideSet != "on") {
def elapsed = now() - state.motionStopTime
if (elapsed >= (delayMinutes ?: 0) * 60000L) {
lights.off()
state.lastStatus = "off"
}
}
}
I then made a virtual switch, and called it living room override.
I did this a while ago, so the base code I got from Tim’s app might be a little out of date, and it could use prefs for the luminosity levels, but it might be helpful here.
Well, I am pretty stuck, I wish the documentation was a little more thorough, its really rough trying to reverse learn from existing code. Obviously I am missing something.
I tried a different approach, but I still have the problem of defined variables not working outside of a single process inside the app. So while things can be defined and echoed to the logs, once I call for the value inside another function it comes back as a null value.
This was my second attempt:
preferences {
section("When there's movement...") {
input "motion1", "capability.motionSensor", title: "Where?", multiple: true
}
section("Unless there has been movement with the past"){
input "minutes1", "number", title: "Minutes?"
}
section("Turn ON light(s)..."){
input "switches", "capability.switch", multiple: true
}
}
def installed()
{
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
//subscribe(motion1, "motion", motionActiveHandler)
}
def updated()
{
unsubscribe()
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
// subscribe(motion1, "motion", motionActiveHandler)
}
def NoActivityClock() {
log.debug "NoActivityClock"
log.debug "motionstate = $motionState.value"
log.debug "inactive running NoActivityClock"
def threshold = 1000 * 60 * minutes1 - 1000
log.debug "$threshold ms Threshold"
log.debug "$elapsed ms Elapsed"
log.debug "$minutes1 Minutes1"
}
def motionActiveHandler(evt) {
log.debug "$evt.name: $evt.value"
def motionState = motion1.currentState("motion")
if (elapsed >= threshold) {
switchs.on()
}
}
def motionInactiveHandler(evt) {
log.debug "$evt.name: $evt.value"
def motionState = motion1.currentState("motion")
def elapsed = now() - motionState.rawDateCreated.time
NoActivityClock()
}
I guess I will take a break from it, and see if I can come back to it with a fresh brain.
You have to use the state variable: state.whatchamacallit
, or state.lastMode
, or state.motionDisabled
. You can invent any state variable you need. And use it however you want.
That was trick! Thank you so much for your help. I seem to have it working now. Let me know if you think there is a better way to do this, but it does give me the results I want… so far.
preferences {
section("When there is movement here:") {
input "motion1", "capability.motionSensor", title: "Where?", multiple: true
}
section("Turn ON light(s):"){
input "switches", "capability.switch", multiple: true
}
section("Unless there has been movement within the past"){
input "minutes1", "number", title: "Minutes?"
}
}
def installed() {
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
}
def updated() {
unsubscribe()
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
}
def ActivityClock() {
log.debug "ActivityClock"
// log.debug "$state.motionEvent"
state.threshold = 1000 * 60 * minutes1
state.elapsed = now() - state.motionEvent
log.debug "ActivityClock: $state.threshold ms Threshold is $minutes1 Minute(s)"
log.debug "ActivityClock: $state.elapsed ms Elapsed"
}
def motionActiveHandler(evt) {
log.debug "motionActiveHandler: $evt.name: $evt.value"
ActivityClock()
if (state.elapsed >= state.threshold) {
log.debug "motionActiveHandler: $state.elapsed ms >= $state.threshold ms LIGHT(s) ON"
switches.on()
} else {
state.motionEvent = now()
log.debug "motionActiveHandler: Do not turn on light"
}
}
def motionInactiveHandler(evt) {
log.debug "motionInactiveHandler: $evt.name: $evt.value"
if (state.elapsed >= state.threshold) {
state.motionEvent = now()
ActivityClock()
} else {
state.motionEvent = now()
ActivityClock()
}
}
I’ve updated the app to allow for an optional luminance sensor
preferences {
section("When there is movement here:") {
input "motion1", "capability.motionSensor", title: "Where?", multiple: true
}
section("Turn ON light(s):"){
input "switches", "capability.switch", multiple: true
}
section("Unless there has been movement within the past"){
input "minutes1", "number", title: "Minutes?"
}
section("And it is dark (OPTIONAL)") {
input "LightMeter", "capability.illuminanceMeasurement", title: "Where?", required: false
input "luminanceLevel", "number", title: "1-1000", defaultValue: "250"
}
}
def installed() {
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
subscribe(LightMeter, "illuminance", handleLuxChange)
ActivityClock()
}
def updated() {
unsubscribe()
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
subscribe(LightMeter, "illuminance", handleLuxChange)
ActivityClock()
}
def ActivityClock() {
log.debug "ActivityClock"
state.threshold = 1000 * 60 * minutes1
state.elapsed = now() - state.motionEvent
log.debug "ActivityClock: $state.threshold ms Threshold is $minutes1 Minute(s)"
log.debug "ActivityClock: $state.elapsed ms Elapsed"
}
def motionActiveHandler(evt) {
log.debug "motionActiveHandler: $evt.name: $evt.value"
ActivityClock()
if (state.elapsed >= state.threshold) {
log.debug "motionActiveHandler: $state.elapsed ms >= $state.threshold ms LIGHT(s) ON"
checkLuminance()
// switches.on()
} else {
state.motionEvent = now()
log.debug "motionActiveHandler: Do not turn on light"
}
}
def motionInactiveHandler(evt) {
log.debug "motionInactiveHandler: $evt.name: $evt.value"
state.motionEvent = now()
ActivityClock()
// if (state.elapsed >= state.threshold) {
// state.motionEvent = now()
// ActivityClock()
// } else {
// state.motionEvent = now()
// ActivityClock()
// log.debug "motionInactiveHander: Scheduling Activity Clock in $state.threshold ms"
// runIn(state.threshold / 1000,"ActivityClock")
// }
}
def handleLuxChange(evt){
if (LightMeter){
def lightSensorState = LightMeter.currentIlluminance
log.debug "handleLuxChange: SENSOR = $lightSensorState"
} else {
log.debug "handleLuxChange: SENSOR = No Light Meter"
}
}
def checkLuminance() {
log.debug "checkLuminance"
if (LightMeter){
def lightSensorState = LightMeter.currentIlluminance
log.debug "checkLuminance: SENSOR = $lightSensorState"
if (lightSensorState != null && lightSensorState <= luminanceLevel) {
log.debug "checkLuminance: Turning ON ... [luminance: ${lightSensorState}]"
switches.on()
} else {
log.debug "checkLuminance: SENSOR = $lightSensorState is > $luminanceLevel: Do Not Turn on Lights"
}
} else {
log.debug "checkLuminance: SENSOR = No Light Meter: TURN ON LIGHTS"
switches.on()
}
}
Updated to include Dimmers Or Switches, and cleaned it up a little.
Is this worth submitting for release? I’ve wanted an app to do this since day one, surprised it didn’t exist.
definition(
name: "Smarter Motion Lights",
namespace: "qwertypo",
author: "JB",
description: "Turn your lights on when motion is detected unless you turn them off!",
category: "Convenience",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/light_motion-outlet.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/light_motion-outlet@2x.png"
)
preferences {
section("When there is movement here:") {
input "motion1", "capability.motionSensor", title: "Where?", multiple: true
}
section("Turn ON These Dimmers"){
input "Dimswitches", "capability.switchLevel", multiple: true, required: false
input "BrightLevel", "number", title: "Dimmer Level %1-99 (OPTIONAL) Zero for no dimming", required: false, defaultValue: "0"
}
section("Turn ON Switches"){
input "switches", "capability.switch", multiple: true, required: false
}
section("Unless there has been movement within the past"){
input "minutes1", "number", title: "Minutes?"
}
section("And it is dark (OPTIONAL)") {
input "LightMeter", "capability.illuminanceMeasurement", title: "Where?", required: false
input "luminanceLevel", "number", title: "1-1000", defaultValue: "250", required: false
}
}
def installed() {
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
subscribe(LightMeter, "illuminance", handleLuxChange)
initialize()
}
def updated() {
unsubscribe()
subscribe(motion1, "motion.active", motionActiveHandler)
subscribe(motion1, "motion.inactive", motionInactiveHandler)
subscribe(LightMeter, "illuminance", handleLuxChange)
initialize()
}
def initialize() {
state.motionEvent = now()
ActivityClock()
state.BrightLevel = BrightLevel as Integer
if (state.BrightLevel >= 100) {
state.BrightLevel = 99
}
if (state.BrightLevel <= 0) {
state.BrightLevel = 0
}
state.luminanceLevel = luminanceLevel as Integer
if (state.luminanceLevel <= 0) {
state.luminanceLevel = 0
}
if (state.luminanceLevel >= 1000) {
state.luminanceLevel = 1000
}
log.debug "Brightness: $state.BrightLevel Luminance: $state.luminanceLevel"
}
def ActivityClock() {
log.debug "ActivityClock"
state.threshold = 1000 * 60 * minutes1
state.elapsed = now() - state.motionEvent
log.debug "ActivityClock: $state.threshold ms Threshold is $minutes1 Minute(s)"
log.debug "ActivityClock: $state.elapsed ms Elapsed"
}
def ResetClock() {
state.motionEvent = now()
log.debug "ResetClock"
}
def motionActiveHandler(evt) {
log.debug "motionActiveHandler: $evt.name: $evt.value"
ActivityClock()
if (state.elapsed >= state.threshold) {
log.debug "motionActiveHandler: $state.elapsed ms >= $state.threshold ms LIGHT(s) ON"
checkLuminance()
ResetClock()
} else {
ResetClock()
log.debug "motionActiveHandler: Do not turn on light"
}
}
def motionInactiveHandler(evt) {
log.debug "motionInactiveHandler: $evt.name: $evt.value"
// ResetClock()
ActivityClock()
}
def handleLuxChange(evt){
if (LightMeter){
def lightSensorState = LightMeter.currentIlluminance
log.debug "handleLuxChange: SENSOR = $lightSensorState"
} else {
log.debug "handleLuxChange: SENSOR = No Light Meter"
}
}
def checkLuminance() {
log.debug "checkLuminance"
if (LightMeter){
def lightSensorState = LightMeter.currentIlluminance
log.debug "checkLuminance: SENSOR = $lightSensorState"
if (lightSensorState != null && lightSensorState <= state.luminanceLevel) {
log.debug "checkLuminance: SENSOR = $lightSensorState is <= $state.luminanceLevel: Turn On Lights"
TurnOnLights()
} else {
log.debug "checkLuminance: SENSOR = $lightSensorState is >= $state.luminanceLevel: Do Not Turn On Lights"
}
} else {
log.debug "checkLuminance: SENSOR = No Light Meter: TURN ON LIGHTS"
//switches.on()
TurnOnLights()
}
}
def TurnOnLights() {
if (switches) {
log.debug "TurnOnLights: Switches ON"
switches.on()
}
if (Dimswitches) {
if (BrightLevel) {
log.debug "TurnOnLights: User set Dim Level $BrightLevel set to Light ON at $state.BrightLevel %"
Dimswitches?.setLevel(state.BrightLevel)
} else {
log.debug "TurnOnLights: Not set to Dimmer Level: Dimmer Light ON"
Dimswitches.on()
}
}
}
So that app has been working great… really happy with it, but if I try to install multiple instances of it I get an error:
Failed to save page: defaultPage
Is this because of using state.variables? What is the solution for this?
Thanks for any help sorting this out!
No, it would not be because of state variables. I have over 20 instances of one app. I’m not sure why you get this error, but I will look a bit more closely at your code this morning to see if I see anything.
From the logs during install, there is this error:
error groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method java.lang.Long#minus.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class java.lang.Character]
[class java.lang.Number] @ line 86
Here is line 86:
state.elapsed = now() - state.motionEvent
For one thing, state.motionEvent
is never initialized. Another issue (not the problem though), is that switches
is an optional preference, but minutes1
is required. Those two should be the same one way or the other.
I think if you initialize state.motionEvent
before the call to ActivityClock()
in initialize()
, it may work for you, or at least get you past this particular bug.
Thank you, that did fix it, I defined state.motionEvent = now()
during initialization and that allowed me to install another instance. Interesting that it allows me to get away with the issue with only one instance, but not when I start to install multiple instances.
As far as the switches
not being required, is because I also have Dimswitches
as an option. Is it possible to have the the app require one or the other? I’ve tested it by leaving them both blank, and the app installs, of course it has nothing to turn on when conditions are met, but the logs don’t create any errors. I’d love to have the app require one, but not both.
I couldn’t install the first instance with that variable uninitialized.
For each section of your preferences, for each input, specify either required: true
or required: false
, as appropriate. The way you had it the number of minutes was required, but I think you want it not required, as the whole part is optional.
strange… i just installed a 3rd instance of the app without defining a switch or dimmer without any issues.
If I make it required for either one of those, then I can not have either a dimmer or a switch, I’d need to have both. which wouldn’t be great for me. If it was possible to make it require one OR the other, that would be great. I’d guess I could re-write the app to only select switches, and then only apply the dim settings IF the switch allows it… but with the current structure I don’t know how to break out the list capability.switch
into two lists segregated by the compatibility to dim with capability.switchLevel
.