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: "timeIntervalInput", title: "Only during a certain time") {
29 input "starting", "time", title: "Starting", required: false
30 input "ending", "time", title: "Ending", required: false
33 section("Change to this mode") {
34 input "newMode", "mode", title: "Mode?"
37 page(name: "selectPhrases")
39 page( name:"Settings", title:"Settings", uninstall:true, install:true ) {
40 section("False alarm threshold (defaults to 10 min)") {
41 input "falseAlarmThreshold", "decimal", title: "Number of minutes", required: false
44 section("Zip code (for sunrise/sunset)") {
45 input "zip", "decimal", required: true
48 section("Notifications") {
49 input "sendPushMessage", "enum", title: "Send a push notification when house is empty?", metadata:[values:["Yes","No"]], required:false
50 input "sendPushMessageHome", "enum", title: "Send a push notification when home is occupied?", metadata:[values:["Yes","No"]], required:false
53 section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
54 label title: "Assign a name", required: false
55 input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
56 options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
57 input "modes", "mode", title: "Only when mode is", multiple: true, required: false
63 def configured = (settings.awayDay && settings.awayNight && settings.homeDay && settings.homeNight)
64 dynamicPage(name: "selectPhrases", title: "Configure", nextPage:"Settings", uninstall: true) {
66 input "people", "capability.presenceSensor", title: "Monitor These Presences", required: true, multiple: true, submitOnChange:true
69 def phrases = location.helloHome?.getPhrases()*.label
72 section("Run This Phrase When...") {
74 input "awayDay", "enum", title: "Everyone Is Away And It's Day", required: true, options: phrases, submitOnChange:true
75 input "awayNight", "enum", title: "Everyone Is Away And It's Night", required: true, options: phrases, submitOnChange:true
76 input "homeDay", "enum", title: "At Least One Person Is Home And It's Day", required: true, options: phrases, submitOnChange:true
77 input "homeNight", "enum", title: "At Least One Person Is Home And It's Night", required: true, options: phrases, submitOnChange:true
79 section("Select modes used for each condition. (Needed for better app logic)") {
80 input "homeModeDay", "mode", title: "Select Mode Used for 'Home Day'", required: true
81 input "homeModeNight", "mode", title: "Select Mode Used for 'Home Night'", required: true
98 subscribe(people, "presence", presence)
100 subscribe(location, "sunrise", setSunrise)
101 subscribe(location, "sunset", setSunset)
104 //check current sun state when installed.
106 def zip = settings.zip as String
107 def sunInfo = getSunriseAndSunset(zipCode: zip)
110 if (sunInfo.sunrise.time < current && sunInfo.sunset.time > current) {
111 state.sunMode = "sunrise"
116 state.sunMode = "sunset"
121 //change to sunrise mode on sunrise event
122 def setSunrise(evt) {
123 state.sunMode = "sunrise";
124 changeSunMode(newMode);
127 //change to sunset mode on sunset event
129 state.sunMode = "sunset";
130 changeSunMode(newMode)
133 //change mode on sun event
134 def changeSunMode(newMode) {
137 if(everyoneIsAway() && (state.sunMode == "sunrise")) {
138 log.info("Home is Empty Setting New Away Mode")
139 def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
140 runIn(delay, "setAway")
143 if(everyoneIsAway() && (state.sunMode == "sunset")) {
144 log.info("Home is Empty Setting New Away Mode")
145 def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
146 runIn(delay, "setAway")
150 log.info("Home is Occupied Setting New Home Mode")
158 //presence change run logic based on presence state of home
161 if(evt.value == "not present") {
162 log.debug("Checking if everyone is away")
164 if(everyoneIsAway()) {
165 log.info("Nobody is home, running away sequence")
166 def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
167 runIn(delay, "setAway")
172 def lastTime = state[evt.deviceId]
173 if (lastTime == null || now() - lastTime >= 1 * 60000) {
174 log.info("Someone is home, running home sequence")
177 state[evt.deviceId] = now()
183 //if empty set home to one of the away modes
185 if(everyoneIsAway()) {
186 if(state.sunMode == "sunset") {
187 def message = "Performing \"${awayNight}\" for you as requested."
190 location.helloHome.execute(settings.awayNight)
193 else if(state.sunMode == "sunrise") {
194 def message = "Performing \"${awayDay}\" for you as requested."
197 location.helloHome.execute(settings.awayDay)
200 log.debug("Mode is the same, not evaluating")
205 log.info("Somebody returned home before we set to '${newAwayMode}'")
209 //set home mode when house is occupied
211 sendOutOfDateNotification()
212 log.info("Setting Home Mode!!")
214 if(state.sunMode == "sunset"){
215 if (location.mode != "${homeModeNight}"){
216 def message = "Performing \"${homeNight}\" for you as requested."
219 location.helloHome.execute(settings.homeNight)
223 if(state.sunMode == "sunrise"){
224 if (location.mode != "${homeModeDay}"){
225 def message = "Performing \"${homeDay}\" for you as requested."
228 location.helloHome.execute(settings.homeDay)
235 private everyoneIsAway() {
238 if(people.findAll { it?.currentPresence == "present" }) {
242 log.debug("everyoneIsAway: ${result}")
247 private anyoneIsHome() {
250 if(people.findAll { it?.currentPresence == "present" }) {
254 log.debug("anyoneIsHome: ${result}")
260 if(sendPushMessage != "No") {
261 log.debug("Sending push message")
269 if(sendPushMessageHome != "No") {
270 log.debug("Sending push message")
278 modeOk && daysOk && timeOk
281 private getModeOk() {
282 def result = !modes || modes.contains(location.mode)
283 log.trace "modeOk = $result"
287 private getDaysOk() {
290 def df = new java.text.SimpleDateFormat("EEEE")
291 if (location.timeZone) {
292 df.setTimeZone(location.timeZone)
295 df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
297 def day = df.format(new Date())
298 result = days.contains(day)
300 log.trace "daysOk = $result"
304 private getTimeOk() {
306 if (starting && ending) {
308 def start = timeToday(starting, location?.timeZone).time
309 def stop = timeToday(ending, location?.timeZone).time
310 result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
312 log.trace "timeOk = $result"
316 private hhmm(time, fmt = "h:mm a")
318 def t = timeToday(time, location.timeZone)
319 def f = new java.text.SimpleDateFormat(fmt)
320 f.setTimeZone(location.timeZone ?: timeZone(time))
324 private getTimeIntervalLabel()
326 (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
329 private hideOptionsSection() {
330 (starting || ending || days || modes) ? false : true
333 def sendOutOfDateNotification(){
335 state.lastTime = (new Date() + 31).getTime()
336 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.")
338 else if (((new Date()).getTime()) >= state.lastTime){
339 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.")
340 state.lastTime = (new Date() + 31).getTime()