Created
August 20, 2013 21:29
-
-
Save danlieberman/6287581 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* SmartWeather Station | |
* | |
* Author: SmartThings | |
* | |
* Date: 2013-04-30 | |
*/ | |
// for the UI | |
metadata { | |
preferences { | |
input "zipCode", "text", title: "Zip Code (optional)", required: false | |
} | |
tiles { | |
valueTile("temperature", "device.temperature") { | |
state "default", label:'${currentValue}°', unit:"F", | |
backgroundColors:[ | |
[value: 31, color: "#153591"], | |
[value: 44, color: "#1e9cbb"], | |
[value: 59, color: "#90d2a7"], | |
[value: 74, color: "#44b621"], | |
[value: 84, color: "#f1d801"], | |
[value: 95, color: "#d04e00"], | |
[value: 96, color: "#bc2323"] | |
] | |
} | |
valueTile("humidity", "device.humidity", decoration: "flat") { | |
state "default", label:'${currentValue}% humidity' | |
} | |
standardTile("weatherIcon", "device.weatherIcon", decoration: "flat") { | |
state "chanceflurries", icon:"st.custom.wu1.chanceflurries", label: "" | |
state "chancerain", icon:"st.custom.wu1.chancerain", label: "" | |
state "chancesleet", icon:"st.custom.wu1.chancesleet", label: "" | |
state "chancesnow", icon:"st.custom.wu1.chancesnow", label: "" | |
state "chancetstorms", icon:"st.custom.wu1.chancetstorms", label: "" | |
state "clear", icon:"st.custom.wu1.clear", label: "" | |
state "cloudy", icon:"st.custom.wu1.cloudy", label: "" | |
state "flurries", icon:"st.custom.wu1.flurries", label: "" | |
state "fog", icon:"st.custom.wu1.fog", label: "" | |
state "hazy", icon:"st.custom.wu1.hazy", label: "" | |
state "mostlycloudy", icon:"st.custom.wu1.mostlycloudy", label: "" | |
state "mostlysunny", icon:"st.custom.wu1.mostlysunny", label: "" | |
state "partlycloudy", icon:"st.custom.wu1.partlycloudy", label: "" | |
state "partlysunny", icon:"st.custom.wu1.partlysunny", label: "" | |
state "rain", icon:"st.custom.wu1.rain", label: "" | |
state "sleet", icon:"st.custom.wu1.sleet", label: "" | |
state "snow", icon:"st.custom.wu1.snow", label: "" | |
state "sunny", icon:"st.custom.wu1.sunny", label: "" | |
state "tstorms", icon:"st.custom.wu1.tstorms", label: "" | |
state "cloudy", icon:"st.custom.wu1.cloudy", label: "" | |
state "partlycloudy", icon:"st.custom.wu1.partlycloudy", label: "" | |
state "nt_chanceflurries", icon:"st.custom.wu1.nt_chanceflurries", label: "" | |
state "nt_chancerain", icon:"st.custom.wu1.nt_chancerain", label: "" | |
state "nt_chancesleet", icon:"st.custom.wu1.nt_chancesleet", label: "" | |
state "nt_chancesnow", icon:"st.custom.wu1.nt_chancesnow", label: "" | |
state "nt_chancetstorms", icon:"st.custom.wu1.nt_chancetstorms", label: "" | |
state "nt_clear", icon:"st.custom.wu1.nt_clear", label: "" | |
state "nt_cloudy", icon:"st.custom.wu1.nt_cloudy", label: "" | |
state "nt_flurries", icon:"st.custom.wu1.nt_flurries", label: "" | |
state "nt_fog", icon:"st.custom.wu1.nt_fog", label: "" | |
state "nt_hazy", icon:"st.custom.wu1.nt_hazy", label: "" | |
state "nt_mostlycloudy", icon:"st.custom.wu1.nt_mostlycloudy", label: "" | |
state "nt_mostlysunny", icon:"st.custom.wu1.nt_mostlysunny", label: "" | |
state "nt_partlycloudy", icon:"st.custom.wu1.nt_partlycloudy", label: "" | |
state "nt_partlysunny", icon:"st.custom.wu1.nt_partlysunny", label: "" | |
state "nt_sleet", icon:"st.custom.wu1.nt_sleet", label: "" | |
state "nt_rain", icon:"st.custom.wu1.nt_rain", label: "" | |
state "nt_sleet", icon:"st.custom.wu1.nt_sleet", label: "" | |
state "nt_snow", icon:"st.custom.wu1.nt_snow", label: "" | |
state "nt_sunny", icon:"st.custom.wu1.nt_sunny", label: "" | |
state "nt_tstorms", icon:"st.custom.wu1.nt_tstorms", label: "" | |
state "nt_cloudy", icon:"st.custom.wu1.nt_cloudy", label: "" | |
state "nt_partlycloudy", icon:"st.custom.wu1.nt_partlycloudy", label: "" | |
} | |
valueTile("feelsLike", "device.feelsLike", decoration: "flat") { | |
state "default", label:'feels like ${currentValue}°' | |
} | |
valueTile("wind", "device.wind", decoration: "flat") { | |
state "default", label:'wind ${currentValue} mph' | |
} | |
valueTile("weather", "device.weather", decoration: "flat") { | |
state "default", label:'${currentValue}' | |
} | |
valueTile("city", "device.city", decoration: "flat") { | |
state "default", label:'${currentValue}' | |
} | |
valueTile("percentPrecip", "device.percentPrecip", decoration: "flat") { | |
state "default", label:'${currentValue}% precip' | |
} | |
standardTile("refresh", "device.weather", decoration: "flat") { | |
state "default", label: "", action: "refresh", icon:"st.secondary.refresh" | |
} | |
valueTile("alert", "device.alert", width: 3, height: 1, decoration: "flat") { | |
state "default", label:'${currentValue}' | |
} | |
valueTile("rise", "device.localSunrise", decoration: "flat") { | |
state "default", label:'${currentValue}' | |
} | |
valueTile("set", "device.localSunset", decoration: "flat") { | |
state "default", label:'${currentValue}' | |
} | |
valueTile("light", "device.illuminance", decoration: "flat") { | |
state "default", label:'${currentValue} lux' | |
} | |
main(["temperature", "weatherIcon","feelsLike"]) | |
details(["temperature", "humidity", "weatherIcon","feelsLike","wind","weather", "city","percentPrecip", "refresh","alert","rise","set","light"])} | |
} | |
// parse events into attributes | |
def parse(String description) { | |
log.debug "Parsing '${description}'" | |
} | |
// handle commands | |
def poll() { | |
log.debug "Executing 'poll', location: ${location.name}" | |
// Current conditions | |
def obs = get("conditions")?.current_observation | |
if (obs) { | |
def weatherIcon = obs.icon_url.split("/")[-1].split("\\.")[0] | |
send(name: "temperature", value: Math.round(obs.temp_f), unit: "F") | |
send(name: "humidity", value: obs.relative_humidity[0..-2] as Integer, unit: "%") | |
send(name: "weather", value: obs.weather) | |
send(name: "weatherIcon", value: weatherIcon, displayed: false) | |
send(name: "wind", value: Math.round(obs.wind_mph) as String, unit: "MPH") // as String because of bug in determining state change of 0 numbers | |
send(name: "feelsLike", value: Math.round(obs.feelslike_f as Double), unit: "F") | |
if (obs.local_tz_offset != device.currentValue("timeZoneOffset")) { | |
send(name: "timeZoneOffset", value: obs.local_tz_offset, isStateChange: true) | |
} | |
def cityValue = "${obs.display_location.city}, ${obs.display_location.state}" | |
if (cityValue != device.currentValue("city")) { | |
send(name: "city", value: cityValue, isStateChange: true) | |
} | |
// Sunrise / sunset | |
def a = get("astronomy")?.moon_phase | |
def today = localDate("GMT${obs.local_tz_offset}") | |
def ltf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm") | |
ltf.setTimeZone(TimeZone.getTimeZone("GMT${obs.local_tz_offset}")) | |
def utf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") | |
utf.setTimeZone(TimeZone.getTimeZone("GMT")) | |
def sunriseDate = ltf.parse("${today} ${a.sunrise.hour}:${a.sunrise.minute}") | |
def sunsetDate = ltf.parse("${today} ${a.sunset.hour}:${a.sunset.minute}") | |
def sunrise = utf.format(sunriseDate) | |
def sunset = utf.format(sunsetDate) | |
if (sunrise != device.currentValue("sunrise")) { | |
send(name: "sunrise", value: sunrise, displayed: false) | |
send(name: "sunset", value: sunset, displayed: false) | |
def tf = new java.text.SimpleDateFormat("h:mm a") | |
tf.setTimeZone(TimeZone.getTimeZone("GMT${obs.local_tz_offset}")) | |
def localSunrise = "${tf.format(sunriseDate)}" | |
def localSunset = "${tf.format(sunsetDate)}" | |
send(name: "localSunrise", value: localSunrise, descriptionText: "Sunrise today is at $localSunrise", isStateChange: true) | |
send(name: "localSunset", value: localSunset, descriptionText: "Sunset today at is $localSunset", isStateChange: true) | |
} | |
send(name: "illuminance", value: estimateLux(sunriseDate, sunsetDate, weatherIcon)) | |
// Forecast | |
def f = get("forecast") | |
def f1= f?.forecast?.simpleforecast?.forecastday | |
if (f1) { | |
def icon = f1[0].icon_url.split("/")[-1].split("\\.")[0] | |
def value = f1[0].pop as String // as String because of bug in determining state change of 0 numbers | |
send(name: "percentPrecip", value: value, unit: "%") | |
send(name: "forecastIcon", value: icon, displayed: false) | |
} | |
else { | |
log.warn "Forecast not found" | |
} | |
// Alerts | |
def alerts = get("alerts")?.alerts | |
def newKeys = alerts?.collect{it.type + it.date_epoch} ?: [] | |
log.debug "WUSTATION: newKeys: $newKeys" | |
log.trace device.currentState("alertKeys") | |
def oldKeys = device.currentState("alertKeys")?.jsonValue | |
log.debug "WUSTATION: oldKeys: $oldKeys" | |
def noneString = "no current weather alerts" | |
if (!newKeys && oldKeys == null) { | |
send(name: "alertKeys", value: newKeys.encodeAsJSON(), displayed: false) | |
send(name: "alert", value: noneString, descriptionText: "${device.displayName} has no current weather alerts", isStateChange: true) | |
} | |
else if (newKeys != oldKeys) { | |
if (oldKeys == null) { | |
oldKeys = [] | |
} | |
send(name: "alertKeys", value: newKeys.encodeAsJSON(), displayed: false) | |
def newAlerts = false | |
alerts.each {alert -> | |
if (!oldKeys.contains(alert.type + alert.date_epoch)) { | |
def msg = "${alert.description} from ${alert.date} until ${alert.expires}" | |
send(name: "alert", value: pad(alert.description), descriptionText: msg, isStateChange: true) | |
newAlerts = true | |
} | |
} | |
if (!newAlerts && device.currentValue("alert") != noneString) { | |
send(name: "alert", value: noneString, descriptionText: "${device.displayName} has no current weather alerts", isStateChange: true) | |
} | |
} | |
} | |
else { | |
log.warn "No response from Weather Underground API" | |
} | |
} | |
def refresh() { | |
poll() | |
} | |
def configure() { | |
poll() | |
} | |
private pad(String s, size = 25) { | |
def n = (size - s.size()) / 2 | |
if (n > 0) { | |
def sb = "" | |
n.times {sb += " "} | |
sb += s | |
n.times {sb += " "} | |
return sb | |
} | |
else { | |
return s | |
} | |
} | |
private get(feature) { | |
getWeatherFeature(feature, zipCode) | |
} | |
private localDate(timeZone) { | |
def df = new java.text.SimpleDateFormat("yyyy-MM-dd") | |
df.setTimeZone(TimeZone.getTimeZone(timeZone)) | |
df.format(new Date()) | |
} | |
private send(map) { | |
log.debug "WUSTATION: event: $map" | |
sendEvent(map) | |
} | |
private estimateLux(sunriseDate, sunsetDate, weatherIcon) { | |
def lux = 0 | |
def now = new Date().time | |
if (now > sunriseDate.time && now < sunsetDate.time) { | |
//day | |
switch(weatherIcon) { | |
case 'tstorms': | |
lux = 200 | |
break | |
case ['cloudy', 'fog', 'rain', 'sleet', 'snow', 'flurries', | |
'chanceflurries', 'chancerain', 'chancesleet', | |
'chancesnow', 'chancetstorms']: | |
lux = 1000 | |
break | |
case 'mostlycloudy': | |
lux = 2500 | |
break | |
case ['partlysunny', 'partlycloudy', 'hazy']: | |
lux = 7500 | |
break | |
default: | |
//sunny, clear | |
lux = 10000 | |
} | |
//adjust for dusk/dawn | |
def afterSunrise = now - sunriseDate.time | |
def beforeSunset = sunsetDate.time - now | |
def oneHour = 1000 * 60 * 60 | |
if(afterSunrise < oneHour) { | |
//dawn | |
lux = (long)(lux * (afterSunrise/oneHour)) | |
} else if (beforeSunset < oneHour) { | |
//dusk | |
lux = (long)(lux * (beforeSunset/oneHour)) | |
} | |
} | |
else { | |
//night - always set to 10 for now | |
//could do calculations for dusk/dawn too | |
lux = 10 | |
} | |
lux | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I created a new SmartApp with this code and published it 'for me' but it seems to make my SmartThings app crash every time I access the 'My Apps' section.