Update hue-mood-lighting.groovy
[smartapps.git] / official / ready-for-rain.groovy
1 /**
2  *  Ready for Rain
3  *
4  *  Author: brian@bevey.org
5  *  Date: 9/10/13
6  *
7  *  Warn if doors or windows are open when inclement weather is approaching.
8  */
9
10 definition(
11   name: "Ready For Rain",
12   namespace: "imbrianj",
13   author: "brian@bevey.org",
14   description: "Warn if doors or windows are open when inclement weather is approaching.",
15   category: "Convenience",
16   iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
17   iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png"
18 )
19
20 preferences {
21
22   if (!(location.zipCode || ( location.latitude && location.longitude )) && location.channelName == 'samsungtv') {
23                 section { paragraph title: "Note:", "Location is required for this SmartApp. Go to 'Location Name' settings to setup your correct location." }
24         }
25
26   if (location.channelName != 'samsungtv') {
27                 section( "Set your location" ) { input "zipCode", "text", title: "Zip code" }
28   }
29
30   section("Things to check?") {
31     input "sensors", "capability.contactSensor", multiple: true
32   }
33
34   section("Notifications?") {
35     input "sendPushMessage", "enum", title: "Send a push notification?", metadata: [values: ["Yes", "No"]], required: false
36     input "phone", "phone", title: "Send a Text Message?", required: false
37   }
38
39   section("Message interval?") {
40     input name: "messageDelay", type: "number", title: "Minutes (default to every message)", required: false
41   }
42 }
43
44 def installed() {
45   init()
46 }
47
48 def updated() {
49   unsubscribe()
50   unschedule()
51   init()
52 }
53
54 def init() {
55   state.lastMessage = 0
56   state.lastCheck = ["time": 0, "result": false]
57   schedule("0 0,30 * * * ?", scheduleCheck) // Check at top and half-past of every hour
58   subscribe(sensors, "contact.open", scheduleCheck)
59 }
60
61 def scheduleCheck(evt) {
62   def open = sensors.findAll { it?.latestValue("contact") == "open" }
63   def plural = open.size() > 1 ? "are" : "is"
64
65   // Only need to poll if we haven't checked in a while - and if something is left open.
66   if((now() - (30 * 60 * 1000) > state.lastCheck["time"]) && open) {
67     log.info("Something's open - let's check the weather.")
68     def response
69     if (location.channelName != 'samsungtv')
70       response = getWeatherFeature("forecast", zipCode)
71     else
72       response = getWeatherFeature("forecast")
73     def weather  = isStormy(response)
74
75     if(weather) {
76       send("${open.join(', ')} ${plural} open and ${weather} coming.")
77     }
78   }
79
80   else if(((now() - (30 * 60 * 1000) <= state.lastCheck["time"]) && state.lastCheck["result"]) && open) {
81     log.info("We have fresh weather data, no need to poll.")
82     send("${open.join(', ')} ${plural} open and ${state.lastCheck["result"]} coming.")
83   }
84
85   else {
86     log.info("Everything looks closed, no reason to check weather.")
87   }
88 }
89
90 private send(msg) {
91   def delay = (messageDelay != null && messageDelay != "") ? messageDelay * 60 * 1000 : 0
92
93   if(now() - delay > state.lastMessage) {
94     state.lastMessage = now()
95     if(sendPushMessage == "Yes") {
96       log.debug("Sending push message.")
97       sendPush(msg)
98     }
99
100     if(phone) {
101       log.debug("Sending text message.")
102       sendSms(phone, msg)
103     }
104
105     log.debug(msg)
106   }
107
108   else {
109     log.info("Have a message to send, but user requested to not get it.")
110   }
111 }
112
113 private isStormy(json) {
114   def types    = ["rain", "snow", "showers", "sprinkles", "precipitation"]
115   def forecast = json?.forecast?.txt_forecast?.forecastday?.first()
116   def result   = false
117
118   if(forecast) {
119     def text = forecast?.fcttext?.toLowerCase()
120
121     log.debug(text)
122
123     if(text) {
124       for (int i = 0; i < types.size() && !result; i++) {
125         if(text.contains(types[i])) {
126           result = types[i]
127         }
128       }
129     }
130
131     else {
132       log.warn("Got forecast, couldn't parse.")
133     }
134   }
135
136   else {
137     log.warn("Did not get a forecast: ${json}")
138   }
139
140   state.lastCheck = ["time": now(), "result": result]
141
142   return result
143 }