Hysterisis functions


(Jim) #1

I’ve been trying to work out a mechanism to notify me if someone enters the house while the primary presence tags are all away. Unfortunately, I get random false negatives that are throwing things off.

My Hub device is about as close as I can get it to the cars (20-30 ft), so I don’t think it’s a range problem. I’m assuming that just occasionally randomness happens. Can I smooth those false negatives out in some way?


(Chrisb) #2

If I understand correctly, you’d saying that you are getting notifications that someone enters the house even when the primary presence tag is home. If this is correct, then I’m guessing this is due to a known problem with the tags “coming and going” randomly. In otherwords, the tag loses connection with the hub for a period of time, then regains connection. This can happen multiple times and happen rapidly.

Check out this thread for a discussion on this problem:
http://build.smartthings.com/forums/topic/presence-sensors-tags/

Now, what’s the solution? ST’s has talked about a firmware update coming that may fix it, but in the mean time, what to do?

Software-wise I’d point you to the Ridiculously Automated Garage Door by SmartThings.

Among the many things it does is watch for a presence tag to show up, and when it does, it opens the garage door. Obviously a false positive (tag “disappearing” and then coming back right away) here would be a bad thing so they wrote in a procedure to ensure that it only opens the garage if the tag returns after having been away AT LEAST 10 minutes.

I run a modified version of this program myself and it works perfectly. I’ve had a handful of incidences with the tag comes and going and it’s never opened my garage when it shouldn’t be.

You can find the program in the shared apps. Other-wise, here’s the code:

def doorOpenCheck()
{
	final thresholdMinutes = openThreshold
	if (thresholdMinutes) {
		def currentState = doorSensor.contactState
		log.debug "doorOpenCheck"
		if (currentState?.value == "open") {
			log.debug "open for ${now() - currentState.date.time}, openDoorNotificationSent: ${state.openDoorNotificationSent}"
			if (!state.openDoorNotificationSent && now() - currentState.date.time > thresholdMinutes * 60 *1000) {
				def msg = "${doorSwitch.displayName} was been open for ${thresholdMinutes} minutes"
				log.info msg
				sendPush msg
				if (phone) {
					sendSms phone, msg
				}
				state.openDoorNotificationSent = true
			}
		}
		else {
			state.openDoorNotificationSent = false
		}
	}
}

(Jim) #3

Yep; amusingly, that’s what I’ve built my script on already. What I was hoping was that there was some macro function that would “buffer” those out of the feed as noise. I could probably write a function to do that that I could re-use.

If anybody’s interested, here’s my “heavily modified” garage door script so far. I’ve optimized it to (a) tell me if the three of us have left the house and someone opens the front door, and to lock/unlock the front door as presence tags arrive. It’s that second part that’s a little dicey; people leaving and then it randomly deciding to unlock the front door might set the wife on edge.

/**
 *  Did you lock the door?
 *
 *  Author: jameslew@live.com
 *  Date: 2013-06-28
 *
 *  Borrowing heavily from Ridiculously Automated Garage Door
 */

preferences {

	section("Which doors?") {
		input "doorSensor", "capability.contactSensor", title: "Which doors?", multiple:true
		input "doorLock", "capability.lock", title: "Which switch?"
        input "openThreshold", "number", title: "Warn when open longer than (optional)",description: "Number of minutes", required: false
		input "phone", "phone", title: "Warn with text message (optional)", description: "Phone Number", required: false
	}
	section("Which people should I watch for?") {
		input "people", "capability.presenceSensor", title: "Presence sensor", description: "Which people(s)?", multiple: true, required: false
	}
	section("False alarm threshold (defaults to 10 min)") {
		input "falseAlarmThreshold", "number", title: "Number of minutes", required: false
        input "timeBetweenNotifications", "number", title: "Number of minutes", required: false
	}
}

def installed() {
	log.trace "installed()"
	subscribe()
}

def updated() {
	log.trace "updated()"
	unsubscribe()
	subscribe()
}

def subscribe() {
	log.debug "present: ${people.collect{it.displayName + ': ' + it.currentPresence}}"
	subscribe(doorSensor, "contact", anyDoorContact)
	subscribe(doorLock, "lock", frontDoorLock)
	subscribe(people, "presence", peoplePresence)
}

def doorOpenCheck()
{
	final thresholdMinutes = openThreshold
	if (thresholdMinutes) {
		def currentSensor
		log.debug "doorOpenCheck started"
		
		def someoneIsPresent = false
        def result = true // all doors are closed
		        
		for (it in (doorSensor ?: [])) {
            if (it.latestValue("contact") == "open") {
            	currentSensor = it
                result = false
                log.debug "${it.displayName} was open"
				break
            }
        }

		for (it in (people ?: [])) {
            if (it.latestValue("presence") == "present") {
            	log.debug "present: ${people.collect{it.displayName + ': ' + it.currentPresence}}"
    			someoneIsPresent = true
                break
            }
         }
	
		if (result == false && someOneIsPresent == false) {
			if (!state.openDoorNotificationSent && now() - state.timeDoorOpened > thresholdMinutes * 60 *1000 && now() - state.lastNotificationTime > timeBetweenNotifications * 60 * 1000) {
				log.debug "completed time and condition test."
                def msg = "${currentSensor.displayName} has been open for ${thresholdMinutes} minutes"
				log.info msg
				sendPush msg
				if (phone) {
					sendSms phone, msg
				}
				state.openDoorNotificationSent = true
                state.lastNotificationTime = now()
	            log.debug "notification sent"
			}
        }
		else {
		state.openDoorNotificationSent = false
        log.debug "Door Opened: ${result}, Someone Is Present: ${someoneIsPresent}, no notification sent"
		}
	}
}

def peoplePresence(evt)
{
	log.info "$evt.name: $evt.value"
	// time in which there must be no "not present" events in order to open the door
	final openDoorAwayInterval = falseAlarmThreshold ? falseAlarmThreshold * 60 : 600

	if (evt.value == "present") {
		// Someone comes home

		def person = getPerson(evt)
		def t0 = new Date(now() - (openDoorAwayInterval * 1000))
		def states = person.statesSince("presence", t0)
		def recentNotPresentState = states.find{it.value == "not present"}

		if (recentNotPresentState) {
			log.debug "Not unlocking ${doorLock.displayName} since person was not present at ${recentNotPresentState.date}, less than ${openDoorAwayInterval} sec ago"
		}
		else {
			if (doorLock.currentValue("lock") == "locked") {
				unlockDoor()
				sendPush "Unlocking front door due to arrival of ${person.displayName}"
				state.appOpenedDoor = now()
			}
			else {
				log.debug "A house door is already open"
			}
		}
	}
	else {
		// The last person departs
        
        def someoneIsPresent = false
        for (it in (people ?: [])) {
            if (it.latestValue("presence") == "present") {
            	log.debug "present: ${people.collect{it.displayName + ': ' + it.currentPresence}}"
    			someoneIsPresent = true
                break
            }
         }
        
		if (doorLock.latestValue("lock") == "unlocked" && someoneIsPresent == false) {
			lockDoor()
			log.debug "Closing ${doorLock.displayName} after departure"
			sendPush("Closing ${doorLock.displayName} after departure")
		}
		else {
			log.debug "Not locking ${doorLock.displayName} because its already locked or someone is still home"
		}
	}
}

def anyDoorContact(evt)
{
    log.info "anyDoorContact, $evt.name: $evt.value"
	if (evt.value == "open") {
    	if( state.timeDoorOpened == 0 ){
        	state.timeDoorOpened = evt.date.time
        }
    	log.info "scheduling doorOpenCheck at ${state.timeDoorOpened}"
		schedule("0 * * * * ?", "doorOpenCheck")
	}
	else {
    	state.timeDoorOpened = 0
    	log.info "unscheduling doorOpenCheck and resetting notification"
        state.openDoorNotificationSent = false
        unschedule("doorOpenCheck")
	}
}

def interiorDoorClosed(evt)
{
	log.info "interiorContact, $evt.name: $evt.value"

	// time during which closing the interior door will shut the garage door, if the app opened it
	final threshold = 15 * 60 * 1000
	if (state.appOpenedDoor && now() - state.appOpenedDoor < threshold) {
		state.appOpenedDoor = 0
		closeDoor()
	}
	else {
		log.debug "app didn't open door"
	}
}

def accelerationActive(evt)
{
	log.info "$evt.name: $evt.value"

	if (doorSensor.currentContact == "closed") {
		log.debug "opening door when car door opened"
		openDoor()
	}
}

private unlockDoor()
{
	if (doorLock.currentValue("lock") == "locked") {
		log.debug "unlocking door"
		doorLock.push()
	}
}

private lockDoor()
{
	if (doorLock.currentValue("lock") == "unlocked") {
		log.debug "locking door"
		doorLock.push()
	}
}

private getPerson(evt)
{
	people.find{it.id == evt.deviceId}
}

(Chrisb) #4
If anybody’s interested, here’s my “heavily modified” garage door script so far. I’ve optimized it to (a) tell me if the three of us have left the house and someone opens the front door, and to lock/unlock the front door as presence tags arrive. It’s that second part that’s a little dicey; people leaving and then it randomly deciding to unlock the front door might set the wife on edge.

I’m using a self modified version of the program as well. I’m unlocking my side door.

As far as I know the problem is only that when a tag is present it some times will “wander away” and then show up a few minutes later. If you tuck the “door unlock” procedure in the same area that opens the garage door, then you shouldn’t get any false unlocks.

For me I have my lock automatically set to relock after 30 seconds (function built into Kwikset, not a SmartThings function) so I’m not as worried about accidental unlocks.


(Zach Naimon) #5

Jim -

We have some known false-negative issues with the SmartPresence tags. Here’s from our support troubleshooting documentation:

Troubleshooting steps:

  1. Plug in a SmartSense Motion or a SmartPower Outlet. Doing so will put the devices into repeating mode, and will effectively extend the range and dependability of your network.

  2. Make sure your SmartThings Hub is at least two feet away from your WiFi router (not directly next to the router itself).

  3. Change the your WiFi router to channel 11 or 1 via your router’s administration page. If you need assistance with this, please contact support@smartthings.com with the model of your router.