From 22357c465081ac834dd61b73abda3511e996aa5e Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Sun, 27 Dec 2020 15:30:27 +0100 Subject: [PATCH 1/8] Vodafone Widget v2 Beta 1 Signed-off-by: Benny Samir Hierl --- VodafoneDE/VodafoneDE.js | 401 ++++++++++++++++++++++++++------------- 1 file changed, 267 insertions(+), 134 deletions(-) diff --git a/VodafoneDE/VodafoneDE.js b/VodafoneDE/VodafoneDE.js index a3fc12b..597133a 100644 --- a/VodafoneDE/VodafoneDE.js +++ b/VodafoneDE/VodafoneDE.js @@ -3,9 +3,16 @@ // icon-color: red; icon-glyph: broadcast-tower; /************** -Version 1.2.4 +Version 2.0.0 -Changelog: +Changelog: + v2.0.0: + - Disable "Dark Mode Support" + - Medium Widget Support + - Switch between used and remaining volume + - Use MSISDN for Cache-Name + - Show amount for prepaid cards + - Show remaining days as progress-bar v1.2.4: - use color.dynamic v1.2.3: @@ -41,18 +48,28 @@ Credits: **************/ // How many minutes should the cache be valid -let cacheMinutes = 60; +const cacheMinutes = 60 + +// Set to false if the widget should display always in red +const darkModeSupport = true + +// Switch between remaining and used contingent. If you want show the used contingent, then change the value from true to false +const showRemainingContingent = true + +// To disable the progressbar for the remaining days, you have to change the value from true to false +const showRemainingDaysAsProgressbar = true // Please add additional values to these list, in case that your contract/tarif isn't supported by these default values. const containerList = ['Daten', 'D_EU_DATA', 'C_DIY_Data_National'] -const codeList = ['-1', '45500', '40100'] +const codeList = ['-1', '-5' ,'45500', '40100'] +// Dev Settings +const debug = false +config.widgetFamily = config.widgetFamily || 'small' //////////////////////////////////////////////////////////////////////////////// let widgetInputRAW = args.widgetParameter; let widgetInput = null; - let user, pass, number - if (widgetInputRAW !== null) { [user, pass, number] = widgetInputRAW.toString().split("|"); @@ -64,56 +81,115 @@ if (widgetInputRAW !== null) { } } -const backColor = Color.dynamic(new Color('D32D1F'), new Color('111111')); -const backColor2 = Color.dynamic(new Color('76150C'), new Color('222222')); -const textColor = Color.dynamic(new Color('EDEDED'), new Color('EDEDED')); -const fillColor = Color.dynamic(new Color('EDEDED'), new Color('EDEDED')); -const strokeColor = Color.dynamic(new Color('B0B0B0'), new Color('121212')); - -const canvas = new DrawContext(); -const canvSize = 200; -const canvTextSize = 36; - -const canvWidth = 22; -const canvRadius = 80; - -canvas.opaque = false -canvas.size = new Size(canvSize, canvSize); -canvas.respectScreenScale = true; - -function sinDeg(deg) { - return Math.sin((deg * Math.PI) / 180); +const h = 5 +let width +if (config.widgetFamily === 'small') { + width = 200 +} else { + width = 400 } -function cosDeg(deg) { - return Math.cos((deg * Math.PI) / 180); +let backColor = new Color('D32D1F') +let backColor2 = new Color('93291E') +let textColor = new Color('EDEDED') +let fillColor = new Color('EDEDED') +let strokeColor = new Color('B0B0B0') + +if (darkModeSupport) { + backColor = Color.dynamic(backColor, new Color('111111')) + backColor2 = Color.dynamic(backColor2, new Color('222222')) + textColor = Color.dynamic(textColor, new Color('EDEDED')) + fillColor = Color.dynamic(fillColor, new Color('EDEDED')) + strokeColor = Color.dynamic(strokeColor, new Color('121212')) } -function drawArc(ctr, rad, w, deg) { - bgx = ctr.x - rad; - bgy = ctr.y - rad; - bgd = 2 * rad; - bgr = new Rect(bgx, bgy, bgd, bgd); +function creatProgress(total, havegone) { + const context = new DrawContext() + context.size = new Size(width, h) + context.opaque = false + context.respectScreenScale = false + + // Background Path + context.setFillColor(fillColor) + const path = new Path() + path.addRoundedRect(new Rect(0, 0, width, h), 3, 2) + context.addPath(path) + context.fillPath() + + // Progress Path + context.setFillColor(strokeColor) + const path1 = new Path() + const path1width = (width * (havegone / total) > width) ? width : width * (havegone / total) + path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2) + context.addPath(path1) + context.fillPath() + return context.getImage() +} - canvas.setFillColor(fillColor); - canvas.setStrokeColor(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); +function getDiagram(percentage) { + 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(fillColor) + canvas.setStrokeColor(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) + } } + function sinDeg(deg) { + return Math.sin((deg * Math.PI) / 180) + } + + function cosDeg(deg) { + return Math.cos((deg * Math.PI) / 180) + } + const canvas = new DrawContext() + const canvSize = 200 + const canvTextSize = 36 + + const canvWidth = 10 + const canvRadius = 80 + + canvas.opaque = false + canvas.size = new Size(canvSize, canvSize) + canvas.respectScreenScale = true + + drawArc( + new Point(canvSize / 2, canvSize / 2), + canvRadius, + canvWidth, + Math.floor(percentage * 3.6) + ) + + const canvTextRect = new Rect( + 0, + 100 - canvTextSize / 2, + canvSize, + canvTextSize + ) + canvas.setTextAlignedCenter() + canvas.setTextColor(textColor) + canvas.setFont(Font.boldSystemFont(canvTextSize)) + canvas.drawTextInRect(`${percentage}%`, canvTextRect) + + return canvas.getImage() } function getTimeRemaining(endtime) { - const total = Date.parse(endtime) - Date.parse(new Date()); - const seconds = Math.floor((total / 1000) % 60); - const minutes = Math.floor((total / 1000 / 60) % 60); - const hours = Math.floor((total / (1000 * 60 * 60)) % 24); - const days = Math.floor(total / (1000 * 60 * 60 * 24)); + const total = Date.parse(endtime) - Date.parse(new Date()) + const seconds = Math.floor((total / 1000) % 60) + const minutes = Math.floor((total / 1000 / 60) % 60) + const hours = Math.floor((total / (1000 * 60 * 60)) % 24) + const days = Math.floor(total / (1000 * 60 * 60 * 24)) return { total, @@ -121,7 +197,7 @@ function getTimeRemaining(endtime) { hours, minutes, seconds - }; + } } async function getSessionCookiesViaNetworkLogin() { @@ -147,7 +223,7 @@ async function getSessionCookiesViaNetworkLogin() { console.log("Login failed! Please check if Wifi is disabled.") throw new Error(`Login failed with HTTP-Status-Code ${req.response.statusCode}`) } -}; +} async function getSessionCookiesViaMeinVodafoneLogin(u, p) { let req; @@ -170,25 +246,25 @@ async function getSessionCookiesViaMeinVodafoneLogin(u, p) { console.log("Login failed!") throw new Error(`Login failed with HTTP-Status-Code ${req.response.statusCode}`) } -}; +} async function getUsage(user, pass, number) { - let cookies, msisdn; + let cookies, msisdn if (user && pass && number) { console.log("Login via MeinVodafone") - let { cookies: c } = await getSessionCookiesViaMeinVodafoneLogin(user, pass); - cookies = c; - msisdn = number; + let { cookies: c } = await getSessionCookiesViaMeinVodafoneLogin(user, pass) + cookies = c + msisdn = number } else { console.log("Login via Network") - let { cookies: c, msisdn: m } = await getSessionCookiesViaNetworkLogin(); - cookies = c; - msisdn = m; + let { cookies: c, msisdn: m } = await getSessionCookiesViaNetworkLogin() + cookies = c + msisdn = m } let CookieValues = cookies.map(function (v) { return v.name + "=" + v.value }) - let req; + let req req = new Request(`https://www.vodafone.de/api/enterprise-resources/core/bss/sub-nil/mobile/payment/service-usages/subscriptions/${msisdn}/unbilled-usage`) req.headers = { 'x-vf-api': '1499082775305', @@ -198,72 +274,91 @@ async function getUsage(user, pass, number) { } try { let res = await req.loadJSON() + if(!res['serviceUsageVBO'] || !res['serviceUsageVBO']['usageAccounts'] || !res['serviceUsageVBO']['usageAccounts'][0]) { + if (debug) { + console.log(JSON.stringify(res, null, 2)) + } + + } console.log("unbilled-usage loaded") - let datenContainer = res['serviceUsageVBO']['usageAccounts'][0]['usageGroup'].find(function (v) { + if (debug) { + console.log(JSON.stringify(res['serviceUsageVBO']['usageAccounts'][0], null, 2)) + } + const marketCode = res['serviceUsageVBO']['usageAccounts'][0]['details']['marketCode'] + const billDate = res['serviceUsageVBO']['usageAccounts'][0]['details']['billDate'] + const amount = res['serviceUsageVBO']['usageAccounts'][0]['details']['amount'] + + let usage = [] + + // Get Main Container + let container = res['serviceUsageVBO']['usageAccounts'][0]['usageGroup'].filter(function (v) { return containerList.includes(v.container) - }) + }) - if (datenContainer === undefined) { + if (container.length === 0) { const ErrorMsg = "Can't find usageGroup with supported Container: " + containerList.join(', ') + "."; console.log(ErrorMsg) const listOfContainerNamesInResponse = res['serviceUsageVBO']['usageAccounts'][0]['usageGroup'].map(function (v) { - return v.container; + return v.container }) - console.log("Please check the following list to find the correct container name for your case and adjust the list of container names at the beginnging: " + listOfContainerNamesInResponse.join(", ")) + console.log("Please check the following list to find the correct container name for your case and adjust the list of container names at the beginnging: " + listOfContainerNamesInResponse.join(", ")) throw new Error(ErrorMsg) } - let datenvolumen; - if (datenContainer.usage.length == 1) { - datenvolumen = datenContainer.usage[0] - } else { - datenvolumen = datenContainer.usage.find(function (v) { - return codeList.includes(v.code) - }) + + for(let i = 0; i < container.length; i++) { + for (let j = 0; j < container[i]['usage'].length; j++) { + if (codeList.includes(container[i]['usage'][j]['code'])) { + usage.push(container[i]['usage'][j]) + } + } } - - if (datenvolumen === undefined) { + if (usage.length === 0) { const ErrorMsg = "Can't find Usage with supported Codes: " + codeList.join(', ') + "."; - console.log(ErrorMsg) - - const listOfCodeInResponse = datenContainer.usage.map(function (v) { - return `Code: "${v.code}" for "${v.description}"`; - }) + console.log(ErrorMsg) + + const listOfCodeInResponse = [] + for(let i = 0; i < container.length; i++) { + for (let j = 0; j < container[i]['usage'].length; j++) { + listOfCodeInResponse.push(`Code: "${container[i]['usage'][j].code}" for "${container[i]['usage'][j].description}"`) + } + } console.log("Please check the following list to find the correct code for your case and adjust the list of codes at the beginnging: " + listOfCodeInResponse.join(", ")) throw new Error(ErrorMsg) } - - - let endDate = datenvolumen.endDate; + + let endDate = usage[0].endDate if (endDate == null) { endDate = res['serviceUsageVBO']['billDetails']['billCycleEndDate'] || null } - + return { - total: datenvolumen.total, - used: datenvolumen.used, - remaining: datenvolumen.remaining, - endDate + billDate, + endDate, + amount, + marketCode, + usage } } catch (e) { console.log("Loading usage data failed") throw e } -}; +} -var today = new Date(); +var today = new Date() // Set up the file manager. const files = FileManager.local() -// Set up cache . -const cachePath = files.joinPath(files.documentsDirectory(), "widget-vodafone") +// Set up cache +const cacheNamePostfix = (number) ? number.substr(number.length - 4) : 'networkLogin' +const cachePath = files.joinPath(files.documentsDirectory(), "widget-vodafone-" + cacheNamePostfix) const cacheExists = files.fileExists(cachePath) const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0 // Get Data -let data; +let data let lastUpdate try { // If cache exists and it's been less than 30 minutes since last request, use cached data. @@ -301,7 +396,9 @@ let widget = new ListWidget(); widget.setPadding(10, 10, 10, 10) if (data !== undefined) { - console.log(data) + if(debug) { + console.log(JSON.stringify(data, null, 2)) + } const gradient = new LinearGradient() gradient.locations = [0, 1] gradient.colors = [ @@ -315,63 +412,94 @@ if (data !== undefined) { let provider = firstLineStack.addText("Vodafone") provider.font = Font.mediumSystemFont(12) provider.textColor = textColor + + if (data.marketCode === 'MMO') { + widget.addSpacer(2) + const amount = widget.addText(`Guthaben: ${data.amount.replace('.', ',')} €`) + amount.font = Font.systemFont(8) + amount.textColor = textColor + } // Last Update firstLineStack.addSpacer() let lastUpdateText = firstLineStack.addDate(lastUpdate) - lastUpdateText.font = Font.mediumSystemFont(8) + lastUpdateText.font = Font.systemFont(8) lastUpdateText.rightAlignText() lastUpdateText.applyTimeStyle() lastUpdateText.textColor = Color.lightGray() widget.addSpacer() - - let remainingPercentage = (100 / data.total * data.remaining).toFixed(0); - - drawArc( - new Point(canvSize / 2, canvSize / 2), - canvRadius, - canvWidth, - Math.floor(remainingPercentage * 3.6) - ); - - const canvTextRect = new Rect( - 0, - 100 - canvTextSize / 2, - canvSize, - canvTextSize - ); - canvas.setTextAlignedCenter(); - canvas.setTextColor(textColor); - canvas.setFont(Font.boldSystemFont(canvTextSize)); - canvas.drawTextInRect(`${remainingPercentage}%`, canvTextRect); - - const canvImage = canvas.getImage(); - let image = widget.addImage(canvImage); - image.centerAlignImage() - + + const stack = widget.addStack() + stack.layoutHorizontally() + + data.usage.slice(0, (config.widgetFamily === 'small' ? 1 : 2)).forEach((v) => { + const column = stack.addStack() + column.layoutVertically() + column.centerAlignContent() + + const percentage = (100 / v.total * (showRemainingContingent ? v.remaining : v.used)).toFixed(0); + const imageStack = column.addStack() + imageStack.layoutHorizontally() + imageStack.addSpacer() + imageStack.addImage(getDiagram(percentage)); + imageStack.addSpacer() + column.addSpacer(2) + + // Total Values + let totalValues; + if (v.unitOfMeasure !== 'MB') { + totalValues = `${(showRemainingContingent ? v.remaining : v.used)} ${v.unitOfMeasure} von ${v.total} ${v.unitOfMeasure}` + } else if (parseInt(v.total) < 1000) { + totalValues = `${(showRemainingContingent ? v.remaining : v.used)} MB von ${v.total} MB` + } else { + let GB = ((showRemainingContingent ? v.remaining : v.used) / 1024).toFixed(2) + let totalGB = (v.total / 1024).toFixed(2) + totalValues = `${GB} GB von ${totalGB} GB` + } + textStack = column.addStack() + textStack.layoutHorizontally() + textStack.addSpacer() + let diagramText = textStack.addText(totalValues) + diagramText.font = Font.mediumSystemFont(11) + diagramText.lineLimit = 1 + diagramText.centerAlignText() + diagramText.textColor = textColor + textStack.addSpacer() + + nameStack = column.addStack() + nameStack.layoutHorizontally() + nameStack.addSpacer() + let diagramName = nameStack.addText(v.name.replace('Inland & EU', '').trim()) + diagramName.font = Font.systemFont(10) + diagramName.lineLimit = 1 + diagramName.centerAlignText() + diagramName.textColor = textColor + nameStack.addSpacer() + }) + widget.addSpacer() - // Total Values - let totalValues; - if (parseInt(data.total) < 1000) { - totalValues = `${data.remaining} MB von ${data.total} MB` - } else { - let remainingGB = (data.remaining / 1024).toFixed(2) - let totalGB = (data.total / 1024).toFixed(0) - totalValues = `${remainingGB} GB von ${totalGB} GB` - } - let totalValuesText = widget.addText(totalValues) - totalValuesText.font = Font.mediumSystemFont(12) - totalValuesText.centerAlignText() - totalValuesText.textColor = textColor - - // Remaining Days + // Remaining Days if (data.endDate) { widget.addSpacer(5) let remainingDays = getTimeRemaining(data.endDate).days + 2 + if(data.billDate && showRemainingDaysAsProgressbar) { + const startDate = new Date(data.billDate) + const endDate = new Date(data.endDate) + const total = (endDate - startDate) / (1000 * 60 * 60 * 24) + + const progressBarStack = widget.addStack() + progressBarStack.layoutHorizontally() + progressBarStack.addSpacer() + + const progressBar = progressBarStack.addImage(creatProgress(total, total - remainingDays)) + progressBarStack.addSpacer() + widget.addSpacer(4) + } + let remainingDaysText = widget.addText(`${remainingDays} Tage verbleibend`) - remainingDaysText.font = Font.mediumSystemFont(8) + remainingDaysText.font = Font.systemFont(8) remainingDaysText.centerAlignText() remainingDaysText.textColor = textColor } @@ -383,7 +511,12 @@ if (data !== undefined) { } if (!config.runsInWidget) { - await widget.presentSmall() + switch (config.widgetFamily) { + case 'small': await widget.presentSmall(); break; + case 'medium': await widget.presentMedium(); break; + case 'large': await widget.presentLarge(); break; + } + } else { // Tell the system to show the widget. Script.setWidget(widget) From 3ea17a9fb8527e6374336f99acc241a6f8b82500 Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Sun, 27 Dec 2020 22:27:07 +0100 Subject: [PATCH 2/8] make it easier to connfigure the optins Signed-off-by: Benny Samir Hierl --- VodafoneDE/VodafoneDE.js | 74 ++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/VodafoneDE/VodafoneDE.js b/VodafoneDE/VodafoneDE.js index 597133a..9ae091c 100644 --- a/VodafoneDE/VodafoneDE.js +++ b/VodafoneDE/VodafoneDE.js @@ -7,12 +7,12 @@ Version 2.0.0 Changelog: v2.0.0: - - Disable "Dark Mode Support" - - Medium Widget Support - - Switch between used and remaining volume - - Use MSISDN for Cache-Name - - Show amount for prepaid cards - - Show remaining days as progress-bar + - Disable Dark Modus Support + - Medium & large Widget Support + - switch between used and remaining volume + - use MSISDN for Cache-Name + - show amount for prepaid cards + - show remaining days as progress-bar v1.2.4: - use color.dynamic v1.2.3: @@ -47,6 +47,10 @@ Credits: - Chaeimg@Github (https://github.com/chaeimg/battCircle) **************/ +//////////////////////////////////////////////////////////////////////////////// +////////////////////////// User-Config ///////////////////////// +//////////////////////////////////////////////////////////////////////////////// + // How many minutes should the cache be valid const cacheMinutes = 60 @@ -63,11 +67,21 @@ const showRemainingDaysAsProgressbar = true const containerList = ['Daten', 'D_EU_DATA', 'C_DIY_Data_National'] const codeList = ['-1', '-5' ,'45500', '40100'] -// Dev Settings -const debug = false -config.widgetFamily = config.widgetFamily || 'small' + //////////////////////////////////////////////////////////////////////////////// +////////////////////////// Dev Settings //////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +const debug = false +config.widgetFamily = config.widgetFamily || 'medium' + +//////////////////////////////////////////////////////////////////////////////// +////////////////////////// System-Config /////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Input let widgetInputRAW = args.widgetParameter; + let widgetInput = null; let user, pass, number if (widgetInputRAW !== null) { @@ -81,6 +95,19 @@ if (widgetInputRAW !== null) { } } +// Text sizes +const fontSizeData = 11 +const lineNumberData = 1 +const minimumScaleFactor = 1.0 // Value between 1.0 and 0.1 + +// Number of data by Size +const numberOfDisplayedDataBySize = { + small: 1, + medium: 2, + large: 4 +} + +// Progressbar const h = 5 let width if (config.widgetFamily === 'small') { @@ -89,6 +116,7 @@ if (config.widgetFamily === 'small') { width = 400 } +// Colors let backColor = new Color('D32D1F') let backColor2 = new Color('93291E') let textColor = new Color('EDEDED') @@ -102,6 +130,7 @@ if (darkModeSupport) { fillColor = Color.dynamic(fillColor, new Color('EDEDED')) strokeColor = Color.dynamic(strokeColor, new Color('121212')) } +//////////////////////////////////////////////////////////////////////////////// function creatProgress(total, havegone) { const context = new DrawContext() @@ -223,7 +252,7 @@ async function getSessionCookiesViaNetworkLogin() { console.log("Login failed! Please check if Wifi is disabled.") throw new Error(`Login failed with HTTP-Status-Code ${req.response.statusCode}`) } -} +}; async function getSessionCookiesViaMeinVodafoneLogin(u, p) { let req; @@ -246,7 +275,7 @@ async function getSessionCookiesViaMeinVodafoneLogin(u, p) { console.log("Login failed!") throw new Error(`Login failed with HTTP-Status-Code ${req.response.statusCode}`) } -} +}; async function getUsage(user, pass, number) { let cookies, msisdn @@ -344,7 +373,7 @@ async function getUsage(user, pass, number) { console.log("Loading usage data failed") throw e } -} +}; var today = new Date() @@ -432,9 +461,16 @@ if (data !== undefined) { const stack = widget.addStack() stack.layoutHorizontally() - - data.usage.slice(0, (config.widgetFamily === 'small' ? 1 : 2)).forEach((v) => { - const column = stack.addStack() + + let i = 0 + let row + data.usage.slice(0, numberOfDisplayedDataBySize[config.widgetFamily]).forEach((v) => { + if (++i % 2 == 1) { + row = widget.addStack() + row.layoutHorizontally() + widget.addSpacer(5) + } + column = row.addStack() column.layoutVertically() column.centerAlignContent() @@ -461,8 +497,9 @@ if (data !== undefined) { textStack.layoutHorizontally() textStack.addSpacer() let diagramText = textStack.addText(totalValues) - diagramText.font = Font.mediumSystemFont(11) - diagramText.lineLimit = 1 + diagramText.font = Font.mediumSystemFont(fontSizeData) + diagramText.minimumScaleFactor = minimumScaleFactor + diagramText.lineLimit = lineNumberData diagramText.centerAlignText() diagramText.textColor = textColor textStack.addSpacer() @@ -471,7 +508,8 @@ if (data !== undefined) { nameStack.layoutHorizontally() nameStack.addSpacer() let diagramName = nameStack.addText(v.name.replace('Inland & EU', '').trim()) - diagramName.font = Font.systemFont(10) + diagramName.font = Font.systemFont(fontSizeData - 1) + diagramName.minimumScaleFactor = minimumScaleFactor diagramName.lineLimit = 1 diagramName.centerAlignText() diagramName.textColor = textColor From e3da7b9b00c0710f2955c251f69e1406dc56f559 Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Mon, 28 Dec 2020 16:25:38 +0100 Subject: [PATCH 3/8] small adjustment Signed-off-by: Benny Samir Hierl --- VodafoneDE/VodafoneDE.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/VodafoneDE/VodafoneDE.js b/VodafoneDE/VodafoneDE.js index 9ae091c..27cf6a7 100644 --- a/VodafoneDE/VodafoneDE.js +++ b/VodafoneDE/VodafoneDE.js @@ -73,7 +73,7 @@ const codeList = ['-1', '-5' ,'45500', '40100'] //////////////////////////////////////////////////////////////////////////////// const debug = false -config.widgetFamily = config.widgetFamily || 'medium' +config.widgetFamily = config.widgetFamily || 'small' //////////////////////////////////////////////////////////////////////////////// ////////////////////////// System-Config /////////////////////// @@ -98,7 +98,7 @@ if (widgetInputRAW !== null) { // Text sizes const fontSizeData = 11 const lineNumberData = 1 -const minimumScaleFactor = 1.0 // Value between 1.0 and 0.1 +const minimumScaleFactor = 0.8 // Value between 1.0 and 0.1 // Number of data by Size const numberOfDisplayedDataBySize = { @@ -120,15 +120,15 @@ if (config.widgetFamily === 'small') { let backColor = new Color('D32D1F') let backColor2 = new Color('93291E') let textColor = new Color('EDEDED') -let fillColor = new Color('EDEDED') -let strokeColor = new Color('B0B0B0') +let strokeColor = new Color('EDEDED') +let fillColor = new Color('B0B0B0') if (darkModeSupport) { backColor = Color.dynamic(backColor, new Color('111111')) backColor2 = Color.dynamic(backColor2, new Color('222222')) textColor = Color.dynamic(textColor, new Color('EDEDED')) - fillColor = Color.dynamic(fillColor, new Color('EDEDED')) - strokeColor = Color.dynamic(strokeColor, new Color('121212')) + strokeColor = Color.dynamic(strokeColor, new Color('EDEDED')) + fillColor = Color.dynamic(fillColor, new Color('111111')) } //////////////////////////////////////////////////////////////////////////////// From 1ff280ef549cbfb780ec3bd92631a97470dda6df Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Mon, 28 Dec 2020 16:57:55 +0100 Subject: [PATCH 4/8] fix color issue Signed-off-by: Benny Samir Hierl --- VodafoneDE/VodafoneDE.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/VodafoneDE/VodafoneDE.js b/VodafoneDE/VodafoneDE.js index 27cf6a7..32742f3 100644 --- a/VodafoneDE/VodafoneDE.js +++ b/VodafoneDE/VodafoneDE.js @@ -120,15 +120,19 @@ if (config.widgetFamily === 'small') { let backColor = new Color('D32D1F') let backColor2 = new Color('93291E') let textColor = new Color('EDEDED') -let strokeColor = new Color('EDEDED') -let fillColor = new Color('B0B0B0') +let strokeColor = new Color('B0B0B0') +let fillColor = new Color('EDEDED') +let strokeColorProgressbar = new Color('EDEDED') +let fillColorProgressbar = new Color('B0B0B0') if (darkModeSupport) { backColor = Color.dynamic(backColor, new Color('111111')) backColor2 = Color.dynamic(backColor2, new Color('222222')) textColor = Color.dynamic(textColor, new Color('EDEDED')) - strokeColor = Color.dynamic(strokeColor, new Color('EDEDED')) - fillColor = Color.dynamic(fillColor, new Color('111111')) + strokeColor = Color.dynamic(strokeColor, new Color('111111')) + fillColor = Color.dynamic(fillColor, new Color('EDEDED')) + strokeColorProgressbar = Color.dynamic(strokeColorProgressbar, new Color('EDEDED')) + fillColorProgressbar = Color.dynamic(fillColorProgressbar, new Color('111111')) } //////////////////////////////////////////////////////////////////////////////// @@ -139,14 +143,14 @@ function creatProgress(total, havegone) { context.respectScreenScale = false // Background Path - context.setFillColor(fillColor) + context.setFillColor(fillColorProgressbar) const path = new Path() path.addRoundedRect(new Rect(0, 0, width, h), 3, 2) context.addPath(path) context.fillPath() // Progress Path - context.setFillColor(strokeColor) + context.setFillColor(strokeColorProgressbar) const path1 = new Path() const path1width = (width * (havegone / total) > width) ? width : width * (havegone / total) path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2) From 5395257a56aadadf614fe61a7d67e6b53ced8b63 Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Mon, 28 Dec 2020 20:24:42 +0100 Subject: [PATCH 5/8] setup assistant added Signed-off-by: Benny Samir Hierl --- VodafoneDE/VodafoneDE.js | 215 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 203 insertions(+), 12 deletions(-) diff --git a/VodafoneDE/VodafoneDE.js b/VodafoneDE/VodafoneDE.js index 32742f3..7bc6b11 100644 --- a/VodafoneDE/VodafoneDE.js +++ b/VodafoneDE/VodafoneDE.js @@ -55,18 +55,17 @@ Credits: const cacheMinutes = 60 // Set to false if the widget should display always in red -const darkModeSupport = true +let darkModeSupport = true // Switch between remaining and used contingent. If you want show the used contingent, then change the value from true to false -const showRemainingContingent = true +let showRemainingContingent = true // To disable the progressbar for the remaining days, you have to change the value from true to false -const showRemainingDaysAsProgressbar = true +let showRemainingDaysAsProgressbar = true // Please add additional values to these list, in case that your contract/tarif isn't supported by these default values. -const containerList = ['Daten', 'D_EU_DATA', 'C_DIY_Data_National'] -const codeList = ['-1', '-5' ,'45500', '40100'] - +let containerList = ['Daten', 'D_EU_DATA', 'C_DIY_Data_National'] +let codeList = ['-1', '-5' ,'45500', '40100'] //////////////////////////////////////////////////////////////////////////////// ////////////////////////// Dev Settings //////////////////////// @@ -83,16 +82,44 @@ config.widgetFamily = config.widgetFamily || 'small' let widgetInputRAW = args.widgetParameter; let widgetInput = null; -let user, pass, number +let user, pass, number, json if (widgetInputRAW !== null) { - [user, pass, number] = widgetInputRAW.toString().split("|"); + const parameter = widgetInputRAW.toString().split("|") + if(parameter.length > 1) { + [user, pass, number, json] = parameter; - if (!user || !pass || !number) { - throw new Error("Invalid Widget parameter. Expected format: username|password|phonenumber") + if (!user || !pass || !number) { + throw new Error("Invalid Widget parameter. Expected format: username|password|phonenumber") + } + if (/^49[\d]{5,}/.test(number) === false) { + throw new Error("Invalid phonenumber format. Expected format: 491721234567") + } + } else { + json = parameter } - if (/^49[\d]{5,}/.test(number) === false) { - throw new Error("Invalid phonenumber format. Expected format: 491721234567") + if (json) { + try { + const c = JSON.parse(json) + + containerList = c.containerList || containerList + codeList = c.codeList || codeList + darkModeSupport = c.darkModeSupport !== undefined ? c.darkModeSupport : darkModeSupport + showRemainingContingent = c.showRemainingContingent !== undefined ? c.showRemainingContingent : showRemainingContingent + showRemainingDaysAsProgressbar = c.showRemainingDaysAsProgressbar !== undefined ? c.showRemainingDaysAsProgressbar : showRemainingDaysAsProgressbar + } catch (error) { + console.log('Faild to extract JSON-config. Fallback to default config') + } } +} else if (!config.runsInWidget && config.runsInApp) { + const prompt = new Alert() + prompt.message = 'Möchtest du den Setup Assistant starten?' + prompt.addAction('Ja') + prompt.addCancelAction('Nein') + + if (await prompt.presentAlert() === 0) { + await setupAssistant() + } + return Script.complete() } // Text sizes @@ -136,6 +163,170 @@ if (darkModeSupport) { } //////////////////////////////////////////////////////////////////////////////// +async function setupAssistant () { + let parameter = '' + + const promptLoginType = new Alert() + promptLoginType.message = 'Welche Login-Methode möchtest du verwenden?' + promptLoginType.addAction('Netzwerk-Login') + promptLoginType.addAction('MeinVodafone-Login') + + let cookies, msisdn + if (await promptLoginType.presentAlert() === 0) { + const promptWlanNotice = new Alert() + promptWlanNotice.title = 'Hinweis' + promptWlanNotice.message = 'Für diese Login-Methode muss das WLAN deaktiviert sein.' + promptWlanNotice.addAction('WLAN ist deaktiviert') + await promptWlanNotice.presentAlert() + + try { + let { cookies: c, msisdn: m } = await getSessionCookiesViaNetworkLogin() + cookies = c + msisdn = m + } catch (error) { + const promptError = new Alert() + promptError.title = 'Login fehlgeschlagen' + promptError.message = 'Der Login ist fehlgeschlagen. Bitte prüfe ob eine Mobilfunk-Verbindung vorhanden ist. Weitere Details findest du in den Logs.' + promptError.addAction('Schließen') + await promptError.present() + + throw error + } + } else { + const promptCredentails = new Alert() + promptCredentails.title = 'Zugangsdaten' + promptCredentails.message = 'Bitte gebe deine MeinVodafone-Zugangsdaten und deine Rufnummer ein:' + promptCredentails.addTextField('Benutzernamen') + promptCredentails.addSecureTextField('Passwort') + promptCredentails.addTextField('Rufnummer') + promptCredentails.addAction('Weiter') + + + await promptCredentails.present() + const user = promptCredentails.textFieldValue(0).trim() + const pass = promptCredentails.textFieldValue(1).trim() + const number = promptCredentails.textFieldValue(2).replace(/^0/, '49').trim() + try { + let { cookies: c } = await getSessionCookiesViaMeinVodafoneLogin(user, pass) + cookies = c + msisdn = number + } catch (error) { + console.error(error) + } + parameter = `${user}|${pass}|${msisdn}` + } + let CookieValues = cookies.map(function (v) { + return v.name + "=" + v.value + }) + let req + req = new Request(`https://www.vodafone.de/api/enterprise-resources/core/bss/sub-nil/mobile/payment/service-usages/subscriptions/${msisdn}/unbilled-usage`) + req.headers = { + 'x-vf-api': '1499082775305', + 'Referer': 'https://www.vodafone.de/meinvodafone/services/', + 'Accept': 'application/json', + 'Cookies': CookieValues.join(';') + } + let res, data + try { + res = await req.loadJSON() + if(!res['serviceUsageVBO'] || !res['serviceUsageVBO']['usageAccounts'] || !res['serviceUsageVBO']['usageAccounts'][0] || !res['serviceUsageVBO']['usageAccounts'][0]['usageGroup']) { + throw new Error('invalid response: ' + JSON.stringify(res)) + } + data = res['serviceUsageVBO']['usageAccounts'][0]['usageGroup'] + } catch (error) { + const promptError = new Alert() + promptError.title = 'Laden der Daten fehlgeschlagen' + promptError.message = 'Das Laden der Vertragsdaten ist fehlgeschlagen. Dies kann verschiedene Gründe haben. Bitte prüfen die Logs für weitere Informationen' + promptError.addAction('Schließen') + await promptError.present() + throw error + } + + const list = data.map(function (o) { + return o.usage.map(function(i) { + i.container = o.container || null + i.selected = false + return i + }).filter(x => x.code) + }).flat() + + const promptBeforeTable = new Alert() + promptBeforeTable.title = 'Hinweis' + promptBeforeTable.message = 'Dein Vertrag wurde analysiert. Bitte wähle im nachfolgenden Dialog die Daten aus, die du im Widget anzeigen möchtest.' + promptBeforeTable.addAction('Weiter') + await promptBeforeTable.present() + + const table = new UITable() + table.showSeparators = true + + function populateTable() { + table.removeAllRows() + + for (i = 0; i < list.length; i++) { + let row = new UITableRow() + row.dismissOnSelect = false + + let selectedCell = row.addText((list[i].selected)? "✓" : "") + selectedCell.widthWeight = 5 + + let textCell = row.addText(list[i].name) + textCell.widthWeight = 70 + + row.onSelect = (number) => { + list[number].selected = !list[number].selected + populateTable() + table.reload() + } + table.addRow(row) + } + } + populateTable() + await QuickLook.present(table) + + const selectedList = list.filter(x => x.selected) + const containerList = [...new Set(selectedList.map(x => x.container))] + const codeList = [...new Set(selectedList.map(x => x.code))] + + const options = { + containerList, + codeList + } + + const promptDarkMode = new Alert() + promptDarkMode.title = 'Dark Mode Unterstützung' + promptDarkMode.message = 'Möchtest du die Dark Mode Unterstützung aktivieren oder deaktivieren? Wenn die Dark Mode Unterstützung deaktiviert ist, bleibt das Widget immer rot.' + promptDarkMode.addAction('Aktivieren') + promptDarkMode.addDestructiveAction('Deaktivieren') + + options.darkModeSupport = await promptDarkMode.present() === 0 ? true : false + + const promptRemainingContingent = new Alert() + promptRemainingContingent.title = 'Anzeige Option' + promptRemainingContingent.message = 'Möchtest du das verbleibende oder verwendte Kontigent angezeigt bekommen?' + promptRemainingContingent.addAction('verbleibende Kontigent') + promptRemainingContingent.addAction('verwendte Kontigent') + + options.showRemainingContingent = await promptRemainingContingent.present() === 0 ? true : false + + const promptRemainingDaysAsProgressbar = new Alert() + promptRemainingDaysAsProgressbar.title = 'Fortschrittsbalken für verbleibenden Tage' + promptRemainingDaysAsProgressbar.message = 'Möchtest du dass ein Fortschrittsbalken für die verbleibenden Tage angezeigt wird?' + promptRemainingDaysAsProgressbar.addAction('Anzeigen') + promptRemainingDaysAsProgressbar.addDestructiveAction('Nicht anzeigen') + + options.showRemainingDaysAsProgressbar = await promptRemainingDaysAsProgressbar.present() === 0 ? true : false + + parameter += `|${JSON.stringify(options)}` + parameter = parameter.replace(/^\|/, '') + console.log('Config: ' + parameter) + Pasteboard.copy(parameter) + const promptSuccess = new Alert() + promptSuccess.title = 'Setup abgeschlossen' + promptSuccess.message = 'Die für dich passende Konfiguration wurde generiert und in die Zwischenablage kopiert.\nFüge diese nun in das Feld "Parameter" in den Widget Einstellungen ein.' + promptSuccess.addAction('Schließen') + await promptSuccess.present() +} + function creatProgress(total, havegone) { const context = new DrawContext() context.size = new Size(width, h) From 57b7da9f883db81c014fbac49d939ba7c8961087 Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Mon, 28 Dec 2020 21:33:01 +0100 Subject: [PATCH 6/8] add UUID to Config and Cache Name Signed-off-by: Benny Samir Hierl --- VodafoneDE/VodafoneDE.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/VodafoneDE/VodafoneDE.js b/VodafoneDE/VodafoneDE.js index 7bc6b11..058c870 100644 --- a/VodafoneDE/VodafoneDE.js +++ b/VodafoneDE/VodafoneDE.js @@ -13,6 +13,7 @@ Changelog: - use MSISDN for Cache-Name - show amount for prepaid cards - show remaining days as progress-bar + - Setup assistent v1.2.4: - use color.dynamic v1.2.3: @@ -82,7 +83,7 @@ config.widgetFamily = config.widgetFamily || 'small' let widgetInputRAW = args.widgetParameter; let widgetInput = null; -let user, pass, number, json +let user, pass, number, json, cacheUUID if (widgetInputRAW !== null) { const parameter = widgetInputRAW.toString().split("|") if(parameter.length > 1) { @@ -100,7 +101,7 @@ if (widgetInputRAW !== null) { if (json) { try { const c = JSON.parse(json) - + cacheUUID = c.uuid || null containerList = c.containerList || containerList codeList = c.codeList || codeList darkModeSupport = c.darkModeSupport !== undefined ? c.darkModeSupport : darkModeSupport @@ -288,6 +289,7 @@ async function setupAssistant () { const codeList = [...new Set(selectedList.map(x => x.code))] const options = { + uuid: UUID.string(), containerList, codeList } @@ -576,7 +578,7 @@ var today = new Date() const files = FileManager.local() // Set up cache -const cacheNamePostfix = (number) ? number.substr(number.length - 4) : 'networkLogin' +const cacheNamePostfix = ((number) ? number.substr(number.length - 4) : 'networkLogin') + ( cacheUUID ? `-${cacheUUID}` : '') const cachePath = files.joinPath(files.documentsDirectory(), "widget-vodafone-" + cacheNamePostfix) const cacheExists = files.fileExists(cachePath) const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0 From ed989b6fdb78af3b631442032d702bebde99911b Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Mon, 28 Dec 2020 21:51:59 +0100 Subject: [PATCH 7/8] readme updated Signed-off-by: Benny Samir Hierl --- VodafoneDE/README.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/VodafoneDE/README.md b/VodafoneDE/README.md index fe506da..41b1776 100644 --- a/VodafoneDE/README.md +++ b/VodafoneDE/README.md @@ -7,20 +7,14 @@ After the first usage, the informations are cached and will be used in case of a [[Download]](https://raw.githubusercontent.com/ThisIsBenny/iOS-Widgets/main/VodafoneDE/VodafoneDE.js) -## Options -### Use MeinVodafone Login -It is possible to set the MeinVodafone Credentails via widget parameter, which allows the widget to update itself even with active WIFI connection or to show data usage of a different mobile contract. -Add your Credentails in this format to your widget parameters: `username|password|mobile-number`. the mobile-number has to start with 49 instead of 0. +## Custom Config +If the default config or the network login isn't working for you, you can use the setup assistant to adjust the config: +* Use Mein Vodafone Login with Username + Password instead of Network Login +* Select the Data you want to show in the Widget +* Disable functions like Dark Mode Support +* Switch from remaining data volume to used data volume -Example: `JohnSmith|TopSecretPassword!|491721234567` - -_Remark:_ If your password contains a | character, you have to adapt the script and use a different divider. - -## Roadmap -* Add Medium Widget Support to display GigaDepot usage - -## Contributors -* [Necriso](https://github.com/Necriso) +Run the script in the scriptable app to start the setup assistant ## Credits * Sillium for the inspiration https://gist.github.com/Sillium/f904fb89444bc8dde12cfc07b8fa8728 From 4412fe44b6b9281cc8a84e272e51858757bc82cb Mon Sep 17 00:00:00 2001 From: Benny Samir Hierl Date: Wed, 30 Dec 2020 01:08:36 +0100 Subject: [PATCH 8/8] fix progressbar issue Signed-off-by: Benny Samir Hierl --- VodafoneDE/VodafoneDE.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/VodafoneDE/VodafoneDE.js b/VodafoneDE/VodafoneDE.js index 058c870..c5d5b25 100644 --- a/VodafoneDE/VodafoneDE.js +++ b/VodafoneDE/VodafoneDE.js @@ -136,12 +136,12 @@ const numberOfDisplayedDataBySize = { } // Progressbar -const h = 5 +const h = 2 let width if (config.widgetFamily === 'small') { - width = 200 + width = 120 } else { - width = 400 + width = 300 } // Colors @@ -163,6 +163,8 @@ if (darkModeSupport) { fillColorProgressbar = Color.dynamic(fillColorProgressbar, new Color('111111')) } //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// async function setupAssistant () { let parameter = '' @@ -333,7 +335,7 @@ function creatProgress(total, havegone) { const context = new DrawContext() context.size = new Size(width, h) context.opaque = false - context.respectScreenScale = false + context.respectScreenScale = true // Background Path context.setFillColor(fillColorProgressbar) @@ -579,7 +581,7 @@ const files = FileManager.local() // Set up cache const cacheNamePostfix = ((number) ? number.substr(number.length - 4) : 'networkLogin') + ( cacheUUID ? `-${cacheUUID}` : '') -const cachePath = files.joinPath(files.documentsDirectory(), "widget-vodafone-" + cacheNamePostfix) +const cachePath = files.joinPath(files.cacheDirectory(), "widget-vodafone-" + cacheNamePostfix) const cacheExists = files.fileExists(cachePath) const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0 @@ -729,6 +731,7 @@ if (data !== undefined) { progressBarStack.addSpacer() const progressBar = progressBarStack.addImage(creatProgress(total, total - remainingDays)) + progressBar.imageSize = new Size(width, h) progressBarStack.addSpacer() widget.addSpacer(4) }