Skip to content

Instantly share code, notes, and snippets.

@mountbatt
Last active December 30, 2023 13:37
Show Gist options
  • Save mountbatt/163c5d8f8bd7978e1f3c06b3dcccf00f to your computer and use it in GitHub Desktop.
Save mountbatt/163c5d8f8bd7978e1f3c06b3dcccf00f to your computer and use it in GitHub Desktop.
Scriptable Widget to get Weather Data from a weewx driven Weather-Station via JSON by weewx-json (https://github.com/teeks99/weewx-json). With Shortcodes this Widget is fully Siri compatible!
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: blue; icon-glyph: sun;
// Check weewx Weather
// Script Updates: https://gist.github.com/mountbatt/163c5d8f8bd7978e1f3c06b3dcccf00f
// Version: 0.7.4
// edit:
let endpoint = "http://www.waldbadviertel-wetter.de/current_minimal.json"
let prefix = "im" // im | in | at ... defines "im Waldbadviertel" oder "in Cologne" (set to "in" or "at" for good english)
let widgetLang = Device.language()
// do not edit:
// language strings
let langWeather
let langHumidity
let langRain
let langWind
let langMessage
let langItsRainingMessage
let langRainIntensityLow
let langRainIntensityHigh
if(widgetLang == "de") {
langWeather = "Wetter"
langHumidity = "RL"
langRain = "R"
langWind = "W"
langMessage = "Aktuell beträgt die Temperatur"
langItsRainingMessage = "Es regnet zur Zeit"
langRainIntensityLow = "ein wenig"
langRainIntensityHigh = "stark"
} else {
langWeather = "Weather"
langHumidity = "RH"
langRain = "R"
langWind = "W"
langMessage = "Currently the temperature"
langItsRainingMessage = "It is currently raining"
langRainIntensityLow = "a bit"
langRainIntensityHigh = "heavily"
}
let req = new Request(endpoint)
let apiResult = await req.loadJSON()
let weatherData = await apiResult
let locationName = weatherData.station.location
// trunkate locationName after comma (Waldbadviertel, Köln => Waldbadviertel)
locationName = locationName.split(',')[0]
// moonEmoji
let moonEmoji = await getMoonPhase("emoji")
let currentRain = weatherData.current['rain rate'].value // with dot 0.2
let sunriseTime = await isDay("sunrise")
let sunsetTime = await isDay("sunset")
let isDayNight = await isDay()
let nextRefresh = new Date(Date.now() + 60 * 2 * 1000); // 5 Min
console.log(nextRefresh)
if (config.runsWithSiri) {
if(weatherData){
let message = langMessage + ' ' + prefix +' '+ locationName +' ' + replaceDot(weatherData.current.temperature.value, 1) + ' ' + weatherData.current.temperature.units + '.';
if(currentRain > 0){
let RainIntensity
if(currentRain > 5 ){
RainIntensity = langRainIntensityHigh
} else {
RainIntensity = langRainIntensityLow
}
message = message + ' ' + langItsRainingMessage + ' ' + RainIntensity
}
Script.setShortcutOutput(message)
}
//Script.complete()
} else if (config.runsInWidget) {
let widget = createWidget(weatherData, moonEmoji, sunriseTime, sunsetTime, isDayNight, currentRain)
widget.refreshAfterDate = nextRefresh;
Script.setWidget(widget)
Script.complete()
} else {
let widget = createWidget(weatherData, moonEmoji, sunriseTime, sunsetTime, isDayNight, currentRain)
widget.refreshAfterDate = nextRefresh;
Script.setWidget(widget)
widget.presentSmall()
Script.complete()
}
function createWidget(weatherData, moonEmoji, sunriseTime, sunsetTime, isDayNight, currentRain) {
let w = new ListWidget()
w.useDefaultPadding()
// add url to open to widget
let widgetLink = weatherData.station.link
if(!widgetLink){
// extract url from endpoint url
var pathArray = endpoint.split( '/' );
var protocol = pathArray[0];
var host = pathArray[2];
var url = protocol + '//' + host;
widgetLink = url
}
if(widgetLink){
w.url = widgetLink
}
// Set gradient background
let daytime
let startColor
let endColor
if(isDayNight == true){
if(currentRain > 0){
// day color during rain
startColor = new Color("#4e6b7d")
endColor = new Color("#5b707d")
} else {
// day color
startColor = new Color("#2e86be")
endColor = new Color("#65a8cb")
}
} else {
// night color
startColor = new Color("#010722")
endColor = new Color("#313b59")
}
let gradient = new LinearGradient()
gradient.colors = [startColor, endColor]
gradient.locations = [0.0, 1]
w.backgroundGradient = gradient
let wHeaderStack = w.addStack()
let wNameStack = wHeaderStack.addStack()
let wEmojiStack = wHeaderStack.addStack()
let wName = wNameStack.addText(langWeather)
wName.font = Font.lightSystemFont(13)
wName.textColor = Color.white()
wEmojiStack.addSpacer()
let wEmoji
if(isDayNight == true){
if(currentRain > 0 && currentRain < 5){
wEmoji = wEmojiStack.addText('🌦️')
}
if(currentRain >= 5){
wEmoji = wEmojiStack.addText('🌧️')
}
if(currentRain == 0){
wEmoji = wEmojiStack.addText('🌤️')
}
} else {
wEmoji = wEmojiStack.addText(moonEmoji)
}
wEmoji.font = Font.regularSystemFont(13)
let wTitle = w.addText(locationName)
wTitle.textColor = Color.white()
wTitle.font = Font.boldSystemFont(13)
w.addSpacer(5)
let wTemp = w.addText(replaceDot(weatherData.current.temperature.value, 1) + '' + weatherData.current.temperature.units)
wTemp.textColor = Color.white()
wTemp.font = Font.lightSystemFont(36)
w.addSpacer(5)
/*
let wHumidity = w.addText(langHumidity + ': ' + replaceDot(weatherData.current.humidity.value) + '' + weatherData.current.humidity.units)
wHumidity.textColor = Color.white()
wHumidity.font = Font.regularSystemFont(12)
let wRain = w.addText(langRain + ': ' + replaceDot(weatherData.current["rain rate"].value, 1) + ' ' + weatherData.current["rain rate"].units)
wRain.textColor = Color.white()
wRain.font = Font.regularSystemFont(12)
*/
let textHumidity = langHumidity + ': ' + replaceDot(weatherData.current.humidity.value) + '' + weatherData.current.humidity.units
let textRain = langRain + ': ' + replaceDot(weatherData.current["rain rate"].value, 2) + ' ' + weatherData.current["rain rate"].units
let wHumidityAndRain = w.addText(textHumidity + " " + textRain)
wHumidityAndRain.textColor = Color.white()
wHumidityAndRain.font = Font.regularSystemFont(11)
w.addSpacer(2)
function getWindDirName(i){
if(i >= 348.75 || i < 11.25){
return "N";
} else if (i >= 11.25 && i < 33.75) {
return "NNO";
} else if (i >= 33.75 && i < 56.25) {
return "NO";
} else if (i >= 56.25 && i < 78.75) {
return "ONO";
} else if (i >= 78.25 && i < 101.25) {
return "O";
} else if (i >= 101.25 && i < 123.75) {
return "OSO";
} else if (i >= 123.75 && i < 146.25) {
return "SO";
} else if (i >= 146.25 && i < 168.75) {
return "SSO";
} else if (i >= 168.75 && i < 191.25) {
return "S";
} else if (i >= 191.25 && i < 213.75) {
return "SSW";
} else if (i >= 213.75 && i < 236.25) {
return "SW";
} else if (i >= 236.25 && i < 258.75) {
return "WSW";
} else if (i >= 258.75 && i < 281.25) {
return "W";
} else if (i >= 281.25 && i < 303.75) {
return "WNW";
} else if (i >= 303.75 && i < 326.25) {
return "NW";
} else if (i >= 326.25 && i < 348.75) {
return "NNW";
} else {
return "";
}
}
let windDirectionText = " "
if(typeof(weatherData.current["wind direction"]) != 'undefined'){
windDirectionText = getWindDirName(weatherData.current["wind direction"].value)
}
let wWind = w.addText(langWind + ': ' + replaceDot(weatherData.current["wind speed"].value, 1) + ' ' + weatherData.current["wind speed"].units + " " + windDirectionText)
wWind.textColor = Color.white()
wWind.font = Font.regularSystemFont(11)
w.addSpacer(2)
let textSunriseTime = new Date(sunriseTime).getHours()+':'+(new Date(sunriseTime).getMinutes()<10?'0':'') + new Date(sunriseTime).getMinutes()
let textSunsetTime = new Date(sunsetTime).getHours()+':'+(new Date(sunsetTime).getMinutes()<10?'0':'') + new Date(sunsetTime).getMinutes()
let wSun = w.addText("☀↑ " + textSunriseTime + " ↓ " + textSunsetTime)
wSun.textColor = Color.white()
wSun.font = Font.regularSystemFont(11)
//w.addSpacer() // push to top
return w
}
function replaceDot(value, decimal = 0){
value = value.toFixed(decimal)
if(widgetLang == "de"){
// if "de" we replace a dot with a comma for decimal numbers
value = value.toString().replace(/\./g, ',')
}
return value
}
async function isDay(response) {
let stationLat = weatherData.station.latitude
let stationLon = weatherData.station.longitude
// get sunrise sunset via https://sunrise-sunset.org/api - thanks!!! <3
let dayEndpoint = "https://api.sunrise-sunset.org/json?lat="+stationLat+"&lng="+stationLon+"&date=today&formatted=0"
let dRequest = new Request(dayEndpoint);
let dApiResult = await dRequest.loadJSON()
if(response && dApiResult.status == "OK"){
if(response == "sunrise") {
return dApiResult.results.sunrise
}
if(response == "sunset") {
return dApiResult.results.sunset
}
} else {
if(dApiResult.status == "OK") {
let sunrise = dApiResult.results.sunrise
sunrise = Math.round(Date.parse(sunrise)/1000)
let sunset = dApiResult.results.sunset
sunset = Math.round(Date.parse(sunset)/1000)
let now = Math.round(Date.now()/1000)
if(now > sunrise && now < sunset){
// day
return true
} else {
// night
return false
}
} else {
const hours = new Date().getHours()
const isDayTime = hours > 6 && hours < 19
return isDayTime // returns true if its daytime
}
}
}
async function getMoonPhase(type = "index"){
let currTime = Math.floor(Date.now() / 1000)
let mEndpoint = "https://api.farmsense.net/v1/moonphases/?d=" + currTime
let mImagePath = "https://www.farmsense.net/demos/images/moonphases/"
let mRequest = new Request(mEndpoint);
let mApiResult = await mRequest.loadJSON()
let moonIndex = mApiResult[0].Index;
let moonPhase = mApiResult[0].Phase;
let moonPhaseSanitized = moonPhase.replace(/\s+/g, '_').toLowerCase();
let moonPhaseEmoji = await getMoonEmoji(mApiResult[0].Age)
mImagePath = mImagePath+moonIndex+'.png'
if(type == "image"){
return mImagePath
}
if(type == "index"){
return moonIndex
}
if(type == "phase"){
return moonPhaseSanitized
}
if(type == "emoji"){
return moonPhaseEmoji
}
}
async function getMoonEmoji(phase){
if( phase < 1.84566 ){ return "🌑" } // new
else if ( phase < 5.53699 ) { return "🌒" } // waxing crescent
else if ( phase < 9.22831 ) { return "🌓" } // first quarter
else if ( phase < 12.91963 ) { return "🌔" } // waxing gibbous
else if ( phase < 16.61096 ) { return "🌕" } // full
else if ( phase < 20.30228 ) { return "🌖" } // waning gibbous
else if ( phase < 23.99361 ) { return "🌗" } // last quarter
else if ( phase < 27.68493 ) { return "🌘" } // waning crescent
else { return "🌑" } // new
}
// end of file
//
@mralext20
Copy link

mralext20 commented Dec 30, 2023

Hello, i was looking at this code and noticied that the isDay function gets called three times, making a new web request each time. i wrote a patch to fix this.

diff --git "a/.\\theirs.js" "b/.\\mine.js"
index f15e0f1..df1c333 100644
--- "a/.\\theirs.js"
+++ "b/.\\mine.js"
@@ -51,9 +51,8 @@ locationName = locationName.split(',')[0]
 // moonEmoji
 let moonEmoji = await getMoonPhase("emoji")
 let currentRain = weatherData.current['rain rate'].value // with dot 0.2
-let sunriseTime = await isDay("sunrise")
-let sunsetTime = await isDay("sunset")
-let isDayNight = await isDay()
+let [ sunriseTime, sunsetTime, isDayNight ] = await isDay()
+
 let nextRefresh = new Date(Date.now() + 60 * 2 * 1000); // 5 Min
 console.log(nextRefresh)
 
@@ -256,44 +255,30 @@ function replaceDot(value, decimal = 0) {
     return value
 }
 
-async function isDay(response) {
+async function isDay() {
     let stationLat = weatherData.station.latitude
     let stationLon = weatherData.station.longitude
     // get sunrise sunset via https://sunrise-sunset.org/api - thanks!!! <3
     let dayEndpoint = "https://api.sunrise-sunset.org/json?lat=" + stationLat + "&lng=" + stationLon + "&date=today&formatted=0"
     let dRequest = new Request(dayEndpoint);
     let dApiResult = await dRequest.loadJSON()
-    if (response && dApiResult.status == "OK") {
-        if (response == "sunrise") {
-            return dApiResult.results.sunrise
-        }
-        if (response == "sunset") {
-            return dApiResult.results.sunset
-        }
-    } else {
-        if (dApiResult.status == "OK") {
-            let sunrise = dApiResult.results.sunrise
-            sunrise = Math.round(Date.parse(sunrise) / 1000)
-            let sunset = dApiResult.results.sunset
-            sunset = Math.round(Date.parse(sunset) / 1000)
-            let now = Math.round(Date.now() / 1000)
-            if (now > sunrise && now < sunset) {
-                // day
-                return true
-
-            } else {
-                // night
-                return false
+    if (dApiResult.status == "OK") {
+        let sunrise = dApiResult.results.sunrise
+        sunrise = Math.round(Date.parse(sunrise) / 1000)
+        let sunset = dApiResult.results.sunset
+        sunset = Math.round(Date.parse(sunset) / 1000)
+        let now = Math.round(Date.now() / 1000)
+        let isSunUp = now > sunrise && now < sunset
+        return [sunrise, sunset, isSunUp];
 
-            }
-        } else {
-            const hours = new Date().getHours()
-            const isDayTime = hours > 6 && hours < 19
-            return isDayTime // returns true if its daytime
-        }
+    } else {
+        const hours = new Date().getHours()
+        const isDayTime = hours > 6 && hours < 19
+        return [undefined, undefined, isDayTime] // returns true if its daytime
     }
 }
 
+
 async function getMoonPhase(type = "index") {
     let currTime = Math.floor(Date.now() / 1000)
     let mEndpoint = "https://api.farmsense.net/v1/moonphases/?d=" + currTime

i used javascript's destructuring to return multiple variables from a single call. this will reduct the number of API hits to the sunrise sunset API from 3 to 1 per call of the script.

@mountbatt
Copy link
Author

mountbatt commented Dec 30, 2023

@mralext20 Hi Alex, thanks! Good point! Could you be so kind and post the full code here or link to a gist? Thanks! (Or otherwise: how can i mix your changes with mine? I am not that deep into git …)

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