// Variables used by Scriptable. // These must be at the very top of the file. Do not edit. // icon-color: deep-green; icon-glyph: bus-alt; /************** Version 1.2.3 Changelog: v1.2.3: - Fix issue with error response, due to new mandatory field v1.2.2: - Fix issue with empty response v1.2.1: - Fix issue with small widget where time will be not correct displayed in case that a delay will be displayed v1.2.0: - show original time and the delay in red v1.1.0: - Line filter support added - Setup Wizard enhanced **************/ let transportTypes = [ { name: 'ICE/IC/EC', show: true, code: 0 }, { name: 'Zug', show: true, code: 1 }, { name: 'S-Bahn', show: true, code: 2 }, { name: 'U-Bahn', show: true, code: 3 }, { name: 'Straßen-Bahn', show: true, code: 4 }, { name: 'Bus', show: true, code: 15 }, { name: 'Schwebe-Bahn', show: true, code: 6 } ] //////////////////////////////////////////////////////////////////////////////// // Wizard let wizardStationId, wizardLines let linesTable = new UITable() linesTable.showSeparators = true //Color let backColor; //Widget background color let backColor2; //Widget background color let textColor; //Widget text color let useGradient = true if (Device.isUsingDarkAppearance()) { backColor = '111111'; backColor2 = '222222'; textColor = 'EDEDED'; } else { backColor = '145A32'; backColor2 = '1E8449'; textColor = 'FFFFFF'; } async function fetchStationdata(Id, linesFilter, distance, transportCodes) { let req = new Request("https://abfahrtsmonitor.vrr.de/backend/api/stations/table") req.headers = { 'Content-Type': 'application/x-www-form-urlencoded', } req.method = "POST"; req.addParameterToMultipart('table[departure][stationId]', Id) req.addParameterToMultipart('table[departure][platformVisibility]', '1') req.addParameterToMultipart('table[departure][transport]', transportCodes.join(',')) req.addParameterToMultipart('table[departure][useAllLines]', '0') req.addParameterToMultipart('table[departure][optimizedForStation]', '0') req.addParameterToMultipart('table[departure][linesFilter]', linesFilter) req.addParameterToMultipart('table[departure][rowCount]', '7') req.addParameterToMultipart('table[departure][distance]', distance) req.addParameterToMultipart('table[sortBy]', '0') try { let res = await req.loadJSON() return {departureData: res['departureData'] || [], stationName: res['stationName']} } catch (e) { console.log(req.response) console.log(e) throw e } }; async function findNextStations(latitude, longitude) { let req = new Request(`https://abfahrtsmonitor.vrr.de/backend/api/stations/coord?long=${longitude}&lat=${latitude}`) let res = await req.loadJSON() return res['suggestions'] }; async function getLines(stationId) { let req = new Request(`https://haltestellenmonitor.vrr.de/backend/api/lines/${stationId}/search`) return await req.loadJSON() }; function populateStationTable(table, stations) { table.removeAllRows() for (i = 0; i < stations.length; i++) { let station = stations[i] let row = new UITableRow() let nameCell = row.addText(station.value) nameCell.leftAligned() row.onSelect = (number) => { wizardStationId = stations[number].data } table.addRow(row) } } function populateLinesTable() { linesTable.removeAllRows() for (i = 0; i < wizardLines.length; i++) { let row = new UITableRow() row.dismissOnSelect = false let selectedCell = row.addText((wizardLines[i].selected)? "✓" : "") selectedCell.widthWeight = 5 let textCell = row.addText(wizardLines[i].name) textCell.widthWeight = 70 row.onSelect = (number) => { wizardLines[number].selected = !wizardLines[number].selected populateLinesTable() linesTable.reload() } linesTable.addRow(row) } } let widgetInputRAW = args.widgetParameter; let widgetInput = null; if (widgetInputRAW !== null) { widgetInput = widgetInputRAW.toString().split(";"); } else { if(!config.runsInWidget) { let prompt1 = new Alert() prompt1.message = 'Do you like to use the Setup Wizard to get the widget parameters?' prompt1.addAction('Yes') prompt1.addCancelAction('No') if ((await prompt1.presentAlert()) === 0) { let l = await Location.current() let stations = await findNextStations(l.latitude, l.longitude) let table = new UITable() table.showSeparators = true populateStationTable(table, stations) await QuickLook.present(table) let prompt2 = new Alert() prompt2.message = 'How many minutes do you need to go to the station?' prompt2.addTextField('Minutes') prompt2.addAction('Weiter') await prompt2.presentAlert() let widgetParameter = `${wizardStationId};${prompt2.textFieldValue(0)}` let prompt3 = new Alert() prompt3.message = 'Do you want to display only specific lines (including direction)?' prompt3.addAction('Yes') prompt3.addCancelAction('No, show all lines') if ((await prompt3.presentAlert()) === 0) { wizardLines = await getLines(wizardStationId) wizardLines = wizardLines.map((v) => { v.selected = false return v }) populateLinesTable() await QuickLook.present(linesTable) let linesConfig = wizardLines.filter(v => v.selected).map((v) => { return { data: `${v.network}:${v.line}:+:${v.directionCode}` } }) widgetParameter = widgetParameter + ";" + JSON.stringify(linesConfig) } Pasteboard.copy(widgetParameter); let alert = new Alert(); alert.message = `Config '${widgetParameter}' was copied to the clipboard. Please paste this config to the widget parameter.` alert.addAction('Ok') alert.present() } return } throw new Error('No Station Code set!') } console.log(widgetInput) let selectedTransportTypes = transportTypes.filter(function(v){ return v.show }) let selectedTransportTypesCodes = selectedTransportTypes.map(function(v){ return v.code }) let { departureData: data, stationName } = await fetchStationdata(widgetInput[0], widgetInput[2] || '', widgetInput[1], selectedTransportTypesCodes); console.log(JSON.stringify(data, null, 2)) // Create Widget let widget = new ListWidget(); widget.setPadding(10, 10, 10, 10) const gradient = new LinearGradient() gradient.locations = [0, 1] gradient.colors = [ new Color(backColor), new Color(backColor2) ] widget.backgroundGradient = gradient let firstLineStack = widget.addStack() let stationText = firstLineStack.addText("🚏 " + stationName) stationText.font = Font.boldSystemFont(12) stationText.textColor = new Color(textColor) stationText.lineLimit = 2 // Last Update firstLineStack.addSpacer() let lastUpdateText = firstLineStack.addDate(new Date()) lastUpdateText.font = Font.mediumSystemFont(10) lastUpdateText.rightAlignText() lastUpdateText.applyTimeStyle() lastUpdateText.textColor = Color.lightGray() widget.addSpacer(10) let row = widget.addStack() row.layoutHorizontally() row.spacing = 10 let timeColumn = row.addStack() timeColumn.layoutVertically(); let lineColumn = row.addStack() lineColumn.layoutVertically() let directionColumn = row.addStack() directionColumn.layoutVertically() data.forEach(function(l) { if (config.widgetFamily !== 'small') { let timeStack = timeColumn.addStack() timeStack.layoutHorizontally() timeStack.spacing = 2 let timeText = timeStack.addText(`${l.orgHour}:${l.orgMinute}`) timeText.font = Font.mediumSystemFont(12) timeText.textColor = new Color(textColor) timeText.leftAlignText() if (parseInt(l.delay) > 0) { let delayText = timeStack.addText(`+${l.delay}`) delayText.font = Font.mediumSystemFont(12) delayText.textColor = Color.red() delayText.leftAlignText() } } else { let timeText = timeColumn.addText(`${l.hour}:${l.minute}`) timeText.font = Font.mediumSystemFont(12) timeText.textColor = new Color(textColor) timeText.leftAlignText() } let lineText = lineColumn.addText(l.name) lineText.font = Font.mediumSystemFont(12) lineText.textColor = new Color(textColor) lineText.leftAlignText() let directionText = directionColumn.addText(l.direction) directionText.font = Font.mediumSystemFont(12) directionText.textColor = new Color(textColor) directionText.leftAlignText() directionText.lineLimit = 1 }) widget.addSpacer() if(!config.runsInWidget) { await widget.presentMedium() } else { // Tell the system to show the widget. Script.setWidget(widget) Script.complete() }