Skip to content

Instantly share code, notes, and snippets.

@wxkeith
Last active September 19, 2022 04:33
Show Gist options
  • Save wxkeith/da736d6a2193705d850e7808fd9b950a to your computer and use it in GitHub Desktop.
Save wxkeith/da736d6a2193705d850e7808fd9b950a to your computer and use it in GitHub Desktop.

WeatherFlow Widget for Scriptable

Requirements

  • You're running iOS/iPadOS 14.
  • You've installed Scriptable (https://scriptable.app).
  • You own a WeatherFlow weather station.

Installation

  1. Create a new script in Scriptable and paste the wxflow-widget.js code into it.
  2. In a web browser, log on to your WeatherFlow account at https://www.tempestwx.com.
  3. Copy your station id from the address bar in your web browser to the STATION_ID variable in wxflow-widget.js (for example, if your address is https://tempestwx.com/station/12345/, your station id is 12345).
  4. Go to Settings, scroll to the bottom and click Data Authorizations, then create a personal use token. Copy your token into the API_KEY variable in wxflow-widget.js.
  5. Long-press anywhere on your home screen to enter edit mode, tap the + in the top left corner, select Scriptable, and add the small variant of the widget.
  6. While in edit mode, tap the new widget, and configure it to use the wxflow-widget.js script.

Recent changes

  • Configuration options to vary the background color or temperature text color according to your outdoor temperature.
  • Parameters now display in the units you've specified in the Tempest app.

Acknowledgements

  • 93ben on the WeatherFlow forum for help with the temperature colors and pointing out the user-specified units returned by the API.
  • GaryFunk on the WeatherFlow forum for posting a consolidated list of formulas and conversions.
  • grantharrison on the WeatherFlow forum for suggesting a cleaner layout.
// Set this to your personal WeatherFlow API key.
const CONFIG_API_KEY = "YOUR API KEY GOES HERE"
// Set this to your station id.
const CONFIG_STATION_ID = 12345
// Set this to true to vary the background color of the widget based on the temperature.
const CONFIG_DYNAMIC_BACKGROUND = false
// Set this to true to vary the color of the temperature text based on the temperature.
const CONFIG_DYNAMIC_TEMPERATURE = true
let jsonData = await requestStationData(CONFIG_API_KEY, CONFIG_STATION_ID)
let widget = await createWidget(jsonData)
if(!config.runsInWidget) {
await widget.presentSmall();
}
Script.setWidget(widget)
Script.complete()
function createWidget(data) {
let w = new ListWidget()
w.spacing = 1
w.addSpacer()
let stationLine = w.addText(data.station_name)
stationLine.font = Font.boldSystemFont(12)
stationLine.textColor = Color.white()
stationLine.centerAlignText()
let temperature = convertTemperature(data.obs[0].air_temperature, data.station_units.units_temp)
if(CONFIG_DYNAMIC_BACKGROUND) {
let bgColor = new LinearGradient()
bgColor.colors = [new Color("#000000"), new Color(temperature["color"])]
bgColor.locations = [0.0, 1.0]
w.backgroundGradient = bgColor
} else {
w.backgroundColor = Color.black()
}
let temperatureLine = w.addText(temperature["display"] + temperature["unit"])
temperatureLine.font = Font.boldSystemFont(28)//regularSystemFont(30)
if(CONFIG_DYNAMIC_TEMPERATURE) {
temperatureLine.textColor = new Color(temperature["color"])
} else {
temperatureLine.textColor = Color.white()
}
temperatureLine.centerAlignText()
let humidityLine = w.addText(data.obs[0].relative_humidity + "%")
humidityLine.font = Font.regularSystemFont(12)
humidityLine.textColor = Color.white()
let precip = convertPrecipitation(data.obs[0].precip_accum_local_day, data.station_units.units_precip)
let precipLine = w.addText(precip["display"] + " " + precip["unit"])
precipLine.font = Font.regularSystemFont(12)
precipLine.textColor = Color.white()
wind = convertWindSpeed(data.obs[0].wind_avg, data.station_units.units_wind)
if(wind == 0) {
displayWind = "Calm"
} else {
windDirection = convertWindDirection(data.obs[0].wind_direction, data.station_units.units_direction)
displayWind = windDirection["display"] + "" + windDirection["unit"] + " " + wind["display"] + " " + wind["unit"]
}
let windLine = w.addText(displayWind)
windLine.font = Font.regularSystemFont(12)
windLine.textColor = Color.white()
let pressure = convertPressure(data.obs[0].sea_level_pressure, data.station_units.units_pressure)
let pressureLine = w.addText(pressure["display"] + " " + pressure["unit"] + " " + pressureTrendEmoji(data.obs[0].pressure_trend))
pressureLine.font = Font.regularSystemFont(12)
pressureLine.textColor = Color.white()
let lightningLine = w.addText("⚡️" + data.obs[0].lightning_strike_count_last_3hr)
lightningLine.font = Font.regularSystemFont(12)
lightningLine.textColor = Color.white()
let date = new Date()
let dateFormatter = new DateFormatter()
dateFormatter.useShortTimeStyle()
let strDate = dateFormatter.string(date)
let timeStamp = w.addText(strDate)
timeStamp.font = Font.lightSystemFont(10)
timeStamp.textColor = Color.white()
timeStamp.rightAlignText()
w.addSpacer()
return w
}
async function requestStationData(key, id) {
let url = "https://swd.weatherflow.com/swd/rest/observations/station/" + id + "?api_key=" + key
let req = new Request(url)
let json = await req.loadJSON()
return json
}
function convertTemperature(temperatureValue, targetTemperatureUnit) {
switch(targetTemperatureUnit) {
case "c": // API default
var temperature = temperatureValue
var displayTemperature = temperatureValue.toFixed(1)
var temperatureUnit = "°C"
var temperatureBackgroundColor = temperatureColor(targetTemperatureUnit, temperature)
break
case "f":
var temperature = (Math.round(((temperatureValue * 9 / 5 + 32) + Number.EPSILON) * 10) / 10)
var displayTemperature = temperature.toFixed(1)
var temperatureUnit = "°F"
var temperatureBackgroundColor = temperatureColor(targetTemperatureUnit, temperature)
break
}
return {"value": temperature, "display": displayTemperature, "unit": temperatureUnit, "color": temperatureBackgroundColor}
}
function convertPressure(pressureValue, targetPressureUnit) {
switch(targetPressureUnit) {
case "mb": // API default
var pressure = pressureValue
var displayPressure = pressure.toFixed(1)
var pressureUnit = "mb"
break
case "inhg":
var pressure = (Math.round(((0.0295301 * pressureValue) + Number.EPSILON) * 100) / 100)
var displayPressure = pressure.toFixed(2)
var pressureUnit = "inHg"
break
case "mmhg":
var pressure = (Math.round(((0.750062 * pressureValue) + Number.EPSILON) * 100) / 100)
var displayPressure = pressure.toFixed(2)
var pressureUnit = "mmHg"
break
case "hpa":
var pressure = pressureValue
var displayPressure = pressure.toFixed(1)
var pressureUnit = "hPa"
break
}
return {"value": pressure, "display": displayPressure, "unit": pressureUnit}
}
function convertPrecipitation(precipValue, targetPrecipUnit) {
switch(targetPrecipUnit) {
case "mm": // API default
var precip = (Math.round(((precipValue) + Number.EPSILON) * 100) / 100)
var displayPrecip = precip.toFixed(2)
var precipUnit = "mm"
break
case "in":
var precip = (Math.round(((precipValue * 0.0393701) + Number.EPSILON) * 100) / 100)
var displayPrecip = precip.toFixed(2)
var precipUnit = "in"
break
case "cm":
var precip = (Math.round(((precipValue * .1) + Number.EPSILON) * 100) / 100)
var displayPrecip = precip.toFixed(2)
var precipUnit = "cm"
break
}
return {"value": precip, "display": displayPrecip, "unit": precipUnit}
}
function convertWindDirection(windDirectionValue, targetWindUnit) {
switch(targetWindUnit) {
case "degrees": // API default
var displayWindDirection = windDirectionValue
var windDirectionUnit = "°"
break
case "cardinal":
let compassDirections = ["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW","N"]
let index = Math.round((windDirectionValue % 360) / 22.5 )
var displayWindDirection = compassDirections[index]
var windDirectionUnit = ""
break
}
return {"value": windDirectionValue, "display": displayWindDirection, "unit": windDirectionUnit}
}
function convertWindSpeed(windValue, targetWindUnit) {
switch(targetWindUnit) {
case "mps": // API default
var wind = windValue
var displayWind = wind.toFixed(1)
var windUnit = "m/s"
break
case "mph":
var wind = (Math.round(((windValue * 2.2369362920544) + Number.EPSILON) * 10) / 10)
var displayWind = wind.toFixed(1)
var windUnit = "mph"
break
case "kts":
var wind = (Math.round(((windValue * 1.943844) + Number.EPSILON) * 10) / 10)
var displayWind = wind.toFixed(1)
var windUnit = "kts"
break
case "kph":
var wind = (Math.round(((windValue / (5/18)) + Number.EPSILON) * 10) / 10)
var displayWind = wind.toFixed(1)
var windUnit = "km/h"
break
case "bft":
var wind = convertBeaufort(windValue)
var displayWind = wind.toFixed(1)
var windUnit = "bft"
break
case "lfm":
var wind = (Math.round(((windValue * 196.850393700787) + Number.EPSILON) * 10) / 10)
var displayWind = wind.toFixed(1)
var windUnit = "lfm"
break
}
return {"value": wind, "display": displayWind, "unit": windUnit}
}
function convertBeaufort(windValue) {
let beaufort = windValue < .3 ? 0 : windValue < 1.6 ? 1 : windValue < 3.5 ? 2 : windValue < 5.5 ? 3 : windValue < 8 ? 4 : windValue < 10.8 ? 5 : windValue < 13.9 ? 6 : windValue < 17.2 ? 7 : windValue < 20.8 ? 8 : windValue < 24.5 ? 9 : windValue < 28.5 ? 10 : windValue < 32.7 ? 11 : windValue >= 32.7 ? 12 : 0;
return beaufort
}
function temperatureColor(temperatureUnit,temperature)
{
if(temperatureUnit == "c") {
if ( temperature >= 49 ) {
color = "#af3190"
} else if( temperature >= 46 && temperature < 49) {
color = "#bd2f82"
} else if( temperature >= 43 && temperature < 46) {
color = "#cb2e73"
} else if( temperature >= 41 && temperature < 43) {
color = "#d82d6e"
} else if( temperature >= 38 && temperature < 40) {
color = "#e62c51"
} else if( temperature >= 35 && temperature < 38) {
color = "#ff2e1f"
} else if( temperature >= 32 && temperature < 35) {
color = "#ff6e35"
} else if( temperature >= 30 && temperature < 32) {
color = "#f3934e"
} else if( temperature >= 27 && temperature < 30) {
color = "#f3ad63"
} else if( temperature >= 24 && temperature < 27) {
color = "#eaca73"
} else if( temperature >= 21 && temperature < 24) {
color = "#f4e376"
} else if( temperature >= 18 && temperature < 21) {
color = "#fcf574"
} else if( temperature >= 16 && temperature < 18) {
color = "#deec62"
} else if( temperature >= 13 && temperature < 16) {
color = "#b1ea3e"
} else if( temperature >= 10 && temperature < 13) {
color = "#20e93b"
} else if( temperature >= 7 && temperature < 10) {
color = "#18eca5"
} else if( temperature >= 5 && temperature < 7) {
color = "#11e1be"
} else if( temperature >= 2 && temperature < 5) {
color = "#06d8d8"
} else if( temperature >= -1 && temperature < 2) {
color = "#29cff0"
} else if( temperature >= -4 && temperature < -1) {
color = "#4fb2ef"
} else if( temperature >= -7 && temperature < -4) {
color = "#4e99ee"
} else if( temperature >= -9 && temperature < -7) {
color = "#4b6bf9"
} else if( temperature >= -12 && temperature < -9) {
color = "#8e4cf9"
} else if( temperature >= -15 && temperature < -12) {
color = "#a051e1"
} else if( temperature >= -18 && temperature < -15) {
color = "#ae4fd4"
} else if( temperature >= -21 && temperature < -18) {
color = "#bd4dc7"
} else if( temperature >= -23 && temperature < -21) {
color = "#ae61ba"
} else if( temperature >= -26 && temperature < -23) {
color = "#a07aad"
} else if( temperature >= -29 && temperature < -26) {
color = "#9286a0"
} else if( temperature >= -32 && temperature < -29) {
color = "#8f819e"
} else if( temperature >= -34 && temperature < -32) {
color = "#afa4bc"
} else if( temperature >= -37 && temperature < -34) {
color = "#bdb2ca"
} else if( temperature >= -40 && temperature < -37) {
color = "#d2c3d1"
} else if( temperature >= -43 && temperature < -40) {
color = "#e3dbe2"
} else if( temperature >= -46 && temperature < -43) {
color = "#e4e4eb"
} else if( temperature < -46 ) {
color = "#eaeaea"
}
} else if(temperatureUnit == "f") {
if ( temperature >= 120 ) {
color = "#af3190"
} else if( temperature >= 115 && temperature < 120) {
color = "#bd2f82"
} else if( temperature >= 110 && temperature < 115) {
color = "#cb2e73"
} else if( temperature >= 105 && temperature < 110) {
color = "#d82d6e"
} else if( temperature >= 100 && temperature < 105) {
color = "#e62c51"
} else if( temperature >= 95 && temperature < 100) {
color = "#ff2e1f"
} else if( temperature >= 90 && temperature < 95) {
color = "#ff6e35"
} else if( temperature >= 85 && temperature < 90) {
color = "#f3934e"
} else if( temperature >= 80 && temperature < 85) {
color = "#f3ad63"
} else if( temperature >= 75 && temperature < 80) {
color = "#eaca73"
} else if( temperature >= 70 && temperature < 75) {
color = "#f4e376"
} else if( temperature >= 65 && temperature < 70) {
color = "#fcf574"
} else if( temperature >= 60 && temperature < 65) {
color = "#deec62"
} else if( temperature >= 55 && temperature < 60) {
color = "#b1ea3e"
} else if( temperature >= 50 && temperature < 55) {
color = "#20e93b"
} else if( temperature >= 45 && temperature < 50) {
color = "#18eca5"
} else if( temperature >= 40 && temperature < 45) {
color = "#11e1be"
} else if( temperature >= 35 && temperature < 40) {
color = "#06d8d8"
} else if( temperature >= 30 && temperature < 35) {
color = "#29cff0"
} else if( temperature >= 25 && temperature < 30) {
color = "#4fb2ef"
} else if( temperature >= 20 && temperature < 25) {
color = "#4e99ee"
} else if( temperature >= 15 && temperature < 20) {
color = "#4b6bf9"
} else if( temperature >= 10 && temperature < 15) {
color = "#8e4cf9"
} else if( temperature >= 5 && temperature < 10) {
color = "#a051e1"
} else if( temperature >= 0 && temperature < 5) {
color = "#ae4fd4"
} else if( temperature >= -5 && temperature < 0) {
color = "#bd4dc7"
} else if( temperature >= -10 && temperature < -5) {
color = "#ae61ba"
} else if( temperature >= -15 && temperature < -10) {
color = "#a07aad"
} else if( temperature >= -20 && temperature < -15) {
color = "#9286a0"
} else if( temperature >= -25 && temperature < -20) {
color = "#8f819e"
} else if( temperature >= -30 && temperature < -25) {
color = "#afa4bc"
} else if( temperature >= -35 && temperature < -30) {
color = "#bdb2ca"
} else if( temperature >= -40 && temperature < -35) {
color = "#d2c3d1"
} else if( temperature >= -45 && temperature < -40) {
color = "#e3dbe2"
} else if( temperature >= -50 && temperature < -45) {
color = "#e4e4eb"
} else if( temperature < -50 ) {
color = "#eaeaea"
}
}
return color
}
function pressureTrendEmoji(trend) {
var pTrend = { "falling": "↓", "rising": "↑", "steady": "" }
return pTrend[trend]
}
@Maricha09
Copy link

Maricha09 commented Jan 22, 2022

@wxkeith Just what I was looking for, after a bit of struggling did I get everything inputted right I believed but receive an error for lines 29 - 30!??
This value is coming from The Tempest, the only possibility I could guess caused the error is for some reason the two ö (lowercase o umlaut) in the station name "Fiskarhöjden, Saltsjöbaden" is somehow not accepted.

Any idéa on a way to fix the error?

Screen dump

@wxkeith
Copy link
Author

wxkeith commented Jun 21, 2022

@wxkeith Just what I was looking for, after a bit of struggling did I get everything inputted right I believed but receive an error for lines 29 - 30!?? This value is coming from The Tempest, the only possibility I could guess caused the error is for some reason the two ö (lowercase o umlaut) in the station name "Fiskarhöjden, Saltsjöbaden" is somehow not accepted.

Any idéa on a way to fix the error?

@Maricha09 Apologies for the really late reply, did you ever get this to work? I tested passing around your station name in the code in a few places and it doesn't have a problem. So unless it's coming in from the WeatherFlow API in some unexpected way, there is likely a problem with your API key or station ID. I would start by double checking those values.

You can test them in a web browser by building a URL of the format:

https://swd.weatherflow.com/swd/rest/observations/station/{station_id}?api_key={api_key}

For example:
https://swd.weatherflow.com/swd/rest/observations/station/12345?api_key=11111111-1111-1111-1111-111111111111
(this is not a valid station id or api key and will fail, but replacing them with your values should work!)

@Maricha09
Copy link

Maricha09 commented Jun 21, 2022 via email

@Maricha09
Copy link

Maricha09 commented Jun 22, 2022 via email

@wxkeith
Copy link
Author

wxkeith commented Jun 22, 2022

@Maricha09 What is your numeric station id? This should be visible in the URL immediately after you log into the Tempest portal at https://tempestwx.com. Be sure you're setting this in the CONFIG_STATION_ID variable at the top of the script.

@Maricha09
Copy link

Maricha09 commented Jun 27, 2022 via email

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