├── LICENSE ├── README.md ├── WarnWetter.js └── img ├── config.png ├── large1.png ├── large2.png ├── large3.png ├── large4.png ├── medium1.png ├── medium2.png └── warncellids.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 MacSchierer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WarnWetter - Ein Scriptable Widget für iOS und Mac 2 | Das Widget zeigt regionale Warnmeldungen des Deutschen Wetterdienstes (DWD) an.
3 | Verwendbar als kleines, mittleres oder großes (empfohlen!) Widget. 4 | 5 | ## Features 6 | * Untestützung bei der Auswahl einer Warncell-ID direkt in der Scriptable App. 7 | * Je nach Geräteeinstellung erfolgt ein automatischer Wechsel des Ernscheinungsbildes. 8 | * Anzeige einer beliebigen Region über die Vorgabe der Warncell-ID. 9 | * Region wird via GPS erkannt. 10 | * Anzeige von bis zu 3 Warnmeldungen (im großen Widget). 11 | * Wenn keine oder wenige Warnmeldungen aktiv sind, wird die allgemeine Wetterlage Deutschlands angezeigt. 12 | * Für mehr Details reicht ein Tap bzw. Klick auf das Widget und die Internetseite des DWD wird geöffnet. 13 | 14 | ## Beispiele 15 |  

16 |   17 | 18 | ## Installation und Verwendung 19 | * Download Scriptable App für iOS oder Mac - https://scriptable.app 20 | * Download/Import der ImpfQuoten.js Datei nach iCloud/Scriptable 21 | * Auf dem Homescreen bzw. in der Mitteilungszentrale ein neues Scriptable Widget (groß) erstellen. 22 | * Die Auswahl der Region (Bezug auf Landkreis) kann über die Vorgabe der Warncell-ID als Widgetparameter erfolgen. Eine Tabelle mit den notwendigen IDs wird angezeigt, wenn das Skript in der Scriptable App ausgeführt wird. 23 | * Wird keine Warncell-ID dem Widget vorgegeben, wird via GPS die Region ermittelt und es wird versucht die zugehörige ID zu ermitteln.

24 |   25 | 26 | ## Bekannte Probleme 27 | * Verzögerung und Fehlermeldung bei der GPS-Lokalisierung
Das Problem tritt sporatisch auf und nach kurzer Zeit aktuallisiert sich die Widgetanzeige korrekt. 28 | 29 | ## Quellen 30 | * Basis ist die bereitgestellte JSONP-File vom DWD
https://www.dwd.de/DE/wetter/warnungen_aktuell/objekt_einbindung/objekteinbindung.html 31 | * Warncell-IDs
https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.html 32 | * Allgemeine Wetterlage
https://www.dwd.de/DWD/wetter/wv_allg/deutschland/text/vhdl13_dwog.html 33 | 34 | ## Changelog 35 | * v1.8 Korrektur: Warnfarbe für Hitze eingefügt. 36 | * v1.7 Korrektur: Wenn keine Warnmeldung für eine Region vorlag, lief das Skript in ein Fehler. 37 | * v1.6 Korrektur: Ausgabe einer Info, wenn die Standortermittlung nicht funktioniert bzw. verzögert ist. 38 | * v1.5 Release GitHub 39 | 40 | ## Dankeschön an... 41 | * ...Dennis für den Projektvorschlag 42 | * ...Florian und Dennis für die ersten Tests und die konstruktiven Verbesserungsvorschläge 43 | * ...die Scriptable Community 44 | * ...den DWD zum Bereitstellen der Daten 45 | -------------------------------------------------------------------------------- /WarnWetter.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: blue; icon-glyph: exclamation-triangle; 4 | // 5 | // Script für https://scriptable.app 6 | // WarnWetter - Ein Scriptable Widget für iOS und Mac 7 | // Das Widget zeigt regionale Warnmeldungen des Deutschen Wetterdienstes (DWD) an. 8 | // Verwendbar als kleines, mittleres oder großes (empfohlen!) Widget. 9 | // 10 | // Script by MacSchierer, 17.06.2021, v1.8 11 | // Download der aktuellen Version hier: GitHub https://github.com/MacSchierer/WarnWetter 12 | // 13 | // Verwendet die bereitgestellte JSONP-File vom DWD 14 | // https://www.dwd.de/DE/wetter/warnungen_aktuell/objekt_einbindung/objekteinbindung.html 15 | // Warncell-IDs: https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.html 16 | // Wetter allgemein: https://www.dwd.de/DWD/wetter/wv_allg/deutschland/text/vhdl13_dwog.html 17 | // 18 | // Die Auswahl der Region (Bezug auf Landkreis) kann über die Vorgabe der Warncell-ID als Widgetparameter erfolgen. 19 | // Eine Tabelle mit den notwendigen IDs wird angezeigt, wenn das Skript in der Scriptable App ausgeführt wird. 20 | // Wird keine Warncell-ID dem Widget vorgegeben, wird via GPS die Region ermittelt und es wird versucht die 21 | // zugehörige ID zu ermitteln 22 | // 23 | // 24 | const debug = false 25 | config.widgetFamily = config.widgetFamily || 'large' 26 | // Zeit für die Warncell-IDs im Cache 27 | const cacheMinutes = 7 * 24 * 60 // 1 Woche = 10080 Minuten 28 | // 29 | // Ab hier nichts ändern 30 | // 31 | const APIurl = "https://www.dwd.de/DWD/warnungen/warnapp/json/warnings.json" 32 | const CSVurl = "https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.csv?__blob=publicationFile&v=3" 33 | const today = new Date() 34 | let hasError = false 35 | let ErrorTxt = "" 36 | let WarnCell = "" 37 | let useGPS = false 38 | let df = new DateFormatter() 39 | df.useMediumDateStyle() 40 | df.useShortTimeStyle() 41 | // Fraben definieren 42 | const BGGradient = new LinearGradient() 43 | BGGradient.locations = [0.0,1] 44 | BGGradient.colors = [Color.dynamic(new Color("#6180c4"), new Color("#000000")), Color.dynamic(new Color("#344b96"), new Color("#222222"))] 45 | const WidgetBgColor = Color.dynamic(new Color("#ffffff"), new Color("#000000")) 46 | const ContentBGColor = Color.dynamic(new Color("#efefef20"), new Color("#ffffff20")) 47 | const MainTextColor = Color.dynamic(new Color("#ffffff"), new Color("#ffffff")) 48 | const SubTextColor = Color.dynamic(new Color("#d4dcf7"), new Color("#aaaaaa")) 49 | const IconColor = MainTextColor 50 | const TitelColor = MainTextColor 51 | 52 | // Warncell-IDs im Cache 53 | WarnCellData = await manageCache() 54 | if (WarnCellData) { 55 | WarnCellData = JSON.parse(WarnCellData) 56 | } else { 57 | hasError = true 58 | ErrorTxt += "Benötigte WarnCell-IDs konnten nicht verarbeitet werden. " 59 | } 60 | // Skript wird in Scriptable ausgeführt --> Zeigt Liste der Warncell-IDs aus der (lokalen) JSON-Datei generiert 61 | if (config.runsInApp && debug == false) { 62 | const webView = new WebView() 63 | TableContent = await loadTable(WarnCellData) 64 | const cssStyles = "body{padding:1em;font-family: Courier;font-size:2em;}table{width:100%;font-size:0.8em;margin:0 auto;text-align:center;border-collapse:collapse;border:1px solid #d4d4d4;}tr:nth-child(even){background:#d4dcf7;}th,td{padding:10px 30px;}th{border-bottom:1px solid #d4d4d4;background:#6180c4;color:#fff;}" 65 | const HTMLHeadElement = "" 66 | const webHtml = HTMLHeadElement+ "

Warncell-IDs des DWD

Hier findest du die Warncell-IDs der zur Verfügung stehenden Regionen. Eine einzelne ID kann optional in den Parameter des Widgets eingetragen werden. Ohne Parameter versucht das Widget, der über GPS ermittelten Region, eine Warncell-ID zuzuordnen.

"+TableContent+"
" 67 | await webView.loadHTML(webHtml) 68 | await webView.present() 69 | Script.complete() 70 | return 71 | } 72 | // Widgetparameter oder GPS benutzen? 73 | if (config.runsInWidget && debug == false) { 74 | WarnCell = args.widgetParameter 75 | } 76 | if (WarnCell == null || WarnCell.toString().length == 0) { 77 | try { 78 | const loc = await getGPS() 79 | let GPSData = await Location.reverseGeocode(loc.latitude, loc.longitude) 80 | // Daten von Apples Geocoding Service 81 | MyCity = GPSData[0].postalAddress.city 82 | MyArea = GPSData[0].postalAddress.subAdministrativeArea 83 | useGPS = true 84 | log("GPS wird verwenden...") 85 | log("Ort: " + MyCity) 86 | log("Region: " + MyArea) 87 | } catch(e) { 88 | console.warn(e) 89 | useGPS = false 90 | hasError = true 91 | ErrorTxt += "GPS Problem...\n" 92 | } 93 | } 94 | // JSON vom DWD abrufen 95 | try { 96 | AllItems = await loadItems(APIurl) 97 | } catch (e) { 98 | hasError = true 99 | ErrorTxt += "Das Widget konnte keine Daten abrufen. " 100 | } 101 | // Wenn GPS verwendet wird, über die Region die Warncell-ID ermitteln 102 | if (useGPS) { 103 | WarnCell = getWarnCellID(WarnCellData, MyArea) 104 | log("WarncellID: " + WarnCell) 105 | } 106 | // Prüfen ob Warncell-ID existiert - Wichtig bei GPS ermittelter Region 107 | if (WarnCellData.hasOwnProperty(WarnCell) == false) { 108 | log(WarnCell + " wurde nicht gefunden!") 109 | hasError = true 110 | ErrorTxt += "Die Region wurde aktuell nicht erkannt.\nSollte das Problem weiterhin bestehen, starte das Skript einmal kurz in der App. Dort erhältst du auch eine Übersicht der Regionen und weitere Infos. " 111 | } else { 112 | if (!useGPS){MyArea = WarnCellData[WarnCell].NAME} 113 | // Wenn Warncell-ID vorhanden ist, kann die "Auswertung" starten 114 | try { 115 | CellWarnings = AllItems.warnings[WarnCell.toString()] 116 | log(CellWarnings) 117 | if (typeof CellWarnings !== "undefined") { 118 | WarnAnz = CellWarnings.length 119 | log(WarnAnz + " Meldung(en) gefunden") 120 | WarnLocation = new Array() 121 | WarnLevel = new Array() 122 | WarnEvent = new Array() 123 | WarnShort = new Array() 124 | WarnStartDate = new Array() 125 | WarnEndeDate = new Array() 126 | WarnDescription = new Array() 127 | WarnInstruction = new Array() 128 | var i; 129 | for (i = 0; i < CellWarnings.length; i++) { 130 | WarnLocation.push(CellWarnings[i].regionName) 131 | WarnLevel.push(CellWarnings[i].level) 132 | WarnEvent.push(CellWarnings[i].event) 133 | WarnShort.push(CellWarnings[i].headline) 134 | WarnStartDate.push(new Date(CellWarnings[i].start)) 135 | WarnEndeDate.push(new Date(CellWarnings[i].end)) 136 | WarnDescription.push(CellWarnings[i].description) 137 | WarnInstruction.push(CellWarnings[i].instruction) 138 | } 139 | WarnIntro = WarnLocation[0] 140 | WarnOutput = true 141 | } else { 142 | WarnIntro = MyArea + ": Keine Warnung aktiv." 143 | WarnIcon = "checkmark.seal" 144 | WarnAnz = 0 145 | WarnOutput = false 146 | } 147 | } catch (e) { 148 | hasError = true 149 | ErrorTxt += "Beim Verarbeiten der Daten ist ein Fehler aufgetreten." 150 | } 151 | } 152 | 153 | class WarnWidget { 154 | async init() { 155 | const widget = await this.createWidget() 156 | switch (config.widgetFamily) { 157 | case 'small': await widget.presentSmall(); break; 158 | case 'medium': await widget.presentMedium(); break; 159 | case 'large': await widget.presentLarge(); break; 160 | } 161 | Script.setWidget(widget) 162 | Script.complete() 163 | } 164 | async createWidget() { 165 | if (hasError) {return ErrorWidget(ErrorTxt)} 166 | const list = new ListWidget() 167 | list.backgroundGradient = BGGradient 168 | list.refreshAfterDate = new Date(Date.now() + 60 * 60 * 1000) // 60 Minuten Refresh-Intervall 169 | let ScaleFactor = 1 170 | let MaxWarn = 1 171 | switch (config.widgetFamily) { 172 | case 'small': 173 | list.setPadding(10, 5, 10, 5) 174 | ScaleFactor = 0.5 175 | break; 176 | case 'medium': 177 | list.setPadding(10, 10, 5, 10) 178 | break; 179 | case 'large': 180 | list.setPadding(15, 10, 15, 10) 181 | MaxWarn = 3 182 | break; 183 | } 184 | if (useGPS) { 185 | list.url = "https://www.dwd.de/DE/wetter/warnungen_landkreise/warnWetter_node.html?ort="+SonderToURL(MyCity) 186 | } else { 187 | list.url = "https://www.dwd.de/DE/wetter/warnungen_landkreise/warnWetter_node.html" 188 | } 189 | const Title = list.addStack() 190 | let TitleText = Title.addText("Wetterwarnung des DWD") 191 | TitleText.textColor = TitelColor 192 | TitleText.font = Font.boldSystemFont(14) 193 | TitleText.lineLimit = 1 194 | TitleText.minimumScaleFactor = ScaleFactor 195 | if (config.widgetFamily != 'small') { 196 | Title.addSpacer() 197 | let DateText = Title.addDate(new Date(AllItems.time)) 198 | DateText.textColor = SubTextColor 199 | DateText.applyDateStyle() 200 | DateText.font = Font.boldSystemFont(8) 201 | } 202 | const SubTitle = list.addStack() 203 | SubTitle.setPadding(0, 4, 0, 4) 204 | if (useGPS == true) { 205 | addSymbol({ 206 | symbol: 'mappin.and.ellipse', 207 | stack: SubTitle, 208 | color: SubTextColor, 209 | size: 10, 210 | }) 211 | SubTitle.addSpacer(4) 212 | } 213 | let SubTitleText = SubTitle.addText(WarnIntro) 214 | SubTitleText.font = Font.systemFont(10) 215 | SubTitleText.textColor = SubTextColor 216 | SubTitleText.lineLimit = 1 217 | SubTitleText.minimumScaleFactor = ScaleFactor 218 | list.addSpacer(2) 219 | const Content = list.addStack() 220 | Content.setPadding(2,2,2,2) 221 | Content.layoutVertically() 222 | if (WarnOutput == true) { 223 | for (i = 0; i < WarnAnz; i++) { 224 | if (i == MaxWarn) {break;} 225 | if (i != 0) {Content.addSpacer(8)} 226 | const WarnStack = Content.addStack() 227 | WarnStack.layoutVertically() 228 | WarnStack.backgroundColor = ContentBGColor 229 | WarnStack.cornerRadius = 4 230 | WarnStack.setPadding(4,4,4,4) 231 | const WarnStackHeader = WarnStack.addStack() 232 | WarnStackHeader.layoutHorizontally() 233 | const WarnStackIcon = WarnStackHeader.addStack() 234 | WarnStackIcon.setPadding(2,2,2,2) 235 | WarnStackIcon.cornerRadius = 2 236 | WarnStackIcon.backgroundColor = getWarnLevelColor(WarnLevel[i]) 237 | addSymbol({ 238 | symbol: getWarnIcon(WarnEvent[i]), 239 | stack: WarnStackIcon, 240 | color: IconColor, 241 | size: 24, 242 | }) 243 | const WarnStackHead = WarnStackHeader.addStack() 244 | WarnStackHead.layoutVertically() 245 | WarnStackHead.setPadding(0,6,0,0) 246 | const WarnStackTitle = WarnStackHead.addStack() 247 | WarnStackTitle.centerAlignContent() 248 | let WarnTitleText = WarnStackTitle.addText(WarnEvent[i]) 249 | WarnTitleText.textColor = MainTextColor 250 | WarnTitleText.font = Font.boldSystemFont(14) 251 | WarnTitleText.lineLimit = 1 252 | WarnTitleText.minimumScaleFactor = ScaleFactor 253 | WarnStackTitle.addSpacer() 254 | if (config.widgetFamily != 'small') { 255 | const WarnStackLevel = WarnStackTitle.addStack() 256 | WarnStackLevel.backgroundColor = getWarnLevelColor(WarnLevel[i]) 257 | WarnStackLevel.setPadding(0,2,0,2) 258 | WarnStackLevel.cornerRadius = 2 259 | WarnStackLevel.centerAlignContent() 260 | let WarnLevelText = WarnStackLevel.addText("Stufe " + WarnLevel[i]) 261 | WarnLevelText.textColor = MainTextColor 262 | WarnLevelText.font = Font.systemFont(12) 263 | WarnLevelText.lineLimit = 1 264 | WarnLevelText.minimumScaleFactor = ScaleFactor 265 | const WarnStackTime = WarnStackHead.addStack() 266 | let WarnTimeText = WarnStackTime.addText(df.string(WarnStartDate[i]) + " Uhr bis " + df.string(WarnEndeDate[i]) + " Uhr") 267 | WarnTimeText.textColor = SubTextColor 268 | WarnTimeText.font = Font.systemFont(10) 269 | WarnStackTime.addSpacer() 270 | } 271 | WarnStack.addSpacer(4) 272 | const WarnStackInfo = WarnStack.addStack() 273 | WarnStackInfo.layoutHorizontally() 274 | WarnStackInfo.setPadding(0,4,0,4) 275 | let InfoText = WarnStackInfo.addText(WarnDescription[i].replace(/\n\n/g, " ")) 276 | InfoText.textColor = MainTextColor 277 | InfoText.font = Font.systemFont(12) 278 | InfoText.minimumScaleFactor = ScaleFactor 279 | InfoText.lineLimit = 4 280 | WarnStackInfo.addSpacer() 281 | WarnStack.addSpacer(4) 282 | } 283 | Content.addSpacer() 284 | // Info über Anzahl der angezeigten und verfügbaren Meldungen 285 | if (config.widgetFamily != 'small') { 286 | SubTitle.addSpacer() 287 | let WarnAnzText 288 | if (WarnAnz > MaxWarn) { 289 | WarnAnzText = i + " von " + WarnAnz + " aktiven Meldungen" 290 | } else if (WarnAnz == 1) { 291 | WarnAnzText = "Eine Meldung aktiv" 292 | } else { 293 | WarnAnzText = WarnAnz + " aktive Meldungen" 294 | } 295 | let AnzWarn = SubTitle.addText(WarnAnzText) 296 | AnzWarn.textColor = SubTextColor 297 | AnzWarn.font = Font.systemFont(8) 298 | } 299 | } 300 | if (WarnOutput == false || i <= 2 && config.widgetFamily == 'large') { 301 | const WarnStack = Content.addStack() 302 | WarnStack.layoutVertically() 303 | WarnStack.backgroundColor = ContentBGColor 304 | WarnStack.cornerRadius = 4 305 | WarnStack.setPadding(4,4,4,4) 306 | const WarnStackTitle = WarnStack.addStack() 307 | let WarnTitleText = WarnStackTitle.addText("Allgemeine Meldung") 308 | WarnTitleText.textColor = MainTextColor 309 | WarnTitleText.font = Font.boldSystemFont(14) 310 | WarnTitleText.lineLimit = 1 311 | const WarnStackInfo = WarnStack.addStack() 312 | WarnStackInfo.layoutHorizontally() 313 | WarnStackInfo.setPadding(0,4,0,4) 314 | let InfoText = WarnStackInfo.addText(await loadHTMLContent("https://www.dwd.de/DWD/wetter/wv_allg/deutschland/text/vhdl13_dwog.html")) 315 | InfoText.textColor = MainTextColor 316 | InfoText.font = Font.systemFont(12) 317 | InfoText.minimumScaleFactor = ScaleFactor 318 | InfoText.lineLimit = 8 319 | WarnStackInfo.addSpacer() 320 | WarnStack.addSpacer() 321 | } 322 | list.addSpacer() 323 | return list 324 | } 325 | // Class Ende 326 | } 327 | 328 | // 329 | // Error Widget 330 | function ErrorWidget(reason) { 331 | const error = new ListWidget() 332 | error.setPadding(10,10,10,10) 333 | error.backgroundGradient = BGGradient 334 | let title = error.addText("Information") 335 | title.centerAlignText() 336 | title.textColor = SubTextColor 337 | title.font = Font.semiboldSystemFont(24) 338 | let reasonText = error.addText(reason) 339 | reasonText.centerAlignText() 340 | reasonText.textColor = MainTextColor 341 | reasonText.font = Font.semiboldSystemFont(12) 342 | reasonText.minimumScaleFactor = 0.7 343 | error.addSpacer() 344 | return error 345 | } 346 | // 347 | // JSON vom DWD abrufen 348 | async function loadItems(APIurl) { 349 | let req = new Request(APIurl) 350 | req.headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'} 351 | let jsonp = await req.loadString() 352 | cut1 = ("warnWetter.loadWarnings(").length 353 | cut2 = (");").length 354 | json = JSON.parse(jsonp.slice(cut1, -cut2)) 355 | return json 356 | } 357 | // 358 | // Cache - Lokale-JSON-Datei mit den WarncellIDs erstellen und speichern 359 | async function manageCache() { 360 | const files = FileManager.local() 361 | const cachePath = files.joinPath(files.cacheDirectory(), "warncellids.json") 362 | const cacheExists = files.fileExists(cachePath) 363 | const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0 364 | let WarnCellData 365 | let lastUpdate 366 | try { 367 | if (cacheExists && (today.getTime() - cacheDate.getTime()) < (cacheMinutes * 60 * 1000)) { 368 | console.log("Lokale JSON-Datei laden") 369 | WarnCellData = files.readString(cachePath) 370 | lastUpdate = cacheDate 371 | } else { 372 | console.log("CSV-Datei extern abrufen") 373 | req = new Request(CSVurl) 374 | RawData = await req.loadString() 375 | WarnCellData = await CSVToJSON(RawData) 376 | lastUpdate = today 377 | console.log("CSV-Datei als JSON lokal abspeichern") 378 | try { 379 | files.writeString(cachePath, (WarnCellData)) 380 | 381 | } catch (e) { 382 | console.log("Fehler beim Speichern der JSON-Datei") 383 | console.log(e) 384 | } 385 | } 386 | } catch (e) { 387 | console.error(e) 388 | if (cacheExists) { 389 | console.log("Lokale JSON-Datei laden") 390 | WarnCellData = files.readString(cachePath) 391 | lastUpdate = cacheDate 392 | } else { 393 | console.log("Fehler beim Speichen/Laden der JSON-Datei") 394 | } 395 | } 396 | return WarnCellData 397 | } 398 | // 399 | // Erstell die Tabelle der Regionen, alphabetisch sortiert 400 | async function loadTable(WarnCells) { 401 | let WorkArray = {} 402 | for (WarnCell in WarnCells) { 403 | key = WarnCells[WarnCell].NAME 404 | key = key.replace("Kreis ", "") 405 | key = key.replace("Stadt ", "") 406 | WorkArray[key] = WarnCell 407 | } 408 | let SortArray = {} 409 | Object.keys(WorkArray).sort().map(i=>SortArray[i]=WorkArray[i]) 410 | let table = '' 411 | for (WarnRegion in SortArray) { 412 | table += '' 413 | } 414 | table += '
RegionWarncell-ID
' + WarnRegion + '' + SortArray[WarnRegion] + '
' 415 | log("HTML-Tabelle erstellt") 416 | return table 417 | } 418 | // 419 | // Verarbeiten der CSV mit den Warncell-IDs 420 | async function CSVToJSON(csvData) { 421 | var data = await CSVToArray(csvData) 422 | var ParentObj = {} 423 | var ChildObj = [] 424 | for (var i = 1; i < data.length; i++) { 425 | ChildObj[i - 1] = {} 426 | for (var k = 0; k < data[0].length && k < data[i].length; k++) { 427 | var key = data[0][k] 428 | ChildObj[i - 1][key] = data[i][k] 429 | } 430 | if (ChildObj[i - 1]["WARNCELLID"] > 200000000) { 431 | break 432 | } 433 | else { 434 | ParentObj[data[i][0]] = ChildObj[i - 1] 435 | } 436 | } 437 | var jsonData = JSON.stringify(ParentObj) 438 | jsonData = jsonData.replace(/},/g, "},\r\n") 439 | return jsonData 440 | } 441 | // 442 | // 443 | // Verarbeiten der CSV mit den Warncell-IDs Hilfsfunktion 444 | async function CSVToArray(csvData, delimiter) { 445 | delimiter = (delimiter || ";") 446 | var pattern = new RegExp(( 447 | "(\\" + delimiter + "|\\r?\\n|\\r|^)" + 448 | "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" + 449 | "([^\"\\" + delimiter + "\\r\\n]*))"), "gi") 450 | var data = [[]] 451 | var matches = null 452 | while (matches = pattern.exec(csvData.replace("# ",""))) { 453 | var matchedDelimiter = matches[1] 454 | if (matchedDelimiter.length && (matchedDelimiter != delimiter)) { 455 | data.push([]) 456 | } 457 | if (matches[2]) { 458 | var matchedDelimiter = matches[2].replace( 459 | new RegExp("\"\"", "g"), "\"") 460 | } else { 461 | var matchedDelimiter = matches[3] 462 | } 463 | data[data.length - 1].push(matchedDelimiter) 464 | } 465 | return (data) 466 | } 467 | // 468 | // Wenn keine Warnungen vorhanden sind, wird die allgemeine Wetterlage vom HTML geparst 469 | async function loadHTMLContent(HTMLurl) { 470 | let HTMLView = new WebView() 471 | await HTMLView.loadURL(HTMLurl) 472 | let js = ` 473 | document 474 | .getElementById("wettertext") 475 | .getElementsByTagName("pre")[0] 476 | .textContent 477 | ` 478 | let HTMContent = await HTMLView.evaluateJavaScript(js) 479 | return HTMContent 480 | } 481 | // 482 | // GPS Details abrufen (3km Radius, Dienst (reverseGeocode) von Apple Karten) 483 | async function getGPS() { 484 | try { 485 | Location.setAccuracyToThreeKilometers() 486 | return await Location.current() 487 | } catch (e) { 488 | logError(e) 489 | return null 490 | } 491 | } 492 | 493 | function getWarnCellID(Haystack, Needle) { 494 | for (var key in Haystack) { 495 | Region = Haystack[key].NAME 496 | let myReg = new RegExp(Needle + ".*") 497 | let myMatch = Region.match(myReg) 498 | if (myMatch) { 499 | result = key 500 | break 501 | } else { 502 | result = "emty" 503 | } 504 | } 505 | return result 506 | } 507 | // 508 | // Icon den verschiedenen Warnarten zuordnen 509 | function getWarnIcon(WarnTag){ 510 | WarnTag = WarnTag.toLowerCase() 511 | WarnWind = ["windböen", "sturmböen", "schwere sturmböen",] 512 | WarnSturm = ["orkanartige böen", "orkanböen", "extreme orkanböen"] 513 | WarnGwitter = ["gewitter", "starkes gewitter", "schwere gewitter", "extremes gewitter"] 514 | WarnRegen = ["starkregen", "heftiger starkregen", "extrem heftiger starkregen", "dauerregen", "ergiebiger dauerregen", "extrem ergiebiger dauerregen"] 515 | WarnSchnee = ["leichter schneefall", "schneefall", "starker schneefall", "extrem starker schneefall"] 516 | WarnSchneeWind = ["schneeverwehung", "starke schneeverwehung", "extrem starke schneeverwehung"] 517 | WarnGlatt = ["glätte", "örtlich glatteis", "glatteis"] 518 | WarnFrost = ["frost", "strenger frost"] 519 | WarnNebel = ["nebel"] 520 | WarnTau = ["tauwetter", "starkes tauwetter"] 521 | WarnHitze = ["starke wärmebelastung", "extreme wärmebelastung"] 522 | WarnUV = ["erhöhte uv-intensität"] 523 | if (WarnWind.includes(WarnTag)) {WarnIcon = "wind"} 524 | else if (WarnSturm.includes(WarnTag)) {WarnIcon = "tornado"} 525 | else if (WarnGwitter.includes(WarnTag)) {WarnIcon = "cloud.bolt"} 526 | else if (WarnRegen.includes(WarnTag)) {WarnIcon = "cloud.heavyrain"} 527 | else if (WarnSchnee.includes(WarnTag)) {WarnIcon = "cloud.snow"} 528 | else if (WarnSchneeWind.includes(WarnTag)) {WarnIcon = "wind.snow"} 529 | else if (WarnGlatt.includes(WarnTag)) {WarnIcon = "snow"} 530 | else if (WarnFrost.includes(WarnTag)) {WarnIcon = "thermometer.snowflake"} 531 | else if (WarnNebel.includes(WarnTag)) {WarnIcon = "cloud.fog"} 532 | else if (WarnTau.includes(WarnTag)) {WarnIcon = "aqi.low"} 533 | else if (WarnHitze.includes(WarnTag)) {WarnIcon = "thermometer.sun"} 534 | else if (WarnHitze.includes(WarnTag)) {WarnIcon = "sun.max.fill"} 535 | else {WarnIcon = "exclamationmark.triangle"} 536 | return WarnIcon 537 | } 538 | // 539 | // Icon für Warnstufen (akutell nicht in verwendung) 540 | function getWarnLevelIcon(Level){ 541 | if (Level == 1) {LevelIcon = "1.square"} 542 | else if (Level == 2) {LevelIcon = "2.square"} 543 | else if (Level == 3) {LevelIcon = "3.square"} 544 | else if (Level == 4) {LevelIcon = "4.square"} 545 | else {LevelIcon = "exclamationmark.circle"} 546 | return LevelIcon 547 | } 548 | // 549 | // Farben für Warnstufen 550 | function getWarnLevelColor(Level){ 551 | if (Level == 1) {LevelColor = new Color("#fbea60")} 552 | else if (Level == 2) {LevelColor = new Color("#ea9037")} 553 | else if (Level == 3) {LevelColor = new Color("#d04b40")} 554 | else if (Level == 4) {LevelColor = new Color("#7a254f")} 555 | else if (Level == 0) {LevelColor = new Color("#cbe277")} 556 | // Hitze Level 557 | else if (Level == 50) {LevelColor = new Color("#cc99ff")} 558 | return LevelColor 559 | } 560 | // 561 | // SF Smbole 562 | function addSymbol({ 563 | symbol = 'applelogo', 564 | stack, 565 | color = Color.white(), 566 | size = 20, 567 | imageOpacity = 1, 568 | }) { 569 | const _sym = SFSymbol.named(symbol) 570 | const wImg = stack.addImage(_sym.image) 571 | wImg.tintColor = color 572 | wImg.imageSize = new Size(size, size) 573 | wImg.containerRelativeShape = false 574 | wImg.imageOpacity = imageOpacity 575 | } 576 | // 577 | // Umlaute und Sonderzeichen für URL optimieren 578 | function SonderToURL(value){ 579 | value = value.toLowerCase(); 580 | value = value.replace(/ä/g, '%C3%A4') 581 | value = value.replace(/ö/g, '%C3%B6') 582 | value = value.replace(/ü/g, '%C3%BC') 583 | value = value.replace(/ß/g, '%C3%9F') 584 | value = value.replace(/ /g, '%20') 585 | return value 586 | } 587 | 588 | await new WarnWidget().init() 589 | // End of Script 590 | -------------------------------------------------------------------------------- /img/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/config.png -------------------------------------------------------------------------------- /img/large1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/large1.png -------------------------------------------------------------------------------- /img/large2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/large2.png -------------------------------------------------------------------------------- /img/large3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/large3.png -------------------------------------------------------------------------------- /img/large4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/large4.png -------------------------------------------------------------------------------- /img/medium1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/medium1.png -------------------------------------------------------------------------------- /img/medium2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/medium2.png -------------------------------------------------------------------------------- /img/warncellids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MacSchierer/WarnWetter/e04b64ec233df6ab74f40af56dafe03ba59d3152/img/warncellids.png --------------------------------------------------------------------------------