mirror of
https://github.com/ThisIsBenny/iOS-Widgets.git
synced 2025-04-19 15:27:40 +00:00
Merge pull request #45 from ThisIsBenny/beta/VodafoneV2
Vodafone Widget v2
This commit is contained in:
commit
a25173712b
2 changed files with 514 additions and 149 deletions
|
@ -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
|
||||
|
|
|
@ -3,9 +3,17 @@
|
|||
// icon-color: red; icon-glyph: broadcast-tower;
|
||||
|
||||
/**************
|
||||
Version 1.2.4
|
||||
Version 2.0.0
|
||||
|
||||
Changelog:
|
||||
Changelog:
|
||||
v2.0.0:
|
||||
- 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
|
||||
- Setup assistent
|
||||
v1.2.4:
|
||||
- use color.dynamic
|
||||
v1.2.3:
|
||||
|
@ -40,80 +48,376 @@ Credits:
|
|||
- Chaeimg@Github (https://github.com/chaeimg/battCircle)
|
||||
**************/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////// User-Config /////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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
|
||||
let darkModeSupport = true
|
||||
|
||||
// Switch between remaining and used contingent. If you want show the used contingent, then change the value from true to false
|
||||
let showRemainingContingent = true
|
||||
|
||||
// To disable the progressbar for the remaining days, you have to change the value from true to false
|
||||
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', '45500', '40100']
|
||||
let containerList = ['Daten', 'D_EU_DATA', 'C_DIY_Data_National']
|
||||
let codeList = ['-1', '-5' ,'45500', '40100']
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////// Dev Settings ////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const debug = false
|
||||
config.widgetFamily = config.widgetFamily || 'small'
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////// System-Config ///////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Input
|
||||
let widgetInputRAW = args.widgetParameter;
|
||||
|
||||
let widgetInput = null;
|
||||
|
||||
let user, pass, number
|
||||
|
||||
let user, pass, number, json, cacheUUID
|
||||
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)
|
||||
cacheUUID = c.uuid || null
|
||||
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()
|
||||
}
|
||||
|
||||
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'));
|
||||
// Text sizes
|
||||
const fontSizeData = 11
|
||||
const lineNumberData = 1
|
||||
const minimumScaleFactor = 0.8 // Value between 1.0 and 0.1
|
||||
|
||||
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);
|
||||
// Number of data by Size
|
||||
const numberOfDisplayedDataBySize = {
|
||||
small: 1,
|
||||
medium: 2,
|
||||
large: 4
|
||||
}
|
||||
|
||||
function cosDeg(deg) {
|
||||
return Math.cos((deg * Math.PI) / 180);
|
||||
// Progressbar
|
||||
const h = 2
|
||||
let width
|
||||
if (config.widgetFamily === 'small') {
|
||||
width = 120
|
||||
} else {
|
||||
width = 300
|
||||
}
|
||||
|
||||
function drawArc(ctr, rad, w, deg) {
|
||||
bgx = ctr.x - rad;
|
||||
bgy = ctr.y - rad;
|
||||
bgd = 2 * rad;
|
||||
bgr = new Rect(bgx, bgy, bgd, bgd);
|
||||
// Colors
|
||||
let backColor = new Color('D32D1F')
|
||||
let backColor2 = new Color('93291E')
|
||||
let textColor = new Color('EDEDED')
|
||||
let strokeColor = new Color('B0B0B0')
|
||||
let fillColor = new Color('EDEDED')
|
||||
let strokeColorProgressbar = new Color('EDEDED')
|
||||
let fillColorProgressbar = new Color('B0B0B0')
|
||||
|
||||
canvas.setFillColor(fillColor);
|
||||
canvas.setStrokeColor(strokeColor);
|
||||
canvas.setLineWidth(w);
|
||||
canvas.strokeEllipse(bgr);
|
||||
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('111111'))
|
||||
fillColor = Color.dynamic(fillColor, new Color('EDEDED'))
|
||||
strokeColorProgressbar = Color.dynamic(strokeColorProgressbar, new Color('EDEDED'))
|
||||
fillColorProgressbar = Color.dynamic(fillColorProgressbar, new Color('111111'))
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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);
|
||||
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 = {
|
||||
uuid: UUID.string(),
|
||||
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)
|
||||
context.opaque = false
|
||||
context.respectScreenScale = true
|
||||
|
||||
// Background Path
|
||||
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(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)
|
||||
context.addPath(path1)
|
||||
context.fillPath()
|
||||
return context.getImage()
|
||||
}
|
||||
|
||||
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 +425,7 @@ function getTimeRemaining(endtime) {
|
|||
hours,
|
||||
minutes,
|
||||
seconds
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function getSessionCookiesViaNetworkLogin() {
|
||||
|
@ -173,22 +477,22 @@ async function getSessionCookiesViaMeinVodafoneLogin(u, p) {
|
|||
};
|
||||
|
||||
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,53 +502,71 @@ 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")
|
||||
|
@ -252,18 +574,19 @@ async function getUsage(user, pass, number) {
|
|||
}
|
||||
};
|
||||
|
||||
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') + ( cacheUUID ? `-${cacheUUID}` : '')
|
||||
const cachePath = files.joinPath(files.cacheDirectory(), "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 +624,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 +640,104 @@ 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()
|
||||
|
||||
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()
|
||||
|
||||
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(fontSizeData)
|
||||
diagramText.minimumScaleFactor = minimumScaleFactor
|
||||
diagramText.lineLimit = lineNumberData
|
||||
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(fontSizeData - 1)
|
||||
diagramName.minimumScaleFactor = minimumScaleFactor
|
||||
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))
|
||||
progressBar.imageSize = new Size(width, h)
|
||||
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 +749,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)
|
||||
|
|
Loading…
Add table
Reference in a new issue