Skip to content

Instantly share code, notes, and snippets.

@danlieberman
Created August 20, 2013 21:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danlieberman/6287581 to your computer and use it in GitHub Desktop.
Save danlieberman/6287581 to your computer and use it in GitHub Desktop.
/**
* 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
}
@BradMcGonigle
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment