Update nfc-tag-toggle.groovy
[smartapps.git] / official / weather-windows.groovy
1 /**
2  *  Weather Windows
3  *      Compares two temperatures – indoor vs outdoor, for example – then sends an alert if windows are open (or closed!).
4  *
5  *  Copyright 2015 Eric Gideon
6  *
7  *      Based in part on the "When it's going to rain" SmartApp by the SmartThings team,
8  *  primarily the message throttling code.
9  *
10  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11  *  in compliance with the License. You may obtain a copy of the License at:
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
16  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
17  *  for the specific language governing permissions and limitations under the License.
18  *
19  */
20 definition(
21         name: "Weather Windows",
22         namespace: "egid",
23         author: "Eric Gideon",
24     category: "Convenience",
25         description: "Compares two temperatures – indoor vs outdoor, for example – then sends an alert if windows are open (or closed!). If you don't use an external temperature device, your zipcode will be used instead.",
26         iconUrl: "https://s3.amazonaws.com/smartthings-device-icons/Home/home9-icn.png",
27         iconX2Url: "https://s3.amazonaws.com/smartthings-device-icons/Home/home9-icn@2x.png"
28 )
29
30
31 preferences {
32         section( "Set the temperature range for your comfort zone..." ) {
33                 input "minTemp", "number", title: "Minimum temperature"
34                 input "maxTemp", "number", title: "Maximum temperature"
35         }
36         section( "Select windows to check..." ) {
37                 input "sensors", "capability.contactSensor", multiple: true
38         }
39         section( "Select temperature devices to monitor..." ) {
40                 input "inTemp", "capability.temperatureMeasurement", title: "Indoor"
41                 input "outTemp", "capability.temperatureMeasurement", title: "Outdoor (optional)", required: false
42         }
43         section( "Set your location" ) {
44                 input "zipCode", "text", title: "Zip code"
45         }
46         section( "Notifications" ) {
47                 input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes","No"]], required:false
48                 input "retryPeriod", "number", title: "Minutes between notifications:"
49         }
50 }
51
52
53 def installed() {
54         log.debug "Installed: $settings"
55         subscribe( inTemp, "temperature", temperatureHandler )
56 }
57
58 def updated() {
59         log.debug "Updated: $settings"
60         unsubscribe()
61         subscribe( inTemp, "temperature", temperatureHandler )
62 }
63
64
65 def temperatureHandler(evt) {
66         def currentOutTemp = null
67         if ( outTemp ) {
68                 currentOutTemp = outTemp.latestValue("temperature")
69         } else {
70                 log.debug "No external temperature device set. Checking WUnderground...."
71                 currentOutTemp = weatherCheck()
72         }
73
74         def currentInTemp = evt.doubleValue
75         def openWindows = sensors.findAll { it?.latestValue("contact") == 'open' }
76
77         log.trace "Temp event: $evt"
78         log.info "In: $currentInTemp; Out: $currentOutTemp"
79
80         // Don't spam notifications
81         // *TODO* use state.foo from Severe Weather Alert to do this better
82         def retryPeriodInMinutes = retryPeriod ?: 30
83         def timeAgo = new Date(now() - (1000 * 60 * retryPeriodInMinutes).toLong())
84         def recentEvents = inTemp.eventsSince(timeAgo)
85         log.trace "Found ${recentEvents?.size() ?: 0} events in the last $retryPeriodInMinutes minutes"
86
87         // Figure out if we should notify
88         if ( currentInTemp > minTemp && currentInTemp < maxTemp ) {
89                 log.info "In comfort zone: $currentInTemp is between $minTemp and $maxTemp."
90                 log.debug "No notifications sent."
91         } else if ( currentInTemp > maxTemp ) {
92                 // Too warm. Can we do anything?
93
94                 def alreadyNotified = recentEvents.count { it.doubleValue > currentOutTemp } > 1
95
96                 if ( !alreadyNotified ) {
97                         if ( currentOutTemp < maxTemp && !openWindows ) {
98                                 send( "Open some windows to cool down the house! Currently ${currentInTemp}°F inside and ${currentOutTemp}°F outside." )
99                         } else if ( currentOutTemp > maxTemp && openWindows ) {
100                                 send( "It's gotten warmer outside! You should close these windows: ${openWindows.join(', ')}. Currently ${currentInTemp}°F inside and ${currentOutTemp}°F outside." )
101                         } else {
102                                 log.debug "No notifications sent. Everything is in the right place."
103                         }
104                 } else {
105                         log.debug "Already notified! No notifications sent."
106                 }
107         } else if ( currentInTemp < minTemp ) {
108                 // Too cold! Is it warmer outside?
109
110                 def alreadyNotified = recentEvents.count { it.doubleValue < currentOutTemp } > 1
111
112                 if ( !alreadyNotified ) {
113                         if ( currentOutTemp > minTemp && !openWindows ) {
114                                 send( "Open some windows to warm up the house! Currently ${currentInTemp}°F inside and ${currentOutTemp}°F outside." )
115                         } else if ( currentOutTemp < minTemp && openWindows ) {
116                                 send( "It's gotten colder outside! You should close these windows: ${openWindows.join(', ')}. Currently ${currentInTemp}°F inside and ${currentOutTemp}°F outside." )
117                         } else {
118                                 log.debug "No notifications sent. Everything is in the right place."
119                         }
120                 } else {
121                         log.debug "Already notified! No notifications sent."
122                 }
123         }
124 }
125
126 def weatherCheck() {
127         def json = getWeatherFeature("conditions", zipCode)
128         def currentTemp = json?.current_observation?.temp_f
129
130         if ( currentTemp ) {
131         log.trace "Temp: $currentTemp (WeatherUnderground)"
132                 return currentTemp
133         } else {
134                 log.warn "Did not get a temp: $json"
135                 return false
136         }
137 }
138
139 private send(msg) {
140         if ( sendPushMessage != "No" ) {
141                 log.debug( "sending push message" )
142                 sendPush( msg )
143         sendEvent(linkText:app.label, descriptionText:msg, eventType:"SOLUTION_EVENT", displayed: true, name:"summary")
144         }
145
146         if ( phone1 ) {
147                 log.debug( "sending text message" )
148                 sendSms( phone1, msg )
149         }
150
151         log.info msg
152 }