Skip to content

Instantly share code, notes, and snippets.

@IgorHalfeld
Created December 17, 2025 22:07
Show Gist options
  • Select an option

  • Save IgorHalfeld/1ec9b51fe148105542cf5b4009098a5e to your computer and use it in GitHub Desktop.

Select an option

Save IgorHalfeld/1ec9b51fe148105542cf5b4009098a5e to your computer and use it in GitHub Desktop.
// Startup Status Widget (Scriptable) — colorized text + emoji
// Checks:
// - UptimeRobot public status page (HTML)
// - Your /health endpoint (HTTP + optional JSON)
// Refresh request: every 30 minutes (not guaranteed by iOS)
const CHECK_INTERVAL_MIN = 30;
const TITLE_STATUS = "Company Servers" // eg. add your company name
const TARGETS = [
{ name: "Service 1", url: "https://...", type: "uptimerobot_html" },
{ name: "API Health", url: "https://...", type: "health_http" },
// ... add more here
];
async function fetchReq(url, timeoutMs = 12000) {
const req = new Request(url);
req.timeoutInterval = timeoutMs / 1000;
req.headers = { "Cache-Control": "no-cache" };
return req;
}
function classifyUptimeRobotPage(html) {
const text = (html || "").toLowerCase();
if (text.includes("all systems operational")) return { state: "UP", detail: "All operational" };
if (text.includes("major outage")) return { state: "DOWN", detail: "Major outage" };
if (text.includes("partial outage")) return { state: "DEGRADED", detail: "Partial outage" };
if (text.includes("degraded performance")) return { state: "DEGRADED", detail: "Degraded" };
if (text.includes("operational")) return { state: "UP", detail: "Operational" };
return { state: "UNKNOWN", detail: "Could not classify" };
}
async function checkTarget(t) {
const started = Date.now();
try {
const req = await fetchReq(t.url);
if (t.type === "uptimerobot_html") {
const html = await req.loadString();
const c = classifyUptimeRobotPage(html);
return { ...c, ms: Date.now() - started };
}
// health_http
let body = null;
let isJson = false;
try {
body = await req.loadJSON();
isJson = true;
} catch {
body = await req.loadString();
}
let detail = isJson ? "JSON OK" : "HTTP OK";
if (isJson) {
const statusVal = (body?.status || body?.state || body?.health || "").toString().toLowerCase();
if (statusVal && !["ok", "healthy", "up"].includes(statusVal)) {
return { state: "DEGRADED", detail: `Status: ${statusVal}`, ms: Date.now() - started };
}
if (body?.version) detail = `OK (v${body.version})`;
}
return { state: "UP", detail, ms: Date.now() - started };
} catch (e) {
return { state: "DOWN", detail: (e?.message || "Request failed").slice(0, 70), ms: Date.now() - started };
}
}
function emojiFor(state) {
if (state === "DOWN") return "🚨";
if (state === "DEGRADED") return "⚠️";
if (state === "UP") return "🤑";
return "❓";
}
function colorFor(state) {
if (state === "UP") return new Color("#2ECC71");
if (state === "DEGRADED") return new Color("#F1C40F");
if (state === "DOWN") return new Color("#E74C3C");
return new Color("#95A5A6");
}
// Run checks
const results = [];
for (const t of TARGETS) results.push({ target: t, result: await checkTarget(t) });
// Build widget
const w = new ListWidget();
w.setPadding(14, 14, 14, 14);
const title = w.addText(TITLE_STATUS);
title.font = Font.boldSystemFont(16);
w.addSpacer(10);
for (const r of results) {
// Line 1: emoji + name
const line1 = w.addText(`${emojiFor(r.result.state)} ${r.target.name}`);
line1.font = Font.semiboldSystemFont(13);
// Line 2: colorized STATE + gray details
const line2 = w.addStack();
line2.centerAlignContent();
const stateTxt = line2.addText(r.result.state);
stateTxt.font = Font.semiboldSystemFont(11);
stateTxt.textColor = colorFor(r.result.state);
const restTxt = line2.addText(` • ${r.result.detail} • ${r.result.ms}ms`);
restTxt.font = Font.systemFont(11);
restTxt.textColor = Color.gray();
w.addSpacer(10);
}
const checked = w.addText(`Last check: ${new Date().toLocaleTimeString()}`);
checked.font = Font.systemFont(10);
checked.textColor = Color.gray();
// Best-effort refresh request
w.refreshAfterDate = new Date(Date.now() + CHECK_INTERVAL_MIN * 60 * 1000);
Script.setWidget(w);
Script.complete();
@IgorHalfeld
Copy link
Author

the result:

image

@BrenoAndrade
Copy link

top

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