// Variables used by Scriptable. // These must be at the very top of the file. Do not edit. // icon-color: deep-green; icon-glyph: power-off; const mumbleApiUrl = "https://status.chaospott.de/chaospott_mumble.json"; const spaceApiUrl = "https://status.chaospott.de/status.json"; const logoUrl = "https://chaospott.de/images/logo.png"; const logoLocalFilename = "chaospott_logo.png"; const mumbleLocalFilename = "chaospott_mumble.json"; const spaceLocalFilename = "chaospott_space.json"; const title = "Chaospott"; const subTitle = "Essen"; var colorSpaceClosed; var colorSpaceOpen; var widget = await createWidget(); if (!config.runsInWidget) { await widget.presentSmall(); } Script.setWidget(widget); Script.complete(); async function createWidget(){ const colorOpenFresh = Color.green(); const colorOpenStale = new Color("#00ff00", 0.3); const colorClosedFresh = new Color("#ff0000", 1.0); const colorClosedStale = new Color("#ff0000", 0.3); const colorLonelyFresh = new Color("#ff8800", 1.0); const colorLonelyStale = new Color("#ff8800", 0.3); var colorBorderOpen = colorOpenFresh; var colorBorderClosed = colorClosedFresh; var colorBorderLonely = colorLonelyFresh; var colorMumbleOpen; var colorMumbleClosed; var colorMumbleLonely; const widget = new ListWidget(); try { var [mumbleStatus, mumbleFresh] = await getJSONandCache(mumbleLocalFilename, mumbleApiUrl); var [spaceStatus, spaceFresh] = await getJSONandCache(spaceLocalFilename, spaceApiUrl); } catch(err) { const errorList = new ListWidget(); errorList.addText("Please enable internet for initial execution."); return errorList; } if (mumbleFresh){ colorMumbleOpen = colorOpenFresh; colorMumbleClosed = colorClosedFresh; colorMumbleLonely = colorLonelyFresh; } else { colorMumbleOpen = colorOpenStale; colorMumbleClosed = colorClosedStale; colorMumbleLonely = colorLonelyStale; colorBorderOpen = colorOpenStale; colorBorderClosed = colorClosedStale; colorBorderLonely = colorLonelyStale; } if (spaceFresh){ colorSpaceOpen = colorOpenFresh; colorSpaceClosed = colorClosedFresh; } else { colorSpaceOpen = colorOpenStale; colorSpaceClosed = colorClosedStale; colorBorderOpen = colorOpenStale; colorBorderClosed = colorClosedStale; colorBorderLonely = colorLonelyStale; } if (spaceStatus.state.open){ widget.backgroundColor = colorBorderOpen; } else { switch(mumbleStatus.connected_users){ case 0: widget.backgroundColor = colorBorderClosed; break; case 1: widget.backgroundColor = colorBorderLonely; break; default: widget.backgroundColor = colorBorderOpen; } } widget.setPadding(0, 5, 0, 5); canvasStack = widget.addStack(); canvasStack.setPadding(8, 15, 8, 15); canvasStack.cornerRadius = 15; canvasStack.layoutVertically(); canvasStack.backgroundColor = Color.dynamic(Color.white(), Color.black()); const headerStack = canvasStack.addStack(); const titleStack = headerStack.addStack(); titleStack.layoutVertically(); const titleText = titleStack.addText(title); titleText.font = Font.regularSystemFont(16); const subTitleText = titleStack.addText(subTitle); subTitleText.font = Font.mediumSystemFont(10); headerStack.addSpacer(5); let logo = await getCachedImage(logoLocalFilename, logoUrl); const logoImage = headerStack.addImage(logo); logoImage.imageSize = new Size(30, 30); canvasStack.addSpacer(5); const middleRow = canvasStack.addStack(); spaceStatus.sensors.door_locked.forEach((obj,i,arr) => { spaceView(middleRow, obj.location, obj.value); if(i !== arr.length - 1){middleRow.addSpacer();} }); const spaceLastUpdate = new Date(spaceStatus.state.lastchange*1000); const spaceLastUpdateStack = canvasStack.addStack(); let spaceLastUpdateLabel = spaceLastUpdateStack.addDate(spaceLastUpdate); spaceLastUpdateLabel.font = Font.mediumSystemFont(6); spaceLastUpdateLabel.applyRelativeStyle(); // const bottomRow = canvasStack.addStack(); bottomRow.useDefaultPadding(); bottomRow.centerAlignContent(); const mumbleLabelStack = bottomRow.addStack(); mumbleLabelStack.layoutVertically(); const labelMumble = mumbleLabelStack.addText("M"); labelMumble.font = Font.regularSystemFont(12); let mumbleLastUpdate = new Date(mumbleStatus.last_update * 1000); const labelMumbleUpdated = mumbleLabelStack.addDate(mumbleLastUpdate); labelMumbleUpdated.font = Font.mediumSystemFont(6); labelMumbleUpdated.applyTimeStyle(); bottomRow.addSpacer(3); const mumbleValueStack = bottomRow.addStack(); const labelMumbleUser = mumbleValueStack.addText(mumbleStatus.connected_users.toString(10)); labelMumbleUser.font = Font.boldSystemFont(23); switch(mumbleStatus.connected_users){ case 0: labelMumbleUser.textColor = colorMumbleClosed; break; case 1: labelMumbleUser.textColor = colorMumbleLonely; break; default: labelMumbleUser.textColor = colorMumbleOpen; } bottomRow.addSpacer(10); // canvasStack.addSpacer(4) dateStack = canvasStack.addStack(); dateStack.layoutHorizontally(); dateStack.bottomAlignContent(); dateStack.addSpacer(41); const now = new Date(); const timeLabel = dateStack.addDate(now) timeLabel.font = Font.mediumSystemFont(10) timeLabel.centerAlignText() timeLabel.applyTimeStyle() timeLabel.textColor = Color.darkGray() return widget; } function spaceView(widget, space, lockStatus) { const viewStack = widget.addStack(); viewStack.layoutVertically(); viewStack.centerAlignContent(); const spaceName = space.charAt(0).toUpperCase() + space.slice(1) const label = viewStack.addText(spaceName); label.font = Font.regularSystemFont(14); const lock = SFSymbol.named("lock." + (lockStatus ? "" : "open.") + "fill"); lock.applyFont(Font.systemFont(20)); const lockImage = viewStack.addImage(lock.image); lockImage.resizable = false; lockImage.imageSize = new Size(25, 25); if(lockStatus){ lockImage.tintColor = colorSpaceClosed; } else { lockImage.tintColor = colorSpaceOpen; } } async function getCachedImage(localFilename, url) { let fm = FileManager.local(); let dir = fm.cacheDirectory(); let path = fm.joinPath(dir, localFilename); if (fm.fileExists(path)) { return fm.readImage(path); } else { let r = new Request(url); try { let returnImage = await r.loadImage(); fm.writeImage(path, returnImage); return returnImage; } catch (err) { // return placeholder return SFSymbol.named("photo").image; } } } async function getJSONandCache(localFilename, url){ let fm = FileManager.local() let dir = fm.cacheDirectory() let path = fm.joinPath(dir, localFilename) let r = new Request(url) try { var data = await r.loadJSON() fm.writeString(path, JSON.stringify(data, null, 2)) var fresh = true; } catch (err) { if (fm.fileExists(path)) { data = JSON.parse(fm.readString(path), null) fresh = false; } else { throw "no data"; } } return [data, fresh]; }