Help: Routine Event Triggering Smartapp to check for open doors


(Glen McGowan) #1

This is my first app from scratch and having a little trouble. Looking for some help.

My use case is simple, I have a routine that shuts a light off. I want this routine execution event to fire a smart app to also check if doors are open (gate, garage, etc ) and notify me if they are. The documentation for Routine Event seems to indicate this is possible, yet I’m having a terrible time getting it to work. I suspect it’s my lack of experience with Groovy and the IDE.

/**
*  Routine Triggers Open Door Check
*
*  Copyright 2015 Glen McGowan
*
*/
definition(
   name: "Routine Triggers Open Door Check",
   namespace: "mobious",
   author: "Glen McGowan",
   description: "Check whether door is opened when Routine Executes.",
   category: "Convenience",
   iconUrl: "http://cdn.device-icons.smartthings.com/Home/home2-icn.png",
   iconX2Url: "http://cdn.device-icons.smartthings.com/Home/home2-icn@2x.png"
 )
preferences
page(name: "selectActions")
}

def selectActions() {
dynamicPage(name: "selectActions", install: true, uninstall: true) {
    
    // Get the available routines
        def actions = location.helloHome?.getPhrases()*.label
        if (actions) {
        // sort them alphabetically
        actions.sort()
                section("Routine") {
                    log.trace actions
            		// use the actions as the options for an enum input
            		input "action", "enum", title: "Select a trigger routine", options: actions
                	}
                section("Doors to check"){
		input "Door", "capability.contactSensor", title: "Which Doors?", multiple: true, required: true
					}
                section("Notification"){
    		input "sendPush", "bool",title: "Send Push Notification?", required: true
    	        input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false
    	        input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"]
					}    
                
        }
}
}
def installed() {
  log.debug "Installed with settings: ${settings}"
  initialize()
   }

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

def initialize() {
       subscribe(location, Door, "routineExecuted", routineChanged, checkDoor, doorCheckHandler)
}


def routineChanged(evt) {
   // name will be "routineExecuted"
   log.debug "evt name: ${evt.name}"
  
   // value will be the ID of the SmartApp that created this event
   log.debug "evt value: ${evt.value}"

   // displayName will be the name of the routine
   // e.g., "I'm Back!" or "Goodbye!"
   log.debug "evt displayName: ${evt.displayName}"

   // descriptionText will be the name of the routine, followed by the action
   // e.g., "I'm Back! was executed" or "Goodbye! was executed"
   log.debug "evt descriptionText: ${evt.descriptionText}"
   }

def doorCheckHandler(evt) {
   log.debug "Detected Routine Complete, Executing Door Check"
       if (evt.displayName == action) {
      checkDoor()        
    }
}


def checkDoor() {
log.debug "Door ${door.displayName} is ${door.currentContact}"
if (door.currentContact == "open") {
   	def msg = "${door.displayName} was left open!"
    log.info msg
    
     if (!phone || pushAndPhone != "No") {
         log.debug "sending push"
         sendPush(msg)
     }
     if (phone) {
        log.debug "sending SMS"
        sendSms(phone, msg)
    
     } else {
   	 log.debug "Door Check Complete: No Open Doors"
   }
  }
}

If I remove doorCheckHandler and checkDoor from the def initialize () I’m able to see the Routine Event captured in the logs.

However, when I add them back I see the following failure when trying to configure the app and I don’t see the routine events being logged any longer.

What am I doing wrong? Any assistance would be greatly appreciated.


(Chris) #2

The method signature for subscribe is:

subscribe(target, “eventToSubscribeTo”, handlerMethodName)

You can only pass in one target (device, collection of devices or location) and one handler.

Looks to me you only need one handler for when the routine changes and then do the subsequent checks within that.

def initialize() {
       subscribe(location, "routineExecuted", routineChanged)
}
def routineChanged(evt) {
     if (evt.displayName == action)
          checkDoor();
}

(Glen McGowan) #3

After some sleep I found a few more bugs and corrected them. The subscription and handler suggestions were key to getting to the current point. I’m able to detect the routine execution event and trigger checkDoor() now.

The problem I have now is that my notifications aren’t working.

def checkDoor() {
log.debug "checkDoor status: ${door.displayName} is ${door.currentContact}"
if (door.currentContact == "open") {
   	log.debug "checkDoor Debug: ${door.currentContact}"
def msg = "${door.displayName} was left open!"
log.debug msg

     if (!phone || pushAndPhone != "No") {
         log.debug "checkDoor: Sending push notificaton"
         sendPush(msg)
     }
     if (phone) {
        log.debug "checkDoor: Sending SMS Notification"
        sendSms(phone, msg)
    
     } else {
   	 log.debug "checkDoor Complete: No Open Doors Detected"
     } 
} else {
log.debug "checkDoor Error: Unable to detect contact states"
}
}

I see this line in the logs. So I know it’s picking up my first log.debug in checkDoor()

It doesn’t appear that the door.currentContact statements are being detected properly. As indicated by the log statement the status of door.currentContact is very clearly open. So I’m not sure why the notifications are failing.

It appears my ‘if’ statements are being ignored and we jump directly to the ‘else’ statement.


(Glen McGowan) #4

I did more testing tonight. There seems to be a bug server side in the implementation somehow? I hard coded the push notification for testing purposes and ran numerous tests using various different approaches.

Here is the notification block:

def checkDoor() {
	log.debug "checkDoor status: ${doors.displayName} is ${doors.currentContact}"
    if (doors.currentContact == "closed") {
		def message = "Door was left open"
    	log.debug message
        if (sendPush) {
     		sendPush(message)
     		}
        }
}

What I’ve found is everything works as expected in the simulator. However, if I try to test with live contacts I don’t get the results I expect.

Below is the code I’m using. I’d really like to get to the bottom of this.

/**
 *  Routine Triggers Open Door Check
 *
 *  Copyright 2015 Glen McGowan
 *
 */
definition(
    name: "Routine Execute Triggers Open Door Check",
    namespace: "mobious",
    author: "Glen McGowan",
    description: "Check for opent doors when routine executes",
    category: "Convenience",
    iconUrl: "http://cdn.device-icons.smartthings.com/Home/home2-icn.png",
    iconX2Url: "http://cdn.device-icons.smartthings.com/Home/home2-icn@2x.png"
)


preferences{
    page(name: "selectActions")
}

def selectActions() {
    dynamicPage(name: "selectActions", install: true, uninstall: true) {

        // Get the available routines
            def actions = location.helloHome?.getPhrases()*.label
            if (actions) {
            // sort them alphabetically
            actions.sort()
                    section("Routine") {
                        log.trace actions
                		// use the actions as the options for an enum input
                		input "action", "enum", title: "Select a trigger routine", options: actions
                    	}
                    section("Doors to check"){
						input "doors", "capability.contactSensor", title: "Which Doors?", multiple: true, required: true
    					}
                    
            }
    }
}
def installed() {
	log.debug "Installed with settings: ${settings}"
	initialize()
}

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

def initialize() {
        subscribe(location, "routineExecuted", routineChanged)
    }


def routineChanged(evt) {
 	if (evt.displayName == action)
        checkDoor();
}


def checkDoor() {
	log.debug "checkDoor status: ${doors.displayName} is ${doors.currentContact}"
    if (doors.currentContact == "closed") {
		def message = "Door was left open"
    	log.debug message
        if (sendPush) {
     		sendPush(message)
     		}
      }
}

Results with using simulator (success!):


(Glen McGowan) #5

I found my mistake, it lies in the section definition:

       section("Doors to check"){
       input "doors", "capability.contactSensor", title: "Which Doors?", multiple: true, required: true
    	}

I’ve been setting multiple: true which was causing the value for currentContact to return as an array. This was failing my notification match on said value. I guess I wasn’t paying attention to the brackets being returned in some of the log entries further up. :unamused:

Now I have to figure out how to match the array values. I want to check multiple doors after the routine executes. Any suggestions?


(Chris) #6

You can maybe use the “any” method

def anyOpen = values.any { it == “open” }

(Out having coffee atm so difficult to give more detail on my phone)


(Glen McGowan) #7

Got it! :+1: Thanks for the guidance @Kriskit.

def checkDoor() {
    def open = doors.findAll { it?.latestValue("contact") == "open" }
    def list = open.size() > 1 ? "are" : "is"
    if (open) {
	def message = "Secuirty Check Failed: ${open.join(', ')} ${list} open"
    	log.info message
        if (sendPush) {
     		sendPush(message)
     		}
      }
    if (doors.currentContact == "closed") {
    log.info "Security Check Successful: No open doors detected." 
	}else {
    log.info "Error: Unable to detect contact status."
    }
 }

(Amit Bahree) #8

Hi @mobious I was intending to use this and was wondering if you have the latest code published somewhere? Thanks.


(Glen McGowan) #9

Hi @bahree,

Sorry for the late reply, been a busy week. This is not pretty but works for what I need. :slight_smile:

I just create a routine called security check.

/**
 *  Copyright 2015 Glen McGowan
 *
 *  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.
 *
 *  Routine Execute Triggers Open Door Check
 *
 *  Author: Glen McGowan
 */
definition(
    name: "Routine Execute Triggers Open Door Check",
    namespace: "mobious",
    author: "Glen McGowan",
    description: "Check for opent doors when routine executes",
    category: "Convenience",
    iconUrl: "http://cdn.device-icons.smartthings.com/Home/home2-icn.png",
    iconX2Url: "http://cdn.device-icons.smartthings.com/Home/home2-icn@2x.png"
)


preferences{
    page(name: "selectActions")
}

def selectActions() {
    dynamicPage(name: "selectActions", install: true, uninstall: true) {

        // Get the available routines
            def actions = location.helloHome?.getPhrases()*.label
            if (actions) {
            // sort them alphabetically
            actions.sort()
                    section("Routine") {
                        log.trace actions
                		// use the actions as the options for an enum input
                		input "action", "enum", title: "Select a trigger routine", options: actions
                    	}
                    section("Doors to check"){
						input "doors", "capability.contactSensor", title: "Which Door?", multiple: true, required: true
    					}
                    
                    /** Will fix this section later...
                    section("Notification"){
        				input "sendPush", "bool",title: "Send Push Notification?", required: true
        				input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false
        				input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"]
   						}
                        **/    
                    
            }
    }
}
def installed() {
	log.debug "Installed with settings: ${settings}"
	initialize()
}

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

def initialize() {
        subscribe(location, "routineExecuted", routineChanged)
    }


def routineChanged(evt) {
 	if (evt.displayName == action)
        checkDoor();
}

def checkDoor() {
	//Find all the doors that are open.
    def open = doors.findAll { it?.latestValue("contact") == "open" }
    //Group all the open doors in a list, insert "is" or "Are" if the list contains more than one entry.
    def list = open.size() > 1 ? "are" : "is"
    if (open) {
    	//format the list and push it.
		def message = "Security Check Failed: ${open.join(', ')} ${list} open"
    	log.info message
        //Hard code push notification for now. 
        if (sendPush) {
     		sendPush(message)
     		}
      }else {
    log.info "Security Check Successful: No open doors detected." 
    sendNotificationEvent("Security Check Successful: No open doors detected.")
	}
    
 }