// Maroochydore sea temperature widget, for Scriptable (https://scriptable.app). Paste this into a new Scriptable script // and choose it for a widget: it downloads the widget's code from the site each time it runs, so the widget updates // itself. Instructions: https://maroochydore.seatemp.eliza.house/iphone // // It keeps the last copy of the code that ran without an error, and runs that when the site can't be reached, or when // a new copy fails (a broken deploy then leaves the previous version on the widget instead of an error). // Lives in dom/sea_temp as widget/loader.js. const CODE = "https://maroochydore.seatemp.eliza.house/seatemp-widget.js"; const fm = FileManager.local(); const saved = fm.joinPath(fm.documentsDirectory(), `seatemp-widget-code-${CODE.split("/")[2]}.js`); // one per place const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; async function download() { try { const req = new Request(CODE); req.timeoutInterval = 15; const text = await req.loadString(); // Only something that is the widget: an error or login page must never replace a working copy. return req.response.statusCode === 200 && text.startsWith("// SEATEMP-WIDGET") ? text : null; } catch (e) { return null; } } async function main() { const fresh = await download(); let failure = null; if (fresh) { try { await new AsyncFunction(fresh)(); fm.writeString(saved, fresh); return; } catch (e) { failure = e; } } if (fm.fileExists(saved)) { await new AsyncFunction(fm.readString(saved))(); return; } const w = new ListWidget(); w.addText("Sea temperature").font = Font.semiboldSystemFont(13); w.addText(failure ? `The widget failed: ${failure.message}` : "Couldn't download the widget yet: check the connection.").font = Font.systemFont(11); if (config.runsInWidget) Script.setWidget(w); else await w.presentSmall(); Script.complete(); } await main();