Created
July 5, 2022 21:24
-
-
Save Sillium/235dc9fd873b25e321b3299da64c9c38 to your computer and use it in GitHub Desktop.
Updated scriptable widget for Telekom data usage
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
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: purple; icon-glyph: magic; | |
const DEBUG = false | |
const log = DEBUG ? console.log.bind(console) : function () { }; | |
const fontSizeBig = 12 | |
const padding = 10 | |
const fontSizeHuge = 36 | |
await createDirectory() | |
// create the widget | |
const params = { | |
widgetParameter: args.widgetParameter, | |
debug: DEBUG | |
} | |
const showData = await fetchData(params) | |
const widget = await createWidget(showData) | |
// preview the widget | |
if (!config.runsInWidget) { | |
await widget.presentSmall() | |
} | |
Script.setWidget(widget) | |
Script.complete() | |
// ==================================================================== | |
async function createDirectory() { | |
let fm = FileManager.local() | |
let scriptPath = module.filename | |
let libraryDir = scriptPath.replace(fm.fileName(scriptPath, true), fm.fileName(scriptPath, false)) | |
let cacheFilePath = fm.joinPath(libraryDir, "cache.json") | |
if (fm.fileExists(libraryDir) && !fm.isDirectory(libraryDir)) { | |
fm.downloadFileFromiCloud(libraryDir) | |
fm.remove(libraryDir) | |
} | |
if (!fm.fileExists(libraryDir)) { | |
fm.createDirectory(libraryDir) | |
} | |
if (!fm.fileExists(cacheFilePath)) { | |
data = {} | |
data.remainingDays = 0 | |
data.availableVolume = 0 | |
data.availablePercentage = -1 | |
data.initialVolume = 0 | |
data.usedVolume = 0 | |
data.usedPercentage = -1 | |
// Write JSON to iCloud file | |
fm.writeString(cacheFilePath, JSON.stringify(data, null, 2)) | |
} | |
} | |
async function fetchData(config) { | |
log(JSON.stringify(config, null, 2)) | |
//=== Extract parameters ========================================== | |
let param = config.widgetParameter | |
let language, whatToShow, look | |
if (param != null && param.length > 0) { | |
const parts = param.split(';') | |
if (parts.length == 3) { | |
language = parts[0].toLowerCase() | |
whatToShow = parts[1].toLowerCase() | |
look = parts[2].toLowerCase() | |
} else { | |
log("Please fix widget parameter configuration.") | |
} | |
} else { | |
language = 'en' | |
whatToShow = 'used' | |
look = 'telekom' | |
} | |
log("language: " + language) | |
log("whatToShow: " + whatToShow) | |
log("look: " + look) | |
if (['de', 'en'].indexOf(language) == -1) { | |
log("Please fix widget parameter configuration 'language'.") | |
} | |
if (['used', 'available'].indexOf(whatToShow) == -1) { | |
log("Please fix widget parameter configuration 'whatToShow'.") | |
} | |
if (['telekom', 'congstar', 'fraenk', 'black', 'gray', 'white'].indexOf(look) == -1) { | |
log("Please fix widget parameter configuration 'look'.") | |
} | |
//=== API Request ================================================ | |
let fm = FileManager.local() | |
let scriptPath = module.filename | |
let libraryDir = scriptPath.replace(fm.fileName(scriptPath, true), fm.fileName(scriptPath, false)) | |
let path = fm.joinPath(libraryDir, "cache.json") | |
const apiUrl = "https://pass.telekom.de/api/service/generic/v1/status" | |
let data, fresh = 0 | |
let r = new Request(apiUrl) | |
// API only answers for mobile Safari | |
r.headers = { | |
"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" | |
} | |
try { | |
// Fetch data from pass.telekom.de | |
data = await r.loadJSON() | |
data.remainingDays = data.remainingSeconds / (24*60*60) | |
data.availableVolume = data.initialVolume - data.usedVolume | |
data.availablePercentage = 100 - data.usedPercentage | |
// Write JSON to iCloud file | |
fm.writeString(path, JSON.stringify(data, null, 2)) | |
fresh = 1 | |
} catch (err) { | |
// Read data from iCloud file | |
fm.downloadFileFromiCloud(path) | |
data = JSON.parse(fm.readString(path), null) | |
} | |
log("fresh: " + fresh) | |
log("data: " + JSON.stringify(data, null, 2)) | |
//=== Formatter ===================================== | |
var formatterDays = new Intl.NumberFormat(language, { | |
style: 'decimal', | |
minimumFractionDigits: 0, | |
maximumFractionDigits: 0 | |
}); | |
var rtfEn = new Intl.RelativeTimeFormat(language, { | |
numeric: 'auto' | |
}); | |
//=== Data to show ===================================== | |
let showData = { | |
'initialVolume': data.initialVolume, | |
'color': {}, | |
'renewsIn': rtfEn.format(formatterDays.format(data.remainingDays), 'day'), | |
'fresh': fresh, | |
'language': language | |
} | |
if (whatToShow == 'used') { | |
showData.percent = data.usedPercentage | |
showData.dataVolume = data.usedVolume | |
} else if (whatToShow == 'available') { | |
showData.percent = data.availablePercentage | |
showData.dataVolume = data.availableVolume | |
} | |
if (language == 'de') { | |
showData.label = { | |
'headline': (fresh == 1) ? 'MOBILE DATEN' : 'API OFFLINE', | |
'dataVolume': (whatToShow == 'used') ? 'Verbraucht' : 'Verfügbar', | |
'initial': 'Inklusive', | |
'renewsIn': "Bis" | |
} | |
} else if (language == 'en') { | |
showData.label = { | |
'headline': (fresh == 1) ? 'MOBILE DATA' : 'API OFFLINE', | |
'dataVolume': (whatToShow == 'used') ? 'Used' : 'Unused', | |
'initial': 'Included', | |
'renewsIn': "Until" | |
} | |
} | |
if (look == 'congstar') { | |
showData.providerImage = await getImage('congstar.png', config.debug) | |
showData.color.foregroundActive = Color.white() | |
showData.color.foregroundInactive = new Color("#444444") | |
showData.color.background = new Color("#212121") | |
} else if (look == 'fraenk') { | |
showData.providerImage = await getImage('fraenk.png', config.debug) | |
showData.color.foregroundActive = Color.black() | |
showData.color.foregroundInactive = new Color("#CCCCCC") | |
showData.color.background = Color.white() | |
} else if (look == 'telekom') { | |
showData.providerImage = await getImage('telekom.png', config.debug) | |
showData.color.foregroundActive = Color.white() | |
showData.color.foregroundInactive = new Color("#FF34A5") | |
showData.color.background = new Color("#DD0273") | |
} else if (look == 'white') { | |
showData.providerImage = await getImage('generic_white.png', config.debug) | |
showData.color.foregroundActive = Color.black() | |
showData.color.foregroundInactive = new Color("#CCCCCC") | |
showData.color.background = Color.white() | |
} else if (look == 'gray') { | |
showData.providerImage = await getImage('generic_gray.png', config.debug) | |
showData.color.foregroundActive = Color.white() | |
showData.color.foregroundInactive = new Color("#555555") | |
showData.color.background = new Color("#2C2C2E") | |
} else if (look == 'black') { | |
showData.providerImage = await getImage('generic_black.png', config.debug) | |
showData.color.foregroundActive = Color.white() | |
showData.color.foregroundInactive = new Color("#444444") | |
showData.color.background = Color.black() | |
} | |
log("showData: " + JSON.stringify(showData, null, 2)) | |
return showData | |
} | |
async function createWidget(showData) { | |
//=== Create Widget ===================================== | |
const widget = new ListWidget() | |
widget.backgroundColor = showData.color.background | |
widget.setPadding (padding, padding, padding, padding) | |
// === Logo ===================================== | |
let rowLogo = addStackTo(widget, 'h') | |
rowLogo.centerAlignContent() | |
const telekomIconImg = rowLogo.addImage(showData.providerImage) | |
telekomIconImg.imageSize = new Size(30, 30) | |
rowLogo.addSpacer(5) | |
let headlineSurroundingStack = addStackTo(rowLogo, 'v') | |
const headlineLabel = headlineSurroundingStack.addText(showData.label.headline) | |
headlineLabel.leftAlignText() | |
headlineLabel.font = Font.mediumSystemFont(12) | |
headlineLabel.textColor = showData.color.foregroundActive | |
widget.addSpacer() | |
//=== Percent =================================== | |
let rowPercentage = addStackTo(widget, 'h') | |
rowPercentage.addSpacer() | |
let percentText | |
if (showData.percent == -1) { | |
percentText = rowPercentage.addText("WiFi is on") | |
percentText.font = Font.heavySystemFont(22) | |
} else { | |
percentText = rowPercentage.addText(showData.percent + " %") | |
percentText.font = Font.heavySystemFont(fontSizeHuge) | |
} | |
percentText.centerAlignText() | |
percentText.textColor = (showData.fresh == 1) ? showData.color.foregroundActive : showData.color.foregroundInactive | |
rowPercentage.addSpacer() | |
widget.addSpacer() | |
// === Details ===================================== | |
let rowDetails = addStackTo(widget, 'h') | |
let rowDetailsLeft = addStackTo(rowDetails, 'v') | |
const detailsLabel = rowDetailsLeft.addText(showData.label.dataVolume) | |
detailsLabel.leftAlignText() | |
detailsLabel.font = Font.regularRoundedSystemFont(fontSizeBig) | |
detailsLabel.textColor = (showData.fresh == 1) ? showData.color.foregroundActive : showData.color.foregroundInactive | |
rowDetails.addSpacer() | |
let rowDetailsRight = addStackTo(rowDetails, 'v') | |
const detailsText = rowDetailsRight.addText(formatVolume(showData.dataVolume, showData.language)) | |
detailsText.rightAlignText() | |
detailsText.font = Font.heavySystemFont(fontSizeBig) | |
detailsText.textColor = (showData.fresh == 1) ? showData.color.foregroundActive : showData.color.foregroundInactive | |
// === Initial ===================================== | |
let rowInitial = addStackTo(widget, 'h') | |
let rowInitialLeft = addStackTo(rowInitial, 'v') | |
const initialLabel = rowInitialLeft.addText(showData.label.initial) | |
initialLabel.leftAlignText() | |
initialLabel.font = Font.regularRoundedSystemFont(fontSizeBig) | |
initialLabel.textColor = (showData.fresh == 1) ? showData.color.foregroundActive : showData.color.foregroundInactive | |
rowInitial.addSpacer() | |
let rowInitialRight = addStackTo(rowInitial, 'v') | |
const initialText = rowInitialRight.addText(formatVolume(showData.initialVolume, showData.language)) | |
initialText.rightAlignText() | |
initialText.font = Font.heavySystemFont(fontSizeBig) | |
initialText.textColor = (showData.fresh == 1) ? showData.color.foregroundActive : showData.color.foregroundInactive | |
// === Time period ===================================== | |
let rowTimePeriod = addStackTo(widget, 'h') | |
let rowTimePeriodLeft = addStackTo(rowTimePeriod, 'v') | |
const timePeriodLabel = rowTimePeriodLeft.addText(showData.label.renewsIn) | |
timePeriodLabel.leftAlignText() | |
timePeriodLabel.font = Font.regularRoundedSystemFont(fontSizeBig) | |
timePeriodLabel.textColor = (showData.fresh == 1) ? showData.color.foregroundActive : showData.color.foregroundInactive | |
rowTimePeriod.addSpacer() | |
let rowTimePeriodRight = addStackTo(rowTimePeriod, 'v') | |
const renewsInText = rowTimePeriodRight.addText(showData.renewsIn) | |
renewsInText.rightAlignText() | |
renewsInText.font = Font.heavySystemFont(fontSizeBig) | |
renewsInText.textColor = (showData.fresh == 1) ? showData.color.foregroundActive : showData.color.foregroundInactive | |
widget.addSpacer(2) | |
return widget | |
} | |
// get images from local filestore or download them once | |
async function getImage(image, forceDownload) { | |
let fm = FileManager.local() | |
let scriptPath = module.filename | |
let libraryDir = scriptPath.replace(fm.fileName(scriptPath, true), fm.fileName(scriptPath, false)) | |
let path = fm.joinPath(libraryDir, image) | |
if (fm.fileExists(path) && !forceDownload) { | |
fm.downloadFileFromiCloud(path) | |
return fm.readImage(path) | |
} else { | |
// download once | |
let imageUrl | |
switch (image) { | |
case 'telekom.png': | |
imageUrl = "https://i.imgur.com/wKKfJdwt.png" | |
break | |
case 'congstar.png': | |
imageUrl = "https://i.imgur.com/z02k5gSt.png" | |
break | |
case 'fraenk.png': | |
imageUrl = "https://i.imgur.com/MtDumhPt.png" | |
break | |
case 'generic_white.png': | |
imageUrl = "https://i.imgur.com/RPf5sYWt.png" | |
break | |
case 'generic_black.png': | |
imageUrl = "https://i.imgur.com/rWz8kpFt.png" | |
break | |
case 'generic_gray.png': | |
imageUrl = "https://i.imgur.com/bfKPtInt.png" | |
break | |
default: | |
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 addStackTo(stack, layout) { | |
const newStack = stack.addStack() | |
if (DEBUG) { | |
newStack.backgroundColor = new Color(randomColor(), 1.0) | |
} | |
if (layout == 'h') { | |
newStack.layoutHorizontally() | |
} else { | |
newStack.layoutVertically() | |
} | |
return newStack | |
} | |
function randomColor() { | |
let color = '#'; | |
for (let i = 0; i < 6; i++){ | |
const random = Math.random(); | |
const bit = (random * 16) | 0; | |
color += (bit).toString(16); | |
} | |
return color; | |
} | |
function formatVolume(amount, language) { | |
var formatterGiga = new Intl.NumberFormat(language, { | |
style: 'decimal', | |
minimumFractionDigits: 0, | |
maximumFractionDigits: 2 | |
}); | |
let amount_str | |
if (amount >= 100*1024*1024) { | |
amount_str = formatterGiga.format(amount / (1024*1024*1024)) + " GB" | |
} else if (amount >= 100*1024) { | |
amount_str = formatterGiga.format(amount / (1024*1024)) + " MB" | |
} else if (amount >= 100) { | |
amount_str = formatterGiga.format(amount / 1024) + " kB" | |
} else { | |
amount_str = formatterGiga.format(amount) + " B" | |
} | |
return amount_str | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment