iobroker.waip-web 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,719 @@
1
+ # ioBroker.WAIP-Web
2
+
3
+ [![Test and Release](https://github.com/rnc11/ioBroker.waip-web/actions/workflows/test-and-release.yml/badge.svg)](https://github.com/rnc11/ioBroker.waip-web/actions/workflows/test-and-release.yml)
4
+
5
+ ## English
6
+
7
+ Unofficial ioBroker adapter for **Wachalarm IP-Web (WAIP-Web)**
8
+
9
+ Connects via Socket.IO to a WAIP-Web dispatch monitor and mirrors incidents
10
+ ("Einsatz"), responder feedback ("Rückmeldung"), routes and TTS
11
+ announcements into ioBroker states – without needing a browser tab to stay
12
+ open.
13
+
14
+ ### About this adapter
15
+
16
+ This adapter is an **unofficial community project** and has no connection
17
+ to the WAIP-Web project, to Robert-112, or to the operator of any specific
18
+ instance (e.g. the Integrated Regional Dispatch Center Lausitz /
19
+ Integrierte Regionalleitstelle Lausitz). It was built by analyzing the
20
+ behavior of the frontend (`client_waip.js`) that a WAIP-Web instance
21
+ publicly serves to any browser, in order to replicate the same Socket.IO
22
+ events and data fields a regular browser client receives.
23
+
24
+ The adapter connects **without logging in** and therefore only ever
25
+ receives WAIP-Web's public permission tier (keyword, location, approximate
26
+ position, alerted resources, feedback) – the same data any anonymous
27
+ browser visitor would see without signing in. No access restrictions are
28
+ bypassed.
29
+
30
+ > **Note:** An always-on automated client like this adapter is different
31
+ > from an occasionally opened browser tab. Before running it against a
32
+ > production instance, briefly check with the operator/your dispatch
33
+ > center whether a permanent automated connection is welcome.
34
+
35
+ ### About WAIP-Web
36
+
37
+ [Wachalarm IP-Web](https://github.com/Robert-112/n112_waip-web) is an
38
+ open-source web application by **Robert-112** that displays dispatch/alert
39
+ information for fire departments and EMS device-independently in the
40
+ browser (Windows, Linux, Mac, smartphone – no installation needed). Among
41
+ other things it offers:
42
+
43
+ - **Alarm monitor** – incident type, keyword, special signal, location,
44
+ map, alerted resources, app-based responder feedback including voice
45
+ announcements
46
+ - **Dashboard** – overview of all ongoing incidents
47
+ - **Feedback function** – app-based responder feedback, grouped by role
48
+ (EK/GF/ZF/VF) and additional qualification (AGT/FZF/MA/MED)
49
+ - **Administration** – user management, station data, monitor overview
50
+
51
+ WAIP-Web itself is licensed under
52
+ [**Creative Commons BY-SA 4.0**](https://creativecommons.org/licenses/by-sa/4.0/).
53
+ This adapter contains no code from the WAIP-Web project; it implements an
54
+ independent client for its Socket.IO interface.
55
+
56
+ ### Features
57
+
58
+ - Connects to the `/waip` namespace via `socket.io-client`, registers via
59
+ `emit('WAIP', monitorId)` (emitted 3× for robustness)
60
+ - Manual reconnect handling (the library's own auto-reconnect is
61
+ disabled) with a configurable delay
62
+ - Registration timeout with an audit log (`debug.monitorAudit`)
63
+ - Geodata normalization (wgs84 fields, `position`, or GeoJSON `geometry`
64
+ → centroid)
65
+ - History of the last 10 completed incidents (`einsatz.history10`)
66
+ - Separate handlers for alarm (`io.new_waip`), feedback (`io.new_rmld`),
67
+ routes (`io.routes`), TTS (`io.playtts`) and standby (`io.standby`)
68
+ - Automatic session-cookie management (see below), so alarm delivery
69
+ keeps working indefinitely without an open browser session
70
+ - Server-restart detection via `io.version`, with automatic session
71
+ refresh + reconnect
72
+ - Complete incident data including nested feedback/routes per incident
73
+ (feedback and routes are 1:n relationships)
74
+ - Aggregated feedback counters per role/capability, mirroring the live
75
+ badges on the web UI
76
+
77
+ #### Why a session cookie is needed
78
+
79
+ The WAIP-Web server ties alarm delivery to an Express session cookie,
80
+ which a browser renews automatically every few minutes via a bundled
81
+ script. A plain Socket.IO client never gets this cookie automatically –
82
+ the adapter therefore fetches it itself via `GET /session/keepalive` and
83
+ attaches it to the Socket.IO connection.
84
+
85
+ According to the WAIP-Web source code, the cookie's lifetime is
86
+ **configurable per instance via an environment variable** (server
87
+ default: 60 seconds; this instance apparently uses 10 minutes) – a fixed
88
+ renewal interval would therefore potentially be wrong for other WAIP-Web
89
+ instances. The adapter instead derives the actual interval **adaptively**
90
+ from the expiry time the server reports on every call (80% of the
91
+ observed lifetime, at least 55 seconds, at most a fixed 5-minute ceiling)
92
+ – the exact same clamping that `/js/session_keepalive.js` on the site
93
+ itself uses.
94
+
95
+ ### Configuration
96
+
97
+ In the admin UI of the adapter instance:
98
+
99
+ | Field | Description | Default |
100
+ | --- | --- | --- |
101
+ | WAIP server URL | Base URL of the WAIP-Web instance | `https://wachalarm.leitstelle-lausitz.de` |
102
+ | Monitor ID | Picked from a live dropdown, fetched from the configured server's `/waip/` overview page and grouped by Leitstelle/Kreis/Träger/Wache; manual entry stays possible if the server can't be reached. Empty/`0` = global monitor (all incidents) | *(empty)* |
103
+ | Registration timeout (s) | Time until a missing registration confirmation is logged | `10` |
104
+ | Reconnect delay (s) | Wait time before a manual reconnect after disconnect/error | `5` |
105
+
106
+ The session keepalive interval is **not configurable** – it's derived
107
+ fully automatically on every renewal from the cookie lifetime the server
108
+ reports (min. 55s, max. 5 min., matching `/js/session_keepalive.js` on
109
+ the site itself).
110
+
111
+ ### States (under `waip-web.0.*`)
112
+
113
+ Feedback and routes are 1:n lists per incident and are therefore stored
114
+ as nested JSON arrays inside `einsatz.json` and in every entry of
115
+ `einsatz.history10` – complemented by quick-to-bind counters so VIS
116
+ bindings and triggers don't need JSON parsing.
117
+
118
+ #### info
119
+
120
+ | State | Type | Description |
121
+ | --- | --- | --- |
122
+ | `connection` | boolean | Standard ioBroker indicator: connection to the WAIP server active |
123
+
124
+ #### status
125
+
126
+ | State | Type | Description |
127
+ | --- | --- | --- |
128
+ | `connected` | boolean | Socket.IO connection technically established |
129
+ | `alarmAktiv` | boolean | `true` since the last `io.new_waip`, `false` since the last `io.standby` |
130
+ | `restzeit` | number (s) | Remaining seconds until `einsatz.ablaufzeit`, updated every second |
131
+ | `registeredMonitor` | string | Monitor ID last registered with the server |
132
+ | `registeredMonitorName` | string | Display name of that monitor, without the ID (e.g. "Leitstelle: Lausitz"); resolved once at startup from the same `/waip/` overview page as the admin dropdown, `null` if it couldn't be resolved |
133
+ | `registrationAccepted` | mixed | `"pending"` right after connecting, `true` once the first event was received, otherwise `false` once the registration timeout elapses |
134
+
135
+ #### einsatz
136
+
137
+ Flat fields of the currently running incident. Cleared (`null`/`0`) on
138
+ `io.standby`, matching the official frontend – `status.alarmAktiv` is
139
+ therefore a reliable switch for whether these fields currently hold real
140
+ live data. The most recently finished incident remains available via
141
+ `einsatz.history10`:
142
+
143
+ | State | Type | Description |
144
+ | --- | --- | --- |
145
+ | `id` | number | Internal incident ID |
146
+ | `uuid` | string | Unique incident UUID (also used to associate feedback) |
147
+ | `einsatzart` | string | e.g. "Brandeinsatz" (fire), "Hilfeleistungseinsatz" (technical assistance), "Rettungseinsatz" (rescue/EMS), "Krankentransport" (patient transport) |
148
+ | `stichwort` | string | Alarm keyword |
149
+ | `ort` | string | Location/town |
150
+ | `ortsteil` | string | District (if different from `ort`) |
151
+ | `strasse` / `hausnummer` | string | Address |
152
+ | `objekt` / `objektteil` | string | Building name and part |
153
+ | `einsatzdetails` | string | Extra details (only populated for fire/technical-assistance incidents) |
154
+ | `besonderheiten` | string | Free-text remarks from the dispatch center |
155
+ | `zeitstempel` | string (date) | Alarm time |
156
+ | `ablaufzeit` | string (date) | End of the standby display duration, basis for `status.restzeit` |
157
+ | `einsatznummer` | string | Incident number (if assigned by the server) |
158
+ | `sondersignal` | number | `1` = special signal (lights & siren), otherwise none |
159
+ | `permissions` | mixed | The registration's permission flag (full access to the detail map yes/no) |
160
+ | `latitude` / `longitude` | number | Incident location (normalized from wgs84 fields or GeoJSON centroid) |
161
+ | `json` | string (JSON) | Complete incident object: all fields above plus `emAlarmiert[]`, `emWeitere[]`, `routen[]`, `rueckmeldungen[]` |
162
+ | `history10` | string (JSON array) | Last 10 completed incidents, same object shape as `json`, written on `io.standby` |
163
+ | `routenGesamt` | number | Number of routes in the current incident (= `json.routen.length`) |
164
+ | `rueckmeldungGesamt` | number | Total feedback count for the current incident |
165
+ | `rueckmeldungAnzahl.ek` | number | Feedback count as team member ("Einsatzkraft") |
166
+ | `rueckmeldungAnzahl.gf` | number | Feedback count as crew leader ("Gruppenführer") |
167
+ | `rueckmeldungAnzahl.zf` | number | Feedback count as division chief ("Zugführer") |
168
+ | `rueckmeldungAnzahl.vf` | number | Feedback count as group commander ("Verbandsführer") |
169
+ | `rueckmeldungAnzahl.agt` | number | Feedback count with breathing-apparatus qualification ("Atemschutzgeräteträger") |
170
+ | `rueckmeldungAnzahl.fzf` | number | Feedback count as vehicle commander ("Fahrzeugführer") |
171
+ | `rueckmeldungAnzahl.ma` | number | Feedback count as driver/operator ("Maschinist") |
172
+ | `rueckmeldungAnzahl.med` | number | Feedback count with a medical qualification |
173
+
174
+ #### tts
175
+
176
+ | State | Type | Description |
177
+ | --- | --- | --- |
178
+ | `last` | string (URL) | URL of the most recently received voice announcement |
179
+ | `lastTimestamp` | string (date) | Time of the last announcement |
180
+ | `history10` | string (JSON array) | Last 10 announcements as `{zeitstempel, url}` |
181
+
182
+ #### debug
183
+
184
+ | State | Type | Description |
185
+ | --- | --- | --- |
186
+ | `lastEvent` | string (JSON) | Last received socket event (name + timestamp), for connection diagnostics |
187
+ | `normalizedPosition` | string (JSON) | Last normalized position of the incident |
188
+ | `rawPayloadShort` | string | Preview (500 characters) of the raw, unnormalized `io.new_waip` payload |
189
+ | `ignoredCount` | number | Count of discarded events (payload explicitly named a different monitor ID) |
190
+ | `monitorAudit` | string (JSON array) | Chronological log of connect/registration/reconnect events (200 entries) |
191
+ | `sessionExpires` | string (date) | Expiry time of the session cookie as of the last renewal |
192
+ | `lastError` | string (JSON) | Last error message reported by the server (`io.error`) |
193
+ | `serverVersion` | string | Last reported server instance ID (`io.version`); a change suggests a server restart |
194
+
195
+ JSON-internal keys inside `einsatz.json` (`emAlarmiert`, `emWeitere`,
196
+ `routen`, `rueckmeldungen`) stay lowercase – these are object properties
197
+ inside the JSON value, not their own ioBroker states.
198
+
199
+ ## Deutsch
200
+
201
+ Inoffizieller ioBroker-Adapter für **Wachalarm IP-Web (WAIP-Web)**
202
+
203
+ Verbindet sich per Socket.IO mit einem WAIP-Web-Wachalarm-Monitor und bildet
204
+ Einsätze, Rückmeldungen, Routen und TTS-Ansagen als ioBroker-States ab –
205
+ ohne dass ein Browser-Tab dauerhaft offen sein muss.
206
+
207
+ ### Über diesen Adapter
208
+
209
+ Dieser Adapter ist ein **inoffizielles Community-Projekt** und steht in
210
+ keiner Verbindung zum WAIP-Web-Projekt, zu Robert-112 oder zum Betreiber
211
+ einer konkreten Instanz (z. B. der Integrierten Regionalleitstelle
212
+ Lausitz). Er wurde entwickelt, indem das öffentlich über den Browser
213
+ ausgelieferte Frontend (`client_waip.js`) einer WAIP-Web-Instanz auf sein
214
+ Verhalten hin analysiert wurde, um dieselben Socket.IO-Events und
215
+ Datenfelder nachzubilden, die auch ein regulärer Browser-Client empfängt.
216
+
217
+ Der Adapter meldet sich **ohne Login** an und erhält dadurch ausschließlich
218
+ die öffentliche Berechtigungsstufe von WAIP-Web (Stichwort, Ort, ungefähre
219
+ Position, alarmierte Einsatzmittel, Rückmeldungen) – dieselben Daten, die
220
+ auch ein anonymer Browser-Besucher ohne Anmeldung sehen würde. Es werden
221
+ keine Zugriffsbeschränkungen umgangen.
222
+
223
+ > **Hinweis:** Ein automatisierter Dauerclient wie dieser Adapter ist etwas
224
+ > anderes als ein gelegentlich geöffneter Browser-Tab. Bevor du den Adapter
225
+ > gegen eine produktive Instanz laufen lässt, sprich kurz mit dem
226
+ > Betreiber/deiner Leitstelle ab, ob eine dauerhafte automatisierte
227
+ > Verbindung erwünscht ist.
228
+
229
+ ### Über WAIP-Web
230
+
231
+ [Wachalarm IP-Web](https://github.com/Robert-112/n112_waip-web) ist eine
232
+ quelloffene Webanwendung von **Robert-112**, die Alarmierungsinformationen
233
+ für Feuerwehr/Rettungsdienst geräteunabhängig im Browser darstellt (Windows,
234
+ Linux, Mac, Smartphone – keine Installation nötig). Sie bietet u. a.:
235
+
236
+ - **Alarmmonitor** – Einsatzart, Stichwort, Sondersignal, Ort, Karte,
237
+ alarmierte Einsatzmittel, App-Rückmeldungen der Einsatzkräfte inkl.
238
+ Sprachansage
239
+ - **Dashboard** – Gesamtübersicht laufender Einsätze
240
+ - **Rückmeldefunktion** – App-basierte Rückmeldungen der Einsatzkräfte,
241
+ gegliedert nach Rolle (EK/GF/ZF/VF) und Zusatzfunktion (AGT/FZF/MA/MED)
242
+ - **Administration** – Nutzerverwaltung, Wachdaten, Monitor-Übersicht
243
+
244
+ WAIP-Web selbst ist unter der
245
+ [**Creative Commons BY-SA 4.0**](https://creativecommons.org/licenses/by-sa/4.0/deed.de)
246
+ lizenziert. Dieser Adapter enthält keinen Code aus dem WAIP-Web-Projekt,
247
+ sondern implementiert eine eigenständige Anbindung an dessen Socket.IO-
248
+ Schnittstelle.
249
+
250
+ ### Funktionen
251
+
252
+ - Verbindung zum Namespace `/waip` per `socket.io-client`, Registrierung
253
+ über `emit('WAIP', monitorId)` (3-faches Emit für Robustheit)
254
+ - Manuelles Reconnect-Handling (kein Auto-Reconnect der Bibliothek) mit
255
+ konfigurierbarer Verzögerung
256
+ - Registrierungs-Timeout mit Audit-Log (`debug.monitorAudit`)
257
+ - Normalisierung von Geodaten (wgs84-Felder, `position` oder
258
+ GeoJSON-`geometry` → Mittelpunkt)
259
+ - History der letzten 10 abgeschlossenen Einsätze (`einsatz.history10`)
260
+ - Getrennte Handler für Alarm (`io.new_waip`), Rückmeldung (`io.new_rmld`),
261
+ Routen (`io.routes`), TTS (`io.playtts`) und Standby (`io.standby`)
262
+ - Automatisches Session-Cookie-Management (siehe unten), damit die
263
+ Alarm-Zustellung auch ohne offene Browsersitzung dauerhaft weiterläuft
264
+ - Server-Neustart-Erkennung über `io.version` mit automatischem
265
+ Session-Refresh + Reconnect
266
+ - Vollständige Einsatzdaten inkl. verschachtelter Rückmeldungen/Routen
267
+ pro Einsatz (Rückmeldungen und Routen sind 1:n-Beziehungen)
268
+ - Aggregierte Rückmeldungs-Zähler pro Rolle/Fähigkeit, analog zu den
269
+ Live-Badges der Weboberfläche
270
+
271
+ #### Warum ein Session-Cookie nötig ist
272
+
273
+ Der WAIP-Web-Server bindet die Alarm-Zustellung an einen
274
+ Express-Session-Cookie, den ein Browser über ein mitgeliefertes Skript
275
+ automatisch alle paar Minuten erneuert. Ein reiner Socket.IO-Client bekommt
276
+ diesen Cookie nie automatisch – der Adapter holt ihn deshalb selbst per
277
+ `GET /session/keepalive` und hängt ihn an die Socket.IO-Verbindung an.
278
+
279
+ Die Cookie-Lebensdauer ist laut Quellcode von WAIP-Web **pro Instanz per
280
+ Umgebungsvariable konfigurierbar** (Server-Standard: 60 Sekunden; diese
281
+ Instanz nutzt offenbar 10 Minuten) – ein fest angenommenes Erneuerungs-
282
+ intervall wäre daher für andere WAIP-Web-Instanzen potenziell falsch. Der
283
+ Adapter leitet das tatsächliche Intervall deshalb **adaptiv** aus der vom
284
+ Server bei jedem Aufruf gemeldeten Ablaufzeit ab (80 % der beobachteten
285
+ Laufzeit, mindestens 55 Sekunden, höchstens die konfigurierte Obergrenze) –
286
+ genau die gleiche Klammerung, die auch `/js/session_keepalive.js` der
287
+ Website selbst verwendet.
288
+
289
+ ### Konfiguration
290
+
291
+ In der Admin-Oberfläche der Adapterinstanz:
292
+
293
+ | Feld | Beschreibung | Default |
294
+ | --- | --- | --- |
295
+ | WAIP-Server-URL | Basis-URL der WAIP-Web-Instanz | `https://wachalarm.leitstelle-lausitz.de` |
296
+ | Monitor-ID | Auswahl per Live-Dropdown, geladen von der `/waip/`-Übersichtsseite des konfigurierten Servers und gruppiert nach Leitstelle/Kreis/Träger/Wache; manuelle Eingabe bleibt möglich, falls der Server nicht erreichbar ist. Leer/`0` = globaler Monitor (alle Einsätze) | *(leer)* |
297
+ | Registrierungs-Timeout (s) | Zeit bis eine ausbleibende Registrierungsbestätigung geloggt wird | `10` |
298
+ | Wiederverbindungs-Verzögerung (s) | Wartezeit vor manuellem Reconnect nach Disconnect/Fehler | `5` |
299
+
300
+ Das Session-Keepalive-Intervall ist **nicht konfigurierbar** – es wird bei
301
+ jeder Erneuerung vollautomatisch aus der vom Server gemeldeten Cookie-
302
+ Laufzeit abgeleitet (min. 55s, max. 5 Min., analog zu
303
+ `/js/session_keepalive.js` der Website selbst).
304
+
305
+ ### States (unter `waip-web.0.*`)
306
+
307
+ Rückmeldungen und Routen sind pro Einsatz Listen (1:n) und liegen deshalb
308
+ als verschachtelte JSON-Arrays in `einsatz.json` bzw. in jedem Eintrag von
309
+ `einsatz.history10` – ergänzt um schnell bindbare Zähler, damit VIS-Bindings
310
+ und Trigger ohne JSON-Parsing auskommen.
311
+
312
+ #### info
313
+
314
+ | State | Typ | Beschreibung |
315
+ | --- | --- | --- |
316
+ | `connection` | boolean | Standard-ioBroker-Indikator: Verbindung zum WAIP-Server aktiv |
317
+
318
+ #### status
319
+
320
+ | State | Typ | Beschreibung |
321
+ | --- | --- | --- |
322
+ | `connected` | boolean | Socket.IO-Verbindung technisch aufgebaut |
323
+ | `alarmAktiv` | boolean | `true` seit dem letzten `io.new_waip`, `false` seit dem letzten `io.standby` |
324
+ | `restzeit` | number (s) | Verbleibende Sekunden bis `einsatz.ablaufzeit`, sekündlich aktualisiert |
325
+ | `registeredMonitor` | string | Zuletzt beim Server registrierte Monitor-ID |
326
+ | `registeredMonitorName` | string | Anzeigename dieses Monitors ohne ID (z. B. „Leitstelle: Lausitz"); wird einmalig beim Start von derselben `/waip/`-Übersichtsseite wie das Admin-Dropdown aufgelöst, `null` falls nicht auflösbar |
327
+ | `registrationAccepted` | mixed | `"pending"` direkt nach Connect, `true` sobald das erste Event empfangen wurde, sonst `false` nach Ablauf des Registrierungs-Timeouts |
328
+
329
+ #### einsatz
330
+
331
+ Flache Felder des aktuell laufenden Einsatzes. Werden bei `io.standby`
332
+ geleert (`null`/`0`), analog zum offiziellen Frontend – `status.alarmAktiv`
333
+ ist damit ein verlässlicher Schalter dafür, ob hier gerade echte Live-Daten
334
+ stehen. Der zuletzt abgeschlossene Einsatz bleibt trotzdem über
335
+ `einsatz.history10` abrufbar:
336
+
337
+ | State | Typ | Beschreibung |
338
+ | --- | --- | --- |
339
+ | `id` | number | Interne Einsatz-ID |
340
+ | `uuid` | string | Eindeutige Einsatz-UUID (dient auch der Zuordnung von Rückmeldungen) |
341
+ | `einsatzart` | string | z. B. „Brandeinsatz", „Hilfeleistungseinsatz", „Rettungseinsatz", „Krankentransport" |
342
+ | `stichwort` | string | Alarmstichwort |
343
+ | `ort` | string | Ort |
344
+ | `ortsteil` | string | Ortsteil (falls abweichend vom Ort) |
345
+ | `strasse` / `hausnummer` | string | Adresse |
346
+ | `objekt` / `objektteil` | string | Gebäude-/Objektname und -teil |
347
+ | `einsatzdetails` | string | Zusatzdetails (nur bei Brand-/Hilfeleistungseinsätzen befüllt) |
348
+ | `besonderheiten` | string | Freitext-Besonderheiten der Leitstelle |
349
+ | `zeitstempel` | string (date) | Alarmzeit |
350
+ | `ablaufzeit` | string (date) | Ende der Standby-Anzeigedauer, Basis für `status.restzeit` |
351
+ | `einsatznummer` | string | Einsatznummer (sofern vom Server vergeben) |
352
+ | `sondersignal` | number | `1` = Sondersignal, sonst kein Sondersignal |
353
+ | `permissions` | mixed | Berechtigungsflag der Registrierung (Vollzugriff auf Detailkarte ja/nein) |
354
+ | `latitude` / `longitude` | number | Position des Einsatzortes (normalisiert aus wgs84-Feldern oder GeoJSON-Mittelpunkt) |
355
+ | `json` | string (JSON) | Vollständiges Einsatz-Objekt: alle Felder oben plus `emAlarmiert[]`, `emWeitere[]`, `routen[]`, `rueckmeldungen[]` |
356
+ | `history10` | string (JSON-Array) | Letzte 10 abgeschlossenen Einsätze, gleicher Objekt-Shape wie `json`, geschrieben bei `io.standby` |
357
+ | `routenGesamt` | number | Anzahl Routen im aktuellen Einsatz (= `json.routen.length`) |
358
+ | `rueckmeldungGesamt` | number | Rückmeldungen gesamt im aktuellen Einsatz |
359
+ | `rueckmeldungAnzahl.ek` | number | Anzahl Rückmeldungen als Einsatzkraft |
360
+ | `rueckmeldungAnzahl.gf` | number | Anzahl Rückmeldungen als Gruppenführer |
361
+ | `rueckmeldungAnzahl.zf` | number | Anzahl Rückmeldungen als Zugführer |
362
+ | `rueckmeldungAnzahl.vf` | number | Anzahl Rückmeldungen als Verbandsführer |
363
+ | `rueckmeldungAnzahl.agt` | number | Anzahl Rückmeldungen mit Atemschutz-Befähigung |
364
+ | `rueckmeldungAnzahl.fzf` | number | Anzahl Rückmeldungen als Fahrzeugführer |
365
+ | `rueckmeldungAnzahl.ma` | number | Anzahl Rückmeldungen als Maschinist |
366
+ | `rueckmeldungAnzahl.med` | number | Anzahl Rückmeldungen mit medizinischer Befähigung |
367
+
368
+ #### tts
369
+
370
+ | State | Typ | Beschreibung |
371
+ | --- | --- | --- |
372
+ | `last` | string (URL) | URL der zuletzt empfangenen Sprachansage |
373
+ | `lastTimestamp` | string (date) | Zeitpunkt der letzten Ansage |
374
+ | `history10` | string (JSON-Array) | Letzte 10 Ansagen als `{zeitstempel, url}` |
375
+
376
+ #### debug
377
+
378
+ | State | Typ | Beschreibung |
379
+ | --- | --- | --- |
380
+ | `lastEvent` | string (JSON) | Letztes empfangenes Socket-Event (Name + Zeitstempel), zur Verbindungsdiagnose |
381
+ | `normalizedPosition` | string (JSON) | Zuletzt normalisierte Position des Einsatzes |
382
+ | `rawPayloadShort` | string | Vorschau (500 Zeichen) der rohen, unnormalisierten `io.new_waip`-Nutzlast |
383
+ | `ignoredCount` | number | Anzahl verworfener Events (Payload nannte explizit eine andere Monitor-ID) |
384
+ | `monitorAudit` | string (JSON-Array) | Chronologisches Log von Connect-/Registrierungs-/Reconnect-Ereignissen (200 Einträge) |
385
+ | `sessionExpires` | string (date) | Ablaufzeit des Session-Cookies laut letzter Erneuerung |
386
+ | `lastError` | string (JSON) | Letzte vom Server gemeldete Fehlermeldung (`io.error`) |
387
+ | `serverVersion` | string | Zuletzt gemeldete Server-Instanz-ID (`io.version`); Änderung deutet auf Server-Neustart hin |
388
+
389
+ JSON-interne Schlüssel innerhalb von `einsatz.json` (`emAlarmiert`,
390
+ `emWeitere`, `routen`, `rueckmeldungen`) bleiben kleingeschrieben – das sind
391
+ Objekteigenschaften im JSON-Wert, keine eigenen ioBroker-States.
392
+
393
+ ## License
394
+
395
+ MIT License (this adapter) – see [LICENSE](LICENSE).
396
+
397
+ The adapter connects to instances of
398
+ [WAIP-Web](https://github.com/Robert-112/n112_waip-web), which is licensed
399
+ under CC BY-SA 4.0 by Robert-112. This adapter contains no code from that
400
+ project.
401
+
402
+ Copyright (c) 2026 rnc11
403
+
404
+ Permission is hereby granted, free of charge, to any person obtaining a copy
405
+ of this software and associated documentation files (the "Software"), to deal
406
+ in the Software without restriction, including without limitation the rights
407
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
408
+ copies of the Software, and to permit persons to whom the Software is
409
+ furnished to do so, subject to the following conditions:
410
+
411
+ The above copyright notice and this permission notice shall be included in all
412
+ copies or substantial portions of the Software.
413
+
414
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
415
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
416
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
417
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
418
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
419
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
420
+ SOFTWARE.
421
+
422
+ ## Changelog
423
+
424
+ ### 0.6.6 (2026-08-21)
425
+
426
+ - Added `.gitattributes` `eol=lf` so all contributors get consistent LF
427
+ line endings regardless of their local git `autocrlf` setting
428
+ (previously caused hundreds of spurious local lint errors on Windows
429
+ without affecting CI, since CI checkouts weren't affected).
430
+ - Added `package-lock.json` and switched CI to the default `npm ci` with
431
+ caching instead of `npm install` without caching, now that a lockfile
432
+ exists.
433
+
434
+ ### 0.6.5 (2026-08-21)
435
+
436
+ - Fixed all issues reported by the official [ioBroker adapter checker](https://adapter-check.iobroker.in/):
437
+ - Bumped the required `js-controller` dependency to `>=5.0.19` and `@iobroker/adapter-core` to `^3.2.2`
438
+ - Replaced the deprecated `common.license`/`common.title`/`common.main` fields with `common.licenseInformation`/`common.titleLang`; `common.main` is redundant with `package.json`'s `main` field
439
+ - Added the required `common.tier` (`2` - external/cloud data source)
440
+ - Added translations of `titleLang`, `desc` and the kept `common.news` entries into all 10 additional ioBroker admin languages (ru, pt, nl, fr, it, es, pl, uk, zh-cn)
441
+ - Trimmed `common.news` to the 7 most recent entries, as recommended (the repository builder truncates there anyway); the full history stays in this changelog
442
+
443
+ ### 0.6.4 (2026-08-21)
444
+
445
+ - New state `status.registeredMonitorName` shows the registered monitor
446
+ as text without the ID (e.g. "Leitstelle: Lausitz"), resolved once at
447
+ startup from the same `/waip/` overview page as the admin dropdown.
448
+ `status.registeredMonitor` is unchanged and keeps showing the ID.
449
+
450
+ ### 0.6.3 (2026-08-21)
451
+
452
+ - Admin UI: the Monitor ID field is now always full-width, placed on its
453
+ own row below the WAIP server URL, so the (sometimes long) dropdown
454
+ entries have enough room to display without being cramped.
455
+
456
+ ### 0.6.2 (2026-08-21)
457
+
458
+ - Monitor dropdown entries are now sorted by numeric monitor ID instead
459
+ of following the source page's (per-category) order.
460
+
461
+ ### 0.6.1 (2026-08-21)
462
+
463
+ - Monitor dropdown entries now start with the actual monitor ID, e.g.
464
+ "4 - Leitstelle: Lausitz", so the numeric ID stays visible even after
465
+ a monitor has been selected.
466
+
467
+ ### 0.6.0 (2026-08-21)
468
+
469
+ - **Admin UI:** Monitor ID is now a live dropdown (`selectSendTo`) instead
470
+ of a plain text field. The adapter fetches the configured server's
471
+ public `/waip/` overview page on demand and offers every monitor it
472
+ finds there, grouped by Leitstelle/Kreis/Träger/Wache, with "Alle
473
+ Wachalarme" (ID `0`) as an explicit first option. Manual entry remains
474
+ available if the server can't be reached or uses a different page
475
+ layout, so existing configurations keep working unchanged.
476
+ - Removed the static "session keepalive interval" info text from the
477
+ admin UI (the behavior itself is unchanged - see [Why a session cookie
478
+ is needed](#why-a-session-cookie-is-needed)).
479
+ - Added help text to the "Registration timeout" and "Reconnect delay"
480
+ fields.
481
+
482
+ ### 0.5.1 (2026-08-21)
483
+
484
+ - **Logging:** Reclassified log levels so `warn` becomes a reliable signal
485
+ again. Session-cookie rotation, the known "Fehler beim Erneuern der
486
+ Session" server message, and detected server restarts are now logged
487
+ as `info` instead of `warn`, since they're part of this instance's
488
+ normal, self-healing ~10-minute session cycle. Events discarded for a
489
+ different monitor ID are now `info` (frequency remains tracked via
490
+ `debug.ignoredCount`). Failures that only affect internal housekeeping
491
+ (`debug.monitorAudit`, socket cleanup) are now `debug`. A duplicate
492
+ `connect_error` log line (logged once at `warn`, once at `info`) was
493
+ removed. Introduced `error` level for cases where an incident,
494
+ feedback, route or TTS event genuinely failed to be processed (actual
495
+ data loss), so these now stand out instead of being buried among
496
+ routine `warn` noise.
497
+
498
+ ### 0.5.0 (2026-08-20)
499
+
500
+ - **Behavior change:** On `io.standby`, all `einsatz.*` states (including
501
+ `einsatz.json` and all counters) are now cleared instead of leaving the
502
+ finished incident's data in place – matching the official frontend,
503
+ which also clears keyword, location data etc. on standby.
504
+ `status.alarmAktiv` is therefore now a reliable switch for whether
505
+ `einsatz.*` currently holds real live data. The finished incident
506
+ remains fully available via `einsatz.history10` (archived there first).
507
+
508
+ ### 0.4.9 (2026-08-20)
509
+
510
+ - **CI fix:** Reverted a self-introduced, unnecessary line wrap in
511
+ `connect()` (engine-packet preview) – `prettier/prettier` flagged it as
512
+ superfluous; the line actually fits on one line within the configured
513
+ print width.
514
+
515
+ ### 0.4.8 (2026-08-20)
516
+
517
+ - **CI fix:** Fixed the final 16 lint errors – this time the complete,
518
+ non-truncated error list including the exact desired replacement text
519
+ was available, so every location could be corrected precisely 1:1
520
+ (line wraps for overly long calls/objects, one `prefer-template`
521
+ case). Lint should now be fully green.
522
+
523
+ ### 0.4.7 (2026-08-20)
524
+
525
+ - **CI fix:** Fixed the remaining lint errors in `main.js` – consistently
526
+ applied `curly` (braces even for single-line `if`/`for`),
527
+ `no-unused-vars` for empty `catch` blocks (`catch (e) {}` → `catch {}`
528
+ where `e` isn't used), and `arrowParens: avoid` (removed parentheses
529
+ around single-parameter arrow functions). Purely mechanical code-style
530
+ fixes, no behavior change.
531
+
532
+ ### 0.4.6 (2026-08-20)
533
+
534
+ - **CI fix:** The `lint` step failed with hundreds of formatting errors
535
+ (e.g. "Replace 'x' with "x""). Cause: `@iobroker/eslint-config`
536
+ requires its own `prettier.config.mjs` in addition to
537
+ `eslint.config.mjs`, re-exporting the ioBroker formatting style
538
+ (single quotes, 4-space indentation, `trailingComma: 'all'`) – without
539
+ it, Prettier fell back to its own defaults (double quotes). File added.
540
+
541
+ ### 0.4.5 (2026-08-20)
542
+
543
+ - **CI fix:** The first pipeline run failed with "Dependencies lock file
544
+ is not found". Cause: `actions/setup-node`'s built-in npm caching
545
+ needs a `package-lock.json` for its cache key, independent of the
546
+ install command used. Since none is committed to the repo yet,
547
+ `package-cache: 'false'` is now set on both jobs.
548
+
549
+ ### 0.4.4 (2026-08-20)
550
+
551
+ - Set up the CI pipeline (`.github/workflows/test-and-release.yml`),
552
+ following the standard pattern of the official
553
+ `@iobroker/create-adapter` template: a `check-and-lint` job (ESLint
554
+ via `@iobroker/eslint-config`) and an `adapter-tests` matrix (Node
555
+ 20/22/24 × Ubuntu/Windows/macOS) via the central
556
+ `testing-action-check`/`testing-action-adapter` actions. Added
557
+ `test/package.js` and `test/integration.js` (`@iobroker/testing`)
558
+ plus `eslint.config.mjs` for this; bumped `@iobroker/testing` to
559
+ `^5.2.2`. Since no `package-lock.json` is committed yet, the pipeline
560
+ uses `npm install` instead of `npm ci`.
561
+ - Raised `engines.node` in `package.json` to `>=18` (matching the tested
562
+ Node versions and `@iobroker/testing` 5.x's requirements)
563
+ - A `deploy` job (automatic npm publishing) is prepared but commented
564
+ out until npm Trusted Publishing is set up
565
+
566
+ ### 0.4.3 (2026-08-20)
567
+
568
+ - **Bugfix:** `einsatz.id` and `einsatz.sondersignal` were declared as
569
+ `string`, but the server actually sends them as numbers
570
+ (`sondersignal`, per `client_waip.js`: `switch (data.sondersignal) {
571
+ case 1: ... }`) – ioBroker therefore logged a type warning on every
572
+ incident. Both states are now declared as `number`. Existing objects
573
+ with the old (wrong) type are automatically recreated on the next
574
+ adapter start (`migrateObjectTypes()`, generic for future type
575
+ corrections).
576
+
577
+ ### 0.4.2 (2026-08-20)
578
+
579
+ - Removed the "Session keepalive interval – upper bound" config field
580
+ again: the official `/js/session_keepalive.js` on the site hard-codes
581
+ this upper bound at 5 minutes and doesn't make it configurable. Having
582
+ an admin field for it wrongly suggested a fixed interval, even though
583
+ the actual renewal has long been fully automatic (see 0.4.1). The
584
+ upper bound is now likewise fixed at 5 minutes.
585
+
586
+ ### 0.4.1 (2026-08-20)
587
+
588
+ - **Robustness:** The session keepalive interval is now adaptive instead
589
+ of a fixed assumption. WAIP-Web's source code (`server/app_cfg.js`)
590
+ shows that the session cookie's lifetime is configurable per instance
591
+ via an environment variable (server default: 60s instead of the
592
+ previously assumed 10 minutes) – the adapter now derives the renewal
593
+ interval from the expiry time actually reported by the server
594
+ (matching `/js/session_keepalive.js` on the site: 80% of the observed
595
+ lifetime, min. 55s, at most the configured upper bound). The "Session
596
+ keepalive interval" config option is accordingly now an upper bound
597
+ rather than a fixed interval.
598
+ - Corrected a comment about `io.version`/server restarts: per
599
+ `server/auth.js`, WAIP-Web stores sessions persistently (SQLite), not
600
+ in-memory – a restart therefore normally doesn't clear them. The
601
+ proactive session refresh + reconnect on server restart remains as a
602
+ general safeguard, only the previous justification was inaccurate.
603
+
604
+ ### 0.4.0 (2026-08-20)
605
+
606
+ - **Restructured the object tree:** Feedback and routes are 1:n lists
607
+ per incident and now live as nested JSON arrays inside one overall
608
+ object `einsatz.json` (including `emAlarmiert[]`, `emWeitere[]`,
609
+ `routen[]`, `rueckmeldungen[]`) instead of several loose states.
610
+ - `einsatz.history10` replaces `history.last10` – now with the **full**
611
+ nested incident object per entry instead of just 6 reduced fields.
612
+ - New counters directly under `einsatz.*`: `routenGesamt`,
613
+ `rueckmeldungGesamt`, `rueckmeldungAnzahl.{ek,gf,zf,vf,agt,fzf,ma,med}`
614
+ (replaces `rueckmeldung.counts.*`).
615
+ - `einsatz.latitude`/`einsatz.longitude` replace
616
+ `geo.latitude`/`geo.longitude` (position is additionally available in
617
+ `einsatz.json.position`).
618
+ - New state `tts.history10` (last 10 TTS announcements).
619
+ - **Removed:** the entire `vis.*` channel, `json.raw`, `json.einsatz`,
620
+ `geo.position`, `rueckmeldung.last.json`, `routen.json`,
621
+ `routen.count`, `einsatz.emWeitere` (now part of `einsatz.json`).
622
+ - The adapter automatically removes all obsolete objects from the
623
+ previous structure on its first start after the update
624
+ (`cleanupObsoleteObjects()`).
625
+
626
+ ### 0.3.4 (2026-08-20)
627
+
628
+ - All state names (`common.name`) consistently switched to German
629
+ (previously a mix of English and German) – the adapter only makes
630
+ sense for German-speaking users anyway
631
+
632
+ ### 0.3.3 (2026-08-20)
633
+
634
+ - **Bugfix (potential data loss):** For a specific monitor ID (≠ `0`),
635
+ events were silently discarded ("unknownMonitor") after the
636
+ registration timeout elapsed, because real WAIP payloads, per
637
+ `client_waip.js`, **never** contain a monitor-identifying field – that
638
+ assignment happens entirely server-side via Socket.IO rooms. As a
639
+ result, alarm delivery could stop completely after 10s even though
640
+ the connection was technically up. `status.registrationAccepted`
641
+ stayed permanently `false` for the same reason, even for the global
642
+ monitor (`0`). Now any received event confirms the registration;
643
+ events are only discarded if a payload explicitly names a different
644
+ monitor ID.
645
+
646
+ ### 0.3.2 (2026-08-20)
647
+
648
+ - Also switched the "Registration timeout" and "Reconnect delay" config
649
+ fields from milliseconds to seconds (`registrationTimeout` →
650
+ `registrationTimeoutSec`, default `10`; `reconnectDelay` →
651
+ `reconnectDelaySec`, default `5`). Existing instances without a newly
652
+ set value automatically use the defaults.
653
+
654
+ ### 0.3.1 (2026-08-20)
655
+
656
+ - Switched the "Session keepalive interval" config field from
657
+ milliseconds to seconds (`sessionKeepaliveInterval` →
658
+ `sessionKeepaliveIntervalSec`, still defaulting to 5 min = `300`).
659
+ Existing instances without a newly set value automatically use the
660
+ default.
661
+
662
+ ### 0.3.0 (2026-08-20)
663
+
664
+ - **Bugfix:** `wgs84_x`/`wgs84_y` were swapped (latitude/longitude). Per
665
+ the official web frontend (`client_waip.js`), `wgs84_x = latitude,
666
+ wgs84_y = longitude` – contrary to the usual GIS convention.
667
+ `geo.latitude`/`geo.longitude` were therefore swapped for directly
668
+ transmitted coordinates (not the GeoJSON fallback path).
669
+ - Added the missing `io.standby` handler: `status.alarmAktiv` was
670
+ previously never reset when an incident ended
671
+ - Captured new incident fields (previously only contained in the raw
672
+ `json.raw`/`json.einsatz`, now as their own states): `zeitstempel`,
673
+ `einsatznummer`, `objekt`, `objektteil`, `strasse`, `hausnummer`,
674
+ `einsatzdetails`, `besonderheiten`, `permissions`
675
+ - `em_alarmiert` (alerted resources) is now stored in
676
+ `vis.fahrzeugTabelle`, `em_weitere` in `einsatz.emWeitere`
677
+ - Feedback is now collected per incident (`vis.rueckmeldungenTabelle`)
678
+ and aggregated into counters per role/capability
679
+ (`rueckmeldung.counts.*`), mirroring the live badges
680
+ (EK/GF/ZF/VF/AGT/FZF/MA/MED) on the web UI
681
+ - New handlers for `io.error` (→ `debug.lastError`) and `io.version`
682
+ (server-restart detection → session-cookie refresh + forced reconnect)
683
+ - Generalized `reconnectForRotatedSession()` into the more generic
684
+ `forceReconnect(reason)` (now also used on a server version change)
685
+
686
+ ### 0.2.1 (2026-08-20)
687
+
688
+ - Detected session-cookie rotation: if `/session/keepalive` returns a
689
+ different cookie value than before (e.g. because the old session was
690
+ already invalid server-side – a missed keepalive, a server restart
691
+ with an in-memory session store), an existing Socket.IO connection is
692
+ now actively rebuilt with the new session instead of waiting for a
693
+ silent failure
694
+
695
+ ### 0.2.0 (2026-08-20)
696
+
697
+ - Introduced session-cookie management: the adapter fetches and renews
698
+ the WAIP server's `connect.sid` session cookie itself (`GET
699
+ /session/keepalive`, matching `/js/session_keepalive.js` on the site)
700
+ and attaches it to the Socket.IO connection. Fixes alarm delivery
701
+ stopping after about 10 minutes without an active browser session.
702
+ - New state `debug.sessionExpires` and a new config option "Session
703
+ keepalive interval (ms)" (default `300000`)
704
+
705
+ ### 0.1.1 (2026-08-20)
706
+
707
+ - Adopted the favicon from `wachalarm.leitstelle-lausitz.de` as the
708
+ adapter icon (`admin/waip-web.png`), replacing the previous
709
+ placeholder
710
+ - Renamed the GitHub repository from `ioBroker.WAIP-Web` to
711
+ `ioBroker.waip-web` (uppercase letters in the repo name prevented
712
+ installation via `iobroker url` with `Process exited with code 25`);
713
+ updated all URLs in `package.json`/`io-package.json` accordingly
714
+
715
+ ### 0.1.0 (2026-08-20)
716
+
717
+ - Initial version: ported the original "WAIP Instrumented v3.9" ioBroker
718
+ JavaScript-adapter script into a standalone adapter. The URL/monitor
719
+ ID now come from the admin configuration instead of a runtime state.