├── .gitignore ├── Apple-Order-Status ├── Apple-Store-Order-Status.js ├── CHANGELOG ├── README.md └── previewLight.jpeg ├── Countdown ├── CHANGELOG ├── Countdown.js ├── README.md ├── previewDark.jpeg ├── previewDark2.jpeg ├── previewLight.jpeg ├── previewLight2.jpeg ├── setup.jpeg └── setup2.jpeg ├── Covid-19 ├── Covid-19.js ├── README.md ├── previewDark.jpeg └── previewLight.jpeg ├── DevTo ├── DevTo.js ├── README.md ├── previewDark.jpeg ├── previewDark2.jpeg ├── previewDark3.jpeg ├── previewLight.jpeg ├── previewLight2.jpeg └── previewLight3.jpeg ├── Ecosia ├── Ecosia.js ├── README.md ├── logo.png ├── previewDark.jpeg ├── previewLight.jpeg ├── setup.jpeg └── token.jpeg ├── LICENSE ├── Meeting-dial-in ├── Meeting-dial-in.js ├── README.md ├── preview.gif ├── previewDark.jpeg ├── previewLight.jpeg └── setup.jpeg ├── PAYBACK-Points ├── PAYBACK-Points.js ├── README.md ├── previewLight.jpeg └── setup.jpeg ├── PeriodOfPregnancy ├── PeriodOfPregnancy.js ├── README.md ├── previewDark.jpeg └── previewLight.jpeg ├── README.md ├── VRR-Monitor ├── README.md ├── VRR-Monitor.js ├── previewDark.jpeg ├── previewLight.jpeg └── setup.jpeg ├── VodafoneDE ├── README.md ├── VodafoneDE.js ├── previewDark.jpeg ├── previewDark2.jpeg ├── previewLight.jpeg ├── previewLight2.jpeg └── previewLockscreen.jpeg ├── Webservices-Health-Check ├── README.md ├── Webservices-Health-Check.js ├── previewDark.jpeg ├── previewDark2.jpeg ├── previewDark3.jpeg ├── previewLight.jpeg ├── previewLight2.jpeg └── previewLight3.jpeg ├── car-location ├── README.md ├── car-location.js ├── previewDark.jpeg ├── previewLight.jpeg ├── setup.jpeg └── xCallbackDemo.gif ├── number-of-covild-19-vaccinations ├── README.md ├── number-of-covild-19-vaccinations.js ├── previewDark.jpeg ├── previewDark2.jpeg ├── previewDark3.jpeg ├── previewLight.jpeg ├── previewLight2.jpeg ├── previewLight3.jpeg └── setup.jpeg └── widgetHub.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Apple-Order-Status/Apple-Store-Order-Status.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: deep-blue; icon-glyph: shopping-cart; 4 | // Version 1.2.1 5 | 6 | /// Used by enums 7 | const enumValue = (name) => Object.freeze({toString: () => name}) 8 | 9 | /// Used by DisplayMode 10 | 11 | const lightBackgroundColor = Color.white() 12 | const darkBackgroundColor = new Color('#222', 1.0) 13 | const autoBackgroundColor = Color.dynamic(lightBackgroundColor, darkBackgroundColor) 14 | 15 | const lightTextColor = Color.black() 16 | const darkTextColor = Color.white() 17 | const autoTextColor = Color.dynamic(lightTextColor, darkTextColor) 18 | 19 | const lightBackgroundProgressColor = new Color('#D2D2D7', 1.0) 20 | const darkBackgroundProgressColor = new Color('#707070', 1.0) 21 | const autoBackgroundProgressColor = Color.dynamic(lightBackgroundProgressColor, darkBackgroundProgressColor) 22 | 23 | const lightFillProgressColor = new Color('#008009', 1.0) 24 | const darkFillProgressColor = new Color('#00A00D', 1.0) 25 | const autoFillProgressColor = Color.dynamic(lightFillProgressColor, darkFillProgressColor) 26 | 27 | /** 28 | * Enum for display mode. 29 | * @readonly 30 | * @enum {{name: string, backgroundColor: Color, textColor: Color}} 31 | */ 32 | const DisplayMode = Object.freeze({ 33 | LIGHT: { 34 | name: "light", 35 | backgroundColor: lightBackgroundColor, 36 | textColor: lightTextColor, 37 | backgroundProgressColor: lightBackgroundProgressColor, 38 | fillProgressColor: lightFillProgressColor, 39 | toString: () => name 40 | }, 41 | DARK: { 42 | name: "dark", 43 | backgroundColor: darkBackgroundColor, 44 | textColor: darkTextColor, 45 | backgroundProgressColor: darkBackgroundProgressColor, 46 | fillProgressColor: darkFillProgressColor, 47 | toString: () => name 48 | }, 49 | AUTO: { 50 | name: "auto", 51 | backgroundColor: autoBackgroundColor, 52 | textColor: autoTextColor, 53 | backgroundProgressColor: autoBackgroundProgressColor, 54 | fillProgressColor: autoFillProgressColor, 55 | toString: () => name 56 | } 57 | }) 58 | 59 | /** 60 | * Enum for widget family. 61 | * @readonly 62 | * @enum {Symbol} 63 | */ 64 | const WidgetFamily = Object.freeze({ 65 | SMALL: enumValue("small"), 66 | MEDIUM: enumValue("medium"), 67 | LARGE: enumValue("large") 68 | }) 69 | 70 | 71 | //////////////////// - EDIT ME - /////////////////////////// 72 | 73 | /// Display mode 74 | /// 75 | /// - DisplayMode.LIGHT: Light mode 76 | /// - DisplayMode.DARK: Dark mode 77 | /// - DisplayMode.AUTO: Follow system settings 78 | 79 | const displayMode = DisplayMode.LIGHT 80 | 81 | /// Debug mode: on / off 82 | const debug = false 83 | 84 | /// Debug input, following widget format: 85 | /// - ";" 86 | /// - ";;" 87 | /// 88 | /// ie. const debugInput = "W111111111;tim@apple.com;5" 89 | const debugInput = null 90 | 91 | /// Debug widget size (LARGE, MEDIUM or SMALL) 92 | const debugWidgetFamily = WidgetFamily.MEDIUM 93 | 94 | //////////////////////////////////////////////////////////// 95 | 96 | 97 | const cacheMinutes = 60 * 2 98 | const today = new Date() 99 | let width; 100 | let widgetFamily; 101 | const h = 5 102 | const backgroundColor = displayMode.backgroundColor 103 | const textColor = displayMode.textColor 104 | const backgroundProgressColor = displayMode.backgroundProgressColor 105 | const fillProgressColor = displayMode.fillProgressColor 106 | 107 | if (debug && debugWidgetFamily !== null) { 108 | widgetFamily = debugWidgetFamily 109 | } else { 110 | switch (config.widgetFamily) { 111 | case 'small': 112 | widgetFamily = WidgetFamily.SMALL 113 | width = 200 114 | break 115 | case 'medium': 116 | widgetFamily = WidgetFamily.MEDIUM 117 | width = 400 118 | break 119 | case 'large': 120 | widgetFamily = WidgetFamily.LARGE 121 | width = 400 122 | break 123 | } 124 | } 125 | 126 | switch (widgetFamily) { 127 | case WidgetFamily.SMALL: 128 | width = 200 129 | break 130 | case WidgetFamily.MEDIUM: 131 | width = 400 132 | break 133 | case WidgetFamily.LARGE: 134 | width = 400 135 | break 136 | } 137 | 138 | //////////////////////////////////////////////////////////// 139 | let widgetInputRAW = args.widgetParameter; 140 | let widgetInput; 141 | if (widgetInputRAW !== null || (debug && debugInput !== null)) { 142 | 143 | if (widgetInputRAW !== null) { 144 | widgetInput = widgetInputRAW.toString().trim().split(';').map(v => v.trim()) 145 | } else { 146 | widgetInput = debugInput.trim().split(';').map(v => v.trim()) 147 | } 148 | 149 | if (!/^[A-Za-z][0-9]+/.test(widgetInput[0])) { 150 | throw new Error('Invalid ordernumber format: "' + widgetInput[0] + '"') 151 | } 152 | if (widgetInput[2] && !/^[\d]+$/.test(widgetInput[2])) { 153 | throw new Error('Third parameter has to be a number') 154 | } 155 | } else { 156 | throw new Error('No Ordernumber and E-Mail address set') 157 | } 158 | //////////////////////////////////////////////////////////// 159 | const files = FileManager.local() 160 | 161 | const path = files.joinPath(files.cacheDirectory(), "widget-apple-store-order-" + widgetInput[0]) 162 | 163 | const cacheExists = files.fileExists(path) 164 | 165 | const cacheDate = cacheExists ? files.modificationDate(path) : 0 166 | //////////////////////////////////////////////////////////// 167 | const localeText = { 168 | default: ['Day', 'Days', { 169 | 'PLACED': 'Order Placed', 170 | 'PROCESSING': 'Processing', 171 | 'PREPARED_FOR_SHIPMENT': 'Preparing for Ship', 172 | 'SHIPPED': 'Shipped', 173 | 'DELIVERED': 'Delivered' 174 | }], 175 | en: ['Day', 'Days', { 176 | 'PLACED': 'Order Placed', 177 | 'PROCESSING': 'Processing', 178 | 'PREPARED_FOR_SHIPMENT': 'Preparing for Ship', 179 | 'SHIPPED': 'Shipped', 180 | 'DELIVERED': 'Delivered' 181 | }], 182 | de: ['Tag', 'Tage', { 183 | 'PLACED': 'Bestellung aufgegeben', 184 | 'PROCESSING': 'Vorgang läuft', 185 | 'PREPARED_FOR_SHIPMENT': 'Versand wird vorbereitet', 186 | 'SHIPPED': 'Bestellung versandt', 187 | 'DELIVERED': 'Geliefert' 188 | }], 189 | fr: ['Jour', 'Jours', { 190 | 'PLACED': 'Commande enregistrée', 191 | 'PROCESSING': 'Traitement', 192 | 'PREPARED_FOR_SHIPMENT': 'En cours de préparation pour expédition', 193 | 'SHIPPED': 'Expédiée', 194 | 'DELIVERED': 'Livrée' 195 | }], 196 | es: ['día', 'días', { 197 | 'PLACED': 'Pedido recibido', 198 | 'PROCESSING': 'Procesando', 199 | 'PREPARED_FOR_SHIPMENT': 'Preparando envío', 200 | 'SHIPPED': 'Enviado', 201 | 'DELIVERED': 'Entregado' 202 | }], 203 | it: ['giorno', 'giorni', { 204 | 'PLACED': 'Ordine inoltrato', 205 | 'PROCESSING': 'ElaborazioneIn', 206 | 'PREPARED_FOR_SHIPMENT': 'Spedizione in preparazione', 207 | 'SHIPPED': 'Spedito', 208 | 'DELIVERED': 'ConsegnatoIncompleto' 209 | }] 210 | } 211 | //////////////////////////////////////////////////////////// 212 | const parseLongDate = (stringDate) => { 213 | const months = { 214 | 'January': 0, 215 | 'February': 1, 216 | 'March': 2, 217 | 'April': 3, 218 | 'May': 4, 219 | 'June': 5, 220 | 'July': 6, 221 | 'August': 7, 222 | 'September': 8, 223 | 'October': 9, 224 | 'November': 10, 225 | 'December': 11 226 | } 227 | const m = stringDate.match(/([\w]+)[\s]([\d]{1,2}),[\s]([0-9]{4})/) 228 | return new Date(m[3], months[m[1]], m[2]) 229 | } 230 | const parseShortDate = (stringDate, orderDate) => { 231 | const months = { 232 | 'Jan': 0, 233 | 'Feb': 1, 234 | 'Mar': 2, 235 | 'Apr': 3, 236 | 'May': 4, 237 | 'Jun': 5, 238 | 'Jul': 6, 239 | 'Aug': 7, 240 | 'Sep': 8, 241 | 'Oct': 9, 242 | 'Okt': 9, 243 | 'Nov': 10, 244 | 'Dec': 11, 245 | 'Dez': 11 246 | } 247 | let m 248 | m = stringDate.match(/([\d]{1,2}) ([\w]{3})/) 249 | if (!m) { 250 | m = stringDate.match(/([\w]+),? ([\d]{1,2})/) 251 | if (m) { 252 | const t = m[1].slice(0, 3) 253 | m[1] = m[2] 254 | m[2] = t 255 | } else { 256 | throw new Error('Failed to extract the delivery date from string: ' + stringDate) 257 | } 258 | } 259 | 260 | let deliveryDate = new Date((new Date().getFullYear()), months[m[2]], m[1]) 261 | if (deliveryDate < orderDate) { 262 | deliveryDate.setFullYear(deliveryDate.getFullYear() + 1) 263 | } 264 | return deliveryDate 265 | } 266 | //////////////////////////////////////////////////////////// 267 | function creatProgress(total, havegone) { 268 | const context = new DrawContext() 269 | context.size = new Size(width, h) 270 | context.opaque = false 271 | context.respectScreenScale = true 272 | context.setFillColor(backgroundProgressColor) 273 | const path = new Path() 274 | path.addRoundedRect(new Rect(0, 0, width, h), 3, 2) 275 | context.addPath(path) 276 | context.fillPath() 277 | context.setFillColor(fillProgressColor) 278 | const path1 = new Path() 279 | const path1width = (width * havegone / total > width) ? width : width * havegone / total 280 | path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2) 281 | context.addPath(path1) 282 | context.fillPath() 283 | return context.getImage() 284 | } 285 | //////////////////////////////////////////////////////////// 286 | const getTimeRemaining = function (endtime) { 287 | const total = Date.parse(endtime) - Date.parse(new Date()); 288 | const seconds = Math.floor((total / 1000) % 60); 289 | const minutes = Math.floor((total / 1000 / 60) % 60); 290 | const hours = Math.floor((total / (1000 * 60 * 60)) % 24); 291 | const days = Math.floor(total / (1000 * 60 * 60 * 24)); 292 | 293 | return { 294 | total, 295 | days, 296 | hours, 297 | minutes, 298 | seconds 299 | }; 300 | } 301 | //////////////////////////////////////////////////////////// 302 | const getOrderdetails = async (ordernumber, email) => { 303 | const reqSession = new Request('https://secure.store.apple.com/shop/order/list') 304 | resSession = await reqSession.loadString() 305 | 306 | const CookieValues = reqSession.response.cookies.map((v) => { 307 | return v.name + "=" + v.value 308 | }) 309 | 310 | const xAosStkMatch = resSession.match(/"x-aos-stk":"([\w-_]+)"/) 311 | if (!xAosStkMatch) { 312 | throw new Error('Needed x-aos-stk token not found') 313 | } 314 | const postUrl = (reqSession.response.url.replace('/orders', '/orderx')) + '&_a=guestUserOrderLookUp&_m=signIn.orderLookUp' 315 | 316 | const postReq = new Request(postUrl) 317 | postReq.headers = { 318 | 'Content-Type': 'application/x-www-form-urlencoded', 319 | 'Referer': reqSession.response.url, 320 | 'x-aos-model-page': 'olssSignInPage', 321 | 'x-aos-stk': xAosStkMatch[1], 322 | 'X-Requested-With': 'XMLHttpRequest', 323 | 'Cookie': CookieValues.join('; ') 324 | } 325 | postReq.method = "POST"; 326 | postReq.body = `signIn.orderLookUp.orderNumber=${ordernumber}&signIn.orderLookUp.emailAddress=${email}` 327 | 328 | const resPostReq = await postReq.loadString() 329 | 330 | if (postReq.response.statusCode !== 200) { 331 | throw new Error(`Got HTTP ${postReq.response.statusCode} from API.`) 332 | } 333 | 334 | let postResData 335 | try { 336 | postResData = JSON.parse(resPostReq) 337 | } catch (e) { 338 | throw new Error('Can\'t parse API response.') 339 | } 340 | 341 | if (postResData['head']['status'] !== 302) { 342 | throw new Error('Fetching the data failed. Got unexpected response. Please try it later.') 343 | } 344 | 345 | const req = new Request(postResData['head']['data']['url']) 346 | const res = await req.loadString() 347 | const rawJSON = res.match(/