Skip to content

Instantly share code, notes, and snippets.

@joshualyon
Created December 2, 2019 20:09
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 joshualyon/d6695d30022c5d0419b1d3b19d2ea98f to your computer and use it in GitHub Desktop.
Save joshualyon/d6695d30022c5d0419b1d3b19d2ea98f to your computer and use it in GitHub Desktop.
Simplified version of Matthew's driver - ALL CREDIT TO MATHEW - this is just a fix for quirks in Hubitat with missing defined attributes
/*
DarkSky.net Weather Driver
Import URL: https://raw.githubusercontent.com/HubitatCommunity/DarkSky.net-Weather-Driver/master/DarkSky.net%20Weather%20Driver.groovy
Copyright 2019 @Matthew (Scottma61)
!!!
IMPORTANT: This is a modified version of Matthew's driver which only reports the attributes supported
by SharpTools. The hub or relay sometimes have issues when a device reports that it supports
an attribute, but does not end up actually reporting that attribute.
As such, this driver eliminates all unused capabilities and attributes and only reports the
actual attributes that it supports.
!!!
Many people contributed to the creation of this driver. Significant contributors include:
- @Cobra who adapted it from @mattw01's work and I thank them for that!
- @bangali for his original APIXU.COM base code that much of the early versions of this driver was
adapted from.
- @bangali for his the Sunrise-Sunset.org code used to calculate illuminance/lux and the more
recent adaptations of that code from @csteele in his continuation driver 'wx-ApiXU'.
- @csteele (and prior versions from @bangali) for the attribute selection code.
- @csteele for his examples on how to convert to asyncHttp calls to reduce Hub resource utilization.
- @bangali also contributed the icon work from
https://github.com/jebbett for new cooler 'Alternative' weather icons with icons courtesy
of https://www.deviantart.com/vclouds/art/VClouds-Weather-Icons-179152045.
- @storageanarchy for his Dark Sky Icon mapping and some new icons to compliment the Vclouds set.
In addition to all the cloned code from the Hubitat community, I have heavily modified/created new
code myself @Matthew (Scottma61) with lots of help from the Hubitat community. If you believe you
should have been acknowledged or received attribution for a code contribution, I will happily do so.
While I compiled and orchestrated the driver, very little is actually original work of mine.
This driver is free to use. I do not accept donations. Please feel free to contribute to those
mentioned here if you like this work, as it would not have been possible without them.
This driver is intended to pull weather data from DarkSky.net (http://darksky.net). You will need your
DarkSky API key to use the data from that site.
You can select to use a base set of condition icons from the forecast source, or an 'alternative'
(fancier) set. The base 'Standard' icon set will be from WeatherUnderground. You may choose the
fancier 'Alternative' icon set if you use the Dark Sky.
The driver exposes both metric and imperial measurements for you to select from.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
for the specific language governing permissions and limitations under the License.
Last Update 11/11/2019
{ Left room below to document version changes...}
V1.2.9s Simplified driver that only reports SharpTools supported attributes / capabilies - 12/02/2019
V1.2.8 Exposed 'feelsLike' so it gets updated - 11/11/2019
V1.2.7 Force three day forcast icons to be 'daytime' (instead of 'nighttime') - 10/23/2019
V1.2.6 Changed 'pressure' to a number from a string, added 'pressured' as a string. - 10/22/2019
V1.2.5 Added three day forecast tile - 10/22/2019
V1.2.4 added meters per second ('m/s') for wind and hectopascals for pressure - 10/14/2019
V1.2.3 forecastIcon & weatherIcon fix. Tuned Lux for 'fully nighttime' - 10/13/2019
V1.2.2 Bug fix for is_day/is_light - 10/02/2019
V1.2.1 Added ability to show 'knots' for wind/gust speeds - 10/01/2019
V1.2.0 Eliminated 'Std' Icons. Reworked condition_code/condition_text. - 09/30/2019
V1.1.9 myTile format tweaking - 09/29/2019
V1.1.8 Bug fixes, optimizations, Added 'wind' and lux jitter control - 09/28/2019
V1.1.7 More myTile 'display:inline' corrections - 09/28/2019
V1.1.6 myTile 'display:inline' correction - 09/27/2019
V1.1.5 myTile enhancement for excessive length - 09/27/2019
V1.1.4 Prevent myTile from exceeding 1,024 characters - 09/26/2019
V1.1.3 Corrected myTile for 'alert' condition - 09/26/2019
V1.1.2 - Added 'wind_cardinal', more code optimization and cleanup - 09/25/2019
V1.1.1 - Corrected MoonPhase, optimized lux updates and code optimizations re-organized - 09/24/2019
preference order and some goupings of 'optional' attributes.
V1.1.0 - Randomized schedule start times, Added 'Powered by DarkSky' attribution - 09/18/2019
V1.0.9 - Default to 'TinyURL' for icon location, added log when changeing schedule - 09/16/2019
V1.0.8 - Changed icon location to prevent duplication - Please update icon file location - 09/16/2019
V1.0.7 - Moved driver to the HubitatCommunity github, added 'Nighttime' schedule option - 09/16/2019
added upDateCheck() to show if driver is current (thanks @csteele)
V1.0.6 - Another optional attribute bug fix. - 09/15/2019
V1.0.5 - Tweaking and bug fixes. - 09/14/2019
V1.0.4 - Added 'weatherIcons' used for OWM icons/dashboard - 09/14/2019
V1.0.3 - Added windSpeed and windDirection, required for some dashboards. - 09/14/2019
V1.0.2 - Attribute now dislplayed for dashboards ** Read caution below ** - 09/14/2019
V1.0.1 - Bug fixes. - 09/13/2019
V1.0.0 - Initial release of driver with ApiXU.com completely removed. - 09/13/2019
=========================================================================================================
*/
public static String version() { return "1.2.8" }
import groovy.transform.Field
metadata {
definition (name: "DarkSky.net Simplified Weather Driver", namespace: "Matthew", author: "Scottma61", importUrl: "https://raw.githubusercontent.com/HubitatCommunity/DarkSky.net-Weather-Driver/master/DarkSky.net%20Weather%20Driver.groovy") {
capability "Sensor"
capability "Temperature Measurement"
capability "Illuminance Measurement"
capability "Relative Humidity Measurement"
//capability "Pressure Measurement"
//capability "Ultraviolet Index"
capability "Refresh"
// The following attributes may be needed for dashboards that require these attributes,
// so they are alway available and shown by default.
attribute "city", "string" //SharpTool.io
attribute "feelsLike", "number" //SharpTool.io
attribute "forecastIcon", "string" //SharpTool.io
attribute "localSunrise", "string" //SharpTool.io
attribute "localSunset", "string" //SharpTool.io
attribute "percentPrecip", "number" //SharpTool.io
attribute "weather", "string" //SharpTool.io
attribute "weatherIcon", "string" //SharpTool.io
attribute "wind", "number" //SharpTool.io
command "pollData"
}
def settingDescr = settingEnable ? "<br><i>Hide many of the optional attributes to reduce the clutter, if needed, by turning OFF this toggle.</i><br>" : "<br><i>Many optional attributes are available to you, if needed, by turning ON this toggle.</i><br>"
preferences() {
section("Query Inputs"){
input "apiKey", "text", required: true, title: "Type DarkSky.net API Key Here", defaultValue: null
input "city", "text", required: true, defaultValue: "City or Location name forecast area", title: "City name"
input "pollIntervalForecast", "enum", title: "External Source Poll Interval (daytime)", required: true, defaultValue: "3 Hours", options: ["Manual Poll Only", "2 Minutes", "5 Minutes", "10 Minutes", "15 Minutes", "30 Minutes", "1 Hour", "3 Hours"]
input "pollIntervalForecastnight", "enum", title: "External Source Poll Interval (nighttime)", required: true, defaultValue: "3 Hours", options: ["Manual Poll Only", "2 Minutes", "5 Minutes", "10 Minutes", "15 Minutes", "30 Minutes", "1 Hour", "3 Hours"]
input "logSet", "bool", title: "Create extended Logging", required: true, defaultValue: false
input "tempFormat", "enum", required: true, defaultValue: "Fahrenheit (°F)", title: "Display Unit - Temperature: Fahrenheit (°F) or Celsius (°C)", options: ["Fahrenheit (°F)", "Celsius (°C)"]
input "datetimeFormat", "enum", required: true, defaultValue: "1", title: "Display Unit - Date-Time Format", options: [1:"m/d/yyyy 12 hour (am|pm)", 2:"m/d/yyyy 24 hour", 3:"mm/dd/yyyy 12 hour (am|pm)", 4:"mm/dd/yyyy 24 hour", 5:"d/m/yyyy 12 hour (am|pm)", 6:"d/m/yyyy 24 hour", 7:"dd/mm/yyyy 12 hour (am|pm)", 8:"dd/mm/yyyy 24 hour", 9:"yyyy/mm/dd 24 hour"]
input "distanceFormat", "enum", required: true, defaultValue: "Miles (mph)", title: "Display Unit - Distance/Speed: Miles, Kilometers, knots or meters", options: ["Miles (mph)", "Kilometers (kph)", "knots", "meters (m/s)"]
input "luxjitter", "bool", title: "Use lux jitter control (rounding)?", required: true, defaultValue: false
}
}
}
// <<<<<<<<<< Begin Sunrise-Sunset Poll Routines >>>>>>>>>>
void pollSunRiseSet() {
currDate = new Date().format("yyyy-MM-dd", TimeZone.getDefault())
log.info("DarkSky.net Weather Driver - INFO: Polling Sunrise-Sunset.org")
def requestParams = [ uri: "https://api.sunrise-sunset.org/json?lat=" + location.latitude + "&lng=" + location.longitude + "&formatted=0" ]
if (currDate) {requestParams = [ uri: "https://api.sunrise-sunset.org/json?lat=" + location.latitude + "&lng=" + location.longitude + "&formatted=0&date=$currDate" ]}
LOGINFO("Poll Sunrise-Sunset: $requestParams")
asynchttpGet("sunRiseSetHandler", requestParams)
return
}
void sunRiseSetHandler(resp, data) {
if(resp.getStatus() == 200 || resp.getStatus() == 207) {
sunRiseSet = resp.getJson().results
updateDataValue("sunRiseSet", resp.data)
LOGINFO("Sunrise-Sunset Data: $sunRiseSet")
setDateTimeFormats(datetimeFormat)
updateDataValue("riseTime", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunrise).format("HH:mm", TimeZone.getDefault()))
updateDataValue("noonTime", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.solar_noon).format("HH:mm", TimeZone.getDefault()))
updateDataValue("setTime", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunset).format("HH:mm", TimeZone.getDefault()))
updateDataValue("tw_begin", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.civil_twilight_begin).format("HH:mm", TimeZone.getDefault()))
updateDataValue("tw_end", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.civil_twilight_end).format("HH:mm", TimeZone.getDefault()))
updateDataValue("localSunset",new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunset).format(timeFormat, TimeZone.getDefault()))
updateDataValue("localSunrise", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunrise).format(timeFormat, TimeZone.getDefault()))
updateDataValue("riseTime1", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunrise + 86400000).format("HH:mm", TimeZone.getDefault()))
updateDataValue("riseTime2", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunrise + 86400000 + 86400000).format("HH:mm", TimeZone.getDefault()))
updateDataValue("setTime1", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunset + 86400000).format("HH:mm", TimeZone.getDefault()))
updateDataValue("setTime2", new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunset + 86400000 + 86400000).format("HH:mm", TimeZone.getDefault()))
} else {
log.warn "DarkSky.net Weather Driver - Sunrise-Sunset api did not return data"
}
return
}
// >>>>>>>>>> End Sunrise-Sunset Poll Routines <<<<<<<<<<
// <<<<<<<<<< Begin DarkSky Poll Routines >>>>>>>>>>
void pollDS() {
if( apiKey == null ) {
log.error "DarkSky API Key not found. Please configure in preferences."
return
}
def ParamsDS = [ uri: "https://api.darksky.net/forecast/${apiKey}/" + location.latitude + ',' + location.longitude + "?units=us&exclude=minutely,hourly,flags" ]
LOGINFO("Poll DarkSky: $ParamsDS")
asynchttpGet("pollDSHandler", ParamsDS)
return
}
void pollDSHandler(resp, data) {
log.info "DarkSky.net Weather Driver - INFO: Polling DarkSky.net"
if(resp.getStatus() == 200 || resp.getStatus() == 207) {
def ds = parseJson(resp.data)
doPollDS(ds) // parse the data returned by DarkSky
} else {
log.error "DarkSky.net Weather Driver - DarkSky weather api did not return data"
}
}
void doPollDS(Map ds) {
// <<<<<<<<<< Begin Setup Global Variables >>>>>>>>>>
setDateTimeFormats(datetimeFormat)
setMeasurementMetrics(distanceFormat, pressureFormat, rainFormat, tempFormat)
updateDataValue("currDate", new Date().format("yyyy-MM-dd", TimeZone.getDefault()))
updateDataValue("currTime", new Date().format("HH:mm", TimeZone.getDefault()))
if(getDataValue("riseTime") <= getDataValue("currTime") && getDataValue("setTime") >= getDataValue("currTime")) {
updateDataValue("is_day", "true")
} else {
updateDataValue("is_day", "false")
}
if(getDataValue("currTime") < getDataValue("tw_begin") || getDataValue("currTime") > getDataValue("tw_end")) {
updateDataValue("is_light", "false")
} else {
updateDataValue("is_light", "true")
}
if(getDataValue("is_light") != getDataValue("is_lightOld")) {
if(getDataValue("is_light")=="true") {
log.info("DarkSky.net Weather Driver - INFO: Switching to Daytime schedule.")
}else{
log.info("DarkSky.net Weather Driver - INFO: Switching to Nighttime schedule.")
}
initialize()
updateDataValue("is_lightOld", getDataValue("is_light"))
}
// >>>>>>>>>> End Setup Global Variables <<<<<<<<<<
// <<<<<<<<<< Begin Setup Forecast Variables >>>>>>>>>>
fotime = new Date(ds.currently.time * 1000L)
updateDataValue("fotime", fotime.toString())
futime = new Date(ds.currently.time * 1000L)
updateDataValue("futime", futime.toString())
// <<<<<<<<<< Begin Process Standard Weather-Station Variables (Regardless of Forecast Selection) >>>>>>>>>>
BigDecimal t_dew
if(tMetric == "°F") {
t_dew = Math.round(ds.currently.dewPoint.toBigDecimal() * 10) / 10
} else {
t_dew = Math.round((ds.currently.dewPoint.toBigDecimal() - 32) / 1.8 * 10) / 10
}
updateDataValue("dewpoint", t_dew.toString())
updateDataValue("humidity", (Math.round(ds.currently.humidity.toBigDecimal() * 1000) / 10).toString())
BigDecimal t_press
if(pMetric == "inHg") {
t_press = Math.round(ds.currently.pressure.toBigDecimal() * 0.029529983071445 * 100) / 100
} else {
t_press = Math.round(ds.currently.pressure.toBigDecimal() * 10) / 10
}
updateDataValue("pressure", t_press.toString())
BigDecimal t_temp
if(tMetric == "°F") {
t_temp = Math.round(ds.currently.temperature.toBigDecimal() * 10) / 10
} else {
t_temp = Math.round((ds.currently.temperature.toBigDecimal() - 32) / 1.8 * 10) / 10
}
updateDataValue("temperature", t_temp.toString())
String w_string_bft
String w_bft_icon
BigDecimal t_ws = ds.currently.windSpeed.toBigDecimal()
if(t_ws < 1.0) {
w_string_bft = "Calm"; w_bft_icon = 'wb0.png'
}else if(t_ws < 4.0) {
w_string_bft = "Light air"; w_bft_icon = 'wb1.png'
}else if(t_ws < 8.0) {
w_string_bft = "Light breeze"; w_bft_icon = 'wb2.png'
}else if(t_ws < 13.0) {
w_string_bft = "Gentle breeze"; w_bft_icon = 'wb3.png'
}else if(t_ws < 19.0) {
w_string_bft = "Moderate breeze"; w_bft_icon = 'wb4.png'
}else if(t_ws < 25.0) {
w_string_bft = "Fresh breeze"; w_bft_icon = 'wb5.png'
}else if(t_ws < 32.0) {
w_string_bft = "Strong breeze"; w_bft_icon = 'wb6.png'
}else if(t_ws < 39.0) {
w_string_bft = "High wind, moderate gale, near gale"; w_bft_icon = 'wb7.png'
}else if(t_ws < 47.0) {
w_string_bft = "Gale, fresh gale"; w_bft_icon = 'wb8.png'
}else if(t_ws < 55.0) {
w_string_bft = "Strong/severe gale"; w_bft_icon = 'wb9.png'
}else if(t_ws < 64.0) {
w_string_bft = "Storm, whole gale"; w_bft_icon = 'wb10.png'
}else if(t_ws < 73.0) {
w_string_bft = "Violent storm"; w_bft_icon = 'wb11.png'
}else if(t_ws >= 73.0) {
w_string_bft = "Hurricane force"; w_bft_icon = 'wb12.png'
}
updateDataValue("wind_string_bft", w_string_bft)
updateDataValue("wind_bft_icon", w_bft_icon)
BigDecimal t_wd
BigDecimal t_wg
if(dMetric == "MPH") {
t_wd = Math.round(ds.currently.windSpeed.toBigDecimal() * 10) / 10
t_wg = Math.round(ds.currently.windGust.toBigDecimal() * 10) / 10
} else if(dMetric == "KPH") {
t_wd = Math.round(ds.currently.windSpeed.toBigDecimal() * 1.609344 * 10) / 10
t_wg = Math.round(ds.currently.windGust.toBigDecimal() * 1.609344 * 10) / 10
} else if(dMetric == "knots") {
t_wd = Math.round(ds.currently.windSpeed.toBigDecimal() * 0.868976 * 10) / 10
t_wg = Math.round(ds.currently.windGust.toBigDecimal() * 0.868976 * 10) / 10
} else { // this leave only m/s
t_wd = Math.round(ds.currently.windSpeed.toBigDecimal() * 0.44704 * 10) / 10
t_wg = Math.round(ds.currently.windGust.toBigDecimal() * 0.44704 * 10) / 10
}
updateDataValue("wind", t_wd.toString())
updateDataValue("wind_gust", t_wg.toString())
updateDataValue("wind_degree", ds.currently.windBearing.toInteger().toString())
String w_cardinal
String w_direction
BigDecimal twb = ds.currently.windBearing.toBigDecimal()
if(twb < 11.25) {
w_cardinal = 'N'; w_direction = 'North'
}else if(twb < 33.75) {
w_cardinal = 'NNE'; w_direction = 'North-Northeast'
}else if(twb < 56.25) {
w_cardinal = 'NE'; w_direction = 'Northeast'
}else if(twb < 56.25) {
w_cardinal = 'ENE'; w_direction = 'East-Northeast'
}else if(twb < 101.25) {
w_cardinal = 'E'; w_direction = 'East'
}else if(twb < 123.75) {
w_cardinal = 'ESE'; w_direction = 'East-Southeast'
}else if(twb < 146.25) {
w_cardinal = 'SE'; w_direction = 'Southeast'
}else if(twb < 168.75) {
w_cardinal = 'SSE'; w_direction = 'South-Southeast'
}else if(twb < 191.25) {
w_cardinal = 'S'; w_direction = 'South'
}else if(twb < 213.75) {
w_cardinal = 'SSW'; w_direction = 'South-Southwest'
}else if(twb < 236.25) {
w_cardinal = 'SW'; w_direction = 'Southwest'
}else if(twb < 258.75) {
w_cardinal = 'WSW'; w_direction = 'West-Southwest'
}else if(twb < 281.25) {
w_cardinal = 'W'; w_direction = 'West'
}else if(twb < 303.75) {
w_cardinal = 'WNW'; w_direction = 'West-Northwest'
}else if(twb < 326.25) {
w_cardinal = 'NW'; w_direction = 'Northwest'
}else if(twb < 348.75) {
w_cardinal = 'NNW'; w_direction = 'North-Northwest'
}else if(twb >= 348.75) {
w_cardinal = 'N'; w_direction = 'North'
}
updateDataValue("wind_direction", w_direction)
updateDataValue("wind_cardinal", w_cardinal)
updateDataValue("wind_string", w_string_bft + " from the " + getDataValue("wind_direction") + (getDataValue("wind").toBigDecimal() < 1.0 ? '': " at " + getDataValue("wind") + " " + dMetric))
String s_cardinal
String s_direction
if(!ds.currently.nearestStormBearing){
updateDataValue("nearestStormBearing", "360")
s_cardinal = 'U'
s_direction = 'Unknown'
}else{
updateDataValue("nearestStormBearing", (Math.round(ds.currently.nearestStormBearing * 10) / 10).toString())
BigDecimal tnsb = ds.currently.nearestStormBearing.toBigDecimal()
if(tnsb < 11.25) {
s_cardinal = 'N'; s_direction = 'North'
}else if(tnsb < 33.75) {
s_cardinal = 'NNE'; s_direction = 'North-Northeast'
}else if(tnsb < 56.25) {
s_cardinal = 'NE'; s_direction = 'Northeast'
}else if(tnsb < 78.75) {
s_cardinal = 'ENE'; s_direction = 'East-Northeast'
}else if(tnsb < 101.25) {
s_cardinal = 'E'; s_direction = 'East'
}else if(tnsb < 123.75) {
s_cardinal = 'ESE'; s_direction = 'East-Southeast'
}else if(tnsb < 146.25) {
s_cardinal = 'SE'; s_direction = 'Southeast'
}else if(tnsb < 168.75) {
s_cardinal = 'SSE'; s_direction = 'South-Southeast'
}else if(tnsb < 191.25) {
s_cardinal = 'S'; s_direction = 'South'
}else if(tnsb < 213.75) {
s_cardinal = 'SSW'; s_direction = 'South-Southwest'
}else if(tnsb < 236.25) {
s_cardinal = 'SW'; s_direction = 'Southwest'
}else if(tnsb < 258.75) {
s_cardinal = 'WSW'; s_direction = 'West-Southwest'
}else if(tnsb < 281.25) {
s_cardinal = 'W'; s_direction = 'West'
}else if(tnsb < 303.75) {
s_cardinal = 'WNW'; s_direction = 'West-Northwest'
}else if(tnsb < 326.26) {
s_cardinal = 'NW'; s_direction = 'Northwest'
}else if(tnsb < 348.75) {
s_cardinal = 'NNW'; s_direction = 'North-Northwest'
}else if(tnsb >= 348.75) {
s_cardinal = 'N'; s_direction = 'North'
}
}
updateDataValue("nearestStormCardinal", s_cardinal)
updateDataValue("nearestStormDirection", s_direction)
BigDecimal t_nsd
if(!ds.currently.nearestStormDistance) {
t_nsd = 9999.9
} else if(dMetric == "MPH") {
t_nsd = Math.round(ds.currently.nearestStormDistance.toBigDecimal() * 10) / 10
} else {
t_nsd = Math.round(ds.currently.nearestStormDistance.toBigDecimal() * 1.609344 * 10) / 10
}
updateDataValue("nearestStormDistance", t_nsd.toString())
updateDataValue("ozone", (Math.round(ds.currently.ozone.toBigDecimal() * 10 ) / 10).toString())
String mPhase
BigDecimal tmnp = ds.daily.data[0].moonPhase.toBigDecimal() * 100
if (tmnp < 6.25) {mPhase = "New Moon"}
else if (tmnp < 18.75) {mPhase = "Waxing Crescent"}
else if (tmnp < 31.25) {mPhase = "First Quarter"}
else if (tmnp < 43.75) {mPhase = "Waxing Gibbous"}
else if (tmnp < 56.25) {mPhase = "Full Moon"}
else if (tmnp < 68.75) {mPhase = "Waning Gibbous"}
else if (tmnp < 81.25) {mPhase = "Last Quarter"}
else if (tmnp < 93.75) {mPhase = "Waxing Gibbous"}
else if (tmnp >= 93.75) {mPhase = "New Moon"}
updateDataValue("moonPhase", mPhase)
// >>>>>>>>>> End Process Standard Weather-Station Variables (Regardless of Forecast Selection) <<<<<<<<<<
int cloudCover = 1
if (!ds.currently.cloudCover) {
cloudCover = 1
} else {
cloudCover = (ds.currently.cloudCover.toBigDecimal() <= 0.01) ? 1 : (ds.currently.cloudCover.toBigDecimal() * 100)
}
updateDataValue("cloud", cloudCover.toString())
if (!ds.alerts){
updateDataValue("alert", "No current weather alerts for this area")
updateDataValue("possAlert", "false")
} else {
updateDataValue("alert", ds.alerts.title.toString().replaceAll("[{}\\[\\]]", "").split(/,/)[0])
updateDataValue("possAlert", "true")
}
updateDataValue("vis", (dMetric!="MPH" ? ds.currently.visibility.toBigDecimal() * 1.60934 : ds.currently.visibility.toBigDecimal()).toString())
updateDataValue("percentPrecip", !ds.daily.data[0].precipProbability ? "1" : (ds.daily.data[0].precipProbability.toBigDecimal() * 100).toInteger().toString())
String c_code = getdsIconCode(ds?.currently?.icon, ds?.currently?.summary, getDataValue("is_day"))
updateDataValue("condition_code", c_code)
updateDataValue("condition_text", getcondText(c_code))
String f_code = getdsIconCode(ds?.daily?.data[0]?.icon, ds?.daily?.data[0]?.summary, getDataValue("is_day"))
updateDataValue("forecast_code", f_code)
updateDataValue("forecast_text", getcondText(f_code))
if(threedayTilePublish) {
updateDataValue("day1", new Date(ds.daily.data[1].time * 1000L).format("EEEE"))
updateDataValue("day2", new Date(ds.daily.data[2].time * 1000L).format("EEEE"))
updateDataValue("is_day1", "true")
updateDataValue("is_day2", "true")
String f_code1 = getdsIconCode(ds?.daily?.data[1]?.icon, ds?.daily?.data[1]?.summary, getDataValue("is_day1"))
updateDataValue("forecast_code1", f_code1)
updateDataValue("forecast_text1", getcondText(f_code1))
String f_code2 = getdsIconCode(ds?.daily?.data[2]?.icon, ds?.daily?.data[2]?.summary, getDataValue("is_day2"))
updateDataValue("forecast_code2", f_code2)
updateDataValue("forecast_text2", getcondText(f_code2))
updateDataValue("forecastHigh1", (tMetric=="°F" ? (Math.round(ds.daily.data[1].temperatureHigh.toBigDecimal() * 10) / 10) : (Math.round((ds.daily.data[1].temperatureHigh.toBigDecimal() - 32) / 1.8 * 10) / 10)).toString())
updateDataValue("forecastHigh2", (tMetric=="°F" ? (Math.round(ds.daily.data[2].temperatureHigh.toBigDecimal() * 10) / 10) : (Math.round((ds.daily.data[2].temperatureHigh.toBigDecimal() - 32) / 1.8 * 10) / 10)).toString())
updateDataValue("forecastLow1", (tMetric=="°F" ? (Math.round(ds.daily.data[1].temperatureLow.toBigDecimal() * 10) / 10) : (Math.round((ds.daily.data[1].temperatureLow.toBigDecimal() - 32) / 1.8 * 10) / 10)).toString())
updateDataValue("forecastLow2", (tMetric=="°F" ? (Math.round(ds.daily.data[2].temperatureLow.toBigDecimal() * 10) / 10) : (Math.round((ds.daily.data[2].temperatureLow.toBigDecimal() - 32) / 1.8 * 10) / 10)).toString())
updateDataValue("imgName0", '<img class=\"centerImage\" style=\"height:50%;\" src=' + getImgName(getDataValue('forecast_code')) + '>')
updateDataValue("imgName1", '<img class=\"centerImage\" style=\"height:50%;\" src=' + getImgName(getDataValue('forecast_code1')) + '>')
updateDataValue("imgName2", '<img class=\"centerImage\" style=\"height:50%;\" src=' + getImgName(getDataValue('forecast_code2')) + '>')
updateDataValue("PoP", (!ds.daily.data[0].precipProbability ? 0 : (ds.daily.data[0].precipProbability.toBigDecimal() * 100).toInteger()).toString())
updateDataValue("PoP1", (!ds.daily.data[1].precipProbability ? 0 : (ds.daily.data[1].precipProbability.toBigDecimal() * 100).toInteger()).toString())
updateDataValue("PoP2", (!ds.daily.data[2].precipProbability ? 0 : (ds.daily.data[2].precipProbability.toBigDecimal() * 100).toInteger()).toString())
}
updateDataValue("forecastHigh", (tMetric=="°F" ? (Math.round(ds.daily.data[0].temperatureHigh.toBigDecimal() * 10) / 10) : (Math.round((ds.daily.data[0].temperatureHigh.toBigDecimal() - 32) / 1.8 * 10) / 10)).toString())
updateDataValue("forecastLow", (tMetric=="°F" ? (Math.round(ds.daily.data[0].temperatureLow.toBigDecimal() * 10) / 10) : (Math.round((ds.daily.data[0].temperatureLow.toBigDecimal() - 32) / 1.8 * 10) / 10)).toString())
if(precipExtendedPublish){
updateDataValue("rainTomorrow", (ds.daily.data[1].precipProbability.toBigDecimal() * 100).toInteger().toString())
updateDataValue("rainDayAfterTomorrow", (ds.daily.data[2].precipProbability.toBigDecimal() * 100).toInteger().toString())
}
updateLux(false)
updateDataValue("ultravioletIndex", ds.currently.uvIndex.toBigDecimal().toString())
BigDecimal t_fl
if(tMetric == "°F") {
t_fl = Math.round(ds.currently.apparentTemperature.toBigDecimal() * 10) / 10
} else {
t_fl = Math.round((ds.currently.apparentTemperature.toBigDecimal() - 32) / 1.8 * 10) / 10
}
updateDataValue("feelsLike", t_fl.toString())
// >>>>>>>>>> End Setup Forecast Variables <<<<<<<<<<
// <<<<<<<<<< Begin Icon Processing >>>>>>>>>>
// if(sourceImg==false){ // 'Alternative' Icons selected
String imgName = (getDataValue("iconType")== 'true' ? getImgName(getDataValue("condition_code")) : getImgName(getDataValue("forecast_code")))
sendEventPublish(name: "condition_icon", value: '<img src=' + imgName + '>')
sendEventPublish(name: "condition_iconWithText", value: "<img src=" + imgName + "><br>" + (getDataValue("iconType")== 'true' ? getDataValue("condition_text") : getDataValue("forecast_text")))
sendEventPublish(name: "condition_icon_url", value: imgName)
updateDataValue("condition_icon_url", imgName)
sendEventPublish(name: "condition_icon_only", value: imgName.split("/")[-1].replaceFirst("\\?raw=true",""))
// >>>>>>>>>> End Icon Processing <<<<<<<<<<
PostPoll()
}
// >>>>>>>>>> End DarkSky Poll Routines <<<<<<<<<<
// >>>>>>>>>> Begin Lux Processing <<<<<<<<<<
void updateLux(boolean pollAgain=true) {
LOGINFO("UpdateLux $pollAgain")
if(pollAgain) {
String curTime = new Date().format("HH:mm", TimeZone.getDefault())
String newLight
if(curTime < getDataValue("tw_begin") || curTime > getDataValue("tw_end")) {
newLight = "false"
} else {
newLight = "true"
}
if(newLight != getDataValue("is_lightOld")) {
pollDS()
return
}
}
def (lux, bwn) = estimateLux(getDataValue("condition_code"), getDataValue("cloud").toInteger())
updateDataValue("bwn", bwn)
updateDataValue("illuminance", !lux ? "0" : lux.toString())
updateDataValue("illuminated", String.format("%,4d", !lux ? 0 : lux).toString())
if(pollAgain) PostPoll()
}
// >>>>>>>>>> End Lux Processing <<<<<<<<<<
// <<<<<<<<<< Begin Icon and condition_code, condition_text processing >>>>>>>>>>
String getdsIconCode(String icon='unknown', String dcs='unknown', String isDay='true') {
switch(icon) {
case 'rain':
// rain=[Possible Light Rain, Light Rain, Rain, Heavy Rain, Drizzle, Light Rain and Breezy, Light Rain and Windy,
// Rain and Breezy, Rain and Windy, Heavy Rain and Breezy, Rain and Dangerously Windy, Light Rain and Dangerously Windy],
if (dcs == 'Drizzle') {
icon = 'drizzle'
} else if (dcs.startsWith('Light Rain')) {
icon = 'lightrain'
if (dcs.contains('Breezy')) icon += 'breezy'
else if (dcs.contains('Windy')) icon += 'windy'
} else if (dcs.startsWith('Heavy Rain')) {
icon = 'heavyrain'
if (dcs.contains('Breezy')) icon += 'breezy'
else if (dcs.contains('Windy')) icon += 'windy'
} else if (dcs == 'Possible Light Rain') {
icon = 'chancelightrain'
} else if (dcs.startsWith('Possible')) {
icon = 'chancerain'
} else if (dcs.startsWith('Rain')) {
if (dcs.contains('Breezy')) icon += 'breezy'
else if (dcs.contains('Windy')) icon += 'windy'
}
break;
case 'snow':
if (dcs == 'Light Snow') icon = 'lightsnow'
else if (dcs == 'Flurries') icon = 'flurries'
else if (dcs == 'Possible Light Snow') icon = 'chancelightsnow'
else if (dcs.startsWith('Possible Light Snow')) {
if (dcs.contains('Breezy')) icon = 'chancelightsnowbreezy'
else if (dcs.contains('Windy')) icon = 'chancelightsnowwindy'
} else if (dcs.startsWith('Possible')) icon = 'chancesnow'
break;
case 'sleet':
if (dcs.startsWith('Possible')) icon = 'chancesleet'
else if (dcs.startsWith('Light')) icon = 'lightsleet'
break;
case 'thunderstorm':
if (dcs.startsWith('Possible')) icon = 'chancetstorms'
break;
case 'partly-cloudy-night':
if (dcs.contains('Mostly Cloudy')) icon = 'mostlycloudy'
else icon = 'partlycloudy'
break;
case 'partly-cloudy-day':
if (dcs.contains('Mostly Cloudy')) icon = 'mostlycloudy'
else icon = 'partlycloudy'
break;
case 'cloudy-night':
icon = 'cloudy'
break;
case 'cloudy':
case 'cloudy-day':
icon = 'cloudy'
break;
case 'clear-night':
icon = 'clear'
break;
case 'clear':
case 'clear-day':
icon = 'clear'
break;
case 'fog':
case 'wind':
// wind=[Windy and Overcast, Windy and Mostly Cloudy, Windy and Partly Cloudy, Breezy and Mostly Cloudy, Breezy and Partly Cloudy,
// Breezy and Overcast, Breezy, Windy, Dangerously Windy and Overcast, Windy and Foggy, Dangerously Windy and Partly Cloudy, Breezy and Foggy]}
if (dcs.contains('Windy')) {
// icon = 'wind'
if (dcs.contains('Overcast')) icon = 'windovercast'
else if (dcs.contains('Mostly Cloudy')) icon = 'windmostlycloudy'
else if (dcs.contains('Partly Cloudy')) icon = 'windpartlycloudy'
else if (dcs.contains('Foggy')) icon = 'windfoggy'
} else if (dcs.contains('Breezy')) {
icon = 'breezy'
if (dcs.contains('Overcast')) icon = 'breezyovercast'
else if (dcs.summary.contains('Mostly Cloudy')) icon = 'breezymostlycloudy'
else if (dcs.contains('Partly Cloudy')) icon = 'breezypartlycloudy'
else if (dcs.contains('Foggy')) icon = 'breezyfoggy'
}
break;
case '':
icon = 'unknown'
break;
default:
icon = 'unknown'
}
if(isDay == 'false') icon = 'nt_' + icon
return icon
}
// >>>>>>>>>> End Icon and condition_code, condition_text processing <<<<<<<<<<
// <<<<<<<<<< Begin Post-Poll Routines >>>>>>>>>>
void PostPoll() {
def sunRiseSet = parseJson(getDataValue("sunRiseSet")).results
setDateTimeFormats(datetimeFormat)
setMeasurementMetrics(distanceFormat, pressureFormat, rainFormat, tempFormat)
/* SunriseSunset Data Eements */
sendEvent(name: "localSunset", value: new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunset).format(timeFormat, TimeZone.getDefault())) // only needed for certain dashboards
sendEvent(name: "localSunrise", value: new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunRiseSet.sunrise).format(timeFormat, TimeZone.getDefault())) // only needed for certain dashboards
/* Weather-Display Data Elements */
sendEvent(name: "humidity", value: getDataValue("humidity").toBigDecimal(), unit: '%')
sendEvent(name: "illuminance", value: getDataValue("illuminance").toInteger(), unit: 'lx')
sendEvent(name: "pressure", value: getDataValue("pressure").toBigDecimal(), unit: pMetric)
sendEvent(name: "temperature", value: String.format("%3.1f", getDataValue("temperature").toBigDecimal()), unit: tMetric)
sendEvent(name: "ultravioletIndex", value: getDataValue("ultravioletIndex").toBigDecimal(), unit: 'uvi')
sendEvent(name: "feelsLike", value: getDataValue("feelsLike").toBigDecimal(), unit: tMetric)
/* 'Required for Dashboards' Data Elements */
sendEvent(name: "city", value: getDataValue("city"))
sendEvent(name: "forecastIcon", value: getstdImgName(getDataValue("condition_code")))
sendEvent(name: "percentPrecip", value: getDataValue("percentPrecip"))
sendEvent(name: "weather", value: getDataValue("condition_text"))
sendEvent(name: "weatherIcon", value: getstdImgName(getDataValue("condition_code")))
sendEvent(name: "wind", value: getDataValue("wind"), unit: dMetric)
}
// >>>>>>>>>> End Post-Poll Routines <<<<<<<<<<
public void refresh() {
pollDS()
}
void updated() {
unschedule()
updateCheck()
initialize()
runEvery5Minutes(updateLux)
Random rand = new Random(now())
int ssseconds = rand.nextInt(60)
schedule("${ssseconds} 20 0/8 ? * * *", pollSunRiseSet)
runIn(5, pollDS)
if(settingEnable) runIn(2100,settingsOff)// "roll up" (hide) the condition selectors after 35 min
if(settings.logSet) runIn(1800,logsOff)
int r_minutes = rand.nextInt(60)
// schedule("0 ${r_minutes} 8 ? * FRI *", updateCheck)
}
void initialize() {
unschedule("pollDS")
boolean logSet = (settings?.logSet ?: false)
String city = (settings?.city ?: "")
updateDataValue("city", city)
String pollIntervalForecast = (settings?.pollIntervalForecast ?: "3 Hours")
String pollIntervalForecastnight = (settings?.pollIntervalForecastnight ?: "3 Hours")
boolean dsIconbackgrounddark = (settings?.dsIconbackgrounddark ?: true)
String datetimeFormat = (settings?.datetimeFormat ?: "1")
String distanceFormat = (settings?.distanceFormat ?: "Miles (mph)")
String pressureFormat = (settings?.pressureFormat ?: "Inches")
String rainFormat = (settings?.rainFormat ?: "Inches")
String tempFormat = (settings?.tempFormat ?: "Fahrenheit (°F)")
boolean luxjitter = (settings?.luxjitter ?: false)
boolean iconType = (settings?.iconType ?: false)
updateDataValue("iconType", iconType ? 'true' : 'false')
boolean summaryType = (settings?.summaryType ?: false)
String iconLocation = (settings?.iconLocation ?: "https://tinyurl.com/y6xrbhpf/")
updateDataValue("iconLocation", iconLocation)
state.DarkSky = '<a href=\"https://darksky.net/poweredby/\"><img src=' + getDataValue("iconLocation") + 'dsD.png style=\"height:2em\";></a>'
setDateTimeFormats(datetimeFormat)
String dMetric
String pMetric
String rMetric
String tMetric
setMeasurementMetrics(distanceFormat, pressureFormat, rainFormat, tempFormat)
pollSunRiseSet()
Random rand = new Random(now())
int ssseconds = rand.nextInt(60)
int minutes2 = rand.nextInt(2)
int minutes5 = rand.nextInt(5)
int minutes10 = rand.nextInt(10)
int minutes15 = rand.nextInt(15)
int minutes30 = rand.nextInt(30)
int minutes60 = rand.nextInt(60)
int hours3 = rand.nextInt(3)
int dsseconds
if(ssseconds < 56 ){
dsseconds = ssseconds + 4
}else{
dsseconds = ssseconds - 60 + 4
}
if(getDataValue("is_light")=="true") {
if(pollIntervalForecast == "Manual Poll Only"){
LOGINFO("MANUAL FORECAST POLLING ONLY")
} else {
pollIntervalForecast = (settings?.pollIntervalForecast ?: "3 Hours").replace(" ", "")
if(pollIntervalForecast=='2Minutes'){
schedule("${dsseconds} ${minutes2}/2 * * * ? *", pollDS)
}else if(pollIntervalForecast=='5Minutes'){
schedule("${dsseconds} ${minutes5}/5 * * * ? *", pollDS)
}else if(pollIntervalForecast=='10Minutes'){
schedule("${dsseconds} ${minutes10}/10 * * * ? *", pollDS)
}else if(pollIntervalForecast=='15Minutes'){
schedule("${dsseconds} ${minutes15}/15 * * * ? *", pollDS)
}else if(pollIntervalForecast=='30Minutes'){
schedule("${dsseconds} ${minutes30}/30 * * * ? *", pollDS)
}else if(pollIntervalForecast=='1Hour'){
schedule("${dsseconds} ${minutes60} * * * ? *", pollDS)
}else if(pollIntervalForecast=='3Hours'){
schedule("${dsseconds} ${minutes60} ${hours3}/3 * * ? *", pollDS)
}
}
}else{
if(pollIntervalForecastnight == "Manual Poll Only"){
LOGINFO("MANUAL FORECAST POLLING ONLY")
} else {
pollIntervalForecastnight = (settings?.pollIntervalForecastnight ?: "3 Hours").replace(" ", "")
if(pollIntervalForecastnight=='2Minutes'){
schedule("${dsseconds} ${minutes2}/2 * * * ? *", pollDS)
}else if(pollIntervalForecastnight=='5Minutes'){
schedule("${dsseconds} ${minutes5}/5 * * * ? *", pollDS)
}else if(pollIntervalForecastnight=='10Minutes'){
schedule("${dsseconds} ${minutes10}/10 * * * ? *", pollDS)
}else if(pollIntervalForecastnight=='15Minutes'){
schedule("${dsseconds} ${minutes15}/15 * * * ? *", pollDS)
}else if(pollIntervalForecastnight=='30Minutes'){
schedule("${dsseconds} ${minutes30}/30 * * * ? *", pollDS)
}else if(pollIntervalForecastnight=='1Hour'){
schedule("${dsseconds} ${minutes60} * * * ? *", pollDS)
}else if(pollIntervalForecastnight=='3Hours'){
schedule("${dsseconds} ${minutes60} ${hours3}/3 * * ? *", pollDS)
}
}
}
return
}
public void pollData() {
pollDS()
return
}
// ************************************************************************************************
public void setDateTimeFormats(String formatselector){
switch(formatselector) {
case "1": DTFormat = "M/d/yyyy h:mm a"; dateFormat = "M/d/yyyy"; timeFormat = "h:mm a"; break;
case "2": DTFormat = "M/d/yyyy HH:mm"; dateFormat = "M/d/yyyy"; timeFormat = "HH:mm"; break;
case "3": DTFormat = "MM/dd/yyyy h:mm a"; dateFormat = "MM/dd/yyyy"; timeFormat = "h:mm a"; break;
case "4": DTFormat = "MM/dd/yyyy HH:mm"; dateFormat = "MM/dd/yyyy"; timeFormat = "HH:mm"; break;
case "5": DTFormat = "d/M/yyyy h:mm a"; dateFormat = "d/M/yyyy"; timeFormat = "h:mm a"; break;
case "6": DTFormat = "d/M/yyyy HH:mm"; dateFormat = "d/M/yyyy"; timeFormat = "HH:mm"; break;
case "7": DTFormat = "dd/MM/yyyy h:mm a"; dateFormat = "dd/MM/yyyy"; timeFormat = "h:mm a"; break;
case "8": DTFormat = "dd/MM/yyyy HH:mm"; dateFormat = "dd/MM/yyyy"; timeFormat = "HH:mm"; break;
case "9": DTFormat = "yyyy/MM/dd HH:mm"; dateFormat = "yyyy/MM/dd"; timeFormat = "HH:mm"; break;
default: DTFormat = "M/d/yyyy h:mm a"; dateFormat = "M/d/yyyy"; timeFormat = "h:mm a"; break;
}
return
}
public void setMeasurementMetrics(distFormat, pressFormat, precipFormat, temptFormat){
if(distFormat == "Miles (mph)") {
dMetric = "MPH"
} else if(distFormat == "knots") {
dMetric = "knots"
} else if(distFormat == "Kilometers (kph)") {
dMetric = "KPH"
} else {
dMetric = "m/s"
}
if(pressFormat == "Millibar") {
pMetric = "MBAR"
} else if(pressFormat == "Inches") {
pMetric = "inHg"
} else {
pMetric = "hPa"
}
if(precipFormat == "Millimeters") {
rMetric = "mm"
} else {
rMetric = "inches"
}
if(temptFormat == "Fahrenheit (°F)") {
tMetric = "°F"
} else {
tMetric = "°C"
}
return
}
def estimateLux(String condition_code, int cloud) {
long lux = 0l
boolean aFCC = true
double l
String bwn
def sunRiseSet = parseJson(getDataValue("sunRiseSet")).results
def tZ = TimeZone.getDefault() //TimeZone.getTimeZone(tz_id)
String lT = new Date().format("yyyy-MM-dd'T'HH:mm:ssXXX", tZ)
long localeMillis = getEpoch(lT)
long twilight_beginMillis = getEpoch(sunRiseSet.civil_twilight_begin)
long sunriseTimeMillis = getEpoch(sunRiseSet.sunrise)
long noonTimeMillis = getEpoch(sunRiseSet.solar_noon)
long sunsetTimeMillis = getEpoch(sunRiseSet.sunset)
long twilight_endMillis = getEpoch(sunRiseSet.civil_twilight_end)
long twiStartNextMillis = twilight_beginMillis + 86400000 // = 24*60*60*1000 --> one day in milliseconds
long sunriseNextMillis = sunriseTimeMillis + 86400000
long noonTimeNextMillis = noonTimeMillis + 86400000
long sunsetNextMillis = sunsetTimeMillis + 86400000
long twiEndNextMillis = twilight_endMillis + 86400000
switch(localeMillis) {
case { it < twilight_beginMillis}:
bwn = "Fully Night Time"
lux = 5l
aFCC = false
break
case { it < sunriseTimeMillis}:
bwn = "between twilight and sunrise"
l = (((localeMillis - twilight_beginMillis) * 50f) / (sunriseTimeMillis - twilight_beginMillis))
lux = (l < 10f ? 10l : l.trunc(0) as long)
break
case { it < noonTimeMillis}:
bwn = "between sunrise and noon"
l = (((localeMillis - sunriseTimeMillis) * 10000f) / (noonTimeMillis - sunriseTimeMillis))
lux = (l < 50f ? 50l : l.trunc(0) as long)
break
case { it < sunsetTimeMillis}:
bwn = "between noon and sunset"
l = (((sunsetTimeMillis - localeMillis) * 10000f) / (sunsetTimeMillis - noonTimeMillis))
lux = (l < 50f ? 50l : l.trunc(0) as long)
break
case { it < twilight_endMillis}:
bwn = "between sunset and twilight"
l = (((twilight_endMillis - localeMillis) * 50f) / (twilight_endMillis - sunsetTimeMillis))
lux = (l < 10f ? 10l : l.trunc(0) as long)
break
case { it < twiStartNextMillis}:
bwn = "Fully Night Time"
lux = 5l
aFCC = false
break
case { it < sunriseNextMillis}:
bwn = "between twilight and sunrise"
l = (((localeMillis - twiStartNextMillis) * 50f) / (sunriseNextMillis - twiStartNextMillis))
lux = (l < 10f ? 10l : l.trunc(0) as long)
break
case { it < noonTimeNextMillis}:
bwn = "between sunrise and noon"
l = (((localeMillis - sunriseNextMillis) * 10000f) / (noonTimeNextMillis - sunriseNextMillis))
lux = (l < 50f ? 50l : l.trunc(0) as long)
break
case { it < sunsetNextMillis}:
bwn = "between noon and sunset"
l = (((sunsetNextMillis - localeMillis) * 10000f) / (sunsetNextMillis - noonTimeNextMillis))
lux = (l < 50f ? 50l : l.trunc(0) as long)
break
case { it < twiEndNextMillis}:
bwn = "between sunset and twilight"
l = (((twiEndNextMillis - localeMillis) * 50f) / (twiEndNextMillis - sunsetNextMillis))
lux = (l < 10f ? 10l : l.trunc(0) as long)
break
default:
bwn = "Fully Night Time"
lux = 5l
aFCC = false
break
}
String cC = condition_code
String cCT = "not set"
double cCF = (!cloud || cloud=="") ? 0.998d : ((100 - (cloud / 3d)) / 100)
if(aFCC){
if(cloud){
LUitem = LUTable.find{ it.ccode == condition_code }
if (LUitem && (condition_code != "unknown")) {
cCF = (LUitem ? LUitem.luxpercent : 0)
cCT = (LUitem ? LUitem.ctext : 'unknown') + ' using cloud cover.'
} else {
cCF = 1.0
cCT = 'cloud not available now.'
}
} else {
cCF = 1.0
cCT = 'cloud not available now.'
}
}
lux = (lux * cCF) as long
if(luxjitter){
// reduce event variability code from @nh.schottfam
if(lux > 1100) {
long t0 = (lux/800)
lux = t0 * 800
} else if(lux <= 1100 && lux > 400) {
long t0 = (lux/400)
lux = t0 * 400
} else {
lux = 5
}
}
lux = Math.max(lux, 5)
LOGDEBUG("condition: $cC | condition factor: $cCF | condition text: $cCT| lux: $lux")
return [lux, bwn]
}
private long getEpoch (String aTime) {
def tZ = TimeZone.getDefault() //TimeZone.getTimeZone(tz_id)
def localeTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", aTime, tZ)
long localeMillis = localeTime.getTime()
return (localeMillis)
}
void SummaryMessage(boolean SType, String Slast_poll_date, String Slast_poll_time, String SforecastTemp, String Sprecip, String Svis){
BigDecimal windgust
if(getDataValue("wind_gust") == "" || getDataValue("wind_gust").toBigDecimal() < 1.0 || getDataValue("wind_gust")==null) {
windgust = 0.00g
} else {
windgust = getDataValue("wind_gust").toBigDecimal()
}
String dsIcon = '<a href=\"https://darksky.net/poweredby/\"><img src=' + getDataValue("iconLocation") + (dsIconbackgrounddark ? 'dsD.png' : 'dsL.png') + ' style=\"height:1.5em;display:inline;\"></a>'
String wSum = (String)null
if(SType == true){
wSum = "Weather summary for " + getDataValue("city") + " updated at ${Slast_poll_time} on ${Slast_poll_date}. "
wSum+= getDataValue("condition_text")
wSum+= (!SforecastTemp || SforecastTemp=="") ? ". " : "${SforecastTemp}"
wSum+= "Humidity is " + getDataValue("humidity") + "% and the temperature is " + String.format("%3.1f", getDataValue("temperature").toBigDecimal()) + tMetric + ". "
wSum+= "The temperature feels like it is " + String.format("%3.1f", getDataValue("feelsLike").toBigDecimal()) + tMetric + ". "
wSum+= "Wind: " + getDataValue("wind_string") + ", gusts: " + ((windgust < 1.00) ? "calm. " : "up to " + windgust.toString() + " " + dMetric + ". ")
wSum+= Sprecip
wSum+= Svis
wSum+= ((!getDataValue("alert") || getDataValue("alert")==null) ? "" : " " + getDataValue("alert") + '. ')
} else {
wSum = getDataValue("condition_text") + " "
wSum+= ((!SforecastTemp || SforecastTemp=="") ? ". " : "${SforecastTemp}")
wSum+= " Humidity: " + getDataValue("humidity") + "%. Temperature: " + String.format("%3.1f", getDataValue("temperature").toBigDecimal()) + tMetric + ". "
wSum+= getDataValue("wind_string") + ", gusts: " + ((windgust == 0.00) ? "calm. " : "up to " + windgust + dMetric + ".")
}
wSum = wSum.take(1024)
sendEvent(name: "weatherSummary", value: wSum)
return
}
String getImgName(String wCode){
LOGINFO("getImgName Input: wCode: " + wCode)
LUitem = LUTable.find{ it.ccode == wCode }
LOGINFO("getImgName Result: image url: " + getDataValue("iconLocation") + (LUitem ? LUitem.altIcon : 'na.png') + "?raw=true")
return (getDataValue("iconLocation") + (LUitem ? LUitem.altIcon : 'na.png') + (((getDataValue("iconLocation").toLowerCase().contains('://github.com/')) && (getDataValue("iconLocation").toLowerCase().contains('/blob/master/'))) ? "?raw=true" : ""))
}
String getowmImgName(String wCode){
LOGINFO("getImgName Input: wCode: " + wCode + " iconLocation: " + getDataValue("iconLocation"))
LUitem = LUTable.find{ it.ccode == wCode }
return (LUitem ? LUitem.owmIcon : '')
}
String getstdImgName(String wCode){
LOGINFO("getImgName Input: wCode: " + wCode + " iconLocation: " + getDataValue("iconLocation"))
LUitem = LUTable.find{ it.ccode == wCode }
return (LUitem ? LUitem.stdIcon : '')
}
String getcondText(String wCode){
LOGINFO("getImgName Input: wCode: " + wCode)
LUitem = LUTable.find{ it.ccode == wCode }
return (LUitem ? LUitem.ctext : '')
}
void logCheck(){
if(settings?.logSet == true){
log.info "DarkSky.net Weather Driver - INFO: All Logging Enabled"
} else {
log.info "DarkSky.net Weather Driver - INFO: Further Logging Disabled"
}
return
}
void LOGDEBUG(txt){
if(settings?.logSet == true){ log.debug("DarkSky.net Weather Driver - DEBUG: ${txt}") }
return
}
void LOGINFO(txt){
if(settings?.logSet == true){log.info("DarkSky.net Weather Driver - INFO: ${txt}") }
return
}
void logsOff(){
log.warn "${device?.displayName} debug logging disabled..."
device.updateSetting("logSet",[value:"false",type:"bool"])
}
void settingsOff(){
log.warn "Settings disabled..."
device.updateSetting("settingEnable",[value:"false",type:"bool"])
}
void sendEventPublish(evt) {
// Purpose: Attribute sent to DB if selected
if (settings."${evt.name + "Publish"}") {
sendEvent(name: evt.name, value: evt.value, descriptionText: evt.descriptionText, unit: evt.unit, displayed: evt.displayed);
LOGDEBUG("$evt.name") //: $evt.name, $evt.value $evt.unit"
}
}
@Field final List LUTable = [
[ ccode: 'breezy', altIcon: '23.png', ctext: 'Breezy', owmIcon: '50d', stdIcon: 'partlycloudy', luxpercent: 0.8 ],
[ ccode: 'breezyfoggy', altIcon: '48.png', ctext: 'Breezy and Foggy', owmIcon: '50d', stdIcon: 'fog', luxpercent: 0.2 ],
[ ccode: 'breezymostlycloudy', altIcon: '51.png', ctext: 'Breezy and Mostly Cloudy', owmIcon: '04d', stdIcon: 'mostlycloudy', luxpercent: 0.6 ],
[ ccode: 'breezyovercast', altIcon: '49.png', ctext: 'Breezy and Overcast', owmIcon: '04d', stdIcon: 'mostlycloudy', luxpercent: 0.6 ],
[ ccode: 'breezypartlycloudy', altIcon: '53.png', ctext: 'Breezy and Partly Cloudy', owmIcon: '03d', stdIcon: 'partlycloudy', luxpercent: 0.8 ],
[ ccode: 'chancelightrain', altIcon: '39.png', ctext: 'Chance of Light Rain', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'chancelightsnow', altIcon: '41.png', ctext: 'Possible Light Snow', owmIcon: '13d', stdIcon: 'snow', luxpercent: 0.3 ],
[ ccode: 'chancelightsnowbreezy', altIcon: '54.png', ctext: 'Possible Light Snow and Breezy', owmIcon: '13d', stdIcon: 'snow', luxpercent: 0.3 ],
[ ccode: 'chancerain', altIcon: '39.png', ctext: 'Chance of Rain', owmIcon: '10d', stdIcon: 'chancerain', luxpercent: 0.7 ],
[ ccode: 'chancesleet', altIcon: '41.png', ctext: 'Chance of Sleet', owmIcon: '13d', stdIcon: 'chancesleet', luxpercent: 0.7 ],
[ ccode: 'chancesnow', altIcon: '41.png', ctext: 'Chance of Snow', owmIcon: '13d', stdIcon: 'chancesnow', luxpercent: 0.3 ],
[ ccode: 'chancetstorms', altIcon: '38.png', ctext: 'Chance of Thunderstorms', owmIcon: '11d', stdIcon: 'chancetstorms', luxpercent: 0.2 ],
[ ccode: 'chancelightsnowwindy', altIcon: '54.png', ctext: 'Possible Light Snow and Windy', owmIcon: '13d', stdIcon: 'chancesnow', luxpercent: 0.3 ],
[ ccode: 'clear', altIcon: '32.png', ctext: 'Clear', owmIcon: '01d', stdIcon: 'clear', luxpercent: 1 ],
[ ccode: 'cloudy', altIcon: '26.png', ctext: 'Overcast', owmIcon: '04d', stdIcon: 'cloudy', luxpercent: 0.6 ],
[ ccode: 'drizzle', altIcon: '9.png', ctext: 'Drizzle', owmIcon: '09d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'flurries', altIcon: '13.png', ctext: 'Snow Flurries', owmIcon: '13d', stdIcon: 'flurries', luxpercent: 0.4 ],
[ ccode: 'fog', altIcon: '19.png', ctext: 'Foggy', owmIcon: '50d', stdIcon: 'fog', luxpercent: 0.2 ],
[ ccode: 'heavyrain', altIcon: '12.png', ctext: 'Heavy Rain', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'heavyrainbreezy', altIcon: '1.png', ctext: 'Heavy Rain and Breezy', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'heavyrainwindy', altIcon: '1.png', ctext: 'Heavy Rain and Windy', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'lightrain', altIcon: '11.png', ctext: 'Light Rain', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'lightrainbreezy', altIcon: '2.png', ctext: 'Light Rain and Breezy', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'lightrainwindy', altIcon: '2.png', ctext: 'Light Rain and Windy', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'lightsleet', altIcon: '8.png', ctext: 'Light Sleet', owmIcon: '13d', stdIcon: 'sleet', luxpercent: 0.5 ],
[ ccode: 'lightsnow', altIcon: '14.png', ctext: 'Light Snow', owmIcon: '13d', stdIcon: 'snow', luxpercent: 0.3 ],
[ ccode: 'mostlycloudy', altIcon: '28.png', ctext: 'Mostly Cloudy', owmIcon: '04d', stdIcon: 'mostlycloudy', luxpercent: 0.6 ],
[ ccode: 'partlycloudy', altIcon: '30.png', ctext: 'Partly Cloudy', owmIcon: '03d', stdIcon: 'partlycloudy', luxpercent: 0.8 ],
[ ccode: 'rain', altIcon: '12.png', ctext: 'Rain', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'rainbreezy', altIcon: '1.png', ctext: 'Rain and Breezy', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'rainwindy', altIcon: '1.png', ctext: 'Rain and Windy', owmIcon: '10d', stdIcon: 'rain', luxpercent: 0.5 ],
[ ccode: 'sleet', altIcon: '10.png', ctext: 'Sleet', owmIcon: '13d', stdIcon: 'sleet', luxpercent: 0.5 ],
[ ccode: 'snow', altIcon: '15.png', ctext: 'Snow', owmIcon: '13d', stdIcon: 'snow', luxpercent: 0.3 ],
[ ccode: 'sunny', altIcon: '36.png', ctext: 'Sunny', owmIcon: '01d', stdIcon: 'clear', luxpercent: 1 ],
[ ccode: 'thunderstorm', altIcon: '0.png', ctext: 'Thunderstorm', owmIcon: '11d', stdIcon: 'tstorms', luxpercent: 0.3 ],
[ ccode: 'wind', altIcon: '23.png', ctext: 'Windy', owmIcon: '50d', stdIcon: 'partlycloudy', luxpercent: 0.8 ],
[ ccode: 'windfoggy', altIcon: '23.png', ctext: 'Windy and Foggy', owmIcon: '50d', stdIcon: 'fog', luxpercent: 0.2 ],
[ ccode: 'windmostlycloudy', altIcon: '51.png', ctext: 'Windy and Mostly Cloudy', owmIcon: '50d', stdIcon: 'mostlycloudy', luxpercent: 0.6 ],
[ ccode: 'windovercast', altIcon: '49.png', ctext: 'Windy and Overcast', owmIcon: '50d', stdIcon: 'mostlycloudy', luxpercent: 0.6 ],
[ ccode: 'windpartlycloudy', altIcon: '53.png', ctext: 'Windy and Partly Cloudy', owmIcon: '50d', stdIcon: 'partlycloudy', luxpercent: 0.8 ],
[ ccode: 'nt_breezy', altIcon: '23.png', ctext: 'Breezy', owmIcon: '50n', stdIcon: 'nt_partlycloudy', luxpercent: 0 ],
[ ccode: 'nt_breezyfoggy', altIcon: '48.png', ctext: 'Breezy and Foggy', owmIcon: '50n', stdIcon: 'nt_fog', luxpercent: 0 ],
[ ccode: 'nt_breezymostlycloudy', altIcon: '50.png', ctext: 'Breezy and Mostly Cloudy', owmIcon: '04n', stdIcon: 'nt_mostlycloudy', luxpercent: 0 ],
[ ccode: 'nt_breezyovercast', altIcon: '49.png', ctext: 'Breezy and Overcast', owmIcon: '04n', stdIcon: 'nt_mostlycloudy', luxpercent: 0 ],
[ ccode: 'nt_breezypartlycloudy', altIcon: '52.png', ctext: 'Breezy and Partly Cloudy', owmIcon: '03n', stdIcon: 'nt_partlycloudy', luxpercent: 0 ],
[ ccode: 'nt_chancelightrain', altIcon: '45.png', ctext: 'Chance of Light Rain', owmIcon: '09n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_chancelightsnow', altIcon: '46.png', ctext: 'Possible Light Snow', owmIcon: '13n', stdIcon: 'nt_snow', luxpercent: 0 ],
[ ccode: 'nt_chancelightsnowbreezy', altIcon: '55.png', ctext: 'Possible Light Snow and Breezy', owmIcon: '13n', stdIcon: 'nt_snow', luxpercent: 0 ],
[ ccode: 'nt_chancerain', altIcon: '39.png', ctext: 'Chance of Rain', owmIcon: '09n', stdIcon: 'nt_chancerain', luxpercent: 0 ],
[ ccode: 'nt_chancesleet', altIcon: '46.png', ctext: 'Chance of Sleet', owmIcon: '13n', stdIcon: 'nt_chancesleet', luxpercent: 0 ],
[ ccode: 'nt_chancesnow', altIcon: '46.png', ctext: 'Chance of Snow', owmIcon: '13n', stdIcon: 'nt_chancesnow', luxpercent: 0 ],
[ ccode: 'nt_chancetstorms', altIcon: '47.png', ctext: 'Chance of Thunderstorms', owmIcon: '11n', stdIcon: 'nt_chancetstorms', luxpercent: 0 ],
[ ccode: 'nt_chancelightsnowwindy', altIcon: '55.png', ctext: 'Possible Light Snow and Windy', owmIcon: '13n', stdIcon: 'nt_chancesnow', luxpercent: 0 ],
[ ccode: 'nt_clear', altIcon: '31.png', ctext: 'Clear', owmIcon: '01n', stdIcon: 'nt_clear', luxpercent: 0 ],
[ ccode: 'nt_cloudy', altIcon: '26.png', ctext: 'Overcast', owmIcon: '04n', stdIcon: 'nt_cloudy', luxpercent: 0 ],
[ ccode: 'nt_drizzle', altIcon: '9.png', ctext: 'Drizzle', owmIcon: '09n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_flurries', altIcon: '13.png', ctext: 'Flurries', owmIcon: '13n', stdIcon: 'nt_flurries', luxpercent: 0 ],
[ ccode: 'nt_fog', altIcon: '22.png', ctext: 'Foggy', owmIcon: '50n', stdIcon: 'nt_fog', luxpercent: 0 ],
[ ccode: 'nt_heavyrain', altIcon: '12.png', ctext: 'Heavy Rain', owmIcon: '10n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_heavyrainbreezy', altIcon: '1.png', ctext: 'Heavy Rain and Breezy', owmIcon: '10n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_heavyrainwindy', altIcon: '1.png', ctext: 'Heavy Rain and Windy', owmIcon: '10n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_lightrain', altIcon: '11.png', ctext: 'Light Rain', owmIcon: '09n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_lightrainbreezy', altIcon: '11.png', ctext: 'Light Rain and Breezy', owmIcon: '09n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_lightrainwindy', altIcon: '11.png', ctext: 'Light Rain and Windy', owmIcon: '09n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_lightsleet', altIcon: '46.png', ctext: 'Sleet', owmIcon: '13n', stdIcon: 'nt_sleet', luxpercent: 0 ],
[ ccode: 'nt_lightsnow', altIcon: '14.png', ctext: 'Light Snow', owmIcon: '13n', stdIcon: 'nt_snow', luxpercent: 0 ],
[ ccode: 'nt_mostlycloudy', altIcon: '27.png', ctext: 'Mostly Cloudy', owmIcon: '04n', stdIcon: 'nt_mostlycloudy', luxpercent: 0 ],
[ ccode: 'nt_partlycloudy', altIcon: '29.png', ctext: 'Partly Cloudy', owmIcon: '03n', stdIcon: 'nt_partlycloudy', luxpercent: 0 ],
[ ccode: 'nt_rain', altIcon: '11.png', ctext: 'Rain', owmIcon: '10n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_rainbreezy', altIcon: '2.png', ctext: 'Rain and Breezy', owmIcon: '10n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_rainwindy', altIcon: '2.png', ctext: 'Rain and Windy', owmIcon: '10n', stdIcon: 'nt_rain', luxpercent: 0 ],
[ ccode: 'nt_sleet', altIcon: '46.png', ctext: 'Sleet', owmIcon: '13n', stdIcon: 'nt_sleet', luxpercent: 0 ],
[ ccode: 'nt_snow', altIcon: '46.png', ctext: 'Snow', owmIcon: '13n', stdIcon: 'nt_snow', luxpercent: 0 ],
[ ccode: 'nt_thunderstorm', altIcon: '0.png', ctext: 'Thunderstorm', owmIcon: '11n', stdIcon: 'nt_clear', luxpercent: 0 ],
[ ccode: 'nt_wind', altIcon: '23.png', ctext: 'Windy', owmIcon: '50n', stdIcon: 'nt_tstorms', luxpercent: 0 ],
[ ccode: 'nt_windfoggy', altIcon: '48.png', ctext: 'Windy and Foggy', owmIcon: '50n', stdIcon: 'nt_partlycloudy', luxpercent: 0 ],
[ ccode: 'nt_windmostlycloudy', altIcon: '50.png', ctext: 'Windy and Mostly Cloudy', owmIcon: '50n', stdIcon: 'nt_fog', luxpercent: 0 ],
[ ccode: 'nt_windovercast', altIcon: '49.png', ctext: 'Windy and Overcast', owmIcon: '50n', stdIcon: 'nt_mostlycloudy', luxpercent: 0 ],
[ ccode: 'nt_windpartlycloudy', altIcon: '52.png', ctext: 'Windy and Partly Cloudy', owmIcon: '50n', stdIcon: 'nt_mostlycloudy', luxpercent: 0 ],
]
@Field static attributesMap = [
"threedayTile": [title: "Three Day Forecast Tile", descr: "Display Three Day Forecast Tile?", typeof: false, default: "false"],
"alert": [title: "Weather Alert", descr: "Display any weather alert?", typeof: "string", default: "false"],
"betwixt": [title: "Slice of Day", descr: "Display the 'slice-of-day'?", typeof: "string", default: "false"],
"cloud": [title: "Cloud", descr: "Display cloud coverage %?", typeof: "number", default: "false"],
"condition_code": [title: "Condition Code", descr: "Display 'condition_code'?", typeof: "string", default: "false"],
"condition_icon_only": [title: "Condition Icon Only", descr: "Display 'condition_code_only'?", typeof: "string", default: "false"],
"condition_icon_url": [title: "Condition Icon URL", descr: "Display 'condition_code_url'?", typeof: "string", default: "false"],
"condition_icon": [title: "Condition Icon", descr: "Display 'condition_icon'?", typeof: "string", default: "false"],
"condition_iconWithText": [title: "Condition Icon With Text", descr: "Display 'condition_iconWithText'?", typeof: "string", default: "false"],
"condition_text": [title: "Condition Text", descr: "Display 'condition_text'?", typeof: "string", default: "false"],
"dashHubitatOWM": [title: "Dash - Hubitat and OpenWeatherMap", descr: "Display attributes required by Hubitat and OpenWeatherMap dashboards?", typeof: false, default: "false"],
"dashSmartTiles": [title: "Dash - SmartTiles", descr: "Display attributes required by SmartTiles dashboards?", typeof: false, default: "false"],
"dashSharpTools": [title: "Dash - SharpTools.io", descr: "Display attributes required by SharpTools.io?", typeof: false, default: "false"],
"dewpoint": [title: "Dewpoint (in default unit)", descr: "Display the dewpoint?", typeof: "number", default: "false"],
"dsAttribution": [title: "Dark Sky Attribution", descr: "Display the 'Dark Sky attribution'?", typeof: false, default: "false"],
"fcstHighLow": [title: "Forecast High/Low Temperatures:", descr: "Display forecast High/Low temperatures?", typeof: false, default: "false"],
"forecast_code": [title: "Forecast Code", descr: "Display 'forecast_code'?", typeof: "string", default: "false"],
"forecast_text": [title: "Forecast Text", descr: "Display 'forecast_text'?", typeof: "string", default: "false"],
"illuminated": [title: "Illuminated", descr: "Display 'illuminated' (with 'lux' added for use on a Dashboard)?", typeof: "string", default: "false"],
"is_day": [title: "Is daytime", descr: "Display 'is_day'?", typeof: "number", default: "false"],
"localSunrise": [title: "Local SunRise and SunSet", descr: "Display the Group of 'Time of Local Sunrise and Sunset,' with and without Dashboard text?", typeof: false, default: "false"],
"myTile": [title: "myTile for dashboard", descr: "Display 'mytile'?", typeof: "string", default: "false"],
"moonPhase": [title: "Moon Phase", descr: "Display 'moonPhase'?", typeof: "string", default: "false"],
"nearestStorm": [title: "Nearest Storm Info", descr: "Display nearest storm data'?", typeof: false, default: "false"],
"ozone": [title: "Ozone", descr: "Display 'ozone'?", typeof: "number", default: "false"],
"percentPrecip": [title: "Percent Precipitation", descr: "Display the Chance of Rain, in percent?", typeof: "number", default: "false"],
"precipExtended": [title: "Precipitation Forecast", descr: "Display precipitation forecast?", typeof: false, default: "false"],
"obspoll": [title: "Observation time", descr: "Display Observation and Poll times?", typeof: false, default: "false"],
"vis": [title: "Visibility (in default unit)", descr: "Display visibility distance?", typeof: "number", default: "false"],
"weatherSummary": [title: "Weather Summary Message", descr: "Display the Weather Summary?", typeof: "string", default: "false"],
"wind_cardinal": [title: "Wind Cardinal", descr: "Display the Wind Direction (text initials)?", typeof: "number", default: "false"],
"wind_degree": [title: "Wind Degree", descr: "Display the Wind Direction (number)?", typeof: "number", default: "false"],
"wind_direction": [title: "Wind direction", descr: "Display the Wind Direction (text words)?", typeof: "string", default: "false"],
"wind_gust": [title: "Wind gust (in default unit)", descr: "Display the Wind Gust?", typeof: "number", default: "false"],
"wind_string": [title: "Wind string", descr: "Display the wind string?", typeof: "string", default: "false"],
]
// Check Version ***** with great thanks and acknowledgment to Cobra (CobraVmax) for his original code ****
def updateCheck()
{
def paramsUD = [uri: "https://raw.githubusercontent.com/Scottma61/Hubitat/master/docs/version2.json"] //https://hubitatcommunity.github.io/???/version2.json"]
//asynchttpGet("updateCheckHandler", paramsUD)
state.Copyright = "Ⓒ 2019 Matthew (scottma61)"
state.UpdateInfo = "12/2/2019 - UNMANAGED"
state.InternalName = "DarkSky.net Weather Driver UNMANAGED"
state.Status = "Current Version: 1.2.9s - UNMANAGED"
}
void updateCheckHandler(resp, data) {
state.InternalName = "DarkSky.net Weather Driver"
boolean descTextEnable = settings?.logSet ?: false
if (resp.getStatus() == 200 || resp.getStatus() == 207) {
def respUD = parseJson(resp.data)
// log.warn " Version Checking - Response Data: $respUD" // Troubleshooting Debug Code - Uncommenting this line should show the JSON response from your webserver
state.Copyright = respUD.copyright
// uses reformattted 'version2.json'
String newVer = padVer(respUD.driver.(state.InternalName).ver)
String currentVer = padVer(version())
state.UpdateInfo = (respUD.driver.(state.InternalName).updated)
// log.debug "updateCheck: ${respUD.driver.(state.InternalName).ver}, $state.UpdateInfo, ${respUD.author}"
switch(newVer) {
case { it == "NLS"}:
state.Status = "<b>** This Driver is no longer supported by ${respUD.author} **</b>"
if (descTextEnable) log.warn "** This Driver is no longer supported by ${respUD.author} **"
break
case { it > currentVer}:
state.Status = "<b>New Version Available (Version: ${respUD.driver.(state.InternalName).ver})</b>"
if (descTextEnable) log.warn "** There is a newer version of this Driver available (Version: ${respUD.driver.(state.InternalName).ver}) **"
if (descTextEnable) log.warn "** $state.UpdateInfo **"
break
case { it < currentVer}:
state.Status = "<b>You are using a Test version of this Driver (Expecting: ${respUD.driver.(state.InternalName).ver})</b>"
if (descTextEnable) log.warn "You are using a Test version of this Driver (Expecting: ${respUD.driver.(state.InternalName).ver})"
break
default:
state.Status = "Current Version: ${respUD.driver.(state.InternalName).ver}"
if (descTextEnable) log.info "You are using the current version of this driver"
break
}
} else {
log.error "Something went wrong: CHECK THE JSON FILE AND IT'S URI"
}
}
/*
padVer
Version progression of 1.4.9 to 1.4.10 would mis-compare unless each duple is padded first.
*/
String padVer(String ver) {
String pad = ""
ver.replaceAll( "[vV]", "" ).split( /\./ ).each { pad += it.padLeft( 2, '0' ) }
return pad
}
String getThisCopyright(){"&copy; 2019 Matthew (scottma61) "}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment