Skip to content

Instantly share code, notes, and snippets.

@coughski
Last active April 13, 2023 06:17
Show Gist options
  • Save coughski/4b3705ae46e1325fab3a6907906068f3 to your computer and use it in GitHub Desktop.
Save coughski/4b3705ae46e1325fab3a6907906068f3 to your computer and use it in GitHub Desktop.
Scriptable widget displaying daily pollen forecast
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: deep-green; icon-glyph: tree;
/*** SETUP ***
* Register for a free Accuweather API Key at https://developer.accuweather.com
* Enter your API key in the field below
*
* Use the Locations API to determine the location key of your city at http://dataservice.accuweather.com/locations/v1/search
* Paste the location key in the appropriate variable below
*
* TIPS
* Set the widget's "When Interacting" parameter to "Run Script" to force the widget to reload in Scriptable
*/
const API_KEY = "api_key_goes_here" // paste your Accuweather developer API key here
const LOCATION_KEY = 349727 // This is the location key for New York City, replace with your city's location key
const KEYCHAIN_KEY = "daily_weather_forecast"
const colorMap = {
"Low": new Color("#67BC40"),
"Moderate": new Color("#DCC600"),
"High": new Color("#DF5A00")
}
const emojiMap = {
"Grass": "🌱",
"Mold": "🍂",
"Tree": "🌲",
"Ragweed": "🌾"
}
const DEBUG_WIDGET = false
let data = await loadData()
if (config.runsInWidget) {
let widget = await createWidget(data)
Script.setWidget(widget)
} else if (config.runsWithSiri) {
let table = createTable(data)
await QuickLook.present(table)
let speech = createSpeech(data)
Speech.speak(speech)
} else if (DEBUG_WIDGET) {
// Running in the app, present a preview of the widget
let widget = await createWidget(data)
await widget.presentSmall()
} else {
let table = createTable(data)
await QuickLook.present(table)
}
Script.complete()
function extractDateUpdated(json) {
// let forecastDate = json["DailyForecasts"][0]["Date"] // 2019-09-21T07:00:00-04:00
return parseInt(json["DailyForecasts"][0]["EpochDate"]) * 1000 // 1569063600
}
function extractPollenForecasts(json) {
return json["DailyForecasts"][0]["AirAndPollen"].slice(1, 5)
}
function summarize(pollenForecasts) {
return pollenForecasts.map(forecast => forecast["Name"] + " is " + forecast["Category"])
}
function createSpeech(json) {
return "Today in New York:\n" + summarize(extractPollenForecasts(json)).map(line => line += ".").join("\n")
}
function createTable(json) {
let pollenForecasts = extractPollenForecasts(json)
let lines = summarize(pollenForecasts)
let table = new UITable()
table.showSeparators = true
for (forecast of pollenForecasts) {
let row = new UITableRow()
let title = emojiMap[forecast["Name"]] + " " + forecast["Name"]
let cell = UITableCell.text(title)
cell.titleColor = colorMap[forecast["Category"]]
// row.backgroundColor = colorMap[forecast["Category"]]
row.addCell(cell)
table.addRow(row)
}
return table
}
async function createWidget(json) {
let pollenForecasts = extractPollenForecasts(json)
let lastUpdated = extractDateUpdated(json)
let w = new ListWidget()
// w.refreshAfterDate = nextForecastAfter(lastUpdated)
// w.backgroundColor = Device.isUsingDarkAppearance() ? Color.black() : Color.white()
// w.url = json["Headline"]["MobileLink"]
let header = w.addStack()
city = header.addText("NYC")
city.font = Font.caption2()
header.addSpacer()
date = header.addDate(new Date())
date.applyDateStyle()
date.textOpacity = 0.7
date.rightAlignText()
date.font = Font.caption2()
w.addSpacer()
for (forecast of pollenForecasts) {
w.addSpacer()
let txt = w.addText(emojiMap[forecast["Name"]] + " " + forecast["Name"])
txt.font = Font.boldSystemFont(16)
txt.textColor = colorMap[forecast["Category"]]
}
w.addSpacer()
return w
}
function nextForecastAfter(epochDate) {
var checkDate = new Date()
checkDate.setTime(epochDate);
todaysForecast = new Date(checkDate.getTime())
todaysForecast.setHours(7)
todaysForecast.setMinutes(0)
todaysForecast.setSeconds(0)
todaysForecast.setMilliseconds(0)
if (checkDate >= todaysForecast) {
todaysForecast.setDate(todaysForecast.getDate() + 1)
}
return todaysForecast
}
function stale(json) {
let lastUpdated = extractDateUpdated(json)
let refreshDate = nextForecastAfter(lastUpdated)
let now = new Date()
console.log("next update: " + refreshDate.toString())
console.log(now >= refreshDate ? "stale" : "fresh")
return now >= refreshDate
}
async function loadData() {
var data
if (cached()) {
console.log("Loading from keychain")
data = loadCache()
}
if (!cached() || stale(data)) {
console.log("Downloading from internet")
data = await download()
cache(data)
}
return data
}
async function download() {
const URL = "http://dataservice.accuweather.com/forecasts/v1/daily/1day/" + LOCATION_KEY + "?apikey=" + API_KEY + "&details=true"
let request = new Request(URL)
let json = await request.loadJSON()
return json
}
function cached() {
return Keychain.contains(KEYCHAIN_KEY)
}
function loadCache() {
return JSON.parse(Keychain.get(KEYCHAIN_KEY))
}
function cache(json) {
let stringified = JSON.stringify(json)
Keychain.set(KEYCHAIN_KEY, stringified)
}
@anthonyvardizzone
Copy link

anthonyvardizzone commented Mar 21, 2023

60BD34FD-509A-41B1-8437-F820AAC9A41F
I’m getting the following error when running the script.

@coughski
Copy link
Author

@anthonyvardizzone I'm assuming you already checked the original Reddit post to see other people's issues and solutions? Did you change anything besides the location key or the API key? What location key did you use? Did you select the “Core Weather Limited Trial” option for the “API Product” setting?

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