Last active
November 3, 2023 18:57
-
-
Save DanielStefanK/487175b6f65ede401e37ee4848970176 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Can be obtained here https://fitx-proxy.daniel-stefan.dev/ | |
let studioId = 1266927; | |
// Proxy for the utilization api | |
let proxyUrl = "https://fitx-proxy.daniel-stefan.dev/api/utilization/" | |
let param = args.widgetParameter; | |
if (param != null && param.length > 0) { | |
studioId = param; | |
} | |
const contextSize = 282; | |
const fitXOrange = new Color("#ff8c00"); | |
const lightGrey = new Color("#bfbbbb"); | |
const widget = new ListWidget(); | |
let studioInfo; | |
try { | |
studioInfo = await fetchStoreInformation(); | |
await createWidget(); | |
} catch (e) { | |
console.log(e); | |
widget.addSpacer(4); | |
const logoImg = await getImage("logo.png"); | |
widget.setPadding(10, 10, 10, 10); | |
const titleFontSize = 14; | |
const detailFontSize = 14; | |
const logoStack = widget.addStack(); | |
logoStack.addSpacer(4); | |
const logoImageStack = logoStack.addStack(); | |
logoStack.layoutHorizontally(); | |
logoImageStack.backgroundColor = new Color("#ffffff", 1.0); | |
logoImageStack.cornerRadius = 8; | |
const wimg = logoImageStack.addImage(logoImg); | |
wimg.imageSize = new Size(40, 40); | |
wimg.rightAlignImage(); | |
widget.addSpacer(); | |
const row = widget.addStack(); | |
row.layoutVertically(); | |
const percentTitle = row.addText("Fehler beim Laden"); | |
} | |
// used for debugging if script runs inside the app | |
if (!config.runsInWidget) { | |
await widget.presentSmall(); | |
} | |
Script.setWidget(widget); | |
Script.complete(); | |
// build the content of the widget | |
async function createWidget() { | |
var d = new Date(); | |
const currentWeekDay = d.getDay(); | |
const currentHour = Math.abs(d.getHours() - 2) % 24; | |
widget.addSpacer(4); | |
const logoImg = await getImage("logo.png"); | |
widget.setPadding(10, 10, 10, 10); | |
const titleFontSize = 14; | |
const detailFontSize = 14; | |
const logoStack = widget.addStack(); | |
const name = logoStack.addText(studioInfo.name); | |
name.font = Font.regularSystemFont(titleFontSize); | |
name.minimumScaleFactor = 0.5; | |
logoStack.addSpacer(4); | |
const logoImageStack = logoStack.addStack(); | |
logoStack.layoutHorizontally(); | |
logoImageStack.backgroundColor = new Color("#ffffff", 1.0); | |
logoImageStack.cornerRadius = 8; | |
const wimg = logoImageStack.addImage(logoImg); | |
wimg.imageSize = new Size(40, 40); | |
wimg.rightAlignImage(); | |
widget.addSpacer(); | |
const row = widget.addStack(); | |
row.layoutVertically(); | |
const percentTitle = row.addText("Aktuelle Auslastung: "); | |
percentTitle.font = Font.regularSystemFont(detailFontSize); | |
const percentage = row.addText(studioInfo.workload + "%"); | |
percentage.font = Font.regularSystemFont(detailFontSize); | |
let drawContext = new DrawContext(); | |
drawContext.size = new Size(contextSize, contextSize); | |
drawContext.opaque = false; | |
drawContext.setTextAlignedCenter(); | |
const data = studioInfo.items.map((i) => i.percentage); | |
let min, max, diff; | |
for (let i = 0; i <= 24; i++) { | |
let temp = data[i]; | |
min = temp < min || min == undefined ? temp : min; | |
max = temp > max || max == undefined ? temp : max; | |
} | |
diff = max - min; | |
let spaceBetweenDays = 10; | |
for (let i = 0; i < 24; i++) { | |
let current = data[i]; | |
let nextHour = data[i + 1]; | |
let delta = diff > 0 ? (current - min) / diff : 0.5; | |
let nextDelta = diff > 0 ? (nextHour - min) / diff : 0.5; | |
drawPoint( | |
spaceBetweenDays * i + 25, | |
175 - 50 * delta, | |
drawContext, | |
fitXOrange, | |
currentHour === i - 2 ? 15 : 3 | |
); | |
if (i % 3 == 0) { | |
drawText( | |
data[i], | |
10, | |
spaceBetweenDays * i + (data[i] > 9 ? 16 : 22), | |
175 - 50 * delta - 25, | |
drawContext, | |
lightGrey | |
); | |
drawText( | |
i, | |
10, | |
spaceBetweenDays * i + (data[i] > 9 ? 16 : 22), | |
180, | |
drawContext, | |
lightGrey | |
); | |
} | |
drawLine( | |
spaceBetweenDays * i + 25, | |
175 - 50 * delta, | |
spaceBetweenDays * (i + 1) + 25, | |
175 - 50 * nextDelta, | |
drawContext, | |
4, | |
fitXOrange | |
); | |
} | |
widget.backgroundImage = drawContext.getImage(); | |
} | |
// fetches information of the configured store, e.g. opening hours, address etc. | |
async function fetchStoreInformation() { | |
let url = proxyUrl + studioId; | |
let req = new Request(url); | |
let apiResult = await req.loadString(); | |
if (req.response.statusCode == 404) { | |
// TODO: implement error handling | |
} else if (req.response.statusCode == 200) { | |
apiResult = JSON.parse(apiResult); | |
widget.url = "https://mein.fitx.de/studio/" + apiResult.uuid; | |
} | |
return apiResult; | |
} | |
// get images from local filestore or download them once | |
async function getImage(image) { | |
let fm = FileManager.local(); | |
let dir = fm.documentsDirectory(); | |
let path = fm.joinPath(dir, image); | |
if (fm.fileExists(path)) { | |
return fm.readImage(path); | |
} else { | |
// download once | |
let imageUrl; | |
switch (image) { | |
case "logo.png": | |
imageUrl = "https://i.imgur.com/2T54ySh.png"; | |
break; | |
default: | |
console.log(`Sorry, couldn't find ${image}.`); | |
} | |
let iconImage = await loadImage(imageUrl); | |
fm.writeImage(path, iconImage); | |
return iconImage; | |
} | |
} | |
// helper function to download an image from a given url | |
async function loadImage(imgUrl) { | |
const req = new Request(imgUrl); | |
return await req.loadImage(); | |
} | |
function drawLine(x1, y1, x2, y2, ctx, width, color) { | |
const path = new Path(); | |
path.move(new Point(x1, y1)); | |
path.addLine(new Point(x2, y2)); | |
ctx.addPath(path); | |
ctx.setStrokeColor(color); | |
ctx.setLineWidth(width); | |
ctx.strokePath(); | |
} | |
function drawPoint(x, y, ctx, color, width = 2) { | |
const rec = new Rect(x - width / 2, y - width / 2, width, width); | |
ctx.setStrokeColor(color); | |
ctx.setFillColor(color); | |
ctx.setLineWidth(2); | |
ctx.strokeEllipse(rec); | |
ctx.fillEllipse(rec); | |
} | |
function drawText(text, fontSize, x, y, ctx, color = Color.black()) { | |
ctx.setFont(Font.boldSystemFont(fontSize)); | |
ctx.setTextColor(color); | |
ctx.drawText(new String(text).toString(), new Point(x, y)); | |
} |
My proxy should work with the other widget. The current workload is found on the response.
I've updated the gist with the current api here: https://gist.github.com/DanielStefanK/adf0fc40d08585e4167f20463fe3b1d1
Thank you very much for this, I appreciate this as I am not super knowledgeable with regard to GitHub and Scriptable in general.
Seeehr geiles script! Habe ich zwar nicht nach gesucht aber installiert. Danke dir <3
finds geil. location service an sich wäre noch nice. ist mit scriptable ja problemlos möglich. Also immer das FitX anzeigen, was in der nähe ist. es müssten nur die locations in der proxy gespeichert werden..
Habe aktuell das Problem das der Graph nicht mehr gerendert wird. Nur ich?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This works flawlessly so far, many thanks! Wouldn’t be possible to provide for an implementation to be used in connection with this simplified widget style created by someone else?
https://gist.github.com/masselmello/6d4f4c533b98b2550ee23a7a5e6c6cff?permalink_comment_id=3500390#gistcomment-3500390
The respective (now non-working) FitX fork can be found here: https://gist.github.com/eopo/aedaf03f1f27a0c9c02f4974f3f4c9ba
It would be great to somehow have a simplified widget without past data, as shown in the example above. 🙂