4 * Copyright 2014 Tim Slagle
6 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 * in compliance with the License. You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 * for the specific language governing permissions and limitations under the License.
17 name: "Hello, Home Phrase Director",
18 namespace: "tslagle13",
20 description: "Monitor a set of presence sensors and activate Hello, Home phrases based on whether your home is empty or occupied. Each presence status change will check against the current 'sun state' to run phrases based on occupancy and whether the sun is up or down.",
21 category: "Convenience",
22 iconUrl: "http://icons.iconarchive.com/icons/icons8/ios7/512/Very-Basic-Home-Filled-icon.png",
23 iconX2Url: "http://icons.iconarchive.com/icons/icons8/ios7/512/Very-Basic-Home-Filled-icon.png"
27 page(name: "selectPhrases")
29 page( name:"Settings", title:"Settings", uninstall:true, install:true ) {
30 section("False alarm threshold (defaults to 10 min)") {
31 input "falseAlarmThreshold", "decimal", title: "Number of minutes", required: false
34 section("Zip code (for sunrise/sunset)") {
35 input "zip", "decimal", required: true
38 section("Notifications") {
39 input "sendPushMessage", "enum", title: "Send a push notification when house is empty?", metadata:[values:["Yes","No"]], required:false
40 input "sendPushMessageHome", "enum", title: "Send a push notification when home is occupied?", metadata:[values:["Yes","No"]], required:false
43 section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
44 label title: "Assign a name", required: false
45 input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
46 options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
47 input "modes", "mode", title: "Only when mode is", multiple: true, required: false
53 def configured = (settings.awayDay && settings.awayNight && settings.homeDay && settings.homeNight)
54 dynamicPage(name: "selectPhrases", title: "Configure", nextPage:"Settings", uninstall: true) {
56 input "people", "capability.presenceSensor", title: "Monitor These Presences", required: true, multiple: true, submitOnChange:true
59 def phrases = location.helloHome?.getPhrases()*.label
62 section("Run This Phrase When...") {
64 input "awayDay", "enum", title: "Everyone Is Away And It's Day", required: true, options: phrases, submitOnChange:true
65 input "awayNight", "enum", title: "Everyone Is Away And It's Night", required: true, options: phrases, submitOnChange:true
66 input "homeDay", "enum", title: "At Least One Person Is Home And It's Day", required: true, options: phrases, submitOnChange:true
67 input "homeNight", "enum", title: "At Least One Person Is Home And It's Night", required: true, options: phrases, submitOnChange:true
69 section("Select modes used for each condition. (Needed for better app logic)") {
70 input "homeModeDay", "mode", title: "Select Mode Used for 'Home Day'", required: true
71 input "homeModeNight", "mode", title: "Select Mode Used for 'Home Night'", required: true
88 subscribe(people, "presence", presence)
90 subscribe(location, "sunrise", setSunrise)
91 subscribe(location, "sunset", setSunset)
94 //check current sun state when installed.
96 def zip = settings.zip as String
97 def sunInfo = getSunriseAndSunset(zipCode: zip)
100 if (sunInfo.sunrise.time < current && sunInfo.sunset.time > current) {
101 state.sunMode = "sunrise"
106 state.sunMode = "sunset"
111 //change to sunrise mode on sunrise event
112 def setSunrise(evt) {
113 state.sunMode = "sunrise";
114 changeSunMode(newMode);
117 //change to sunset mode on sunset event
119 state.sunMode = "sunset";
120 changeSunMode(newMode)
123 //change mode on sun event
124 def changeSunMode(newMode) {
127 if(everyoneIsAway() && (state.sunMode == "sunrise")) {
128 log.info("Home is Empty Setting New Away Mode")
129 def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
130 runIn(delay, "setAway")
133 if(everyoneIsAway() && (state.sunMode == "sunset")) {
134 log.info("Home is Empty Setting New Away Mode")
135 def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
136 runIn(delay, "setAway")
140 log.info("Home is Occupied Setting New Home Mode")
148 //presence change run logic based on presence state of home
151 if(evt.value == "not present") {
152 log.debug("Checking if everyone is away")
154 if(everyoneIsAway()) {
155 log.info("Nobody is home, running away sequence")
156 def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
157 runIn(delay, "setAway")
162 def lastTime = state[evt.deviceId]
163 if (lastTime == null || now() - lastTime >= 1 * 60000) {
164 log.info("Someone is home, running home sequence")
167 state[evt.deviceId] = now()
173 //if empty set home to one of the away modes
175 if(everyoneIsAway()) {
176 if(state.sunMode == "sunset") {
177 def message = "Performing \"${awayNight}\" for you as requested."
180 location.helloHome.execute(settings.awayNight)
183 else if(state.sunMode == "sunrise") {
184 def message = "Performing \"${awayDay}\" for you as requested."
187 location.helloHome.execute(settings.awayDay)
190 log.debug("Mode is the same, not evaluating")
195 log.info("Somebody returned home before we set to '${newAwayMode}'")
199 //set home mode when house is occupied
201 sendOutOfDateNotification()
202 log.info("Setting Home Mode!!")
204 if(state.sunMode == "sunset"){
205 if (location.mode != "${homeModeNight}"){
206 def message = "Performing \"${homeNight}\" for you as requested."
209 location.helloHome.execute(settings.homeNight)
213 if(state.sunMode == "sunrise"){
214 if (location.mode != "${homeModeDay}"){
215 def message = "Performing \"${homeDay}\" for you as requested."
218 location.helloHome.execute(settings.homeDay)
225 private everyoneIsAway() {
228 if(people.findAll { it?.currentPresence == "present" }) {
232 log.debug("everyoneIsAway: ${result}")
237 private anyoneIsHome() {
240 if(people.findAll { it?.currentPresence == "present" }) {
244 log.debug("anyoneIsHome: ${result}")
250 if(sendPushMessage != "No") {
251 log.debug("Sending push message")
259 if(sendPushMessageHome != "No") {
260 log.debug("Sending push message")
268 modeOk && daysOk && timeOk
271 private getModeOk() {
272 def result = !modes || modes.contains(location.mode)
273 log.trace "modeOk = $result"
277 private getDaysOk() {
280 def df = new java.text.SimpleDateFormat("EEEE")
281 if (location.timeZone) {
282 df.setTimeZone(location.timeZone)
285 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
287 def day = df.format(new Date())
288 result = days.contains(day)
290 log.trace "daysOk = $result"
294 private getTimeOk() {
296 if (starting && ending) {
298 def start = timeToday(starting, location?.timeZone).time
299 def stop = timeToday(ending, location?.timeZone).time
300 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
302 log.trace "timeOk = $result"
306 private hhmm(time, fmt = "h:mm a")
308 def t = timeToday(time, location.timeZone)
309 def f = new java.text.SimpleDateFormat(fmt)
310 f.setTimeZone(location.timeZone ?: timeZone(time))
314 private getTimeIntervalLabel()
316 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
319 private hideOptionsSection() {
320 (starting || ending || days || modes) ? false : true
323 def sendOutOfDateNotification(){
325 state.lastTime = (new Date() + 31).getTime()
326 sendNotification("Your version of Hello, Home Phrase Director is currently out of date. Please look for the new version of Hello, Home Phrase Director now called 'Routine Director' in the marketplace.")
328 else if (((new Date()).getTime()) >= state.lastTime){
329 sendNotification("Your version of Hello, Home Phrase Director is currently out of date. Please look for the new version of Hello, Home Phrase Director now called 'Routine Director' in the marketplace.")
330 state.lastTime = (new Date() + 31).getTime()