|
// Variables used by Scriptable. |
|
// These must be at the very top of the file. Do not edit. |
|
// icon-color: purple; icon-glyph: magic; |
|
const API_URL = "https://pass.telekom.de/api/service/generic/v1/status"; |
|
const LANGUAGE = "de"; |
|
|
|
async function createWidget() { |
|
// get usage data |
|
const { error, data, connection } = await getUsageData(); |
|
|
|
// calculate data |
|
const now = new Date(); |
|
const lastUpdated = new Date(data.date); |
|
const firstDayOfMonth = new Date(lastUpdated.getFullYear(), lastUpdated.getMonth(), -1, 0, 0, 0, 0); |
|
const lastDayOfMonth = new Date(lastUpdated.getFullYear(), lastUpdated.getMonth() + 1, 0, 0, 0, 0, 0); |
|
const passedSeconds = lastUpdated - firstDayOfMonth; |
|
const totalSeconds = lastDayOfMonth - firstDayOfMonth; |
|
const extrapolatedPercentage = passedSeconds / totalSeconds; |
|
const usedPercentage = data.usedVolume / data.initialVolume; |
|
|
|
let usedColor = "#4BB543"; |
|
if (usedPercentage > extrapolatedPercentage) usedColor = "#ff6600"; |
|
if (usedPercentage >= 0.75) usedColor = "#ff6600"; |
|
if (usedPercentage >= 0.9) usedColor = "#ff0000"; |
|
|
|
// ---------- construct widget ---------- |
|
const widget = new ListWidget(); |
|
widget.backgroundColor = new Color("#DD0273"); |
|
|
|
// ---------- logo headline ---------- |
|
const headlineRow = widget.addStack(); |
|
headlineRow.centerAlignContent(); |
|
|
|
// logo |
|
const telekomImageRAW = await getImage("telekom.png", "https://i.imgur.com/wKKfJdwt.png"); |
|
const telekomImageStack = headlineRow.addImage(telekomImageRAW); |
|
telekomImageStack.imageSize = new Size(30, 30); |
|
headlineRow.addSpacer(5); |
|
|
|
// headline |
|
const headlineTextStack = headlineRow.addStack(); |
|
headlineTextStack.layoutVertically(); |
|
const headlineTextLabel = headlineTextStack.addText("Mobile Daten"); |
|
headlineTextLabel.leftAlignText(); |
|
headlineTextLabel.font = Font.mediumSystemFont(12); |
|
headlineTextLabel.textColor = new Color("#ffffff"); |
|
|
|
widget.addSpacer(2); |
|
|
|
// ---------- Percent ---------- |
|
const percentageRow = widget.addStack(); |
|
percentageRow.addSpacer(); |
|
const percentageText = percentageRow.addText(`${data.usedPercentage}%`); |
|
percentageText.centerAlignText(); |
|
percentageText.font = Font.heavySystemFont(36); |
|
percentageText.textColor = new Color("#ffffff"); |
|
percentageRow.addSpacer(); |
|
|
|
widget.addSpacer(2); |
|
|
|
// ---------- Status Bar ---------- |
|
const statusRow = widget.addStack(); |
|
statusRow.size = new Size(130,13); |
|
const sctx = new DrawContext(); |
|
sctx.opaque = false; |
|
sctx.size = new Size(500, 50); |
|
|
|
const addBarToCtx = (percentage, color) => { |
|
const path = new Path(); |
|
path.addRoundedRect(new Rect(1,1,500 * percentage,50),25,25); |
|
sctx.addPath(path); |
|
sctx.setFillColor(color); |
|
sctx.fillPath(); |
|
} |
|
|
|
addBarToCtx(1, new Color("#cccccc")); |
|
addBarToCtx(extrapolatedPercentage, new Color("#888888")); |
|
addBarToCtx(usedPercentage, new Color(usedColor)); |
|
|
|
const statusBarImage = sctx.getImage(); |
|
const statusBar = statusRow.addImage(statusBarImage); |
|
statusBar.centerAlignImage(); |
|
|
|
widget.addSpacer(1); |
|
|
|
// ---------- Total volume indicator ---------- |
|
const totalIndicatorRow = widget.addStack(); |
|
totalIndicatorRow.layoutVertically(); |
|
const totalIndicatorText = totalIndicatorRow.addText(`${data.usedVolumeStr}/${data.initialVolumeStr}`); |
|
totalIndicatorText.font = Font.boldRoundedSystemFont(13); |
|
|
|
const remainingTimeIndicatorText = totalIndicatorRow.addText(`verbl. für ${data.remainingTimeStr}`); |
|
remainingTimeIndicatorText.font = Font.lightRoundedSystemFont(11); |
|
|
|
widget.addSpacer(4); |
|
|
|
// ---------- Last update time indicator ---------- |
|
const lastUpdatedRow = widget.addStack(); |
|
lastUpdatedRow.centerAlignContent(); |
|
lastUpdatedRow.addSpacer(); |
|
const radioSymbol = SFSymbol.named(connection ? "antenna.radiowaves.left.and.right" : "antenna.radiowaves.left.and.right.slash"); |
|
radioSymbol.applyFont(Font.systemFont(11)); |
|
const radioSymbolImage = lastUpdatedRow.addImage(radioSymbol.image); |
|
radioSymbolImage.resizable = false; |
|
radioSymbolImage.tintColor = new Color("#ffffff"); |
|
lastUpdatedRow.addSpacer(4); |
|
|
|
let outText = ""; |
|
const lastUpdatedDayDiff = dayDiff(now, lastUpdated); |
|
const lastUpdatedTime = `${`${lastUpdated.getHours()}`.padStart(2,"0")}:${`${lastUpdated.getMinutes()}`.padStart(2,"0")}`; |
|
if (lastUpdatedDayDiff === 0) outText = `${lastUpdatedTime}`; |
|
else if (lastUpdatedDayDiff === -1) outText = `gestern ${lastUpdatedTime}`; |
|
else outText = `vor ${Math.abs(lastUpdatedDayDiff)} Tagen`; |
|
const lastUpdatedText = lastUpdatedRow.addText(outText); |
|
lastUpdatedText.font = Font.lightRoundedSystemFont(11); |
|
lastUpdatedRow.addSpacer(); |
|
|
|
return widget; |
|
} |
|
|
|
async function getUsageData() { |
|
const fm = FileManager.local(); |
|
const dir = fm.documentsDirectory(); |
|
const path = fm.joinPath(dir, "scriptable-telekom.json"); |
|
|
|
try { |
|
const req = new Request(API_URL); |
|
req.headers = { |
|
// API only answers for mobile Safari |
|
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Mobile/15E148 Safari/604.1" |
|
}; |
|
|
|
const data = await req.loadJSON(); |
|
data.date = Date.now(); |
|
fm.writeString(path, JSON.stringify(data, null, 2)); |
|
|
|
return { error: null, data, connection: true }; |
|
} catch (err) { |
|
const data = JSON.parse(fm.readString(path), null); |
|
|
|
if (!data || !data.usedPercentage) { |
|
return { error: "Please disable WiFi for initial execution." }; |
|
} |
|
|
|
return { error: null, data, connection: false }; |
|
} |
|
} |
|
|
|
async function getImage(key, url) { |
|
const fm = FileManager.local(); |
|
const dir = fm.documentsDirectory(); |
|
const path = fm.joinPath(dir, key); |
|
|
|
if(fm.fileExists(path)) { |
|
return fm.readImage(path); |
|
} |
|
|
|
const imageReq = new Request(url); |
|
const image = await imageReq.loadImage(); |
|
fm.writeImage(path, image); |
|
return image; |
|
} |
|
|
|
function dayDiff(date1, date2) { |
|
return Math.ceil((date2 - date1) / (1000 * 60 * 60 * 24)); |
|
} |
|
|
|
function isInSameMonth(date1, date2) { |
|
return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth(); |
|
} |
|
|
|
function createErrorWidget(text) { |
|
const errorWidget = new ListWidget(); |
|
console.error(text); |
|
} |
|
|
|
const widget = await createWidget().catch((e) => console.error(e)); |
|
|
|
if(config.runsInWidget) { |
|
Script.setWidget(widget); |
|
} else { |
|
widget.presentSmall(); |
|
} |
|
|
|
Script.complete(); |