Skip to content

Instantly share code, notes, and snippets.

@wxkeith

wxkeith/README.md

Last active May 11, 2021
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

@svedatom

This comment has been minimized.

Copy link

@svedatom svedatom commented Apr 26, 2021

Works great save on possible issue.I don't get the emojis I see on the sample above. I only see the lightening bolt.

I found where they go in the code on each line. Where to the emojis come from?

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