Hello Home Advanced Rules

I’m very much appreciative to @tslagle13 and his “Magic Home” SmartApp, but we really need a way to build advanced automatic logic rules for Hello Home.

Please excuse me if this is confusing… I’ve been confused by this for quite a while.

For example…

I have four Hello Home phrases linked to four modes. ‘Home-Sunrise’, ‘Home-Sunset’, ‘Away-Sunrise’, and ‘Away-Sunset’ that all perform different actions. There is no way to tell ST to automatically run a phrase in two different scenarios. For example, if I’m in either of the ‘home’ modes, and I leave, there is no way to tell the system to run the phrase automatically based on a time of day (sunrise/sunset) AND/OR based upon everyone being away. So, if I configure away-sunrise to run when everyone leaves, and if I configure away-sunset to run when everyone leaves, the system doesn’t know which is the correct phrase because I can’t specify an and/or operator.

It appears the automatic logic is based upon the ‘or’ operator.

Basically, I would need this:

-Automatically change to ‘away-sunset’ if everyone leaves AND it’s sunset, OR automatically change to ‘away-sunset’ at sunset IF I’m in ‘away-sunrise’ mode.
-Automatically change to ‘home-sunset’ if at least one person is home AND it’s sunset, OR automatically change to ‘home-sunset’ at sunset IF I’m in ‘home-sunrise’ mode.
-Automatically change to ‘away-sunrise’ if everyone leaves AND it’s sunrise.
-Automatically change to ‘home-sunrise’ if at least one person is home AND it’s sunrise, OR automatically change to ‘home-sunrise’ at sunrise IF I’m in ‘night’ mode.

Surely, I’m not the only one who would put advanced logic to use.

3 Likes

A rule builde has been in development for quite some time. These types of Boolean logic rules will come when/if they release the rule builder

1 Like

I too would like to see more advanced capabilities with Hello Home. To minimize confusion and problems with events triggering based on modes, I’ve reduced my home to just two: Home and Away. I would like an if/then/elseif style of rule building using modes. I’ll be looking forward to see if that capability is added in the future.

3 Likes

Maybe my “Magic Home” app can help? Allows for 4 phrases to be run depending on the state of the sun (sunrise/sunset) and the state of your home (empty/occupied)

/**
 *  Auto Home/Away with night modes
 *
 *  Copyright 2014 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: "Magic Home",
    namespace: "tslagle13",
    author: "Tim Slagle",
    description: "Monitor a set of presence sensors and change mode based on when your home is empty or occupied.  Included Night and Day modes for both an occupied and unoccupied house.",
    category: "Convenience",
    iconUrl: "http://icons.iconarchive.com/icons/icons8/ios7/512/Very-Basic-Home-Filled-icon.png",
    iconX2Url: "http://icons.iconarchive.com/icons/icons8/ios7/512/Very-Basic-Home-Filled-icon.png"
)

preferences {
  page(name: "selectPhrases")
    
  page( name:"Settings", title:"Settings", uninstall:true, install:true ) {
  	section("False alarm threshold (defaults to 10 min)") {
    	input "falseAlarmThreshold", "decimal", title: "Number of minutes", required: false
  	}

  	section("Zip code (for sunrise/sunset)") {
   		input "zip", "decimal", required: true
  	}

      section("Notifications") {
        input "sendPushMessage", "enum", title: "Send a push notification when house is empty?", metadata:[values:["Yes","No"]], required:false
        input "sendPushMessageHome", "enum", title: "Send a push notification when home is occupied?", metadata:[values:["Yes","No"]], required:false
  	}

    section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
    		label title: "Assign a name", required: false
			input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
				options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
			input "modes", "mode", title: "Only when mode is", multiple: true, required: false
		}
  }
}

def selectPhrases() {
	def configured = (settings.awayDay && settings.awayNight && settings.homeDay && settings.homeNight)
    dynamicPage(name: "selectPhrases", title: "Configure", nextPage:"Settings", uninstall: true) {		
		section("Who?") {
			input "people", "capability.presenceSensor", title: "Monitor These Presences", required: true, multiple: true,  refreshAfterSelection:true
		}
        
		def phrases = location.helloHome?.getPhrases()*.label
		if (phrases) {
        	phrases.sort()
			section("Run This Phrase When...") {
				log.trace phrases
				input "awayDay", "enum", title: "Everyone Is Away And It's Day", required: true, options: phrases,  refreshAfterSelection:true
				input "awayNight", "enum", title: "Everyone Is Away And It's Night", required: true, options: phrases,  refreshAfterSelection:true
                input "homeDay", "enum", title: "At Least One Person Is Home And It's Day", required: true, options: phrases,  refreshAfterSelection:true
                input "homeNight", "enum", title: "At Least One Person Is Home And It's Night", required: true, options: phrases,  refreshAfterSelection:true
			}
            section("Select modes used for each condition. (Needed for better app logic)") {
        input "homeModeDay", "mode", title: "Select Mode Used for 'Home Day'", required: true
        input "homeModeNight", "mode", title: "Select Mode Used for 'Home Night'", required: true
  	}
		}
    }
}

def installed() {
  init()
  initialize()

}

def updated() {
  unsubscribe()
  initialize()

  init()
}

def init() {
  subscribe(people, "presence", presence)
}

def uninstalled() {
unsubscribe()
}

def initialize() {
    runIn(60, checkSun)
	subscribe(location, "sunrise", setSunrise)
	subscribe(location, "sunset", setSunset)
}

def checkSun() {
  def zip     = settings.zip as String
  def sunInfo = getSunriseAndSunset(zipCode: zip)
 def current = now()

if (sunInfo.sunrise.time < current && sunInfo.sunset.time > current) {
    state.sunMode = "sunrise"
   setSunrise()
  }
  
else {
   state.sunMode = "sunset"
    setSunset()
  }

log.info("Sunset: ${sunInfo.sunset.time}")
 log.info("Sunrise: ${sunInfo.sunrise.time}")
 log.info("Current: ${current}")
log.info("sunMode: ${state.sunMode}")

 if(current < sunInfo.sunrise.time) {
  runIn(((sunInfo.sunrise.time - current) / 1000).toInteger(), setSunrise)
  }

  if(current < sunInfo.sunset.time) {
  runIn(((sunInfo.sunset.time - current) / 1000).toInteger(), setSunset)
 }
}

def setSunrise(evt) {
  state.sunMode = "sunrise";
  changeSunMode(newMode);
  sendNotificationEvent("Setting Sunrise Mode")
}

def setSunset(evt) {
  state.sunMode = "sunset";
  changeSunMode(newMode)
  sendNotificationEvent("Setting Sunset Mode")
}


def changeSunMode(newMode) {
  if(allOk) {

  if(everyoneIsAway() && (state.sunMode == "sunrise")) {
    log.info("Home is Empty  Setting New Away Mode")
    def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60 
    runIn(delay, "setAway")
  }

  if(everyoneIsAway() && (state.sunMode == "sunset")) {
    log.info("Home is Empty  Setting New Away Mode")
    def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60 
    runIn(delay, "setAway")
  }
  
  else {
  log.info("Home is Occupied Setting New Home Mode")
  setHome()


  }
}
}

def presence(evt) {
  if(allOk) {
  if(evt.value == "not present") {
    log.debug("Checking if everyone is away")

    if(everyoneIsAway()) {
      log.info("Nobody is home, running away sequence")
      def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60 
      runIn(delay, "setAway")
    }
  }

else {
	def lastTime = state[evt.deviceId]
    if (lastTime == null || now() - lastTime >= 1 * 60000) {
  		log.info("Someone is home, running home sequence")
  		setHome()
    }    
	state[evt.deviceId] = now()

  }
}
}

def setAway() {
  if(everyoneIsAway()) {
    if(state.sunMode == "sunset") {
      def message = "Performing \"${awayNight}\" for you as requested."
      log.info(message)
      sendAway(message)
      location.helloHome.execute(settings.awayNight)
    }
    
    else if(state.sunMode == "sunrise") {
      def message = "Performing \"${awayDay}\" for you as requested."
      log.info(message)
      sendAway(message)
      location.helloHome.execute(settings.awayDay)
      }
    else {
      log.debug("Mode is the same, not evaluating")
    }
  }

  else {
    log.info("Somebody returned home before we set to '${newAwayMode}'")
  }
}

def setHome() {

log.info("Setting Home Mode!!")
if(anyoneIsHome()) {
      if(state.sunMode == "sunset"){
      if (location.mode != "${homeModeNight}"){
      def message = "Performing \"${homeNight}\" for you as requested."
        log.info(message)
        sendHome(message)
        location.helloHome.execute(settings.homeNight)
        }
       }
       
      if(state.sunMode == "sunrise"){
      if (location.mode != "${homeModeDay}"){
      def message = "Performing \"${homeDay}\" for you as requested."
        log.info(message)
        sendHome(message)
        location.helloHome.execute(settings.homeDay)
            }
      }      
    }
    
}

private everyoneIsAway() {
  def result = true

  if(people.findAll { it?.currentPresence == "present" }) {
    result = false
  }

  log.debug("everyoneIsAway: ${result}")

  return result
}

private anyoneIsHome() {
  def result = false

  if(people.findAll { it?.currentPresence == "present" }) {
    result = true
  }

  log.debug("anyoneIsHome: ${result}")

  return result
}

def sendAway(msg) {
  if(sendPushMessage != "No") {
    log.debug("Sending push message")
    sendPush(msg)
  }

  log.debug(msg)
}

def sendHome(msg) {
  if(sendPushMessageHome != "No") {
    log.debug("Sending push message")
    sendPush(msg)
  }

  log.debug(msg)
}

private getAllOk() {
	modeOk && daysOk && timeOk
}

private getModeOk() {
	def result = !modes || modes.contains(location.mode)
	log.trace "modeOk = $result"
	result
}

private getDaysOk() {
	def result = true
	if (days) {
		def df = new java.text.SimpleDateFormat("EEEE")
		if (location.timeZone) {
			df.setTimeZone(location.timeZone)
		}
		else {
			df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
		}
		def day = df.format(new Date())
		result = days.contains(day)
	}
	log.trace "daysOk = $result"
	result
}

private getTimeOk() {
	def result = true
	if (starting && ending) {
		def currTime = now()
		def start = timeToday(starting).time
		def stop = timeToday(ending).time
		result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
	}
	log.trace "timeOk = $result"
	result
}

private hhmm(time, fmt = "h:mm a")
{
	def t = timeToday(time, location.timeZone)
	def f = new java.text.SimpleDateFormat(fmt)
	f.setTimeZone(location.timeZone ?: timeZone(time))
	f.format(t)
}

private getTimeIntervalLabel()
{
	(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
}

private hideOptionsSection() {
	(starting || ending || days || modes) ? false : true
}
2 Likes

Magic Home is great… now what I really need is something similar based on state of home and motion (as a back up for faulty presence sensors or guests)

That’s awesome. Anyone know when that will be available?

Check out Rule Machine.

1 Like