Skip to content

Instantly share code, notes, and snippets.

@DanielStefanK
Last active November 3, 2023 18:57
Show Gist options
  • Save DanielStefanK/487175b6f65ede401e37ee4848970176 to your computer and use it in GitHub Desktop.
Save DanielStefanK/487175b6f65ede401e37ee4848970176 to your computer and use it in GitHub Desktop.
//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));
}
@DanielStefanK
Copy link
Author

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

@krmkrx
Copy link

krmkrx commented Nov 23, 2022

Thank you very much for this, I appreciate this as I am not super knowledgeable with regard to GitHub and Scriptable in general.

@brusayannick
Copy link

Seeehr geiles script! Habe ich zwar nicht nach gesucht aber installiert. Danke dir <3

@9dc
Copy link

9dc commented May 8, 2023

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..

@HeyMeco
Copy link

HeyMeco commented Nov 3, 2023

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