Switching between modes programically

So I’m working on an app where the following ocurs

  1. when a specified sensor is triggered it will switch the mode to what a user selected.
  2. After a certain amount of time the mode then switches back to what it was originally (user specifies time).

I can get #1 to work, #2 however doesn’t seem to switch back. The timer fires but the mode just tries to switch back to itself not the original.

When running this in the debug mode on the online IDE it seems to work fine.

I have the code here.
http://pastebin.com/Gy20hYYX

Hopefully this makes sense.

Is there motion happening between trigger #1 and the timer ending? If so, that would reset the “old mode” to the new mode, which at that time is the current mode. You may need an extra “triggered” state to only change the value of oldMode once

2 Likes

Yes, there’s motion between #1 and #2 but in the mode that it’s in, it shouldn’t matter.

The mode that this app is enabled for is “night” and the mode I’m switching to is “day”. Day doesn’t do anything other then turn everything off (no alerts or anything). I want it to turn back to night after a set amount of time (like 20 minutes).

If you add oldMode to the debug statement what is the value?

I’m wondering if the app’s oldMode state is maintained when the mode is day since the app is set to only run in night?

So the oldMode is “day” when the timer goes off (should be night).

I’m not a groovy or java developer but it makes me think that this line “state.oldMode = location.mode” is a reference copy and not a value copy which then oldMode would switch then the mode switches. The behavior kind of acts that way. I’m not sure how to copy it though.

I can’t look at the code (I use a text to speech reader, so following groovy is a bear), but just wanted to mention that the device itself has no idea what the mode is. Mode is just a value used by code in the smartthings cloud. So if you have a physical device like a motion sensor, mode can’t actually inactivate it or turn it off, it just tells the cloud to ignore the reports.

So the first thing, if possible, is to put the motion sensor inside a cabinet. Open the door, trigger it, close the door.

Now see what all the values look like when the next timer happens.

It’s really common with motion sensors for them to be physically triggered which resets the device’s internal sleep/wake/report/inactivity counters, which then produces unpredictable results with code that assumes the device acts as though it was turned off during the ignore period.

FWIW…

Give this a try :smile:

This will protect the timer from changing until the timer runs.

   /**
     *  Disable Sensors when sensor gets active
     *
     *  Copyright 2015 Ryan Knopp
     *
     *  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: "Disable Sensors when sensor gets active",
        namespace: "ryan@theknopps.com",
        author: "Ryan Knopp",
        description: "When a motion sensor gets triggered it will disable the rest for a set amount of time. ",
        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("Motion sensor to disable others") {
                    input name: "motionSensors", title: "Motion here", type: "capability.motionSensor", multiple: true, required: true
            }
        section("Change to this mode") {
                    input "newMode", "mode", title: "Mode?"
            }
        section("For this amount of time") {
                    input name: "minutes", title: "Minutes?", type: "number", multiple: false
            }
    }
     
    def installed() {
            log.debug "Installed with settings: ${settings}"
     
            initialize()
    }
     
    def updated() {
            log.debug "Updated with settings: ${settings}"
     
            unsubscribe()
            initialize()
    }
     
    def initialize() {
        subscribe(motionSensors, "motion.active", motionHandler)
        state.running = null
    }
     
    def motionHandler(evt) {
    if (state.running != "true"){
            log.debug "Current mode = ${location.mode} TIMER START"
        state.oldMode = location.mode
            setLocationMode(newMode)
        log.debug "Change mode = ${location.mode}"
       
        def delay = minutes.toInteger() * 60
        runIn(delay, executeTimer)
        state.running = "true"
    }
    else{
    log.debug("motion sensed but not running because i am within the execution timer")
    }
    }
     
    def executeTimer(evt) {
            state.running = "false"
            log.debug "Current mode = ${location.mode} TIMER CHANGED"
            setLocationMode(state.oldMode);
    }

now if you wanted to have the timer be reset every time it senses motion you can do something like this…

   /**
     *  Disable Sensors when sensor gets active
     *
     *  Copyright 2015 Ryan Knopp
     *
     *  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: "Disable Sensors when sensor gets active",
        namespace: "ryan@theknopps.com",
        author: "Ryan Knopp",
        description: "When a motion sensor gets triggered it will disable the rest for a set amount of time. ",
        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("Motion sensor to disable others") {
                    input name: "motionSensors", title: "Motion here", type: "capability.motionSensor", multiple: true, required: true
            }
        section("Change to this mode") {
                    input "newMode", "mode", title: "Mode?"
            }
        section("For this amount of time") {
                    input name: "minutes", title: "Minutes?", type: "number", multiple: false
            }
    }
     
    def installed() {
            log.debug "Installed with settings: ${settings}"
     
            initialize()
    }
     
    def updated() {
            log.debug "Updated with settings: ${settings}"
     
            unsubscribe()
            initialize()
    }
     
    def initialize() {
        subscribe(motionSensors, "motion.active", motionHandler)
        state.running = null
    }
     
    def motionHandler(evt) {
    if (state.running != "true"){
         state.oldMode = location.mode
          state.running = "true"
   
            log.debug "Current mode = ${location.mode} TIMER START"
            setLocationMode(newMode)
        log.debug "Change mode = ${location.mode}"
    }   
        def delay = minutes.toInteger() * 60
        runIn(delay, executeTimer)
    }
     
    def executeTimer(evt) {
            state.running = "false"
            log.debug "Current mode = ${location.mode} TIMER CHANGED"
            setLocationMode(state.oldMode);
    }

Interesting, but this leads me to a question.

So when I install my app, I only have the app run or triggered when it’s in “night” mode. This app switches it to “day” mode when that sensor is triggered so now this app shouldn’t be triggered anymore (it’s not in “night” mode) so wouldn’t that eliminate the “state.running” logic? It will be able to get triggered again when it switches back to night mode after the timer goes off (which i can’t seem to get working, hence this forum post). Am I missing something?