Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

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]
}
@owen123t

This comment has been minimized.

Copy link

@owen123t owen123t commented Sep 26, 2020

it seems i have to have a tempest device to have the access for the station

@wxkeith

This comment has been minimized.

Copy link
Owner Author

@wxkeith wxkeith commented Sep 26, 2020

it seems i have to have a tempest device to have the access for the station

Do you have a WeatherFlow device? I've only tested it against what their API returns for a Tempest.

@happy2Camp

This comment has been minimized.

Copy link

@happy2Camp happy2Camp commented Sep 26, 2020

@owen123t

This comment has been minimized.

Copy link

@owen123t owen123t commented Sep 26, 2020

it seems i have to have a tempest device to have the access for the station

Do you have a WeatherFlow device? I've only tested it against what their API returns for a Tempest.

I don't have any Weatherflow devices. it means i could not use this script ?

@wxkeith

This comment has been minimized.

Copy link
Owner Author

@wxkeith wxkeith commented Sep 27, 2020

@owen123t Unfortunately, the script requires API access to a WeatherFlow account, so it won't work if you don't own a device.

@kokos1405

This comment has been minimized.

Copy link

@kokos1405 kokos1405 commented Oct 22, 2020

I'm getting the following error:

Error on line 34:39:
ReferenceError: Can't find variable:convertTemperature

@wxkeith

This comment has been minimized.

Copy link
Owner Author

@wxkeith wxkeith commented Oct 22, 2020

I'm getting the following error:

Error on line 34:39:
ReferenceError: Can't find variable:convertTemperature

@kokos1405, Try starting out with a fresh copy of the script and double check your API key and station id. It's probably best copying the source using the "Raw" option at the top of the script. I just did that using this source and it's working here.

@kokos1405

This comment has been minimized.

Copy link

@kokos1405 kokos1405 commented Oct 23, 2020

I'm getting the following error:
Error on line 34:39:
ReferenceError: Can't find variable:convertTemperature

@kokos1405, Try starting out with a fresh copy of the script and double check your API key and station id. It's probably best copying the source using the "Raw" option at the top of the script. I just did that using this source and it's working here.

Thanks, it's working now. Is it possible to open the Tempest app when pressing the widget?

@nak0604

This comment has been minimized.

Copy link

@nak0604 nak0604 commented Nov 13, 2020

I just tried using this, pasted the raw format and filled in the information. I tried adding it and I get this error:

2020-11-13 01:40:42: Error on line 79:104: ReferenceError: Can't find variable: pressureTrendEmoji

@emanonx

This comment has been minimized.

Copy link

@emanonx emanonx commented Nov 23, 2020

I just tried using this, pasted the raw format and filled in the information. I tried adding it and I get this error:

2020-11-13 01:40:42: Error on line 79:104: ReferenceError: Can't find variable: pressureTrendEmoji

You probably missed line 407-410 when you copy/paste. Check code.

@Patto-star

This comment has been minimized.

Copy link

@Patto-star Patto-star commented Dec 18, 2020

Hey everyone,

I am having one problem i cant seem to find where API key is generated for the script is it in my Iphone, Scriptable or Tempest ?

Thanks for any replies.

@wxkeith

This comment has been minimized.

Copy link
Owner Author

@wxkeith wxkeith commented Dec 18, 2020

It’s on the Tempest website. Starting with step #4 on the installation instructions.

@Patto-star

This comment has been minimized.

Copy link

@Patto-star Patto-star commented Dec 18, 2020

Thank WXKeith, i managed to find it but because i had no idea where on website it took some hunting around. Maybe the author should help noobies with API by saying that as i had no idea if i was looking for an API key in scriptable, iphone or tempest.

Cheers

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