├── 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 = 'Region | Warncell-ID |
'
411 | for (WarnRegion in SortArray) {
412 | table += '' + WarnRegion + ' | ' + SortArray[WarnRegion] + ' |
'
413 | }
414 | table += '
'
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
--------------------------------------------------------------------------------