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
}
@mIgU0815
Copy link
Copy Markdown

mIgU0815 commented Oct 24, 2020

hello, what struck me, a time out is missing ... if a page / server cannot be reached, the request runs for a very long time until the iOS time out comes. can you build in your own time out here and configure it with repetition?
for example: error if there is no answer for more than 15 seconds after 3 repetitions?
I adjusted the code a little ...
but I like it a lot and is simply built

#######
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: purple; icon-glyph: laptop;
const initialData = {
	servers: [
		{
			url: 'https://zellerberg.net/',
			title: 'Zellerberg.net',
			online: null,
		},
		{
			url: 'http://migu.mine.nu/',
			title: 'migu.mine.nu',
			online: null,
		},
		{
			url: 'http://zellerberg.homeip.net/',
			title: 'HomeIP',
			online: null,
		},
		{
			url: 'http://fritz.box/',
			title: 'fritz.box',
			online: null,
		},
	],
	lastUpdate: null
}

// Refresh Interval in seconds
const refreshInterval = 301

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()
// BackGroundColor
const gradient = new LinearGradient();
gradient.locations = [0, 1];
gradient.colors = [new Color('#2D1925'), new Color('#402938')];
list.backgroundGradient = gradient;
//
	//	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)
    header.textColor = Color.white()
	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.white()
		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)
        lastRefreshLabel.textColor = Color.white()
	}

	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()
	let secs = now.getSeconds()
	data.lastUpdate = (hours > 9 ? hours : '0' + hours) + ':' + (mins > 9 ? mins : '0' + mins) + ':' + (secs > 9 ? secs : '0' + secs)
	return data
}

@dioncodes
Copy link
Copy Markdown
Author

Thanks for sharing! A timeout would be great indeed, unfortunately I didn't find anything in the Scriptable docs at the first sight using the default methods… Not sure if it's possible to perform own http requests there, but I might check that out. Also the string response comparison is not really nice. I tried to check the http status code using load() instead of loadString() but couldn't get any useful response from there.

@MrTheFirst
Copy link
Copy Markdown

I a'm added push notifications for down servers and add HTTP status codes from response.

const initialData = {
  servers: [
    {
      url: 'https://server1.com/',
      title: 'server1',
      online: null,
    },
    {
      url: 'https://server2.com/',
      title: 'server2',
      online: null,
    },
  ],
  lastUpdate: null
}

// Refresh Interval in seconds
const refreshInterval = 300

const widget = await createWidget()

if (!config.runsInWidget) {
  await widget.presentLarge()
}

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()

  await data.servers.forEach((server) => {
    let status = '';
    if (String(server.online).indexOf('20') !== -1 || String(server.online).indexOf('30') !== -1) {
      status = '🟢 ' + server.online + ' ' + server.title
    } else {
      status = '🔴 ' + server.online + ' ' + server.title
      sendNotification(server)
    }
    const label = list.addText(status)
    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) {
    let request = new Request(server.url)
    request.method = 'GET'
    try {
      await request.load()
      server.online = request.response.statusCode
    } catch (response) {
      server.online = 0
    }
  }

  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
}
async function sendNotification(server) {
  let alert = new Notification()
  alert.sound = 'failure'
  alert.title = 'Server down'
  alert.body = 'Status of server "' + server.title + '" is ' + server.online
  alert.openURL = server.url
  await alert.schedule()
}

@dioncodes
Copy link
Copy Markdown
Author

dioncodes commented Oct 30, 2020

I a'm added push notifications for down servers and add HTTP status codes from response.

@MrTheFirst Thanks, this is a great extension to the initial script. Would you mind if I adopt your notification functionality in the gist?

@MrTheFirst
Copy link
Copy Markdown

MrTheFirst commented Nov 2, 2020

@MrTheFirst Thanks, this is a great extension to the initial script. Would you mind if I adopt your notification functionality in the gist?

Of course use it! I just used the notification from the documentation. But I noticed an error in my script, if the server is not available, then a lot of notifications come (it seems as much as there are servers in the settings). This is because of the asynchronous functions in the loops.

@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