Based on Matt Silverback’s original code, I added AQI calculations and set the widget to present the background color based on the current air quality status.
code here: https://gist.github.com/jasonsnell/4b458e2775e11ff7dd8b21dd26aa504e
Based on Matt Silverback’s original code, I added AQI calculations and set the widget to present the background color based on the current air quality status.
code here: https://gist.github.com/jasonsnell/4b458e2775e11ff7dd8b21dd26aa504e
My new widget shuffles one of your latest Pocket bookmarks and opens the article in Safari once you tap it.
I also built a shortcut which makes it easy to obtain your Pocket access token and stores the whole script at the end. Just save it to your scriptable folder in iCloud Drive.
Gist & HowTo: https://gist.github.com/marco79cgn/e05ca19ea2d15194bc7991f7efab8083
Update 24.09.2020: fixed fonts
My next one (Sorry I’m addicted):
I ported my Simpsons Randomizer shortcut to a widget. It shuffles a random episode of The Simpsons. Upon tapping on the widget it plays the episode on Disney+. ¡Ay, caramba!
Gist and instructions: https://gist.github.com/marco79cgn/ac9a8add1c7dc5a6749b751a1d2a05a4
Cool Thanks for sharing!
PS: Reached you on Twitter, waiting for your response:-)
Okay. I must be dumb. What am I doing wrong? I can’t get a script to show up. I pulled the xkcd example (and others) and this is what I see when I select it from the widget…
Running beta 6
update: now supports build (155) - removed centerContent for addSpacer
I made a Nintendo Switch eShop widget, based on my Nintendo Switch eShop script.
The latest Scriptables update made it easier to debug widgets so I fixed a script I had been working on: a way to quickly see what is on the eShop. Tapping will take you to that game on the eShop’s website.
I wish I could make a shelf or a grid of these, but for now this is pretty cool.
I made a widget that shows the most recent article on a Feedbin account. It uses etags for caching and won’t break even when there is no internet connection - it will just show the last item it downloaded and indicate that it’s cached.
Here’s the code:
let fm = FileManager.local()
const etagPath = fm.joinPath(fm.documentsDirectory(), `feedbin-widget-etag-${Device.name()}.txt`)
const itemPath = fm.joinPath(fm.documentsDirectory(), `feedbin-widget-item-${Device.name()}.json`)
const authHeader = btoa(Keychain.get("feedbin-auth"))
let headers = {"base": {"Authorization": `Basic ${authHeader}`}}
headers["unread_entries"] = Object.assign({}, headers["base"])
if (fm.fileExists(etagPath)) {
headers["unread_entries"]["If-None-Match"] = fm.readString(etagPath)
}
let item = {}
try {
item = await loadItem()
} catch {
item = JSON.parse(fm.readString(itemPath))
item.author += item.author != "" ? " — Cached" : "Cached"
}
let widget = createWidget(item)
widget.presentMedium()
if (config.runsInWidget) {
Script.setWidget(widget)
Script.complete()
}
function createWidget(item) {
const textColor = new Color("#ffffff")
let gradient = new LinearGradient()
gradient.colors = [new Color("#1c1c1e"), new Color("#0c0c0e")]
gradient.locations = [0.5, 1]
let w = new ListWidget()
w.backgroundGradient = gradient
w.setPadding(10, 20, 10, 20)
let titleTxt = w.addText(item.title)
titleTxt.applyHeadlineTextStyling()
titleTxt.textColor = textColor
titleTxt.lineLimit = 2
w.addSpacer(7)
let authorTxt = w.addText(item.author)
authorTxt.applyBodyTextStyling()
authorTxt.textColor = textColor
authorTxt.textOpacity = 0.8
authorTxt.textSize = 12
w.addSpacer(7)
let summaryTxt = w.addText(item.summary)
summaryTxt.applySubheadlineTextStyling()
summaryTxt.textColor = textColor
summaryTxt.textOpacity = 0.8
summaryTxt.lineLimit = 2
w.url = item.url
return w
}
async function loadItem() {
const baseURL = "https://api.feedbin.com/v2/"
const unreadURL = "unread_entries.json"
const entryURL = "entries.json"
// get latest unread entry id
let unreadRequest = new Request(baseURL + unreadURL)
unreadRequest.headers = headers["unread_entries"]
const unreadResponse = await unreadRequest.load()
if (unreadRequest.response.statusCode == "200") {
const unreadEntryIDs = JSON.parse(unreadResponse.toRawString())
const latestID = unreadEntryIDs[unreadEntryIDs.length - 1]
const etag = unreadRequest.response.headers["Etag"]
// get entry
let entryRequest = new Request(baseURL + entryURL + `?ids=${latestID}`)
entryRequest.headers = headers["base"]
const unreadEntry = await entryRequest.loadJSON()
const latestEntry = unreadEntry[0]
if (latestEntry == undefined) {
entry = {"title": "No unread articles", "author": "", "summary": "—"}
} else {
entry = latestEntry
}
fm.writeString(itemPath, JSON.stringify(entry))
fm.writeString(etagPath, etag)
return entry
} else if (unreadRequest.response.statusCode == "304") {
// nothing changed, read from file
return JSON.parse(fm.readString(itemPath))
}
}
Please note as of the latest beta build (155), any script that is calling calling topAlignContent()
, centerAlignContent()
or bottomAlignContent()
on a ListWidget will now throw an error. I’m sorry, but I’ve replaced the functions during the beta with the more flexible addSpacer()
function.
If you add a spacer at the top of your widget by calling addSpacer()
before adding other elements, your content will be bottom aligned. If you add the spacer at the bottom of your widget, the content will be top aligned. You can also call addSpacer(5)
to add a spacer with a fixed height of 5 pixels.
This may happen if the widget crashes for some reason. If you’re using scripts that for others, it’s probably not due to memory constraints. Can you try rebooting your device? Sometimes that helps, at least for a while. It would be interesting to know if that helped.
Thanks for your great work so far.
Would it be possible to create a way to run the widget without actually running the installed version? Debugging is a pain because iOS doesn’t have a way to force refresh a widget.
Also am I missing something or is there no way to find & replace in the script editor?
You can call widget.presentSmall()
to view the widget in the app. There’s also functions for viewing the medium and large size widgets.
There’s not currently any way to find and replace. It’s a popular feature request, so I’ll probably get to it at some point. There’s just so many things to do right now
Thanks. I’m surprised native F&R isn’t baked into the OS.
Let’s Test Flight release fixed it. Thanks!
Hi Simon and fellow automators,
maybe my question is stupid, but I hope you can help me.
I wanted to create a widget in which a script should run.
In my script I’m calling a URL and normally use WebView to show the result in Siri.
But now I want the widget to display it.
Do you have an example for me?
Unfortunately, the website I want to see doesnt support rss or json or any public api I could work with.
Many thanks in advance and keep on your great work!
At the moment, Scriptable widgets can only show lines of text and images, not web content.
It’s correct that Scriptable doesn’t support showing web content. I’d have to test it but I’m not sure it’s possible to show a web view in a widget. Essentially iOS/iPadOS periodically takes a “snapshot” of a widget and shows a cached result. I’m not sure that would work for web content but I could be wrong.
I just stumbled upon Glimpse, an iOS 14 beta app that can take web content and show it in widgets. I haven’t tested it yet but here is the TestFlight link for those who’d like to give it a go:
I made a widget that takes a random quote from an Airtable base where I store a selection I curate.
Airtable is convenient because it can be enriched from anywhere and it exposes APIs that are super easy to use. Now, I’d be happy to have the possibility to do the same with DataJar @simonbs
Here is the code - Not so neat but it does the job:
let baseURL = "https://api.airtable.com/v0/REPLACEWITHYOURS/Quotes?maxRecords=100&view=Grid%20view"
let r = new Request(baseURL);
r.headers = { 'Authorization': 'Bearer REPLACEWITHYOURS'};
let json = await r.loadJSON();
let quotes = json['records'];
let randomQuote = (quotes[Math.floor(Math.random()*quotes.length)]);
let quoteText = randomQuote['fields']['Quote'];
let quoteAuthor = randomQuote['fields']['Author'];
let widget = createWidget();
if (config.runsInWidget) {
let widget = createWidget();
Script.setWidget(widget);
Script.complete();
}
function createWidget() {
let w = new ListWidget();
let widgetText = w.addText(quoteText);
widgetText.textSize = 15;
let widgetSubText = w.addText(quoteAuthor);
widgetText.textSize = 13;
return w
}
Quick question: can the content of a widget only be updated when the script is run? For example, could the example News in Widget script refresh every so often?