Skip to content

Instantly share code, notes, and snippets.

@dioncodes
Last active January 8, 2026 03:33
Show Gist options
  • Select an option

  • Save dioncodes/cd4554d8593814a94925735cbcdea0c8 to your computer and use it in GitHub Desktop.

Select an option

Save dioncodes/cd4554d8593814a94925735cbcdea0c8 to your computer and use it in GitHub Desktop.
Scriptable iOS Server Status Widget
const initialData = {
servers: [
{
url: 'https://1.example.com/',
title: 'Server 1',
online: null,
},
{
url: 'https://2.example.com/',
title: 'Server 2',
online: null,
},
{
url: 'https://3.example.com/',
title: 'Server 3',
online: null,
},
],
lastUpdate: null
}
// Refresh Interval in seconds
const refreshInterval = 300
const widget = await createWidget()
if (!config.runsInWidget) {
await widget.presentSmall()
}
Script.setWidget(widget)
Script.complete()
async function createWidget(items) {
const data = await refresh()
const list = new ListWidget()
// uncomment the lines below if you want to show the header (not working with more than ~5 servers)
// const header = list.addText("Server Status")
// header.font = Font.mediumSystemFont(13)
// list.addSpacer()
data.servers.forEach((server) => {
const label = list.addText((server.online ? '🟢' : (server.online === false ? '🔴' : '❔')) + ' ' + server.title)
label.font = Font.boldSystemFont(12)
label.textColor = Color.gray()
list.refreshAfterDate = new Date(Date.now() + refreshInterval)
list.addSpacer(3)
})
if (data.lastUpdate) {
list.addSpacer()
const lastRefreshLabel = list.addText('Last refresh: ' + data.lastUpdate)
lastRefreshLabel.font = Font.mediumSystemFont(8)
}
return list
}
async function refresh() {
let data = initialData
for (let server of data.servers) {
try {
let response = await new Request(server.url).loadString()
server.online = response && response.length > 0
} catch (e) {
server.online = false
}
}
let now = new Date()
let hours = now.getHours()
let mins = now.getMinutes()
data.lastUpdate = (hours > 9 ? hours : '0' + hours) + ':' + (mins > 9 ? mins : '0' + mins)
return data
}
@Sergey20482048
Copy link
Copy Markdown

I wanted to add ip addresses instead of http and for some reason it doesn't work. Help me how to do this?

@tommyd75
Copy link
Copy Markdown

@Sergey20482048 I used the above script with IP addresses by just using http://192.168.1.1/ in the server line , but my servers have a web page that you land on when you navigate to that IP. I'm not sure if yours are running a web server..

Does anyone know how to make this script use two columns so I can squeeze in two columns of 4 servers each?

@ChenYuZe519
Copy link
Copy Markdown

Incorporating the advantages of the aforementioned modifications, this version also utilizes Promise.all for concurrent network requests to enhance the efficiency of checking server statuses. Additionally, it implements a retry mechanism among other optimization features. Moreover, this widget can display a list of up to 6-7 servers while showing a title.

// Variables used by Scriptable.
// icon-color: purple; icon-glyph: globe;

const initialData = {
  servers: [
    { url: 'https://server1.example.com/', title: 'Server 1' },
    { url: 'https://server2.example.com/', title: 'Server 2' },
    { url: 'https://server3.example.com/', title: 'Server 3' },
    { url: 'https://server4.example.com/', title: 'Server 4' },
    { url: 'https://server5.example.com/', title: 'Server 5' },
    { url: 'https://server6.example.com/', title: 'Server 6' },
    // 可以继续添加更多服务器...
  ],
  lastUpdate: null
};

const refreshInterval = 161; // Refresh Interval in seconds

async function createWidget() {
  const data = await refreshStatus();
  const list = new ListWidget();
  setGradientBackground(list);

  addHeader(list, "CY Server&API Stat");

  data.servers.forEach(server => {
    addServerStatus(list, server);
    list.addSpacer(3);
  });

  addFooter(list, data.lastUpdate);
  list.refreshAfterDate = new Date(Date.now() + refreshInterval * 1000);
  return list;
}

async function refreshStatus() {
  let data = { ...initialData, servers: await checkServers(initialData.servers) };
  data.lastUpdate = new Date().toLocaleTimeString();
  return data;
}

async function checkServers(servers) {
  const checks = servers.map(server => (
    fetchServerStatus(server)
      .then(online => ({ ...server, online }))
      .catch(() => ({ ...server, online: false }))
  ));
  return Promise.all(checks);
}

async function fetchServerStatus(server, retries = 3, delay = 1000) {
  async function attemptFetch(remainingRetries) {
    try {
      const request = new Request(server.url);
      await request.loadString(); // 尝试加载数据
      return true; // 加载成功,返回在线状态
    } catch (e) {
      if (remainingRetries <= 0) throw e; // 重试次数用尽,抛出异常
      await new Promise(resolve => setTimeout(resolve, delay)); // 等待一段时间再重试
      return attemptFetch(remainingRetries - 1); // 递归调用,减少剩余重试次数
    }
  }
  try {
    return await attemptFetch(retries); // 初始调用,尝试获取状态
  } catch (e) {
    return false; // 所有尝试失败,返回离线状态
  }
}

function setGradientBackground(list) {
  const gradient = new LinearGradient();
  gradient.locations = [0, 1];
  gradient.colors = [
    new Color('#2D1925'),
    new Color('#402938')
  ];
  list.backgroundGradient = gradient;
}

function addHeader(list, text) {
  const header = list.addText(text);
  header.font = Font.mediumSystemFont(13);
  header.textColor = Color.white();
  list.addSpacer(5);
}

function addServerStatus(list, server) {
  let symbol = '❔'; // 默认状态未知
  if (server.online === true) {
    symbol = '🟢'; // 在线
  } else if (server.online === false) {
    symbol = '🔴'; // 离线
  }
  const label = list.addText(`${symbol} ${server.title}`);
  label.font = Font.boldSystemFont(12);
  label.textColor = Color.white();
}

function addFooter(list, lastUpdate) {
  list.addSpacer();
  const footer = list.addText(`Last refresh: ${lastUpdate}`);
  footer.font = Font.mediumSystemFont(8);
  footer.textColor = Color.white();
}

const widget = await createWidget();
if (!config.runsInWidget) {
  await widget.presentSmall();
}
Script.setWidget(widget);
Script.complete();

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