/*********************************************************************************************************************** * Copyright 2018 bangali * * Contributors: * https://github.com/jebbett code for new weather icons based on weather condition data * https://www.deviantart.com/vclouds/art/VClouds-Weather-Icons-179152045 new weather icons courtesy of VClouds * * 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. * * ApiXU Weather Driver * * Author: bangali * ***********************************************************************************************************************/ public static String version() { return "v1.0.4" } /*********************************************************************************************************************** * * Based off Version: 4.2.0 */ import groovy.transform.Field metadata { definition (name: "ApiXU Weather Driver Min", namespace: "bangali", author: "bangali", importUrl: "https://raw.githubusercontent.com/tonesto7/nst-manager-he/master/drivers/apixu-weather-min.groovy") { capability "Initialize" // capability "Actuator" capability "Sensor" capability "Polling" capability "Illuminance Measurement" capability "Temperature Measurement" capability "Relative Humidity Measurement" capability "Pressure Measurement" capability "Ultraviolet Index" // capability "Switch" // Standard attributes from capabilities // attribute "temperature", "string" // attribute "humidity", "string" // attribute "illuminance", "string" // attribute "pressure", "string" // attribute "ultravioletIndex", "string" // name not needed city has this // attribute "name", "string" attribute "region", "string" attribute "country", "string" // not needed // attribute "lat", "string" // attribute "lon", "string" // attribute "tz_id", "string" // these only used in dashboards // attribute "localtime_epoch", "string" // attribute "local_time", "string" // attribute "local_date", "string" // attribute "last_updated_epoch", "string" attribute "last_updated", "string" // not needed // attribute "temp_c", "string" // attribute "temp_f", "string" // not needed // attribute "is_day", "string" // attribute "condition_text", "string" // attribute "condition_icon", "string" // attribute "condition_icon_url", "string" // attribute "condition_icon_only", "string" attribute "condition_code", "string" // attribute "visual", "string" // attribute "visualWithText", "string" // obsolete // attribute "wind_mph", "string" // attribute "wind_kph", "string" // attribute "wind_mps", "string" attribute "wind_degree", "string" attribute "wind_dir", "string" // attribute "pressure_mb", "string" // attribute "pressure_in", "string" // not needed // attribute "precip_mm", "string" // attribute "precip_in", "string" attribute "precip_today", "string" // not needed feelsLike covers this // attribute "cloud", "string" // attribute "feelslike_c", "string" // attribute "feelslike_f", "string" attribute "dewpoint", "string" attribute "visibility", "string" // not needed (visibility covers these) // attribute "vis_km", "string" // attribute "vis_miles", "string" attribute "location", "string" attribute "city", "string" // used by dashboards as eye candy // attribute "local_sunrise", "string" // attribute "local_sunset", "string" // attribute "twilight_begin", "string" // attribute "twilight_end", "string" // obsolete covered by illuminance // attribute "illuminated", "string" //not needed // attribute "cCF", "string" // attribute "lastXUupdate", "string" attribute "weather", "string" attribute "weatherIcon", "string" attribute "feelsLike", "string" attribute "wind", "string" // not filled in by apiXU // attribute "percentPrecip", "string" // used by dashboards - not needed // attribute "localSunrise", "string" // attribute "localSunset", "string" // attribute "visualDayPlus1", "string" // attribute "visualDayPlus1WithText", "string" // attribute "temperatureLowDayPlus1", "string" // attribute "temperatureHighDayPlus1", "string" attribute "temperatureLow", "string" attribute "temperatureHigh", "string" // attribute "forecastIcon", "string" // attribute "forecast_icon_url", "string" // attribute "forecast_text", "string" // attribute "forecast_code", "string" // attribute "wind_mytile", "string" // attribute "mytile", "string" attribute "apiXUquery", "string" command "refresh" } preferences { input "apixuKey", "text", title: "ApiXU key?", required: true input "zipCode", "text", title: "Override Zip code or set city name or latitude,longitude? (Default: ${location.zipCode})", defaultValue: null, required: false input "cityName", "text", title: "Override default city name?", required: false, defaultValue: null // input "isFahrenheit", "bool", title: "Use Imperial units?", required: false, defaultValue: true // input "publishWU", "bool", title: "Publish WU mappings?", required: true, defaultValue: false // input "dashClock", "bool", title: "Flash time ':' every 2 seconds?", required: false, defaultValue: false input "pollEvery", "enum", title: "Poll ApiXU how frequently?\nrecommended setting 30 minutes.\nilluminance is always updated every 5 minutes.", required: false, defaultValue: 30, options: [5:"5 minutes",10:"10 minutes",15:"15 minutes",30:"30 minutes"] } } def installed() { updated() } def initialize() { updated() } def updated() { if(apixuKey) { state.zipCode = settings?.zipCode ?: location.zipCode state.poll = settings?.pollEvery ?: 30 state.wantMetric = settings?.isFahrenheit != null ? !settings?.isFahrenheit : (getTemperatureScale() == "C") unschedule() state.remove("tz_id") state.remove("today") //state.remove("cloud") //state.remove("clockSeconds") poll() "runEvery${state.poll}Minutes"(poll) runEvery5Minutes(updateLux) // schedule("0 * * * * ?", updateClock) // schedule("0/2 0 0 ? * * *", updateClock) // if (dashClock) updateClock(); } else { log.error "apixuKey not set" } } def wantMetric() { def t0 = state?.wantMetric == true ? true : false return t0 } def poll() { def obs = getXUdata() // this is not async call if (!obs) { log.warn "No response from ApiXU API" return } } def finishPoll(obs) { if (!obs) { log.warn "No response from ApiXU API" return } log.debug ">>>>> apixu: Executing 'poll', location: ${state?.zipCode}" def now = new Date().format('yyyy-MM-dd HH:mm', location.timeZone) // sendEvent(name: "lastXUupdate", value: now, /* isStateChange: true, */ displayed: true) def tZ = TimeZone.getTimeZone(obs.location.tz_id) state.tz_id = obs.location.tz_id def localTime = new Date().parse("yyyy-MM-dd HH:mm", obs.location.localtime, tZ) def localDate = localTime.format("yyyy-MM-dd", tZ) def localTimeOnly = localTime.format("HH:mm", tZ) def todayDay = localTime.format("dd", tZ) if (!state?.today || state.today != todayDay) { state.today = todayDay def sunriseAndSunset = getSunriseAndSunset(obs.location.lat, obs.location.lon, localDate) def sunriseTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunriseAndSunset.results.sunrise, tZ) def sunsetTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunriseAndSunset.results.sunset, tZ) def noonTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunriseAndSunset.results.solar_noon, tZ) def twilight_begin = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunriseAndSunset.results.civil_twilight_begin, tZ) def twilight_end = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", sunriseAndSunset.results.civil_twilight_end, tZ) def localSunrise = sunriseTime.format("HH:mm", tZ) // sendEvent(name: "local_sunrise", value: localSunrise, descriptionText: "Sunrise today is at $localSunrise", /* isStateChange: true, */ displayed: true) def localSunset = sunsetTime.format("HH:mm", tZ) // sendEvent(name: "local_sunset", value: localSunset, descriptionText: "Sunset today at is $localSunset", /* isStateChange: true, */ displayed: true) def tB = twilight_begin.format("HH:mm", tZ) // sendEvent(name: "twilight_begin", value: tB, descriptionText: "Twilight begins today at $tB", /* isStateChange: true, */ displayed: true) def tE = twilight_end.format("HH:mm", tZ) // sendEvent(name: "twilight_end", value: tE, descriptionText: "Twilight ends today at $tE", /* isStateChange: true, */ displayed: true) // sendEvent(name: "localSunrise", value: localSunrise, /* isStateChange: true, */ displayed: true) // sendEvent(name: "localSunset", value: localSunset, /* isStateChange: true, */ displayed: true) state.sunriseTime = sunriseTime.format("yyyy-MM-dd'T'HH:mm:ssXXX", tZ) state.sunsetTime = sunsetTime.format("yyyy-MM-dd'T'HH:mm:ssXXX", tZ) state.noonTime = noonTime.format("yyyy-MM-dd'T'HH:mm:ssXXX", tZ) state.twilight_begin = twilight_begin.format("yyyy-MM-dd'T'HH:mm:ssXXX", tZ) state.twilight_end = twilight_end.format("yyyy-MM-dd'T'HH:mm:ssXXX", tZ) } // sendEvent(name: "name", value: obs.location.name, /* isStateChange: true, */ displayed: true) sendEvent(name: "region", value: obs.location.region, /* isStateChange: true, */ displayed: true) sendEvent(name: "country", value: obs.location.country, /* isStateChange: true, */ displayed: true) // sendEvent(name: "lat", value: obs.location.lat, /* isStateChange: true, */ displayed: true) // sendEvent(name: "lon", value: obs.location.lon, /* isStateChange: true, */ displayed: true) // sendEvent(name: "tz_id", value: obs.location.tz_id, /* isStateChange: true, */ displayed: true) // sendEvent(name: "localtime_epoch", value: obs.location.localtime_epoch, /* isStateChange: true, */ displayed: false) // sendEvent(name: "local_time", value: localTimeOnly, /* isStateChange: true, */ displayed: false) // sendEvent(name: "local_date", value: localDate, /* isStateChange: true, */ displayed: false) // sendEvent(name: "last_updated_epoch", value: obs.current.last_updated_epoch, /* isStateChange: true, */ displayed: true) if(isStateChange(device, "last_updated", obs.current.last_updated.toString())) { sendEvent(name: "last_updated", value: obs.current.last_updated, isStateChange: true, displayed: true) } // sendEvent(name: "temp_c", value: obs.current.temp_c, unit: "C") // sendEvent(name: "temp_f", value: obs.current.temp_f, unit: "F") def t0 = (!wantMetric() ? obs.current.temp_f : obs.current.temp_c) sendEvent(name: "temperature", value: t0, unit: "${(!wantMetric() ? 'F' : 'C')}", /* isStateChange: true, */ displayed: true) // sendEvent(name: "is_day", value: obs.current.is_day, /* isStateChange: true, */ displayed: true) // sendEvent(name: "condition_text", value: obs.current.condition.text, /* isStateChange: true, */ displayed: true) // same as "weather" below // sendEvent(name: "condition_icon", value: '', /* isStateChange: true, */ displayed: true) // sendEvent(name: "condition_icon_url", value: 'https:' + obs.current.condition.icon, /* isStateChange: true, */ displayed: true) sendEvent(name: "condition_code", value: obs.current.condition.code, /* isStateChange: true, */ displayed: true) // sendEvent(name: "condition_icon_only", value: obs.current.condition.icon.split("/")[-1], /* isStateChange: true, */ displayed: true) def imgName = getImgName(obs.current.condition.code, obs.current.is_day) // sendEvent(name: "visual", value: '', /* isStateChange: true, */ displayed: true) // sendEvent(name: "visualWithText", value: '
' + obs.current.condition.text, /* isStateChange: true, */ displayed: true) // sendEvent(name: "wind_mph", value: obs.current.wind_mph, unit: "MPH", /* isStateChange: true, */ displayed: true) // sendEvent(name: "wind_kph", value: obs.current.wind_kph, unit: "KPH", /* isStateChange: true, */ displayed: true) // sendEvent(name: "wind_mps", value: ((obs.current.wind_kph / 3.6f).round(1)), unit: "MPS", /* isStateChange: true, */ displayed: true) sendEvent(name: "wind_degree", value: obs.current.wind_degree, unit: "DEGREE", /* isStateChange: true, */ displayed: true) sendEvent(name: "wind_dir", value: obs.current.wind_dir, /* isStateChange: true, */ displayed: true) sendEvent(name: "ultravioletIndex", value: obs.current.uv, /* isStateChange: true, */ displayed: true) // sendEvent(name: "pressure_mb", value: obs.current.pressure_mb, unit: "MBAR") // sendEvent(name: "pressure_in", value: obs.current.pressure_in, unit: "IN") sendEvent(name: "pressure", value: (!wantMetric() ? obs.current.pressure_in : obs.current.pressure_mb), unit: "${(!wantMetric() ? 'IN' : 'MBAR')}", /* isStateChange: true, */ displayed: true) // sendEvent(name: "precip_mm", value: obs.current.precip_mm, unit: "MM", /* isStateChange: true, */ displayed: true) // sendEvent(name: "precip_in", value: obs.current.precip_in, unit: "IN", /* isStateChange: true, */ displayed: true) sendEvent(name: "humidity", value: obs.current.humidity, unit: "%", /* isStateChange: true, */ displayed: true) // sendEvent(name: "cloud", value: obs.current.cloud, unit: "%", /* isStateChange: true, */ displayed: true) // sendEvent(name: "feelslike_c", value: obs.current.feelslike_c, unit: "C", /* isStateChange: true, */ displayed: true) // sendEvent(name: "feelslike_f", value: obs.current.feelslike_f, unit: "F", /* isStateChange: true, */ displayed: true) double hum = obs.current.humidity?.toString().replaceAll("\\%", "") as Double double Tc = Math.round(obs.current.feelslike_c as Double) as Double float curDew = estimateDewPoint(hum,Tc) if(obs.current.temp_c < curDew) { curDew = obs.current.temp_c } curDew = !wantMetric() ? (curDew * 9/5 + 32).round(1) : curDew sendEvent(name: "dewpoint", value: curDew, unit: "${(!wantMetric() ? 'F' : 'C')}", /* isStateChange: true, */ displayed: true) // sendEvent(name: "vis_km", value: obs.current.vis_km, unit: "KM", /* isStateChange: true, */ displayed: true) // sendEvent(name: "vis_miles", value: obs.current.vis_miles, unit: "MILES", /* isStateChange: true, */ displayed: true) def myCity = cityName ?: obs.location.name sendEvent(name: "location", value: myCity + ', ' + obs.location.region, /* isStateChange: true, */ displayed: true) state.condition_code = obs.current.condition.code //state.cloud = obs.current.cloud updateLux() // if (publishWU) { sendEvent(name: "city", value: myCity, /* isStateChange: true, */ displayed: true) sendEvent(name: "weather", value: getWUIconName(obs.current.condition.code, 1), /* isStateChange: true, */ displayed: true) sendEvent(name: "weatherIcon", value: getWUIconNum(obs.current.condition.code), /* isStateChange: true, */ displayed: true) // sendEvent(name: "weather", value: obs.current.condition.text, /* isStateChange: true, */ displayed: true) sendEvent(name: "feelsLike", value: (!wantMetric() ? obs.current.feelslike_f : obs.current.feelslike_c), unit: "${(!wantMetric() ? 'F' : 'C')}", /* isStateChange: true, */ displayed: true) sendEvent(name: "wind", value: (!wantMetric() ? obs.current.wind_mph : obs.current.wind_kph), unit: "${(!wantMetric() ? 'MPH' : 'KPH')}", /* isStateChange: true, */ displayed: true) sendEvent(name: "visibility", value: (!wantMetric() ? obs.current.vis_miles : obs.current.vis_km), unit: "${(!wantMetric() ? 'MILES' : 'KM')}", /* isStateChange: true, */ displayed: true) sendEvent(name: "precip_today", value: (!wantMetric() ? obs.current.precip_in : obs.current.precip_mm), unit: "${(!wantMetric() ? 'IN' : 'MM')}", /* isStateChange: true, */ displayed: true) // sendEvent(name: "percentPrecip", value: (!wantMetric() ? obs.current.precip_in : obs.current.precip_mm), unit: "${(!wantMetric() ? 'IN' : 'MM')}", /* isStateChange: true, */ displayed: true) // } // sendEvent(name: "forecastIcon", value: getWUIconName(obs.forecast.forecastday[1].day.condition.code, 1), /* isStateChange: true, */ displayed: true) // sendEvent(name: "forecast_icon_url", value: 'https:' + obs.forecast.forecastday[1].day.condition.icon, /* isStateChange: true, */ displayed: true) // sendEvent(name: "forecast_text", value: obs.forecast.forecastday[1].day.condition.text, /* isStateChange: true, */ displayed: true) // sendEvent(name: "forecast_code", value: obs.forecast.forecastday[1].day.condition.code, /* isStateChange: true, */ displayed: true) // imgName = getImgName(obs.forecast.forecastday[1].day.condition.code, 1) // sendEvent(name: "visualDayPlus1", value: '', /* isStateChange: true, */ displayed: true) // sendEvent(name: "visualDayPlus1WithText", value: '
' + obs.forecast.forecastday[1].day.condition.text, /* isStateChange: true, */ displayed: true) sendEvent(name: "temperatureHigh", value: (!wantMetric() ? obs.forecast.forecastday[0].day.maxtemp_f : obs.forecast.forecastday[1].day.maxtemp_c), unit: "${(!wantMetric() ? 'F' : 'C')}", /* isStateChange: true, */ displayed: true) // sendEvent(name: "temperatureHighDayPlus1", value: (!wantMetric() ? obs.forecast.forecastday[1].day.maxtemp_f : // obs.forecast.forecastday[1].day.maxtemp_c), unit: "${(!wantMetric() ? 'F' : 'C')}", /* isStateChange: true, */ displayed: true) sendEvent(name: "temperatureLow", value: (!wantMetric() ? obs.forecast.forecastday[0].day.mintemp_f : obs.forecast.forecastday[1].day.mintemp_c), unit: "${(!wantMetric() ? 'F' : 'C')}", /* isStateChange: true, */ displayed: true) // sendEvent(name: "temperatureLowDayPlus1", value: (!wantMetric() ? obs.forecast.forecastday[1].day.mintemp_f : // obs.forecast.forecastday[1].day.mintemp_c), unit: "${(!wantMetric() ? 'F' : 'C')}", /* isStateChange: true, */ displayed: true) // def wind_mytile=(!wantMetric() ? "${Math.round(obs.current.wind_mph)}" + " mph " : "${Math.round(obs.current.wind_kph)}" + " kph ") // sendEvent(name: "wind_mytile", value: wind_mytile, /* isStateChange: true, */ displayed: true) // def mytext = myCity + ', ' + obs.location.region // mytext += '
' + (!wantMetric() ? "${Math.round(obs.current.temp_f)}" + '°F ' : obs.current.temp_c + '°C ') + obs.current.humidity + '%' // mytext += '
' + device.currentState("localSunrise")?.value + ' ' + device.currentState("localSunset")?.value // mytext += (wind_mytile == (!wantMetric() ? "0 mph " : "0 kph ") ? '
Wind is calm' : '
' + obs.current.wind_dir + ' ' + wind_mytile) // mytext += '
' + obs.current.condition.text // sendEvent(name: "mytile", value: mytext, /* isStateChange: true, */ displayed: true) return } def refresh() { poll() } def configure() { poll() } private getXUdata() { //def obs = [:] def myUri = "https://api.apixu.com/v1/forecast.json?key=${apixuKey}&q=${state?.zipCode}&days=7" def params = [ uri: myUri ] try { asynchttpGet('ahttpRequestHandler', params, [tt: 'finishPoll']) /* httpGet(params) { resp -> if (resp?.data) obs << resp.data; else log.error "http call for ApiXU weather api did not return data: $resp"; } */ } catch (e) { log.error "http call failed for ApiXU weather api: $e" return false } sendEvent(name: "apiXUquery", value: myUri, /* isStateChange: true, */ displayed: true) return true } public ahttpRequestHandler(resp, callbackData) { def json = [:] def obs = [:] if ((resp.status == 200) && resp.data) { try { json = resp.getJson() } catch (all) { json = [:] return } } else { if(resp.hasError()) { log.error "http Response Status: ${resp.status} error Message: ${resp.getErrorMessage()}" return } log.error "no data: ${resp.status} resp.data: ${resp.data} resp.json: ${resp.json}" return } obs = json //log.debug "$obs" def t0 = callbackData.tt "${t0}"(obs) } private getSunriseAndSunset(latitude, longitude, forDate) { def params = [ uri: "https://api.sunrise-sunset.org/json?lat=$latitude&lng=$longitude&date=$forDate&formatted=0" ] def sunRiseAndSet = [:] try { httpGet(params) { resp -> sunRiseAndSet = resp.data } } catch (e) { log.error "http call failed for sunrise and sunset api: $e" } return sunRiseAndSet } private estimateDewPoint(double rh,double t) { double L = Math.log(rh/100) double M = 17.27 * t double N = 237.3 + t double B = (L + (M/N)) / 17.27 double dp = (237.3 * B) / (1 - B) double dp1 = 243.04 * ( Math.log(rh / 100) + ( (17.625 * t) / (243.04 + t) ) ) / (17.625 - Math.log(rh / 100) - ( (17.625 * t) / (243.04 + t) ) ) double ave = (dp + dp1)/2 //log.debug "dp: ${dp.round(1)} dp1: ${dp1.round(1)} ave: ${ave.round(1)}" ave = dp1 return ave.round(1) } def updateLux() { if (!state.sunriseTime || !state.sunsetTime || !state.noonTime || !state.twilight_begin || !state.twilight_end || !state.tz_id) return def tZ = TimeZone.getTimeZone(state.tz_id) def lT = new Date().format("yyyy-MM-dd'T'HH:mm:ssXXX", tZ) def localTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", lT, tZ) def sunriseTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", state.sunriseTime, tZ) def sunsetTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", state.sunsetTime, tZ) def noonTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", state.noonTime, tZ) def twilight_begin = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", state.twilight_begin, tZ) def twilight_end = new Date().parse("yyyy-MM-dd'T'HH:mm:ssXXX", state.twilight_end, tZ) def lux = estimateLux(localTime, sunriseTime, sunsetTime, noonTime, twilight_begin, twilight_end, state.condition_code, state.cloud, state.tz_id) sendEvent(name: "illuminance", value: lux, unit: "lux", /* isStateChange: true, */ displayed: true) //sendEvent(name: "illuminated", value: String.format("%,d lux", lux), /* isStateChange: true, */ displayed: true) } private estimateLux(localTime, sunriseTime, sunsetTime, noonTime, twilight_begin, twilight_end, condition_code, cloud, tz_id) { // log.debug "condition_code: $condition_code | cloud: $cloud" // log.debug "twilight_begin: $twilight_begin | twilight_end: $twilight_end | tz_id: $tz_id" // log.debug "localTime: $localTime | sunriseTime: $sunriseTime | noonTime: $noonTime | sunsetTime: $sunsetTime" def tZ = TimeZone.getTimeZone(tz_id) long lux = 0l boolean aFCC = true float l if (timeOfDayIsBetween(sunriseTime, noonTime, localTime, tZ)) { //log.debug "between sunrise and noon" l = (((localTime.getTime() - sunriseTime.getTime()) * 10000f) / (noonTime.getTime() - sunriseTime.getTime())) lux = (l < 50f ? 50l : l.trunc(0) as long) } else if (timeOfDayIsBetween(noonTime, sunsetTime, localTime, tZ)) { //log.debug "between noon and sunset" l = (((sunsetTime.getTime() - localTime.getTime()) * 10000f) / (sunsetTime.getTime() - noonTime.getTime())) lux = (l < 50f ? 50l : l.trunc(0) as long) } else if (timeOfDayIsBetween(twilight_begin, sunriseTime, localTime, tZ)) { //log.debug "between sunrise and twilight" l = (((localTime.getTime() - twilight_begin.getTime()) * 50f) / (sunriseTime.getTime() - twilight_begin.getTime())) lux = (l < 10f ? 10l : l.trunc(0) as long) } else if (timeOfDayIsBetween(sunsetTime, twilight_end, localTime, tZ)) { //log.debug "between sunset and twilight" l = (((twilight_end.getTime() - localTime.getTime()) * 50f) / (twilight_end.getTime() - sunsetTime.getTime())) lux = (l < 10f ? 10l : l.trunc(0) as long) } else if (!timeOfDayIsBetween(twilight_begin, twilight_end, localTime, tZ)) { //log.debug "between non-twilight" lux = 5l aFCC = false } int cC = condition_code.toInteger() String cCT = '' float cCF if (aFCC) if (conditionFactor[cC]) { cCF = conditionFactor[cC][1] cCT = conditionFactor[cC][0] } else { cCF = ((100 - (cloud.toInteger() / 3d)) / 100).round(1) cCT = 'using cloud cover' } else { cCF = 1.0 cCT = 'night time now' } lux = (lux * cCF) as long 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 } // log.debug "condition: $cC | condition text: $cCT | condition factor: $cCF | lux: $lux" // sendEvent(name: "cCF", value: cCF, /* isStateChange: true, */ displayed: true) return lux } private timeOfDayIsBetween(fromDate, toDate, checkDate, timeZone) { return (!checkDate.before(fromDate) && !checkDate.after(toDate)) } def updateClock() { runIn(2, updateClock) if (!state.tz_id) return; if (!tz_id) return; def nowTime = new Date() def tZ = TimeZone.getTimeZone(state.tz_id) sendEvent(name: "local_time", value: nowTime.format("HH:mm", tZ), displayed: true) def localDate = nowTime.format("yyyy-MM-dd", tZ) if (localDate != state.localDate) { state.localDate = localDate sendEvent(name: "local_date", value: localDate, displayed: true) } } def getWUIconName(condition_code, is_day) { def cC = condition_code.toInteger() def wuIcon = (conditionFactor[cC] ? conditionFactor[cC][2] : '') if (is_day != 1 && wuIcon) wuIcon = 'nt_' + wuIcon; return wuIcon } def getWUIconNum(wCode) { def imgItem = imgNames.find{ it.code == wCode } return (imgItem ? imgItem.img : '44') } @Field final Map conditionFactor = [ 1000: ['Sunny', 1, 'sunny'], 1003: ['Partly cloudy', 0.8, 'partlycloudy'], 1006: ['Cloudy', 0.6, 'cloudy'], 1009: ['Overcast', 0.5, 'cloudy'], 1030: ['Mist', 0.5, 'fog'], 1063: ['Patchy rain possible', 0.8, 'chancerain'], 1066: ['Patchy snow possible', 0.6, 'chancesnow'], 1069: ['Patchy sleet possible', 0.6, 'chancesleet'], 1072: ['Patchy freezing drizzle possible', 0.4, 'chancesleet'], 1087: ['Thundery outbreaks possible', 0.2, 'chancetstorms'], 1114: ['Blowing snow', 0.3, 'snow'], 1117: ['Blizzard', 0.1, 'snow'], 1135: ['Fog', 0.2, 'fog'], 1147: ['Freezing fog', 0.1, 'fog'], 1150: ['Patchy light drizzle', 0.8, 'rain'], 1153: ['Light drizzle', 0.7, 'rain'], 1168: ['Freezing drizzle', 0.5, 'sleet'], 1171: ['Heavy freezing drizzle', 0.2, 'sleet'], 1180: ['Patchy light rain', 0.8, 'rain'], 1183: ['Light rain', 0.7, 'rain'], 1186: ['Moderate rain at times', 0.5, 'rain'], 1189: ['Moderate rain', 0.4, 'rain'], 1192: ['Heavy rain at times', 0.3, 'rain'], 1195: ['Heavy rain', 0.2, 'rain'], 1198: ['Light freezing rain', 0.7, 'sleet'], 1201: ['Moderate or heavy freezing rain', 0.3, 'sleet'], 1204: ['Light sleet', 0.5, 'sleet'], 1207: ['Moderate or heavy sleet', 0.3, 'sleet'], 1210: ['Patchy light snow', 0.8, 'flurries'], 1213: ['Light snow', 0.7, 'snow'], 1216: ['Patchy moderate snow', 0.6, 'snow'], 1219: ['Moderate snow', 0.5, 'snow'], 1222: ['Patchy heavy snow', 0.4, 'snow'], 1225: ['Heavy snow', 0.3, 'snow'], 1237: ['Ice pellets', 0.5, 'sleet'], 1240: ['Light rain shower', 0.8, 'rain'], 1243: ['Moderate or heavy rain shower', 0.3, 'rain'], 1246: ['Torrential rain shower', 0.1, 'rain'], 1249: ['Light sleet showers', 0.7, 'sleet'], 1252: ['Moderate or heavy sleet showers', 0.5, 'sleet'], 1255: ['Light snow showers', 0.7, 'snow'], 1258: ['Moderate or heavy snow showers', 0.5, 'snow'], 1261: ['Light showers of ice pellets', 0.7, 'sleet'], 1264: ['Moderate or heavy showers of ice pellets',0.3, 'sleet'], 1273: ['Patchy light rain with thunder', 0.5, 'tstorms'], 1276: ['Moderate or heavy rain with thunder', 0.3, 'tstorms'], 1279: ['Patchy light snow with thunder', 0.5, 'tstorms'], 1282: ['Moderate or heavy snow with thunder', 0.3, 'tstorms'] ] private getImgName(wCode, is_day) { def url = "https://cdn.rawgit.com/adey/bangali/master/resources/icons/weather/" def imgItem = imgNames.find{ it.code == wCode && it.day == is_day } return (url + (imgItem ? imgItem.img : 'na') + '.png') } @Field final List imgNames = [ [code: 1000, day: 1, img: '32', ], // DAY - Sunny [code: 1003, day: 1, img: '30', ], // DAY - Partly cloudy [code: 1006, day: 1, img: '28', ], // DAY - Cloudy [code: 1009, day: 1, img: '26', ], // DAY - Overcast [code: 1030, day: 1, img: '20', ], // DAY - Mist [code: 1063, day: 1, img: '39', ], // DAY - Patchy rain possible [code: 1066, day: 1, img: '41', ], // DAY - Patchy snow possible [code: 1069, day: 1, img: '41', ], // DAY - Patchy sleet possible [code: 1072, day: 1, img: '39', ], // DAY - Patchy freezing drizzle possible [code: 1087, day: 1, img: '38', ], // DAY - Thundery outbreaks possible [code: 1114, day: 1, img: '15', ], // DAY - Blowing snow [code: 1117, day: 1, img: '16', ], // DAY - Blizzard [code: 1135, day: 1, img: '21', ], // DAY - Fog [code: 1147, day: 1, img: '21', ], // DAY - Freezing fog [code: 1150, day: 1, img: '39', ], // DAY - Patchy light drizzle [code: 1153, day: 1, img: '11', ], // DAY - Light drizzle [code: 1168, day: 1, img: '8', ], // DAY - Freezing drizzle [code: 1171, day: 1, img: '10', ], // DAY - Heavy freezing drizzle [code: 1180, day: 1, img: '39', ], // DAY - Patchy light rain [code: 1183, day: 1, img: '11', ], // DAY - Light rain [code: 1186, day: 1, img: '39', ], // DAY - Moderate rain at times [code: 1189, day: 1, img: '12', ], // DAY - Moderate rain [code: 1192, day: 1, img: '39', ], // DAY - Heavy rain at times [code: 1195, day: 1, img: '12', ], // DAY - Heavy rain [code: 1198, day: 1, img: '8', ], // DAY - Light freezing rain [code: 1201, day: 1, img: '10', ], // DAY - Moderate or heavy freezing rain [code: 1204, day: 1, img: '5', ], // DAY - Light sleet [code: 1207, day: 1, img: '6', ], // DAY - Moderate or heavy sleet [code: 1210, day: 1, img: '41', ], // DAY - Patchy light snow [code: 1213, day: 1, img: '18', ], // DAY - Light snow [code: 1216, day: 1, img: '41', ], // DAY - Patchy moderate snow [code: 1219, day: 1, img: '16', ], // DAY - Moderate snow [code: 1222, day: 1, img: '41', ], // DAY - Patchy heavy snow [code: 1225, day: 1, img: '16', ], // DAY - Heavy snow [code: 1237, day: 1, img: '18', ], // DAY - Ice pellets [code: 1240, day: 1, img: '11', ], // DAY - Light rain shower [code: 1243, day: 1, img: '12', ], // DAY - Moderate or heavy rain shower [code: 1246, day: 1, img: '12', ], // DAY - Torrential rain shower [code: 1249, day: 1, img: '5', ], // DAY - Light sleet showers [code: 1252, day: 1, img: '6', ], // DAY - Moderate or heavy sleet showers [code: 1255, day: 1, img: '16', ], // DAY - Light snow showers [code: 1258, day: 1, img: '16', ], // DAY - Moderate or heavy snow showers [code: 1261, day: 1, img: '8', ], // DAY - Light showers of ice pellets [code: 1264, day: 1, img: '10', ], // DAY - Moderate or heavy showers of ice pellets [code: 1273, day: 1, img: '38', ], // DAY - Patchy light rain with thunder [code: 1276, day: 1, img: '35', ], // DAY - Moderate or heavy rain with thunder [code: 1279, day: 1, img: '41', ], // DAY - Patchy light snow with thunder [code: 1282, day: 1, img: '18', ], // DAY - Moderate or heavy snow with thunder [code: 1000, day: 0, img: '31', ], // NIGHT - Clear [code: 1003, day: 0, img: '29', ], // NIGHT - Partly cloudy [code: 1006, day: 0, img: '27', ], // NIGHT - Cloudy [code: 1009, day: 0, img: '26', ], // NIGHT - Overcast [code: 1030, day: 0, img: '20', ], // NIGHT - Mist [code: 1063, day: 0, img: '45', ], // NIGHT - Patchy rain possible [code: 1066, day: 0, img: '46', ], // NIGHT - Patchy snow possible [code: 1069, day: 0, img: '46', ], // NIGHT - Patchy sleet possible [code: 1072, day: 0, img: '45', ], // NIGHT - Patchy freezing drizzle possible [code: 1087, day: 0, img: '47', ], // NIGHT - Thundery outbreaks possible [code: 1114, day: 0, img: '15', ], // NIGHT - Blowing snow [code: 1117, day: 0, img: '16', ], // NIGHT - Blizzard [code: 1135, day: 0, img: '21', ], // NIGHT - Fog [code: 1147, day: 0, img: '21', ], // NIGHT - Freezing fog [code: 1150, day: 0, img: '45', ], // NIGHT - Patchy light drizzle [code: 1153, day: 0, img: '11', ], // NIGHT - Light drizzle [code: 1168, day: 0, img: '8', ], // NIGHT - Freezing drizzle [code: 1171, day: 0, img: '10', ], // NIGHT - Heavy freezing drizzle [code: 1180, day: 0, img: '45', ], // NIGHT - Patchy light rain [code: 1183, day: 0, img: '11', ], // NIGHT - Light rain [code: 1186, day: 0, img: '45', ], // NIGHT - Moderate rain at times [code: 1189, day: 0, img: '12', ], // NIGHT - Moderate rain [code: 1192, day: 0, img: '45', ], // NIGHT - Heavy rain at times [code: 1195, day: 0, img: '12', ], // NIGHT - Heavy rain [code: 1198, day: 0, img: '8', ], // NIGHT - Light freezing rain [code: 1201, day: 0, img: '10', ], // NIGHT - Moderate or heavy freezing rain [code: 1204, day: 0, img: '5', ], // NIGHT - Light sleet [code: 1207, day: 0, img: '6', ], // NIGHT - Moderate or heavy sleet [code: 1210, day: 0, img: '41', ], // NIGHT - Patchy light snow [code: 1213, day: 0, img: '18', ], // NIGHT - Light snow [code: 1216, day: 0, img: '41', ], // NIGHT - Patchy moderate snow [code: 1219, day: 0, img: '16', ], // NIGHT - Moderate snow [code: 1222, day: 0, img: '41', ], // NIGHT - Patchy heavy snow [code: 1225, day: 0, img: '16', ], // NIGHT - Heavy snow [code: 1237, day: 0, img: '18', ], // NIGHT - Ice pellets [code: 1240, day: 0, img: '11', ], // NIGHT - Light rain shower [code: 1243, day: 0, img: '12', ], // NIGHT - Moderate or heavy rain shower [code: 1246, day: 0, img: '12', ], // NIGHT - Torrential rain shower [code: 1249, day: 0, img: '5', ], // NIGHT - Light sleet showers [code: 1252, day: 0, img: '6', ], // NIGHT - Moderate or heavy sleet showers [code: 1255, day: 0, img: '16', ], // NIGHT - Light snow showers [code: 1258, day: 0, img: '16', ], // NIGHT - Moderate or heavy snow showers [code: 1261, day: 0, img: '8', ], // NIGHT - Light showers of ice pellets [code: 1264, day: 0, img: '10', ], // NIGHT - Moderate or heavy showers of ice pellets [code: 1273, day: 0, img: '47', ], // NIGHT - Patchy light rain with thunder [code: 1276, day: 0, img: '35', ], // NIGHT - Moderate or heavy rain with thunder [code: 1279, day: 0, img: '46', ], // NIGHT - Patchy light snow with thunder [code: 1282, day: 0, img: '18', ] // NIGHT - Moderate or heavy snow with thunder ] //**********************************************************************************************************************