Skip to content

Instantly share code, notes, and snippets.

@donfelipo
Forked from DanielStefanK/fitx-widget.js
Last active June 23, 2024 17:49
Show Gist options
  • Save donfelipo/69cf704362f35c7ca5625f2e33893e24 to your computer and use it in GitHub Desktop.
Save donfelipo/69cf704362f35c7ca5625f2e33893e24 to your computer and use it in GitHub Desktop.
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: deep-green; icon-glyph: eye;
let location = "1";
let param = args.widgetParameter;
if (param != null && param.length > 0) {
location = param;
}
// TODO embed https://www.mysports.com/nox/public/v1/studios/1225816400/utilization/v2/indicator/limits with different color scheme
// utilizationIndicatorLowLimitPercentage:37
// utilizationIndicatorNormalLimitPercentage:62
const studios = {
"1": {
studioName:"Brunnthal",
studioId:"1225817660",
referer:"c3BlZWRmaXRuZXNzOjEyMjU4MTc2NjA%3D",
tenant:"speedfitness",
URL:"speedfitness-brunnthal",
logo:"Logo_Brunnthal.png"
},
"2": {
studioName:"Sendling",
studioId:"1217120190",
referer:"c3BlZWRmaXRuZXNzOjEyMTcxMjAxOTA%3D",
tenant:"speedfitness",
URL:"speedfitness-sendling",
logo:"Logo_Sendling.png"
},
"3": {
studioName:"Bad_Aibling",
studioId:"1210005780",
referer:"c3BlZWRmaXRuZXNzOjEyMTAwMDU3ODA%3D",
tenant:"speedfitness",
URL:"speedfitness-badaibling",
logo:"Logo_Bad_Aibling.png"
},
"4": {
studioName:"Bruckmuehl",
studioId:"1212577740",
referer:"c3BlZWRmaXRuZXNzOjEyMTI1Nzc3NDA%3D",
tenant:"speedfitness",
URL:"speedfitness-bruckmuehl",
logo:"Logo_Bruckmuehl.png"
},
"5": {
studioName:"Grafing",
studioId:"1225816400",
referer:"c3BlZWRmaXRuZXNzOjEyMjU4MTY0MDA%3D",
tenant:"speedfitness",
URL:"speedfitness-grafing",
logo:"Logo_Grafing.png"
},
"6": {
studioName:"Gunzenhausen",
studioId:"1210005450",
referer:"d2VoZWJhOjEyMTAwMDU0NTA%3D",
tenant:"speedfitness",
tenant:"weheba",
URL:"speedfitness-gunzenhausen",
logo:"Logo_Gunzenhausen.png"
},
"7": {
studioName:"Mainburg",
studioId:"speedfitness-mainburg",
referer:"Not yet available",
tenant:"speedfitness",
URL:"speedfitness-mainburg",
logo:"Logo_Mainburg.png"
},
"8": {
studioName:"Wasserburg",
studioId:"1217120110",
referer:"c3BlZWRmaXRuZXNzOjEyMTcxMjAxMTA%3D",
tenant:"speedfitness",
URL:"speedfitness-wasserburg",
logo:"Logo_Wasserburg.png"
}
}
console.log(studios[location].studioId)
const studioId = studios[location].studioId;
const studioURL = studios[location].URL;
const studioTenant = studios[location].tenant;
const contextSize = 282;
const fitXOrange = new Color("#ff8c00");
const lightGrey = new Color("#bfbbbb");
const widgetBackgroundColor = Color.black()
// Adjustments for the API CALL
const apiURL = "https://www.mysports.com/nox/public/v1/studios/" + studioId + "/utilization/v2/today";
const reqBodyData = {};
const logoURL = "https://" + studioURL + ".de/files/cto_layout/themedesigner/uploads/" + studios[location].logo;
const tapURL = "https://www.mysports.com/studio/" + studios[location].referer + "?ref=portal";
console.log(tapURL)
console.log(logoURL)
var colorConfig = {
fitXOrange: new Color("#ff8c00"),
lightGrey: new Color("#bfbbbb"),
widgetBackgroundColor: Color.black(),
canvFillColor: new Color("#ed721b"),
canvStrokeColor: new Color("#B0B0B0"),
canvTextColor: new Color("#f7f7f7")
}
var sizeConfig = {
context: 282,
canvas: 200,
canvText: 45,
canvWidth: 10,
canvRadius: 90
}
// Get the Info from Studio
const studioInfo = await fetchStoreInformation();
// DrawContext for circle
// Circle Diagram vars
let fillColor = 'ed721b';
let strokeColor = 'B0B0B0';
let textColor = 'f7f7f7';
const canvas = new DrawContext();
const canvSize = 200;
const canvTextSize = 45
const canvWidth = 10
const canvRadius = 90;
canvas.opaque = false
canvas.size = new Size(canvSize, canvSize);
canvas.respectScreenScale = true;
// Build Widget
const mainWidget = await createWidget();
// used for debugging if script runs inside the app
if (!config.runsInWidget) {
await mainWidget.presentSmall();
}
Script.setWidget(mainWidget);
Script.complete();
// ### Modules or Functions ####
// Build the content of the widget
async function createWidget() {
const widget = new ListWidget();
widget.backgroundColor = widgetBackgroundColor;
//get the current date
var d = new Date();
const currentWeekDay = d.getDay();
// not sure when their time start
const currentHour = d.getHours();
// top row this studio name and logo
widget.addSpacer(4);
const logoImg = await getImage(studios[location].logo);
widget.setPadding(5, 5, 5, 5);
const titleFontSize = 14;
const detailFontSize = 14;
// url whem tap widget
console.log(tapURL)
widget.url = tapURL;
// present the studio logo
const logoImageStack = widget.addStack();
logoImageStack.backgroundColor = widgetBackgroundColor
logoImageStack.cornerRadius = 8;
const wimg = logoImageStack.addImage(logoImg);
wimg.centerAlignImage();
if (studioInfo == "No data") {
wTxt = widget.addText(studioInfo)
wTxt.centerAlignText()
wTxt.textColor = new Color(textColor)
return widget
}
// Row1 Title Stack
const percentTitle = widget.addText("Live-Auslastung")
percentTitle.centerAlignText()
percentTitle.font = Font.regularSystemFont(detailFontSize);
percentTitle.textColor = new Color(textColor)
// Row2 Draw circle with percentage in the middle
// get the reamaining percentage
let remainingPercentage = (studioInfo.percentage).toFixed(0);
// draw canvas
drawArc(
new Point(canvSize / 2, canvSize / 2),
canvRadius,
canvWidth,
Math.floor(remainingPercentage * 3.6)
);
// draw text rectangle
const canvTextRect = new Rect(
0,
100 - canvTextSize / 2,
canvSize,
canvTextSize
);
// format the text
canvas.setTextAlignedCenter();
canvas.setTextColor(new Color(textColor));
canvas.setFont(Font.regularSystemFont(canvTextSize));
canvas.drawTextInRect(`${studioInfo.percentage}%`, canvTextRect);
// present the image
const canvImage = canvas.getImage();
let image = widget.addImage(canvImage);
image.centerAlignImage()
//adjust position
widget.addSpacer()
// Row3 display the time of last updastee
const dateLabel = widget.addDate(d);
dateLabel.font = Font.regularSystemFont(10);
dateLabel.textColor = Color.lightGray();
dateLabel.applyTimeStyle();
dateLabel.centerAlignText();
// finished return widget
return widget
}
// fetches information of the configured studio
async function fetchStoreInformation() {
let url = apiURL;
let tenant = studioTenant;
if (!tenant) {
tenant = "speedfitness";
}
// let data = JSON.stringify(reqBodyData);
let req = new Request(url);
req.method = "GET"
req.headers = { 'Connection':'keep-alive', 'Accept': 'application/json, text/plain, */*', 'referer': tapURL, 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7', 'Content-Type': 'application/json', 'x-tenant': tenant }
// req.body = data
console.log("Request:")
console.log(req)
let apiResults = await req.loadString();
console.log("Response:")
console.log(apiResults)
console.log("Return Code:" + req.response.statusCode)
if (req.response.statusCode != 200) {
console.error(req.response.statusCode + "https - Bad Request")
return "No data"
} else if (req.response.statusCode == 404) {
// TODO: implement error handling
console.log("error code:" + req.response.statusCode)
return "No data"
} else if (req.response.statusCode == 200) {
apiResults = JSON.parse(apiResults);
}
for (let data_set of apiResults) {
if (data_set.current == true) {
apiResult = data_set
}
}
if (!apiResult) {
return "No data"
}
return apiResult;
}
// get images from local filestore or download them once
async function getImage(localImage) {
let fm = FileManager.local();
let dir = fm.documentsDirectory();
let path = fm.joinPath(dir, localImage);
if (fm.fileExists(path)) {
return fm.readImage(path);
} else {
// download once
let imageUrl = logoURL;
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();
}
// Helper for drawing Canvas
// Math Sig
function sinDeg(deg) {
return Math.sin((deg * Math.PI) / 180);
}
// Math Cos
function cosDeg(deg) {
return Math.cos((deg * Math.PI) / 180);
}
// Draw an Arc
function drawArc(ctr, rad, w, deg) {
bgx = ctr.x - rad;
bgy = ctr.y - rad;
bgd = 2 * rad;
bgr = new Rect(bgx, bgy, bgd, bgd);
canvas.setFillColor(new Color(fillColor));
canvas.setStrokeColor(new Color(strokeColor));
canvas.setLineWidth(w);
canvas.strokeEllipse(bgr);
for (t = 0; t < deg; t++) {
rect_x = ctr.x + rad * sinDeg(t) - w / 2;
rect_y = ctr.y - rad * cosDeg(t) - w / 2;
rect_r = new Rect(rect_x, rect_y, w, w);
canvas.fillEllipse(rect_r);
}
}
// Draw a line
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();
}
// Draw a Point
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);
}
// Draw text
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));
}
@PrintStructor
Copy link

Where do you get the STUDIO IDs from?

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