Skip to content

Instantly share code, notes, and snippets.

@Sillium
Created July 5, 2022 21:24
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
Updated scriptable widget for Telekom data usage
// 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