iobroker.waip-web 0.7.13 → 0.7.15

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.de.md CHANGED
@@ -62,17 +62,25 @@ Schnittstelle.
62
62
  - Registrierungs-Timeout mit Audit-Log (`debug.monitorAudit`)
63
63
  - Normalisierung von Geodaten (wgs84-Felder, `position` oder
64
64
  GeoJSON-`geometry` → Mittelpunkt)
65
- - History der letzten 10 abgeschlossenen Einsätze (`einsatz.history10`)
65
+ - History der letzten 10 abgeschlossenen Einsätze (`einsatz.json.history10`)
66
66
  - Getrennte Handler für Alarm (`io.new_waip`), Rückmeldung (`io.new_rmld`),
67
67
  Routen (`io.routes`), TTS (`io.playtts`) und Standby (`io.standby`)
68
68
  - Automatisches Session-Cookie-Management (siehe unten), damit die
69
69
  Alarm-Zustellung auch ohne offene Browsersitzung dauerhaft weiterläuft
70
70
  - Server-Neustart-Erkennung über `io.version` mit automatischem
71
71
  Session-Refresh + Reconnect
72
- - Vollständige Einsatzdaten inkl. verschachtelter Rückmeldungen/Routen
73
- pro Einsatz (Rückmeldungen und Routen sind 1:n-Beziehungen)
72
+ - Einsatz-, Rückmeldungs-, Routen- und Einsatzmittel-Daten als eigene,
73
+ flache JSON-Arrays unter `einsatz.json.*` ohne Verschachtelung, damit
74
+ VIS-Tabellen-Widgets direkt daran binden können
74
75
  - Aggregierte Rückmeldungs-Zähler pro Rolle/Fähigkeit, analog zu den
75
76
  Live-Badges der Weboberfläche
77
+ - Sauberer Zustand bei jedem Neustart: alle States werden beim Adapter-
78
+ Start aktiv auf ihren leeren Wert zurückgesetzt (`false`/`0`/`null`/
79
+ `[]`), außer `einsatz.json.history10` und `debug.monitorAudit` (beide
80
+ bleiben über Neustarts hinweg erhalten). Startet der Adapter neu,
81
+ während gerade ein Einsatz läuft, werden dessen Live-Felder
82
+ (`einsatz.*`) ebenfalls geleert und füllen sich erst wieder, sobald
83
+ der Server das nächste Event zu diesem Einsatz sendet.
76
84
 
77
85
  ### Warum ein Session-Cookie nötig ist
78
86
 
@@ -110,10 +118,11 @@ Laufzeit abgeleitet (min. 55s, max. 5 Min., analog zu
110
118
 
111
119
  ## States (unter `waip-web.0.*`)
112
120
 
113
- Rückmeldungen und Routen sind pro Einsatz Listen (1:n) und liegen deshalb
114
- als verschachtelte JSON-Arrays in `einsatz.json` bzw. in jedem Eintrag von
115
- `einsatz.history10` ergänzt um schnell bindbare Zähler, damit VIS-Bindings
116
- und Trigger ohne JSON-Parsing auskommen.
121
+ Rückmeldungen und Routen sind pro Einsatz Listen (1:n). Sie liegen als
122
+ **flache** JSON-Arrays unter `einsatz.json.*` (keine verschachtelten
123
+ Objekte/Arrays innerhalb einer Zeile), damit sie direkt an VIS-Tabellen-
124
+ Widgets gebunden werden können – ergänzt um schnell bindbare Zähler,
125
+ damit Bindings und Trigger komplett ohne JSON-Parsing auskommen.
117
126
 
118
127
  ### info
119
128
 
@@ -126,8 +135,6 @@ und Trigger ohne JSON-Parsing auskommen.
126
135
  | State | Typ | Beschreibung |
127
136
  | --- | --- | --- |
128
137
  | `connected` | boolean | Socket.IO-Verbindung technisch aufgebaut |
129
- | `alarmAktiv` | boolean | `true` seit dem letzten `io.new_waip`, `false` seit dem letzten `io.standby` |
130
- | `restzeit` | number (s) | Verbleibende Sekunden bis `einsatz.ablaufzeit`, sekündlich aktualisiert |
131
138
  | `registeredMonitor` | string | Zuletzt beim Server registrierte Monitor-ID |
132
139
  | `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 |
133
140
  | `registrationAccepted` | boolean | `true` sobald das erste Event empfangen wurde, sonst `false` direkt nach Connect oder nach Ablauf des Registrierungs-Timeouts |
@@ -136,13 +143,15 @@ und Trigger ohne JSON-Parsing auskommen.
136
143
  ### einsatz
137
144
 
138
145
  Flache Felder des aktuell laufenden Einsatzes. Werden bei `io.standby`
139
- geleert (`null`/`0`), analog zum offiziellen Frontend – `status.alarmAktiv`
140
- ist damit ein verlässlicher Schalter dafür, ob hier gerade echte Live-Daten
146
+ geleert (`null`/`0`), analog zum offiziellen Frontend – `alarmAktiv` ist
147
+ damit ein verlässlicher Schalter dafür, ob hier gerade echte Live-Daten
141
148
  stehen. Der zuletzt abgeschlossene Einsatz bleibt trotzdem über
142
- `einsatz.history10` abrufbar:
149
+ `einsatz.json.history10` abrufbar:
143
150
 
144
151
  | State | Typ | Beschreibung |
145
152
  | --- | --- | --- |
153
+ | `alarmAktiv` | boolean | `true` seit dem letzten `io.new_waip`, `false` seit dem letzten `io.standby` |
154
+ | `restzeit` | number (s) | Verbleibende Sekunden bis `ablaufzeit`, sekündlich aktualisiert |
146
155
  | `id` | number | Interne Einsatz-ID |
147
156
  | `uuid` | string | Eindeutige Einsatz-UUID (dient auch der Zuordnung von Rückmeldungen) |
148
157
  | `einsatzart` | string | z. B. „Brandeinsatz", „Hilfeleistungseinsatz", „Rettungseinsatz", „Krankentransport" |
@@ -154,14 +163,12 @@ stehen. Der zuletzt abgeschlossene Einsatz bleibt trotzdem über
154
163
  | `einsatzdetails` | string | Zusatzdetails (nur bei Brand-/Hilfeleistungseinsätzen befüllt) |
155
164
  | `besonderheiten` | string | Freitext-Besonderheiten der Leitstelle |
156
165
  | `zeitstempel` | string (date) | Alarmzeit |
157
- | `ablaufzeit` | string (date) | Ende der Standby-Anzeigedauer, Basis für `status.restzeit` |
166
+ | `ablaufzeit` | string (date) | Ende der Standby-Anzeigedauer, Basis für `restzeit` |
158
167
  | `einsatznummer` | string | Einsatznummer (sofern vom Server vergeben) |
159
168
  | `sondersignal` | number | `1` = Sondersignal, sonst kein Sondersignal |
160
169
  | `permissions` | string | Berechtigungsflag der Registrierung (Vollzugriff auf Detailkarte ja/nein); wird immer als String gespeichert (z.B. `"true"`), da der Server hier ein rohes Boolean sendet |
161
170
  | `latitude` / `longitude` | number | Position des Einsatzortes (normalisiert aus wgs84-Feldern oder GeoJSON-Mittelpunkt) |
162
- | `json` | string (JSON) | Vollständiges Einsatz-Objekt: alle Felder oben plus `emAlarmiert[]`, `emWeitere[]`, `routen[]`, `rueckmeldungen[]` |
163
- | `history10` | string (JSON-Array) | Letzte 10 abgeschlossenen Einsätze, gleicher Objekt-Shape wie `json`, geschrieben bei `io.standby` |
164
- | `routenGesamt` | number | Anzahl Routen im aktuellen Einsatz (= `json.routen.length`) |
171
+ | `routenGesamt` | number | Anzahl Routen im aktuellen Einsatz |
165
172
  | `rueckmeldungGesamt` | number | Rückmeldungen gesamt im aktuellen Einsatz |
166
173
  | `rueckmeldungAnzahl.ek` | number | Anzahl Rückmeldungen als Einsatzkraft |
167
174
  | `rueckmeldungAnzahl.gf` | number | Anzahl Rückmeldungen als Gruppenführer |
@@ -172,30 +179,50 @@ stehen. Der zuletzt abgeschlossene Einsatz bleibt trotzdem über
172
179
  | `rueckmeldungAnzahl.ma` | number | Anzahl Rückmeldungen als Maschinist |
173
180
  | `rueckmeldungAnzahl.med` | number | Anzahl Rückmeldungen mit medizinischer Befähigung |
174
181
 
175
- ### tts
182
+ ### einsatz.json
183
+
184
+ Flache JSON-Objekte/Arrays, maximal eine Verschachtelungsebene, gedacht
185
+ zum direkten Binden an VIS-Tabellen-Widgets (verschachtelte Strukturen wie
186
+ ein einfaches `{routen, rueckmeldungen, ...}`-Objekt werden von diesen
187
+ Widgets in der Regel nicht dargestellt). `routen`/`rueckmeldungen`/
188
+ `emAlarmiert`/`emWeitere` enthalten immer nur die Daten des **aktuellen**
189
+ Einsatzes – sie werden bei `io.standby` geleert (`[]`) und sind **nicht**
190
+ Teil der History.
191
+
192
+ | State | Typ | Beschreibung |
193
+ | --- | --- | --- |
194
+ | `current` | string (JSON) | Flache Daten des aktuellen Einsatzes: dieselben 19 Felder wie die einzelnen `einsatz.*`-States oben (`id` … `permissions`, plus `lat`/`lon`), gebündelt als ein Objekt |
195
+ | `history10` | string (JSON-Array) | Letzte 10 abgeschlossenen Einsätze, gleiches Schema wie `current`, ein Array-Eintrag pro Einsatz, geschrieben bei `io.standby` |
196
+ | `routen` | string (JSON-Array) | Routen des aktuellen Einsatzes; jeder Eintrag hat `nr_wache`, `name_wache`, `color`, `lat`, `lon` (`position` zu flachem `lat`/`lon` aufgelöst) |
197
+ | `rueckmeldungen` | string (JSON-Array) | Rückmeldungen des aktuellen Einsatzes, wie vom Server empfangen |
198
+ | `emAlarmiert` | string (JSON-Array) | Alarmierte Einsatzmittel des aktuellen Einsatzes; jeder Eintrag hat `name`, `zeit`, `wache`, `zeit_alarmierung_iso`, `zeit_ausgerueckt_iso` |
199
+ | `emWeitere` | string (JSON-Array) | Weitere Einsatzmittel des aktuellen Einsatzes, gleiches Schema wie `emAlarmiert` |
200
+
201
+ ### einsatz.tts
202
+
203
+ Sprachansage (`io.playtts`) zum aktuell laufenden Einsatz – liegt unter
204
+ `einsatz` statt in einem eigenen Top-Level-Kanal, da sie ohne Einsatzbezug
205
+ keine Bedeutung hat. Keine History: eine TTS-Ansage ist nur im Moment
206
+ relevant, deshalb wird nur die jeweils letzte vorgehalten.
176
207
 
177
208
  | State | Typ | Beschreibung |
178
209
  | --- | --- | --- |
179
- | `last` | string (URL) | URL der zuletzt empfangenen Sprachansage |
210
+ | `last` | string (URL) | Vollständige, absolute URL zur mp3-Datei der letzten Sprachansage. Der Server sendet nur einen (oft relativen) Pfad, der als `audio.src` in einem Browser mit derselben Origin gedacht ist; der Adapter löst diesen gegen die konfigurierte WAIP-Server-URL auf, damit der Link auch außerhalb der WAIP-Web-Seite funktioniert (z.B. in einem VIS-Audio-Widget) |
180
211
  | `lastTimestamp` | string (date) | Zeitpunkt der letzten Ansage |
181
- | `history10` | string (JSON-Array) | Letzte 10 Ansagen als `{zeitstempel, url}` |
182
212
 
183
213
  ### debug
184
214
 
185
215
  | State | Typ | Beschreibung |
186
216
  | --- | --- | --- |
187
217
  | `lastEvent` | string (JSON) | Letztes empfangenes Socket-Event (Name + Zeitstempel), zur Verbindungsdiagnose |
188
- | `normalizedPosition` | string (JSON) | Zuletzt normalisierte Position des Einsatzes |
218
+ | `normalizedPosition` | string (JSON) | Ergebnis der Geodaten-Normalisierung für das letzte `io.new_waip`-Event, als flaches `{lat, lon}`-Objekt (beide `null`, falls keine gültige Position ermittelt werden konnte) |
189
219
  | `rawPayloadShort` | string | Vorschau (500 Zeichen) der rohen, unnormalisierten `io.new_waip`-Nutzlast |
190
220
  | `ignoredCount` | number | Anzahl verworfener Events (Payload nannte explizit eine andere Monitor-ID) |
191
221
  | `monitorAudit` | string (JSON-Array) | Chronologisches Log von Connect-/Registrierungs-/Reconnect-Ereignissen (200 Einträge) |
192
222
  | `sessionExpires` | string (date) | Ablaufzeit des Session-Cookies laut letzter Erneuerung |
193
- | `lastError` | string (JSON) | Letzte vom Server gemeldete Fehlermeldung (`io.error`) |
223
+ | `lastError` | string | Letzte vom Server gemeldete Fehlermeldung (`io.error`); reiner Text, kein JSON, da der Server hier einen bloßen String sendet |
194
224
  | `serverVersion` | string | Zuletzt gemeldete Server-Instanz-ID (`io.version`); Änderung deutet auf Server-Neustart hin |
195
225
 
196
- JSON-interne Schlüssel innerhalb von `einsatz.json` (`emAlarmiert`,
197
- `emWeitere`, `routen`, `rueckmeldungen`) bleiben kleingeschrieben – das sind
198
- Objekteigenschaften im JSON-Wert, keine eigenen ioBroker-States.
199
226
 
200
227
  ---
201
228
 
package/README.md CHANGED
@@ -70,17 +70,25 @@ independent client for its Socket.IO interface.
70
70
  - Registration timeout with an audit log (`debug.monitorAudit`)
71
71
  - Geodata normalization (wgs84 fields, `position`, or GeoJSON `geometry`
72
72
  → centroid)
73
- - History of the last 10 completed incidents (`einsatz.history10`)
73
+ - History of the last 10 completed incidents (`einsatz.json.history10`)
74
74
  - Separate handlers for alarm (`io.new_waip`), feedback (`io.new_rmld`),
75
75
  routes (`io.routes`), TTS (`io.playtts`) and standby (`io.standby`)
76
76
  - Automatic session-cookie management (see below), so alarm delivery
77
77
  keeps working indefinitely without an open browser session
78
78
  - Server-restart detection via `io.version`, with automatic session
79
79
  refresh + reconnect
80
- - Complete incident data including nested feedback/routes per incident
81
- (feedback and routes are 1:n relationships)
80
+ - Incident, feedback, route and alerted-resource data available as
81
+ separate, flat JSON arrays under `einsatz.json.*` – no nesting, so VIS
82
+ table widgets can bind to them directly
82
83
  - Aggregated feedback counters per role/capability, mirroring the live
83
84
  badges on the web UI
85
+ - Clean state on every restart: all states are actively reset to their
86
+ empty value (`false`/`0`/`null`/`[]`) on adapter start, except
87
+ `einsatz.json.history10` and `debug.monitorAudit` (both kept across
88
+ restarts). Note that if the adapter restarts while an incident is
89
+ actively running, its live fields (`einsatz.*`) are cleared too and
90
+ only repopulate once the server sends the next event for that
91
+ incident.
84
92
 
85
93
  ### Why a session cookie is needed
86
94
 
@@ -118,10 +126,11 @@ the site itself).
118
126
 
119
127
  ## States (under `waip-web.0.*`)
120
128
 
121
- Feedback and routes are 1:n lists per incident and are therefore stored
122
- as nested JSON arrays inside `einsatz.json` and in every entry of
123
- `einsatz.history10` complemented by quick-to-bind counters so VIS
124
- bindings and triggers don't need JSON parsing.
129
+ Feedback and routes are 1:n lists per incident. They are stored as
130
+ **flat** JSON arrays under `einsatz.json.*` (no nested objects/arrays
131
+ inside a row) so they can be bound directly to VIS table widgets –
132
+ complemented by quick-to-bind counters so bindings and triggers don't
133
+ need JSON parsing at all.
125
134
 
126
135
  ### info
127
136
 
@@ -134,8 +143,6 @@ bindings and triggers don't need JSON parsing.
134
143
  | State | Type | Description |
135
144
  | --- | --- | --- |
136
145
  | `connected` | boolean | Socket.IO connection technically established |
137
- | `alarmAktiv` | boolean | `true` since the last `io.new_waip`, `false` since the last `io.standby` |
138
- | `restzeit` | number (s) | Remaining seconds until `einsatz.ablaufzeit`, updated every second |
139
146
  | `registeredMonitor` | string | Monitor ID last registered with the server |
140
147
  | `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 |
141
148
  | `registrationAccepted` | boolean | `true` once the first event was received, `false` right after connecting or once the registration timeout elapses |
@@ -144,13 +151,15 @@ bindings and triggers don't need JSON parsing.
144
151
  ### einsatz
145
152
 
146
153
  Flat fields of the currently running incident. Cleared (`null`/`0`) on
147
- `io.standby`, matching the official frontend – `status.alarmAktiv` is
148
- therefore a reliable switch for whether these fields currently hold real
149
- live data. The most recently finished incident remains available via
150
- `einsatz.history10`:
154
+ `io.standby`, matching the official frontend – `alarmAktiv` is therefore
155
+ a reliable switch for whether these fields currently hold real live data.
156
+ The most recently finished incident remains available via
157
+ `einsatz.json.history10`:
151
158
 
152
159
  | State | Type | Description |
153
160
  | --- | --- | --- |
161
+ | `alarmAktiv` | boolean | `true` since the last `io.new_waip`, `false` since the last `io.standby` |
162
+ | `restzeit` | number (s) | Remaining seconds until `ablaufzeit`, updated every second |
154
163
  | `id` | number | Internal incident ID |
155
164
  | `uuid` | string | Unique incident UUID (also used to associate feedback) |
156
165
  | `einsatzart` | string | e.g. "Brandeinsatz" (fire), "Hilfeleistungseinsatz" (technical assistance), "Rettungseinsatz" (rescue/EMS), "Krankentransport" (patient transport) |
@@ -162,14 +171,12 @@ live data. The most recently finished incident remains available via
162
171
  | `einsatzdetails` | string | Extra details (only populated for fire/technical-assistance incidents) |
163
172
  | `besonderheiten` | string | Free-text remarks from the dispatch center |
164
173
  | `zeitstempel` | string (date) | Alarm time |
165
- | `ablaufzeit` | string (date) | End of the standby display duration, basis for `status.restzeit` |
174
+ | `ablaufzeit` | string (date) | End of the standby display duration, basis for `restzeit` |
166
175
  | `einsatznummer` | string | Incident number (if assigned by the server) |
167
176
  | `sondersignal` | number | `1` = special signal (lights & siren), otherwise none |
168
177
  | `permissions` | string | The registration's permission flag (full access to the detail map yes/no); always stringified (e.g. `"true"`), since the server sends it as a raw boolean |
169
178
  | `latitude` / `longitude` | number | Incident location (normalized from wgs84 fields or GeoJSON centroid) |
170
- | `json` | string (JSON) | Complete incident object: all fields above plus `emAlarmiert[]`, `emWeitere[]`, `routen[]`, `rueckmeldungen[]` |
171
- | `history10` | string (JSON array) | Last 10 completed incidents, same object shape as `json`, written on `io.standby` |
172
- | `routenGesamt` | number | Number of routes in the current incident (= `json.routen.length`) |
179
+ | `routenGesamt` | number | Number of routes in the current incident |
173
180
  | `rueckmeldungGesamt` | number | Total feedback count for the current incident |
174
181
  | `rueckmeldungAnzahl.ek` | number | Feedback count as team member ("Einsatzkraft") |
175
182
  | `rueckmeldungAnzahl.gf` | number | Feedback count as crew leader ("Gruppenführer") |
@@ -180,33 +187,85 @@ live data. The most recently finished incident remains available via
180
187
  | `rueckmeldungAnzahl.ma` | number | Feedback count as driver/operator ("Maschinist") |
181
188
  | `rueckmeldungAnzahl.med` | number | Feedback count with a medical qualification |
182
189
 
183
- ### tts
190
+ ### einsatz.json
191
+
192
+ Flat JSON objects/arrays, one level deep at most, meant to be bound
193
+ directly to VIS table widgets (nested structures like a plain
194
+ `{routen, rueckmeldungen, ...}` object generally aren't rendered by
195
+ those widgets). `routen`/`rueckmeldungen`/`emAlarmiert`/`emWeitere` only
196
+ ever hold the *current* incident's data – they are cleared (`[]`) on
197
+ `io.standby` and are **not** part of the history.
198
+
199
+ | State | Type | Description |
200
+ | --- | --- | --- |
201
+ | `current` | string (JSON) | Current incident's flat data: the same 19 fields as the individual `einsatz.*` states above (`id` … `permissions`, plus `lat`/`lon`), bundled as one object |
202
+ | `history10` | string (JSON array) | Last 10 completed incidents, same shape as `current`, one array entry per incident, written on `io.standby` |
203
+ | `routen` | string (JSON array) | Routes of the current incident; each entry has `nr_wache`, `name_wache`, `color`, `lat`, `lon` (`position` resolved to flat `lat`/`lon`) |
204
+ | `rueckmeldungen` | string (JSON array) | Feedback entries of the current incident, as received from the server |
205
+ | `emAlarmiert` | string (JSON array) | Alerted resources of the current incident; each entry has `name`, `zeit`, `wache`, `zeit_alarmierung_iso`, `zeit_ausgerueckt_iso` |
206
+ | `emWeitere` | string (JSON array) | Additional resources of the current incident, same shape as `emAlarmiert` |
207
+
208
+ ### einsatz.tts
209
+
210
+ Voice announcement (`io.playtts`) for the currently running incident –
211
+ lives under `einsatz` rather than its own top-level channel since it has
212
+ no meaning without an incident. No history: a TTS announcement only
213
+ matters in the moment, so only the most recent one is kept.
184
214
 
185
215
  | State | Type | Description |
186
216
  | --- | --- | --- |
187
- | `last` | string (URL) | URL of the most recently received voice announcement |
217
+ | `last` | string (URL) | Full absolute URL of the most recent voice announcement's mp3 file. The server sends only a bare (often relative) path meant to be used as `audio.src` in a browser that shares its origin; the adapter resolves that against the configured WAIP server URL so the link also works outside the WAIP-Web page (e.g. in a VIS audio widget) |
188
218
  | `lastTimestamp` | string (date) | Time of the last announcement |
189
- | `history10` | string (JSON array) | Last 10 announcements as `{zeitstempel, url}` |
190
219
 
191
220
  ### debug
192
221
 
193
222
  | State | Type | Description |
194
223
  | --- | --- | --- |
195
224
  | `lastEvent` | string (JSON) | Last received socket event (name + timestamp), for connection diagnostics |
196
- | `normalizedPosition` | string (JSON) | Last normalized position of the incident |
225
+ | `normalizedPosition` | string (JSON) | Result of the geodata normalization for the last `io.new_waip` event, as a flat `{lat, lon}` object (both `null` if no valid position could be derived) |
197
226
  | `rawPayloadShort` | string | Preview (500 characters) of the raw, unnormalized `io.new_waip` payload |
198
227
  | `ignoredCount` | number | Count of discarded events (payload explicitly named a different monitor ID) |
199
228
  | `monitorAudit` | string (JSON array) | Chronological log of connect/registration/reconnect events (200 entries) |
200
229
  | `sessionExpires` | string (date) | Expiry time of the session cookie as of the last renewal |
201
- | `lastError` | string (JSON) | Last error message reported by the server (`io.error`) |
230
+ | `lastError` | string | Last error message reported by the server (`io.error`); plain text, not JSON, as the server sends this as a bare string |
202
231
  | `serverVersion` | string | Last reported server instance ID (`io.version`); a change suggests a server restart |
203
232
 
204
- JSON-internal keys inside `einsatz.json` (`emAlarmiert`, `emWeitere`,
205
- `routen`, `rueckmeldungen`) stay lowercase – these are object properties
206
- inside the JSON value, not their own ioBroker states.
207
-
208
233
  ## Changelog
209
234
 
235
+ ### 0.7.15 (2026-08-22)
236
+
237
+ - Restructured `einsatz.json` into a channel with flat,
238
+ table-widget-friendly sub-states (`current`/`history10`/`routen`/
239
+ `rueckmeldungen`/`emAlarmiert`/`emWeitere`) - nested JSON wasn't
240
+ rendering in VIS table widgets.
241
+ - Moved `tts.*` under `einsatz.tts.*` (removed the meaningless
242
+ `tts.history10`); `tts.last` now resolves to a full absolute mp3 URL
243
+ instead of the server's often-relative path.
244
+ - Moved `status.alarmAktiv`/`status.restzeit` to
245
+ `einsatz.alarmAktiv`/`einsatz.restzeit`.
246
+ - Flattened `debug.normalizedPosition` and corrected
247
+ `debug.lastError`'s role from `json` to `text`.
248
+ - All states are now actively reset to their empty value on every
249
+ adapter start, except `einsatz.json.history10` and
250
+ `debug.monitorAudit`, which persist across restarts.
251
+
252
+ ### 0.7.14 (2026-08-22)
253
+
254
+ - Fixed **[E1032]**/**[E2004]**: trimmed `common.news` to the 7
255
+ entries allowed by the repository builder and removed the orphaned
256
+ `0.7.10` entry (never published to npm).
257
+ - Fixed **[W0066]**: pinned `@types/node` to `^22.0.0` (was `>=22`,
258
+ resolving to a mismatched `26.x`).
259
+ - Fixed **[W4040]**/**[W4042]**: corrected the `.vscode/settings.json`
260
+ JSON schema URLs for `io-package.json`/`jsonConfig` to the ones
261
+ ioBroker actually expects.
262
+ - Fixed **[S8914]**: replaced the custom Dependabot auto-merge
263
+ workflow with the canonical
264
+ `iobroker-bot-orga/action-automerge-dependabot@v1` action and added
265
+ the matching `.github/auto-merge.yml` (production: patch always,
266
+ minor only for security fixes; development: minor allowed too).
267
+ - No runtime changes.
268
+
210
269
  ### 0.7.13 (2026-08-22)
211
270
 
212
271
  - Addressed all remaining `ioBroker.repositories` checker suggestions:
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "waip-web",
4
- "version": "0.7.13",
4
+ "version": "0.7.15",
5
5
  "news": {
6
+ "0.7.15": {
7
+ "en": "Restructured einsatz.json into a channel with flat, table-widget-friendly sub-states (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) - nested JSON wasn't rendering in VIS table widgets. Moved tts.* under einsatz.tts.* (removed the meaningless tts.history10) and tts.last now resolves to a full absolute mp3 URL instead of the server's often-relative path. Moved status.alarmAktiv/status.restzeit to einsatz.alarmAktiv/einsatz.restzeit. Flattened debug.normalizedPosition and corrected debug.lastError's role from json to text. All states are now actively reset to their empty value on every adapter start, except einsatz.json.history10 and debug.monitorAudit, which persist across restarts.",
8
+ "de": "einsatz.json in einen Channel mit flachen, tabellentauglichen Unter-States umstrukturiert (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) - verschachteltes JSON wurde in VIS-Tabellen-Widgets nicht dargestellt. tts.* nach einsatz.tts.* verschoben (tts.history10 ohne Einsatzbezug entfernt) und tts.last löst jetzt zur vollständigen absoluten mp3-URL auf statt des oft relativen Server-Pfads. status.alarmAktiv/status.restzeit nach einsatz.alarmAktiv/einsatz.restzeit verschoben. debug.normalizedPosition abgeflacht und die Rolle von debug.lastError von json auf text korrigiert. Alle States werden jetzt bei jedem Adapter-Start aktiv auf ihren leeren Wert zurückgesetzt, außer einsatz.json.history10 und debug.monitorAudit, die über Neustarts hinweg erhalten bleiben.",
9
+ "ru": "Einsatz.json реструктурирован в канал с плоскими, удобными для табличных виджетов подсостояниями (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) – вложенный JSON не отображался в виджетах таблиц VIS. tts.* перенесен в einsatz.tts.* (удален бессмысленный tts.history10), и tts.last теперь преобразуется в полный абсолютный URL-адрес mp3 вместо часто используемого относительного пути сервера. Статус.alarmAktiv/status.restzeit перемещен в einsatz.alarmAktiv/einsatz.restzeit. Уплощен файл debug.normalizedPosition и исправлена ​​роль debug.lastError с json на текстовый формат. Все состояния теперь активно сбрасываются в пустое значение при каждом запуске адаптера, за исключением einsatz.json.history10 и debug.monitorAudit, которые сохраняются при перезапуске.",
10
+ "pt": "Einsatz.json reestruturado em um canal com subestados simples e compatíveis com widgets de tabela (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) - JSON aninhado não estava sendo renderizado em widgets de tabela VIS. Movido tts.* para einsatz.tts.* (removido o sem sentido tts.history10) e tts.last agora resolve para um URL mp3 absoluto completo em vez do caminho frequentemente relativo do servidor. Status.alarmAktiv/status.restzeit movido para einsatz.alarmAktiv/einsatz.restzeit. Achatado debug.normalizedPosition e corrigido o papel de debug.lastError de json para texto. Todos os estados agora são redefinidos ativamente para seus valores vazios em cada inicialização do adaptador, exceto einsatz.json.history10 e debug.monitorAudit, que persistem durante as reinicializações.",
11
+ "nl": "Einsatz.json geherstructureerd in een kanaal met platte, tabelwidgetvriendelijke substaten (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) - geneste JSON werd niet weergegeven in VIS-tabelwidgets. Verplaatst tts.* onder einsatz.tts.* (het betekenisloze tts.history10 verwijderd) en tts.last wordt nu omgezet in een volledige absolute mp3-URL in plaats van het vaak relatieve pad van de server. Status.alarmAktiv/status.restzeit verplaatst naar einsatz.alarmAktiv/einsatz.restzeit. Debug.normalizedPosition is afgevlakt en de rol van debug.lastError is gecorrigeerd van json naar tekst. Alle statussen worden nu bij elke start van de adapter actief teruggezet naar hun lege waarde, behalve einsatz.json.history10 en debug.monitorAudit, die bij het opnieuw opstarten blijven bestaan.",
12
+ "fr": "Einsatz.json restructuré en un canal avec des sous-états plats et conviviaux pour les widgets de table (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) - JSON imbriqué n'était pas rendu dans les widgets de table VIS. tts.* déplacé sous einsatz.tts.* (suppression du tts.history10 dénué de sens) et tts.last se résout désormais en une URL mp3 absolue complète au lieu du chemin souvent relatif du serveur. Statut.alarmAktiv/status.restzeit déplacé vers einsatz.alarmAktiv/einsatz.restzeit. Debug.normalizedPosition aplati et le rôle de debug.lastError corrigé de json à text. Tous les états sont désormais activement réinitialisés à leur valeur vide à chaque démarrage de l'adaptateur, à l'exception de einsatz.json.history10 et debug.monitorAudit, qui persistent lors des redémarrages.",
13
+ "it": "Ristrutturato einsatz.json in un canale con sottostati piatti e adatti ai widget di tabella (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere): JSON nidificato non veniva visualizzato nei widget di tabella VIS. Spostato tts.* sotto einsatz.tts.* (rimosso tts.history10 senza significato) e tts.last ora si risolve in un URL mp3 assoluto completo invece del percorso spesso relativo del server. Status.alarmAktiv/status.restzeit spostato in einsatz.alarmAktiv/einsatz.restzeit. Debug.normalizedPosition appiattito e corretto il ruolo di debug.lastError da json a testo. Tutti gli stati vengono ora reimpostati attivamente sul valore vuoto a ogni avvio dell'adattatore, ad eccezione di einsatz.json.history10 e debug.monitorAudit, che persistono dopo i riavvii.",
14
+ "es": "Se reestructuró einsatz.json en un canal con subestados planos y compatibles con widgets de tabla (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere): JSON anidado no se representaba en los widgets de tabla VIS. Se movió tts.* bajo einsatz.tts.* (se eliminó el tts.history10 sin sentido) y tts.last ahora se resuelve en una URL mp3 absoluta y completa en lugar de la ruta, a menudo relativa, del servidor. Se movió status.alarmAktiv/status.restzeit a einsatz.alarmAktiv/einsatz.restzeit. Se aplanó debug.normalizedPosition y se corrigió la función de debug.lastError de json a texto. Todos los estados ahora se restablecen activamente a su valor vacío en cada inicio del adaptador, excepto einsatz.json.history10 y debug.monitorAudit, que persisten durante los reinicios.",
15
+ "pl": "Zrestrukturyzowano plik einsatz.json w kanał z płaskimi, przyjaznymi dla widgetów tabeli stanami podrzędnymi (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) — zagnieżdżony kod JSON nie był renderowany w widżetach tabeli VIS. Przeniesiono tts.* do einsatz.tts.* (usunięto bezsensowne tts.history10) i tts.last są teraz rozpoznawane jako pełny bezwzględny adres URL mp3 zamiast często względnej ścieżki serwera. Przeniesiono status.alarmAktiv/status.restzeit do einsatz.alarmAktiv/einsatz.restzeit. Spłaszczono debug.normalizedPosition i poprawiono rolę debug.lastError z json na tekst. Wszystkie stany są teraz aktywnie resetowane do pustych wartości przy każdym uruchomieniu adaptera, z wyjątkiem einsatz.json.history10 i debug.monitorAudit, które utrzymują się po ponownym uruchomieniu.",
16
+ "uk": "Реструктуризовано einsatz.json у канал із плоскими підстанами, зручними для віджетів таблиць (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) – вкладений JSON не відтворювався у віджетах таблиці VIS. Переміщено tts.* у einsatz.tts.* (вилучено безглуздий tts.history10) і tts.last тепер перетворюється на повну абсолютну URL-адресу mp3 замість відносного шляху сервера. Переміщено status.alarmAktiv/status.restzeit до einsatz.alarmAktiv/einsatz.restzeit. Зведено debug.normalizedPosition і виправлено роль debug.lastError з json на текст. Усі стани тепер активно скидаються до порожніх значень під час кожного запуску адаптера, за винятком einsatz.json.history10 і debug.monitorAudit, які зберігаються під час перезапусків.",
17
+ "zh-cn": "将 einsatz.json 重构为具有扁平、表格小部件友好的子状态的通道 (current/history10/routen/rueckmeldungen/emAlarmiert/emWeitere) - 嵌套 JSON 未在 VIS 表格小部件中呈现。将 tts.* 移至 einsatz.tts.* 下(删除了无意义的 tts.history10),tts.last 现在解析为完整的绝对 mp3 URL,而不是服务器的通常相对路径。将 status.alarmAktiv/status.restzeit 移至 einsatz.alarmAktiv/einsatz.restzeit。扁平化 debug.normalizedPosition 并将 debug.lastError 的角色从 json 更正为文本。现在,所有状态在每次适配器启动时都会主动重置为空值,但 einsatz.json.history10 和 debug.monitorAudit 除外,它们在重新启动后仍然存在。"
18
+ },
19
+ "0.7.14": {
20
+ "en": "Fixed [E1032]/[E2004]: trimmed common.news to the 7 entries allowed by the repository builder and removed the orphaned 0.7.10 entry (never published to npm). Fixed [W0066]: pinned @types/node to ^22.0.0 (was >=22, resolving to a mismatched 26.x). Fixed [W4040]/[W4042]: corrected the .vscode/settings.json JSON schema URLs for io-package.json/jsonConfig to the ones ioBroker actually expects. Fixed [S8914]: replaced the custom Dependabot automerge workflow with the canonical iobroker-bot-orga/action-automerge-dependabot@v1 action and added the matching .github/auto-merge.yml (production: patch always, minor only for security fixes; development: minor allowed too). No runtime changes.",
21
+ "de": "[E1032]/[E2004] behoben: common.news auf die vom Repository-Builder erlaubten 7 Einträge gekürzt und den verwaisten 0.7.10-Eintrag entfernt (wurde nie auf npm veröffentlicht). [W0066] behoben: @types/node auf ^22.0.0 gepinnt (vorher >=22, was auf ein nicht passendes 26.x auflöste). [W4040]/[W4042] behoben: die JSON-Schema-URLs in .vscode/settings.json für io-package.json/jsonConfig auf die von ioBroker tatsächlich erwarteten korrigiert. [S8914] behoben: den eigenen Dependabot-Automerge-Workflow durch die kanonische iobroker-bot-orga/action-automerge-dependabot@v1-Action ersetzt und die passende .github/auto-merge.yml ergänzt (Production: Patch immer, Minor nur bei Security-Fixes; Development: auch normales Minor erlaubt). Keine Laufzeit-Änderungen.",
22
+ "ru": "Исправлено [E1032]/[E2004]: файл common.news обрезан до семи записей, разрешенных сборщиком репозитория, и удалена потерянная запись 0.7.10 (никогда не публикуемая в npm). Исправлено [W0066]: @types/node закреплен за ^22.0.0 (было >=22, что приводило к несоответствию 26.x). Исправлено [W4040]/[W4042]: исправлены URL-адреса схемы JSON .vscode/settings.json для io-package.json/jsonConfig на те, которые фактически ожидает ioBroker. Исправлено [S8914]: заменен пользовательский рабочий процесс автоматического слияния Dependabot на каноническое действие iobroker-bot-orga/action-automerge-dependentabot@v1 и добавлен соответствующий .github/auto-merge.yml (производство: исправление всегда, второстепенное только для исправлений безопасности; разработка: второстепенное тоже разрешено). Никаких изменений во время выполнения.",
23
+ "pt": "Corrigido [E1032]/[E2004]: common.news foi cortado para as 7 entradas permitidas pelo construtor do repositório e removido a entrada 0.7.10 órfã (nunca publicada no npm). Corrigido [W0066]: fixou @types/node em ^22.0.0 (era >=22, resolvendo para um 26.x incompatível). Corrigido [W4040]/[W4042]: corrigidos os URLs do esquema JSON .vscode/settings.json para io-package.json/jsonConfig para aqueles que o ioBroker realmente espera. Corrigido [S8914]: substituiu o fluxo de trabalho de mesclagem automática personalizado do Dependabot pela ação canônica iobroker-bot-orga/action-automerge-dependabot@v1 e adicionou o .github/auto-merge.yml correspondente (produção: patch sempre, secundário apenas para correções de segurança; desenvolvimento: secundário permitido também). Nenhuma alteração no tempo de execução.",
24
+ "nl": "[E1032]/[E2004] opgelost: common.news is teruggebracht tot de 7 items die zijn toegestaan ​​door de repositorybouwer en de verweesde 0.7.10-item is verwijderd (nooit gepubliceerd naar npm). Opgelost [W0066]: @types/node vastgezet op ^22.0.0 (was >=22, opgelost naar een niet-overeenkomende 26.x). [W4040]/[W4042] opgelost: de .vscode/settings.json JSON-schema-URL's voor io-package.json/jsonConfig zijn gecorrigeerd naar de URL's die ioBroker daadwerkelijk verwacht. Opgelost [S8914]: de aangepaste Dependabot automerge-workflow vervangen door de canonieke iobroker-bot-orga/action-automerge-dependabot@v1 actie en de overeenkomende .github/auto-merge.yml toegevoegd (productie: patch altijd, minor alleen voor beveiligingsoplossingen; ontwikkeling: minor ook toegestaan). Geen runtime-wijzigingen.",
25
+ "fr": "Correction de [E1032]/[E2004] : suppression de common.news aux 7 entrées autorisées par le constructeur de référentiel et suppression de l'entrée 0.7.10 orpheline (jamais publiée sur npm). Corrigé [W0066] : @types/node épinglé à ^22.0.0 (était >=22, résolvant un 26.x incompatible). Correction [W4040]/[W4042] : correction des URL de schéma JSON .vscode/settings.json pour io-package.json/jsonConfig par celles attendues réellement par ioBroker. Corrigé [S8914] : remplacement du flux de travail de fusion automatique Dependabot personnalisé par l'action canonique iobroker-bot-orga/action-automerge-dependabot@v1 et ajout du .github/auto-merge.yml correspondant (production : correctif toujours, mineur uniquement pour les correctifs de sécurité ; développement : mineur également autorisé). Aucun changement d'exécution.",
26
+ "it": "Risolto il problema con [E1032]/[E2004]: ridotto common.news alle 7 voci consentite dal costruttore del repository e rimossa la voce 0.7.10 orfana (mai pubblicata su npm). Risolto [W0066]: @types/node bloccato su ^22.0.0 (era >=22, si risolveva in un 26.x non corrispondente). Risolto il problema [W4040]/[W4042]: corretti gli URL dello schema JSON .vscode/settings.json per io-package.json/jsonConfig in quelli che ioBroker si aspetta effettivamente. Risolto [S8914]: sostituito il flusso di lavoro personalizzato Dependabot automerge con l'azione canonica iobroker-bot-orga/action-automerge-dependabot@v1 e aggiunto il corrispondente .github/auto-merge.yml (produzione: patch sempre, minore solo per correzioni di sicurezza; sviluppo: consentito anche minore). Nessuna modifica all'esecuzione.",
27
+ "es": "Se corrigió [E1032]/[E2004]: se recortó common.news a las 7 entradas permitidas por el creador del repositorio y se eliminó la entrada huérfana 0.7.10 (nunca publicada en npm). Se corrigió [W0066]: @types/node fijado en ^22.0.0 (era >=22, resolviendo en un 26.x que no coincide). Se corrigió [W4040]/[W4042]: se corrigieron las URL del esquema JSON .vscode/settings.json para io-package.json/jsonConfig a las que ioBroker realmente espera. Corregido [S8914]: reemplazó el flujo de trabajo de fusión automática de Dependabot personalizado con la acción canónica iobroker-bot-orga/action-automerge-dependabot@v1 y agregó el .github/auto-merge.yml correspondiente (producción: parche siempre, menor solo para correcciones de seguridad; desarrollo: menor permitido también). Sin cambios en el tiempo de ejecución.",
28
+ "pl": "Naprawiono [E1032]/[E2004]: przycięto plik common.news do 7 wpisów dozwolonych przez kreatora repozytorium i usunięto osierocony wpis 0.7.10 (nigdy nie opublikowany w npm). Naprawiono [W0066]: przypięto @types/node do ^22.0.0 (było >=22, co spowodowało niedopasowanie 26.x). Naprawiono [W4040]/[W4042]: poprawiono adresy URL schematu JSON .vscode/settings.json dla io-package.json/jsonConfig na te, których faktycznie oczekuje ioBroker. Naprawiono [S8914]: zastąpiono niestandardowy przepływ pracy automerge zależnego od kanonicznej akcji iobroker-bot-orga/action-automerge-dependentabot@v1 i dodano pasujący plik .github/auto-merge.yml (produkcja: zawsze poprawki, drobne tylko dla poprawek bezpieczeństwa; rozwój: drobne też dozwolone). Brak zmian w czasie wykonywania.",
29
+ "uk": "Виправлено [E1032]/[E2004]: обрізано common.news до 7 записів, дозволених конструктором сховища, і видалено загублений запис 0.7.10 (ніколи не публікувався в npm). Виправлено [W0066]: закріплено @types/node до ^22.0.0 (було >=22, вирішувалося до невідповідного 26.x). Виправлено [W4040]/[W4042]: виправлено URL-адреси схеми JSON .vscode/settings.json для io-package.json/jsonConfig на ті, які насправді очікує ioBroker. Виправлено [S8914]: замінено настроюваний робочий процес автоматичного об’єднання Dependabot канонічною дією iobroker-bot-orga/action-automerge-dependabot@v1 і додано відповідний .github/auto-merge.yml (виробництво: виправлення завжди, незначні лише для виправлень безпеки; розробка: також дозволено незначні). Без змін часу виконання.",
30
+ "zh-cn": "修复了 [E1032]/[E2004]:将 common.news 修剪为存储库构建器允许的 7 个条目,并删除了孤立的 0.7.10 条目(从未发布到 npm)。已修复 [W0066]:将 @types/node 固定到 ^22.0.0(>=22,解析为不匹配的 26.x)。修复了 [W4040]/[W4042]:将 io-package.json/jsonConfig 的 .vscode/settings.json JSON 架构 URL 更正为 ioBroker 实际期望的 URL。已修复 [S8914]:用规范的 iobroker-bot-orga/action-automerge-dependabot@v1 操作替换了自定义 Dependabot 自动合并工作流程,并添加了匹配的 .github/auto-merge.yml (生产:始终打补丁,仅针对安全修复进行次要修复;开发:也允许进行次要修复)。没有运行时变化。"
31
+ },
6
32
  "0.7.13": {
7
33
  "en": "Addressed all remaining ioBroker.repositories checker suggestions: added devDependencies @types/node and @tsconfig/node22 plus a tsconfig.json for editor tooling (S0065/S0085/S0087), added .vscode/settings.json with JSON schemas for io-package.json/admin/jsonConfig.json (S4036), added the release-script-plugin-manual-review plugin (S5026), and added a Dependabot auto-merge workflow for patch/minor updates - major version bumps still require manual review (S8913). No runtime changes.",
8
34
  "de": "Alle verbleibenden Suggestions des ioBroker.repositories-Checkers umgesetzt: devDependencies @types/node und @tsconfig/node22 sowie eine tsconfig.json für das Editor-Tooling ergänzt (S0065/S0085/S0087), .vscode/settings.json mit JSON-Schemas für io-package.json/admin/jsonConfig.json ergänzt (S4036), das Plugin release-script-plugin-manual-review ergänzt (S5026), und einen Dependabot-Automerge-Workflow für Patch-/Minor-Updates ergänzt - Major-Updates erfordern weiterhin manuellen Review (S8913). Keine Laufzeit-Änderungen.",
@@ -42,19 +68,6 @@
42
68
  "uk": "Виправлено [E3009] (57 знахідок): додано відсутні об’єкти каналу/папки (status, einsatz, einsatz.rueckmeldungAnzahl, debug, tts), які потрібні ioBroker для кожного сегмента шляху стану. Виправлено [E3005]/[E1009]: замінено непідтримуваний common.type «mixed» на einsatz.permissions (тепер рядок) і status.registrationAccepted (тепер розділений на два логічних значення: registrationAccepted і нове registrationPending) — усе це виявлено під час перевірки структури об’єкта ioBroker.repositories.",
43
69
  "zh-cn": "修复了 [E3009](57 个结果):添加了 ioBroker 为每个状态路径段所需的缺失通道/文件夹对象(状态、einsatz、einsatz.rueckmeldungAnzahl、调试、tts)。修复了 [E3005]/[E1009]:替换了 einsatz.permissions(现在为字符串)和 status.registrationAccepted(现在分为两个布尔值:registrationAccepted 和新的 RegistrationPending)上不支持的 common.type 'mixed' - 所有这些都由 ioBroker.repositories 对象结构检查找到。"
44
70
  },
45
- "0.7.10": {
46
- "en": "Fixed [S9508]: excluded CHANGELOG_OLD.md from the npm package (removed from package.json's files allowlist) - ioBroker shows the README via a GitHub link, not from the installed npm package, so the file remains fully readable on GitHub without needing to ship inside the tarball.",
47
- "de": "[S9508] behoben: CHANGELOG_OLD.md aus dem npm-Paket ausgeschlossen (aus der files-Liste in package.json entfernt) - ioBroker zeigt das README über einen GitHub-Link an, nicht aus dem installierten npm-Paket, die Datei bleibt also auf GitHub weiterhin vollständig lesbar, ohne im Tarball enthalten sein zu müssen.",
48
- "ru": "Исправлено [S9508]: CHANGELOG_OLD.md исключён из npm-пакета (удалён из списка files в package.json) - ioBroker показывает README по ссылке на GitHub, а не из установленного npm-пакета, поэтому файл остаётся полностью читаемым на GitHub без необходимости включать его в архив.",
49
- "pt": "Corrigido [S9508]: CHANGELOG_OLD.md excluído do pacote npm (removido da lista files em package.json) - o ioBroker exibe o README via link do GitHub, não a partir do pacote npm instalado, então o arquivo permanece totalmente legível no GitHub sem precisar estar no tarball.",
50
- "nl": "[S9508] opgelost: CHANGELOG_OLD.md uitgesloten van het npm-pakket (verwijderd uit de files-lijst in package.json) - ioBroker toont het README via een GitHub-link, niet vanuit het geïnstalleerde npm-pakket, dus het bestand blijft volledig leesbaar op GitHub zonder in de tarball te hoeven zitten.",
51
- "fr": "Correction de [S9508] : exclusion de CHANGELOG_OLD.md du paquet npm (retiré de la liste files dans package.json) - ioBroker affiche le README via un lien GitHub, pas depuis le paquet npm installé, le fichier reste donc entièrement lisible sur GitHub sans devoir être inclus dans l'archive.",
52
- "it": "Risolto [S9508]: escluso CHANGELOG_OLD.md dal pacchetto npm (rimosso dall'elenco files in package.json) - ioBroker mostra il README tramite un link GitHub, non dal pacchetto npm installato, quindi il file resta pienamente leggibile su GitHub senza dover essere incluso nel tarball.",
53
- "es": "Se corrigió [S9508]: se excluyó CHANGELOG_OLD.md del paquete npm (eliminado de la lista files en package.json) - ioBroker muestra el README mediante un enlace de GitHub, no desde el paquete npm instalado, por lo que el archivo sigue siendo totalmente legible en GitHub sin necesidad de incluirlo en el tarball.",
54
- "pl": "Naprawiono [S9508]: wykluczono CHANGELOG_OLD.md z pakietu npm (usunięto z listy files w package.json) - ioBroker wyświetla README przez link do GitHub, a nie z zainstalowanego pakietu npm, więc plik pozostaje w pełni czytelny na GitHub bez konieczności umieszczania go w tarballu.",
55
- "uk": "Виправлено [S9508]: виключено CHANGELOG_OLD.md з npm-пакета (видалено зі списку files у package.json) - ioBroker показує README через посилання на GitHub, а не з встановленого npm-пакета, тож файл залишається повністю доступним для читання на GitHub без потреби включати його до архіву.",
56
- "zh-cn": "修复了 [S9508]:已将 CHANGELOG_OLD.md 从 npm 包中排除(从 package.json 的 files 列表中移除)——ioBroker 通过 GitHub 链接显示 README,而不是从已安装的 npm 包中读取,因此该文件在 GitHub 上仍然完全可读,无需包含在发布包中。"
57
- },
58
71
  "0.7.9": {
59
72
  "en": "Fixed [E5025]/[E5036]: installed the missing @alcalzone/release-script-plugin-license devDependency required for the 'license' plugin referenced in .releaseconfig.json.",
60
73
  "de": "[E5025]/[E5036] behoben: fehlende Dev-Dependency @alcalzone/release-script-plugin-license nachinstalliert, die für das in .releaseconfig.json referenzierte 'license'-Plugin benötigt wird.",
@@ -80,58 +93,6 @@
80
93
  "pl": "Naprawiono [E5018]: dodano brakujący plik .releaseconfig.json (plugins: iobroker, license), wymagany teraz, ponieważ @alcalzone/release-script jest zależnością deweloperską. Wykryte podczas kontroli zgłoszenia 'ADD TO LATEST' w ioBroker.repositories.",
81
94
  "uk": "Виправлено [E5018]: додано відсутній файл .releaseconfig.json (plugins: iobroker, license), який тепер потрібен, оскільки @alcalzone/release-script є dev-залежністю. Виявлено під час перевірки подання 'ADD TO LATEST' у ioBroker.repositories.",
82
95
  "zh-cn": "修复了 [E5018]:添加了缺失的 .releaseconfig.json 文件(plugins: iobroker, license),由于 @alcalzone/release-script 已作为开发依赖,现在需要此文件。此问题是在 ioBroker.repositories 的 'ADD TO LATEST' 提交检查中发现的。"
83
- },
84
- "0.7.7": {
85
- "en": "Fixed [E254]: removed the orphaned 0.7.5 news entry - like 0.7.0 and 0.7.3 before it, that version was never actually tagged/published to npm (0.7.4 was followed directly by 0.7.6).",
86
- "de": "[E254] behoben: verwaisten 0.7.5-News-Eintrag entfernt - wie zuvor schon 0.7.0 und 0.7.3 wurde diese Version nie tatsächlich getaggt/bei npm veröffentlicht (auf 0.7.4 folgte direkt 0.7.6).",
87
- "ru": "Исправлено [E254]: удалена осиротевшая запись новостей 0.7.5 - как и ранее 0.7.0 и 0.7.3, эта версия так и не была помечена тегом/опубликована на npm (после 0.7.4 сразу последовала 0.7.6).",
88
- "pt": "Corrigido [E254]: removida a entrada de notícias órfã 0.7.5 - assim como 0.7.0 e 0.7.3 antes, essa versão nunca foi de fato marcada com tag/publicada no npm (0.7.4 foi seguida diretamente por 0.7.6).",
89
- "nl": "[E254] opgelost: verweesd 0.7.5-nieuwsitem verwijderd - net als eerder 0.7.0 en 0.7.3 is die versie nooit daadwerkelijk getagd/gepubliceerd op npm (op 0.7.4 volgde direct 0.7.6).",
90
- "fr": "Correction de [E254] : suppression de l'entrée de nouvelles orpheline 0.7.5 - comme 0.7.0 et 0.7.3 auparavant, cette version n'a jamais été réellement taguée/publiée sur npm (0.7.4 a été directement suivie de 0.7.6).",
91
- "it": "Risolto [E254]: rimossa la voce di news orfana 0.7.5 - come già 0.7.0 e 0.7.3 in precedenza, quella versione non è mai stata effettivamente taggata/pubblicata su npm (a 0.7.4 è seguita direttamente 0.7.6).",
92
- "es": "Se corrigió [E254]: se eliminó la entrada de noticias huérfana 0.7.5 - al igual que 0.7.0 y 0.7.3 antes, esa versión nunca se etiquetó/publicó realmente en npm (a 0.7.4 le siguió directamente 0.7.6).",
93
- "pl": "Naprawiono [E254]: usunięto osierocony wpis news 0.7.5 - podobnie jak wcześniej 0.7.0 i 0.7.3, ta wersja nigdy nie została faktycznie otagowana/opublikowana w npm (po 0.7.4 nastąpiło bezpośrednio 0.7.6).",
94
- "uk": "Виправлено [E254]: видалено осиротілий запис новин 0.7.5 - як і раніше 0.7.0 та 0.7.3, цю версію так і не було позначено тегом/опубліковано на npm (після 0.7.4 одразу йшла 0.7.6).",
95
- "zh-cn": "修复了 [E254]:移除了孤立的 0.7.5 新闻条目——与之前的 0.7.0 和 0.7.3 一样,该版本从未真正打过标签/发布到 npm(0.7.4 之后直接跳到了 0.7.6)。"
96
- },
97
- "0.7.6": {
98
- "en": "Fixed [W6019] and [W0062]: added @alcalzone/release-script and @alcalzone/release-script-plugin-iobroker as dev dependencies (with an npm run release script), and split the README changelog - the 5 most recent entries stay in README.md, everything older moved to a new CHANGELOG_OLD.md. Our existing manual version-bump/tag workflow is unchanged; the release-script tooling is available but not actively used for releasing yet.",
99
- "de": "[W6019] und [W0062] behoben: @alcalzone/release-script sowie @alcalzone/release-script-plugin-iobroker als Dev-Dependencies ergänzt (mit npm run release-Skript), README-Changelog aufgeteilt - die 5 neuesten Einträge bleiben in README.md, alles Ältere ist in eine neue CHANGELOG_OLD.md ausgelagert. Unser bisheriger manueller Versions-/Tag-Workflow bleibt unverändert; das release-script-Tooling steht bereit, wird aber noch nicht aktiv zum Veröffentlichen genutzt.",
100
- "ru": "Исправлены [W6019] и [W0062]: добавлены dev-зависимости @alcalzone/release-script и @alcalzone/release-script-plugin-iobroker (со скриптом npm run release), а changelog в README разделён — 5 последних записей остаются в README.md, всё более старое перенесено в новый файл CHANGELOG_OLD.md. Наш существующий ручной процесс повышения версии/тегирования не изменился; инструмент release-script доступен, но пока активно не используется для релизов.",
101
- "pt": "Corrigidos [W6019] e [W0062]: adicionadas as dependências de desenvolvimento @alcalzone/release-script e @alcalzone/release-script-plugin-iobroker (com um script npm run release), e o changelog do README foi dividido - as 5 entradas mais recentes permanecem no README.md, tudo o que é mais antigo foi movido para um novo CHANGELOG_OLD.md. Nosso fluxo manual existente de aumento de versão/tag permanece inalterado; a ferramenta release-script está disponível, mas ainda não é usada ativamente para lançamentos.",
102
- "nl": "[W6019] en [W0062] opgelost: @alcalzone/release-script en @alcalzone/release-script-plugin-iobroker toegevoegd als dev-dependencies (met een npm run release-script), en de README-changelog opgesplitst - de 5 meest recente items blijven in README.md, al het oudere is verplaatst naar een nieuwe CHANGELOG_OLD.md. Onze bestaande handmatige versie-/tag-workflow blijft ongewijzigd; de release-script-tooling is beschikbaar maar wordt nog niet actief gebruikt om te releasen.",
103
- "fr": "Correction de [W6019] et [W0062] : ajout de @alcalzone/release-script et @alcalzone/release-script-plugin-iobroker comme dépendances de développement (avec un script npm run release), et division du changelog du README - les 5 entrées les plus récentes restent dans README.md, tout ce qui est plus ancien a été déplacé vers un nouveau CHANGELOG_OLD.md. Notre flux de travail manuel existant de bump de version/tag reste inchangé ; l'outillage release-script est disponible mais pas encore activement utilisé pour publier.",
104
- "it": "Risolti [W6019] e [W0062]: aggiunte le dev-dependency @alcalzone/release-script e @alcalzone/release-script-plugin-iobroker (con uno script npm run release), e il changelog del README è stato suddiviso - le 5 voci più recenti restano in README.md, tutto ciò che è più vecchio è stato spostato in un nuovo CHANGELOG_OLD.md. Il nostro flusso di lavoro manuale esistente per bump di versione/tag resta invariato; lo strumento release-script è disponibile ma non ancora usato attivamente per i rilasci.",
105
- "es": "Se corrigieron [W6019] y [W0062]: se añadieron @alcalzone/release-script y @alcalzone/release-script-plugin-iobroker como dependencias de desarrollo (con un script npm run release), y se dividió el changelog del README - las 5 entradas más recientes permanecen en README.md, todo lo más antiguo se movió a un nuevo CHANGELOG_OLD.md. Nuestro flujo de trabajo manual existente de incremento de versión/etiquetado no cambia; la herramienta release-script está disponible pero aún no se usa activamente para publicar.",
106
- "pl": "Naprawiono [W6019] i [W0062]: dodano @alcalzone/release-script oraz @alcalzone/release-script-plugin-iobroker jako zależności deweloperskie (ze skryptem npm run release), a changelog w README podzielono - 5 najnowszych wpisów pozostaje w README.md, wszystko starsze przeniesiono do nowego pliku CHANGELOG_OLD.md. Nasz dotychczasowy ręczny proces podnoszenia wersji/tagowania pozostaje bez zmian; narzędzie release-script jest dostępne, ale nie jest jeszcze aktywnie używane do wydawania wersji.",
107
- "uk": "Виправлено [W6019] і [W0062]: додано @alcalzone/release-script та @alcalzone/release-script-plugin-iobroker як dev-залежності (зі скриптом npm run release), а changelog у README розділено - 5 найновіших записів залишаються в README.md, усе старіше перенесено до нового файлу CHANGELOG_OLD.md. Наш наявний ручний процес підвищення версії/тегування не змінився; інструмент release-script доступний, але поки що активно не використовується для релізів.",
108
- "zh-cn": "修复了 [W6019] 和 [W0062]:添加了 @alcalzone/release-script 和 @alcalzone/release-script-plugin-iobroker 作为开发依赖(附带 npm run release 脚本),并拆分了 README 的更新日志——最近的 5 个条目保留在 README.md 中,更早的全部移至新的 CHANGELOG_OLD.md。我们现有的手动版本提升/打标签流程保持不变;release-script 工具已就绪,但目前尚未积极用于发布。"
109
- },
110
- "0.7.4": {
111
- "en": "Bumped the ioBroker/testing-action-check GitHub Action used by the check-and-lint CI job from v1 to v2 (Dependabot PR #10) - purely additive (adds an optional test-command: 'false' flag), no behavior change for us.",
112
- "de": "GitHub-Action ioBroker/testing-action-check im check-and-lint-CI-Job von v1 auf v2 angehoben (Dependabot-PR #10) - rein additiv (fügt eine optionale test-command: 'false'-Option hinzu), kein Verhaltensunterschied für uns.",
113
- "ru": "Обновлён GitHub Action ioBroker/testing-action-check, используемый в задаче CI check-and-lint, с v1 до v2 (Dependabot PR #10) — чисто аддитивное изменение (добавляет опциональный флаг test-command: 'false'), поведение для нас не меняется.",
114
- "pt": "Atualizada a GitHub Action ioBroker/testing-action-check usada pelo job de CI check-and-lint de v1 para v2 (Dependabot PR #10) - puramente aditiva (adiciona uma flag opcional test-command: 'false'), sem mudança de comportamento para nós.",
115
- "nl": "GitHub Action ioBroker/testing-action-check, gebruikt door de CI-job check-and-lint, bijgewerkt van v1 naar v2 (Dependabot PR #10) - puur additief (voegt een optionele test-command: 'false'-vlag toe), geen gedragsverandering voor ons.",
116
- "fr": "Mise à jour de l'action GitHub ioBroker/testing-action-check utilisée par le job CI check-and-lint, de v1 à v2 (PR Dependabot #10) - purement additive (ajoute un indicateur optionnel test-command: 'false'), aucun changement de comportement pour nous.",
117
- "it": "Aggiornata l'azione GitHub ioBroker/testing-action-check usata dal job CI check-and-lint da v1 a v2 (PR Dependabot #10) - puramente additiva (aggiunge un flag opzionale test-command: 'false'), nessun cambiamento di comportamento per noi.",
118
- "es": "Actualizada la GitHub Action ioBroker/testing-action-check usada por el job de CI check-and-lint de v1 a v2 (PR de Dependabot #10) - puramente aditiva (añade una opción opcional test-command: 'false'), sin cambio de comportamiento para nosotros.",
119
- "pl": "Zaktualizowano akcję GitHub ioBroker/testing-action-check używaną przez zadanie CI check-and-lint z v1 do v2 (PR Dependabota #10) - czysto addytywnie (dodaje opcjonalną flagę test-command: 'false'), bez zmiany zachowania dla nas.",
120
- "uk": "Оновлено GitHub Action ioBroker/testing-action-check, яку використовує завдання CI check-and-lint, з v1 до v2 (Dependabot PR #10) - суто адитивно (додає опціональний прапорець test-command: 'false'), без зміни поведінки для нас.",
121
- "zh-cn": "将 check-and-lint CI 任务使用的 ioBroker/testing-action-check GitHub Action 从 v1 升级到 v2(Dependabot PR #10)——纯增量更新(新增可选的 test-command: 'false' 标志),对我们没有行为影响。"
122
- },
123
- "0.7.2": {
124
- "en": "Fixed all findings from the official ioBroker Check and Service Bot: raised Node.js/dependency minimums (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), split the README into English-only (README.md) and German (README.de.md) since the checker requires English-only READMEs, moved License to the very last section, added the required i18n/size attributes to jsonConfig.json, switched to node: prefixes for built-in requires, removed the redundant .npmignore, and added .commitinfo to .gitignore. Also removed the orphaned 0.7.0 news entry (never actually published to npm).",
125
- "de": "Alle Findings des offiziellen ioBroker Check and Service Bot behoben: Mindestversionen angehoben (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README aufgeteilt in rein englisches README.md und deutsches README.de.md (der Checker verlangt rein englische READMEs), Lizenz-Abschnitt ans Dateiende verschoben, fehlende i18n-/Größenattribute in jsonConfig.json ergänzt, auf node:-Präfixe bei Built-in-Requires umgestellt, überflüssige .npmignore entfernt und .commitinfo zur .gitignore hinzugefügt. Außerdem den verwaisten 0.7.0-News-Eintrag entfernt (wurde nie tatsächlich bei npm veröffentlicht).",
126
- "ru": "Устранены все замечания официального ioBroker Check and Service Bot: подняты минимальные версии (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README разделён на англоязычный README.md и немецкий README.de.md (чекер требует README только на английском), раздел License перемещён в самый конец, добавлены недостающие атрибуты i18n/размеров в jsonConfig.json, встроенные require переведены на префикс node:, удалён избыточный .npmignore, добавлен .commitinfo в .gitignore. Также удалена осиротевшая запись новостей 0.7.0 (никогда не публиковалась на npm).",
127
- "pt": "Corrigidos todos os apontamentos do ioBroker Check and Service Bot oficial: versões mínimas elevadas (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README dividido em README.md somente em inglês e README.de.md em alemão (o verificador exige READMEs somente em inglês), seção License movida para o final, atributos i18n/tamanho ausentes adicionados ao jsonConfig.json, requires nativos alterados para o prefixo node:, .npmignore redundante removido e .commitinfo adicionado ao .gitignore. Também removida a entrada de notícias órfã 0.7.0 (nunca publicada no npm).",
128
- "nl": "Alle bevindingen van de officiële ioBroker Check and Service Bot opgelost: minimumversies verhoogd (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README opgesplitst in Engelstalige README.md en Duitse README.de.md (de checker vereist Engelstalige READMEs), License-sectie naar het einde verplaatst, ontbrekende i18n-/formaatattributen toegevoegd aan jsonConfig.json, ingebouwde requires overgezet naar het node:-voorvoegsel, overbodige .npmignore verwijderd en .commitinfo toegevoegd aan .gitignore. Ook het verweesde 0.7.0-nieuwsitem verwijderd (nooit echt op npm gepubliceerd).",
129
- "fr": "Correction de toutes les remarques du ioBroker Check and Service Bot officiel : versions minimales relevées (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README divisé en README.md uniquement en anglais et README.de.md en allemand (le vérificateur exige des README uniquement en anglais), section License déplacée tout à la fin, attributs i18n/taille manquants ajoutés à jsonConfig.json, passage au préfixe node: pour les requires natifs, suppression du .npmignore redondant et ajout de .commitinfo au .gitignore. Suppression également de l'entrée de nouvelles orpheline 0.7.0 (jamais réellement publiée sur npm).",
130
- "it": "Risolte tutte le segnalazioni del bot ufficiale ioBroker Check and Service: versioni minime innalzate (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README diviso in README.md solo in inglese e README.de.md in tedesco (il checker richiede README solo in inglese), sezione License spostata alla fine, attributi i18n/dimensione mancanti aggiunti a jsonConfig.json, passaggio al prefisso node: per i require nativi, rimosso il .npmignore ridondante e aggiunto .commitinfo al .gitignore. Rimossa anche la voce di news orfana 0.7.0 (mai effettivamente pubblicata su npm).",
131
- "es": "Se corrigieron todos los hallazgos del ioBroker Check and Service Bot oficial: se elevaron las versiones mínimas (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), el README se dividió en README.md solo en inglés y README.de.md en alemán (el verificador exige READMEs solo en inglés), la sección License se movió al final, se añadieron los atributos i18n/tamaño faltantes en jsonConfig.json, se cambió a prefijos node: para los requires nativos, se eliminó el .npmignore redundante y se añadió .commitinfo al .gitignore. También se eliminó la entrada de noticias huérfana 0.7.0 (nunca publicada realmente en npm).",
132
- "pl": "Naprawiono wszystkie ustalenia oficjalnego ioBroker Check and Service Bot: podniesiono minimalne wersje (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README podzielono na anglojęzyczny README.md i niemiecki README.de.md (checker wymaga README wyłącznie po angielsku), sekcję License przeniesiono na sam koniec, dodano brakujące atrybuty i18n/rozmiaru w jsonConfig.json, przełączono na prefiks node: dla wbudowanych require, usunięto zbędny .npmignore i dodano .commitinfo do .gitignore. Usunięto też osierocony wpis news 0.7.0 (nigdy faktycznie nie opublikowany w npm).",
133
- "uk": "Виправлено всі зауваження офіційного ioBroker Check and Service Bot: підвищено мінімальні версії (engines.node >=20, @iobroker/adapter-core ^3.4.1, js-controller >=6.0.11, admin >=7.6.17), README розділено на англомовний README.md та німецький README.de.md (перевірник вимагає README лише англійською), розділ License переміщено в самий кінець, додано відсутні атрибути i18n/розміру в jsonConfig.json, вбудовані require переведено на префікс node:, видалено зайвий .npmignore та додано .commitinfo до .gitignore. Також видалено осиротілий запис новин 0.7.0 (ніколи не публікувався на npm).",
134
- "zh-cn": "修复了官方 ioBroker Check and Service Bot 报告的所有问题:提升了最低版本要求(engines.node >=20、@iobroker/adapter-core ^3.4.1、js-controller >=6.0.11、admin >=7.6.17);将 README 拆分为纯英文的 README.md 和德文的 README.de.md(检查器要求 README 仅使用英文);将 License 部分移动到文件末尾;为 jsonConfig.json 添加了缺失的 i18n/尺寸属性;将内置模块的 require 改为使用 node: 前缀;移除了多余的 .npmignore;并在 .gitignore 中添加了 .commitinfo。同时移除了从未真正发布到 npm 的孤立新闻条目 0.7.0。"
135
96
  }
136
97
  },
137
98
  "titleLang": {
package/main.js CHANGED
@@ -10,11 +10,17 @@
10
10
  * script into a standalone adapter: URL/Monitor-ID kommen jetzt aus der
11
11
  * Admin-Konfiguration statt aus einem Laufzeit-State.
12
12
  *
13
- * Objektstruktur (Stand 0.4.0): Rückmeldungen und Routen sind pro Einsatz
14
- * Listen (1:n) und liegen deshalb als verschachtelte JSON-Arrays innerhalb
15
- * von einsatz.json bzw. jedem Eintrag von einsatz.history10 - ergänzt um
16
- * schnell bindbare Zähler (einsatz.routenGesamt, einsatz.rueckmeldungGesamt,
17
- * einsatz.rueckmeldungAnzahl.*). Der frühere vis.*-Kanal entfällt komplett.
13
+ * Objektstruktur (Stand 0.7.15): einsatz.json ist ein eigener Channel mit
14
+ * ausschließlich flachen JSON-States, da VIS-Tabellen-Widgets keine
15
+ * verschachtelten Strukturen darstellen können. einsatz.json.current /
16
+ * .history10 enthalten nur den flachen Einsatzstamm (dieselben Felder wie
17
+ * die einzelnen einsatz.*-States, nur als ein JSON-Objekt bzw. -Array
18
+ * gebündelt); Routen/Rückmeldungen/Alarmierungen liegen als eigene, ebenfalls
19
+ * flache Arrays in einsatz.json.routen/.rueckmeldungen/.emAlarmiert/
20
+ * .emWeitere - nur für den jeweils aktuellen Einsatz, nicht historisiert.
21
+ * Ergänzt um schnell bindbare Zähler (einsatz.routenGesamt,
22
+ * einsatz.rueckmeldungGesamt, einsatz.rueckmeldungAnzahl.*). Der frühere
23
+ * vis.*-Kanal entfällt komplett.
18
24
  */
19
25
 
20
26
  const https = require('node:https');
@@ -81,6 +87,23 @@ const OBSOLETE_OBJECT_IDS = [
81
87
  'geo.position',
82
88
  'history.last10',
83
89
  'einsatz.emWeitere',
90
+ // Umstrukturierung in 0.7.15: einsatz.json/einsatz.history10 wechseln von State zu
91
+ // Channel (einsatz.json.current/.history10/.routen/.rueckmeldungen/.emAlarmiert/
92
+ // .emWeitere) - ein Objekt kann nicht gleichzeitig State und Channel sein, die alten
93
+ // States müssen daher vor initObjects() entfernt werden.
94
+ 'einsatz.json',
95
+ 'einsatz.history10',
96
+ // Umstrukturierung in 0.7.15: Kanal tts zieht komplett unter einsatz um
97
+ // (einsatz.tts.last/.lastTimestamp), da er sich auf den aktuellen Einsatz bezieht.
98
+ // tts.history10 entfällt ersatzlos (keine sinnvolle Historie ohne Einsatzbezug).
99
+ 'tts.last',
100
+ 'tts.lastTimestamp',
101
+ 'tts.history10',
102
+ 'tts',
103
+ // Umstrukturierung in 0.7.15: status.alarmAktiv/status.restzeit ziehen unter einsatz
104
+ // um (einsatz.alarmAktiv/.restzeit), da sie sich auf den aktuellen Einsatz beziehen.
105
+ 'status.alarmAktiv',
106
+ 'status.restzeit',
84
107
  'rueckmeldung.last.json',
85
108
  'rueckmeldung.counts.ek',
86
109
  'rueckmeldung.counts.gf',
@@ -102,8 +125,9 @@ const CHANNEL_DEFS = [
102
125
  { id: 'status', type: 'channel', name: 'Verbindungs- und Registrierungsstatus' },
103
126
  { id: 'einsatz', type: 'channel', name: 'Aktueller Einsatz' },
104
127
  { id: 'einsatz.rueckmeldungAnzahl', type: 'folder', name: 'Rückmeldungen nach Funktion' },
128
+ { id: 'einsatz.json', type: 'folder', name: 'Einsatzdaten als flache JSON-Objekte/Arrays für Tabellen-Widgets' },
129
+ { id: 'einsatz.tts', type: 'folder', name: 'TTS-Ansage des aktuellen Einsatzes' },
105
130
  { id: 'debug', type: 'channel', name: 'Diagnose- und Debug-Informationen' },
106
- { id: 'tts', type: 'channel', name: 'TTS-Ansagen' },
107
131
  ];
108
132
 
109
133
  // Definition aller States, die beim Start sichergestellt werden.
@@ -115,15 +139,6 @@ const STATE_DEFS = [
115
139
  name: 'Verbunden mit WAIP-Server',
116
140
  def: false,
117
141
  },
118
- { id: 'status.alarmAktiv', type: 'boolean', role: 'indicator.alarm', name: 'Alarm aktiv', def: false },
119
- {
120
- id: 'status.restzeit',
121
- type: 'number',
122
- role: 'value.interval',
123
- name: 'Restzeit bis Einsatzende',
124
- unit: 's',
125
- def: 0,
126
- },
127
142
  { id: 'status.registeredMonitor', type: 'string', role: 'text', name: 'Aktuell registrierte Monitor-ID' },
128
143
  {
129
144
  id: 'status.registeredMonitorName',
@@ -157,7 +172,7 @@ const STATE_DEFS = [
157
172
  },
158
173
  { id: 'debug.monitorAudit', type: 'string', role: 'json', name: 'Monitor-Audit-Log (letzte 200 Einträge)' },
159
174
  { id: 'debug.sessionExpires', type: 'string', role: 'date', name: 'Session-Cookie gültig bis (letzte Erneuerung)' },
160
- { id: 'debug.lastError', type: 'string', role: 'json', name: 'Letzte Server-Fehlermeldung (io.error)' },
175
+ { id: 'debug.lastError', type: 'string', role: 'text', name: 'Letzte Server-Fehlermeldung (io.error)' },
161
176
  {
162
177
  id: 'debug.serverVersion',
163
178
  type: 'string',
@@ -165,6 +180,15 @@ const STATE_DEFS = [
165
180
  name: 'Zuletzt gemeldete Server-Version/Instanz-ID (io.version)',
166
181
  },
167
182
  // flache Felder des aktuellen Einsatzes
183
+ { id: 'einsatz.alarmAktiv', type: 'boolean', role: 'indicator.alarm', name: 'Alarm aktiv', def: false },
184
+ {
185
+ id: 'einsatz.restzeit',
186
+ type: 'number',
187
+ role: 'value.interval',
188
+ name: 'Restzeit bis Einsatzende',
189
+ unit: 's',
190
+ def: 0,
191
+ },
168
192
  { id: 'einsatz.id', type: 'number', role: 'value', name: 'Einsatz ID' },
169
193
  { id: 'einsatz.uuid', type: 'string', role: 'text', name: 'Einsatz UUID' },
170
194
  { id: 'einsatz.einsatzart', type: 'string', role: 'text', name: 'Einsatzart' },
@@ -184,18 +208,45 @@ const STATE_DEFS = [
184
208
  { id: 'einsatz.permissions', type: 'string', role: 'json', name: 'Berechtigungs-Flag der Registrierung' },
185
209
  { id: 'einsatz.latitude', type: 'number', role: 'value.gps.latitude', name: 'Breitengrad' },
186
210
  { id: 'einsatz.longitude', type: 'number', role: 'value.gps.longitude', name: 'Längengrad' },
187
- // verschachteltes Gesamtobjekt + Historie
211
+ // Flache JSON-Objekte/Arrays für Tabellen-Widgets (siehe einsatz.json.*-Channel).
212
+ // Jedes dieser States ist entweder ein flaches Objekt oder ein Array flacher Objekte -
213
+ // bewusst ohne weitere Verschachtelung, da VIS-Tabellen-Widgets nur eine Ebene abflachen
214
+ // können (siehe Diskussion zu den ursprünglich verschachtelten einsatz.json/history10).
215
+ {
216
+ id: 'einsatz.json.current',
217
+ type: 'string',
218
+ role: 'json',
219
+ name: 'Einsatzstamm des aktuellen Einsatzes, flaches Objekt (JSON)',
220
+ },
188
221
  {
189
- id: 'einsatz.json',
222
+ id: 'einsatz.json.history10',
190
223
  type: 'string',
191
224
  role: 'json',
192
- name: 'Vollständiger aktueller Einsatz inkl. Einsatzmittel/Routen/Rückmeldungen (JSON)',
225
+ name: `Einsatzstamm der letzten ${HISTORY_SIZE} abgeschlossenen Einsätze, gleiches Schema wie einsatz.json.current (JSON-Array)`,
193
226
  },
194
227
  {
195
- id: 'einsatz.history10',
228
+ id: 'einsatz.json.routen',
196
229
  type: 'string',
197
230
  role: 'json',
198
- name: `Letzte ${HISTORY_SIZE} abgeschlossene Einsätze, gleicher Objekt-Shape wie einsatz.json (JSON-Array)`,
231
+ name: 'Routen des aktuellen Einsatzes, flaches Array (JSON)',
232
+ },
233
+ {
234
+ id: 'einsatz.json.rueckmeldungen',
235
+ type: 'string',
236
+ role: 'json',
237
+ name: 'Rückmeldungen des aktuellen Einsatzes, flaches Array (JSON)',
238
+ },
239
+ {
240
+ id: 'einsatz.json.emAlarmiert',
241
+ type: 'string',
242
+ role: 'json',
243
+ name: 'Alarmierte Einsatzmittel des aktuellen Einsatzes, flaches Array (JSON)',
244
+ },
245
+ {
246
+ id: 'einsatz.json.emWeitere',
247
+ type: 'string',
248
+ role: 'json',
249
+ name: 'Weitere Einsatzmittel des aktuellen Einsatzes, flaches Array (JSON)',
199
250
  },
200
251
  // abgeleitete Zähler
201
252
  { id: 'einsatz.routenGesamt', type: 'number', role: 'value', name: 'Anzahl Routen im aktuellen Einsatz', def: 0 },
@@ -250,14 +301,40 @@ const STATE_DEFS = [
250
301
  name: 'Rückmeldungen: Medizinisch/Sanitäter',
251
302
  def: 0,
252
303
  },
253
- // TTS
254
- { id: 'tts.last', type: 'string', role: 'json', name: 'Letzte TTS-Ansage (JSON)' },
255
- { id: 'tts.lastTimestamp', type: 'string', role: 'date', name: 'Zeitstempel letzte TTS-Ansage' },
256
- { id: 'tts.history10', type: 'string', role: 'json', name: `Letzte ${HISTORY_SIZE} TTS-Ansagen (JSON-Array)` },
304
+ // TTS des aktuellen Einsatzes (Kanal liegt unter einsatz, siehe CHANNEL_DEFS)
305
+ {
306
+ id: 'einsatz.tts.last',
307
+ type: 'string',
308
+ role: 'text.url',
309
+ name: 'Vollständige URL der letzten TTS-Ansage (mp3)',
310
+ },
311
+ { id: 'einsatz.tts.lastTimestamp', type: 'string', role: 'date', name: 'Zeitstempel letzte TTS-Ansage' },
257
312
  ];
258
313
  // Schneller Zugriff von setField() auf den deklarierten Typ eines States (siehe dort).
259
314
  const STATE_DEF_BY_ID = new Map(STATE_DEFS.map(def => [def.id, def]));
260
315
 
316
+ // IDs, deren "string"-Wert tatsächlich ein JSON-*Array* enthält - werden von
317
+ // resetAllStates() auf "[]" statt null zurückgesetzt (alle anderen "string"-States,
318
+ // auch JSON-*Objekte* wie debug.lastEvent/einsatz.json.current, werden auf null gesetzt).
319
+ // debug.monitorAudit ist ebenfalls ein JSON-Array, steht aber bewusst NICHT hier, da es
320
+ // komplett in RESET_EXCLUDED_STATE_IDS ausgenommen ist (siehe dort).
321
+ const JSON_ARRAY_STATE_IDS = new Set([
322
+ 'einsatz.json.routen',
323
+ 'einsatz.json.rueckmeldungen',
324
+ 'einsatz.json.emAlarmiert',
325
+ 'einsatz.json.emWeitere',
326
+ ]);
327
+
328
+ // "number"-States, bei denen 0 ein irreführender "leerer" Wert wäre (Einsatz-ID,
329
+ // Koordinaten - 0/0 wäre eine reale, aber falsche Position) - resetAllStates() setzt
330
+ // diese auf null statt 0 zurück.
331
+ const NULLABLE_NUMBER_STATE_IDS = new Set(['einsatz.id', 'einsatz.latitude', 'einsatz.longitude']);
332
+
333
+ // States, die resetAllStates() bewusst NICHT bei jedem Adapter-Start leert - die Historie
334
+ // der letzten Einsätze (einsatz.json.history10) und das Verbindungs-/Registrierungs-
335
+ // Audit-Log (debug.monitorAudit) sollen über Neustarts hinweg erhalten bleiben.
336
+ const RESET_EXCLUDED_STATE_IDS = new Set(['einsatz.json.history10', 'debug.monitorAudit']);
337
+
261
338
  /* Prüft ob eine monitorID gültig ist (nicht-leer). */
262
339
  function isValidMonitor(mon) {
263
340
  if (mon === undefined || mon === null) {
@@ -489,8 +566,7 @@ class WaipWeb extends utils.Adapter {
489
566
  this.nextSessionKeepaliveDelayMs = null;
490
567
  this.sessionCookie = null;
491
568
  this.currentEinsatzUuid = null;
492
- this.currentEinsatzSnapshot = null; // verschachteltes Objekt -> einsatz.json
493
- this.ttsHistory = [];
569
+ this.currentEinsatzSnapshot = null; // -> einsatz.json.current/.routen/.rueckmeldungen/...
494
570
  this.lastServerVersion = null;
495
571
 
496
572
  this._lastDisconnectMsg = null;
@@ -519,6 +595,7 @@ class WaipWeb extends utils.Adapter {
519
595
  await this.cleanupObsoleteObjects();
520
596
  await this.migrateObjectTypes();
521
597
  await this.initObjects();
598
+ await this.resetAllStates();
522
599
  // Session-Cookie holen, bevor die erste Socket.IO-Verbindung aufgebaut wird
523
600
  await this.refreshSessionCookie();
524
601
  this.startSessionKeepalive();
@@ -571,19 +648,28 @@ class WaipWeb extends utils.Adapter {
571
648
  }
572
649
  }
573
650
 
574
- /* Löscht bestehende State-Objekte, deren common.type nicht mehr zur aktuellen
575
- STATE_DEFS-Definition passt (z.B. weil sich herausstellt, dass der Server ein Feld
576
- als Zahl statt als String schickt - siehe einsatz.id/einsatz.sondersignal in 0.4.3).
577
- setObjectNotExistsAsync legt danach in initObjects() ein frisches Objekt mit dem
578
- korrekten Typ an. Generisch für alle künftigen Typ-Korrekturen, nicht nur diese. */
651
+ /* Löscht bestehende State-Objekte, deren common.type oder common.role nicht mehr zur
652
+ aktuellen STATE_DEFS-Definition passt (z.B. weil sich herausstellt, dass der Server
653
+ ein Feld als Zahl statt als String schickt - siehe einsatz.id/einsatz.sondersignal
654
+ in 0.4.3 -, oder weil sich eine Rolle als falsch gewählt herausstellt - siehe
655
+ debug.lastError in 0.7.15, das trotz role "json" meist nur einen reinen Fehlertext
656
+ enthält). setObjectNotExistsAsync legt danach in initObjects() ein frisches Objekt
657
+ mit der korrekten Definition an. Generisch für alle künftigen Typ-/Rollen-
658
+ Korrekturen, nicht nur diese. */
579
659
  async migrateObjectTypes() {
580
660
  for (const def of STATE_DEFS) {
581
661
  try {
582
662
  const obj = await this.getObjectAsync(def.id);
583
- if (obj && obj.common && obj.common.type && obj.common.type !== def.type) {
663
+ if (!obj || !obj.common) {
664
+ continue;
665
+ }
666
+ const typeChanged = obj.common.type && obj.common.type !== def.type;
667
+ const roleChanged = obj.common.role && obj.common.role !== def.role;
668
+ if (typeChanged || roleChanged) {
584
669
  await this.delObjectAsync(def.id);
585
670
  this.log.info(
586
- `State-Objekt mit geändertem Datentyp neu angelegt: ${def.id} (${obj.common.type} -> ${def.type})`,
671
+ `State-Objekt mit geänderter Definition neu angelegt: ${def.id} ` +
672
+ `(type ${obj.common.type} -> ${def.type}, role ${obj.common.role} -> ${def.role})`,
587
673
  );
588
674
  }
589
675
  } catch {
@@ -617,6 +703,37 @@ class WaipWeb extends utils.Adapter {
617
703
  }
618
704
  }
619
705
 
706
+ /* Setzt bei jedem Adapter-Start aktiv alle States (außer RESET_EXCLUDED_STATE_IDS) auf
707
+ ihren "leeren" Wert zurück - anders als initObjects()/setObjectNotExistsAsync(), das
708
+ einen bereits vorhandenen Wert unangetastet lässt. Sorgt dafür, dass jeder Neustart
709
+ mit einem sauber initialisierten Zustand beginnt, unabhängig vom Stand davor. Läuft
710
+ nach initObjects(), die States müssen also bereits existieren. */
711
+ async resetAllStates() {
712
+ const tasks = [];
713
+ for (const def of STATE_DEFS) {
714
+ if (RESET_EXCLUDED_STATE_IDS.has(def.id)) {
715
+ continue;
716
+ }
717
+ let value;
718
+ if (def.type === 'boolean') {
719
+ value = false;
720
+ } else if (def.type === 'number') {
721
+ value = NULLABLE_NUMBER_STATE_IDS.has(def.id) ? null : 0;
722
+ } else if (JSON_ARRAY_STATE_IDS.has(def.id)) {
723
+ value = '[]';
724
+ } else {
725
+ value = null;
726
+ }
727
+ tasks.push(this.setStateAsync(def.id, value, true));
728
+ }
729
+ const results = await Promise.allSettled(tasks);
730
+ for (const r of results) {
731
+ if (r.status === 'rejected') {
732
+ this.safeWarn('resetAllStates', r.reason);
733
+ }
734
+ }
735
+ }
736
+
620
737
  /* Einfacher HTTP(S)-GET ohne zusätzliche Dependency; optional mit Cookie-Header. */
621
738
  httpGet(targetUrl, cookie) {
622
739
  return new Promise((resolve, reject) => {
@@ -982,6 +1099,63 @@ class WaipWeb extends utils.Adapter {
982
1099
  }
983
1100
  }
984
1101
 
1102
+ /* Schreibt ein flaches Array (routen/rueckmeldungen/emAlarmiert/emWeitere) in einen
1103
+ einsatz.json.*-State. Wandelt Nicht-Arrays defensiv in ein leeres Array um, damit
1104
+ Tabellen-Widgets nie auf einen unerwarteten Werttyp treffen. */
1105
+ async writeJsonArrayState(id, value) {
1106
+ try {
1107
+ await this.setStateAsync(id, JSON.stringify(Array.isArray(value) ? value : []), true);
1108
+ } catch (e) {
1109
+ this.safeWarn(`${id}.setState`, e);
1110
+ }
1111
+ }
1112
+
1113
+ /* Löst das verschachtelte position-Objekt eines Routen-Eintrags zu flachen lat/lon-Feldern
1114
+ auf (siehe einsatz.json.routen) - Tabellen-Widgets können nur eine Verschachtelungsebene
1115
+ abflachen, position{lat,lon} wäre bereits eine Ebene zu viel. */
1116
+ flattenRoutenEntry(r) {
1117
+ if (!r || typeof r !== 'object') {
1118
+ return r;
1119
+ }
1120
+ const { position, ...rest } = r;
1121
+ return {
1122
+ ...rest,
1123
+ lat: position && typeof position.lat === 'number' ? position.lat : null,
1124
+ lon: position && typeof position.lon === 'number' ? position.lon : null,
1125
+ };
1126
+ }
1127
+
1128
+ /* Extrahiert aus einem Einsatz-Snapshot nur die flachen Einsatzstamm-Felder
1129
+ (ALLOWED_EINSATZ_FIELDS + lat/lon aus position) - ohne routen/rueckmeldungen/
1130
+ emAlarmiert/emWeitere. Gemeinsam genutzt von persistEinsatzSnapshot()
1131
+ (einsatz.json.current) und pushEinsatzToHistory() (einsatz.json.history10), damit
1132
+ beide garantiert dasselbe Schema haben. */
1133
+ buildFlatEinsatzJson(snapshot) {
1134
+ const src = snapshot || {};
1135
+ const flat = {};
1136
+ for (const k of this.ALLOWED_EINSATZ_FIELDS) {
1137
+ flat[k] = Object.prototype.hasOwnProperty.call(src, k) ? src[k] : null;
1138
+ }
1139
+ flat.lat = src.position && typeof src.position.lat === 'number' ? src.position.lat : null;
1140
+ flat.lon = src.position && typeof src.position.lon === 'number' ? src.position.lon : null;
1141
+ return flat;
1142
+ }
1143
+
1144
+ /* Löst eine vom Server per io.playtts gesendete TTS-URL zu einer vollständigen absoluten
1145
+ URL auf. Bereits absolute URLs (http(s)://...) bleiben unverändert, relative Pfade
1146
+ (z.B. "/tts/xyz.mp3") werden mit der konfigurierten WAIP-Server-URL zusammengesetzt. */
1147
+ resolveTtsUrl(raw) {
1148
+ if (typeof raw !== 'string' || !raw) {
1149
+ return raw;
1150
+ }
1151
+ if (/^https?:\/\//i.test(raw)) {
1152
+ return raw;
1153
+ }
1154
+ const base = (this.url || '').replace(/\/+$/, '');
1155
+ const path = raw.startsWith('/') ? raw : `/${raw}`;
1156
+ return `${base}${path}`;
1157
+ }
1158
+
985
1159
  /* Prüft ob eine eingehende Payload eindeutig einem Monitor zuordenbar ist und mit currentMonitor übereinstimmt. */
986
1160
  payloadMonitorMatch(p) {
987
1161
  if (!p || typeof p !== 'object') {
@@ -1084,13 +1258,14 @@ class WaipWeb extends utils.Adapter {
1084
1258
  /* Schreibt this.currentEinsatzSnapshot komplett (inkl. verschachtelter Arrays) nach einsatz.json. */
1085
1259
  async persistEinsatzSnapshot() {
1086
1260
  try {
1087
- await this.setStateAsync('einsatz.json', JSON.stringify(this.currentEinsatzSnapshot), true);
1261
+ const flat = this.buildFlatEinsatzJson(this.currentEinsatzSnapshot);
1262
+ await this.setStateAsync('einsatz.json.current', JSON.stringify(flat), true);
1088
1263
  } catch (e) {
1089
1264
  this.safeWarn('persistEinsatzSnapshot', e);
1090
1265
  }
1091
1266
  }
1092
1267
 
1093
- /* Legt den aktuellen Einsatz-Snapshot als abgeschlossenen Eintrag vorne in einsatz.history10 ab
1268
+ /* Legt den aktuellen Einsatz-Snapshot als abgeschlossenen Eintrag vorne in einsatz.json.history10 ab
1094
1269
  (z.B. bei io.standby oder wenn ein neuer Einsatz beginnt, ohne dass zuvor io.standby kam).
1095
1270
  Dedupliziert über die uuid, damit derselbe Einsatz nicht doppelt eingetragen wird, falls
1096
1271
  sowohl io.standby als auch der nächste io.new_waip diese Methode auslösen. */
@@ -1099,7 +1274,7 @@ class WaipWeb extends utils.Adapter {
1099
1274
  if (!this.currentEinsatzSnapshot || !this.currentEinsatzSnapshot.uuid) {
1100
1275
  return;
1101
1276
  }
1102
- const st = await this.getStateAsync('einsatz.history10');
1277
+ const st = await this.getStateAsync('einsatz.json.history10');
1103
1278
  let arr = [];
1104
1279
  try {
1105
1280
  arr = st && st.val ? JSON.parse(st.val) : [];
@@ -1109,11 +1284,13 @@ class WaipWeb extends utils.Adapter {
1109
1284
  if (arr.length && arr[0] && arr[0].uuid === this.currentEinsatzSnapshot.uuid) {
1110
1285
  return;
1111
1286
  }
1112
- arr.unshift(this.currentEinsatzSnapshot);
1287
+ // Nur den flachen Einsatzstamm archivieren - Routen/Rückmeldungen/Alarmierungen
1288
+ // gelten nur für den jeweils aktuellen Einsatz und werden nicht historisiert.
1289
+ arr.unshift(this.buildFlatEinsatzJson(this.currentEinsatzSnapshot));
1113
1290
  if (arr.length > this.HISTORY_SIZE) {
1114
1291
  arr = arr.slice(0, this.HISTORY_SIZE);
1115
1292
  }
1116
- await this.setStateAsync('einsatz.history10', JSON.stringify(arr), true);
1293
+ await this.setStateAsync('einsatz.json.history10', JSON.stringify(arr), true);
1117
1294
  } catch (e) {
1118
1295
  this.safeWarn('pushEinsatzToHistory', e);
1119
1296
  }
@@ -1171,9 +1348,14 @@ class WaipWeb extends utils.Adapter {
1171
1348
 
1172
1349
  const data = normalizeData(incoming || {});
1173
1350
  try {
1351
+ // Flach halten (lat/lon statt eines verschachtelten position-Objekts), damit
1352
+ // dieser Debug-State ebenfalls direkt an ein Tabellen-Widget gebunden werden kann.
1174
1353
  await this.setStateAsync(
1175
1354
  'debug.normalizedPosition',
1176
- JSON.stringify({ position: data.position ?? null }),
1355
+ JSON.stringify({
1356
+ lat: data.position && typeof data.position.lat === 'number' ? data.position.lat : null,
1357
+ lon: data.position && typeof data.position.lon === 'number' ? data.position.lon : null,
1358
+ }),
1177
1359
  true,
1178
1360
  );
1179
1361
  } catch {
@@ -1228,9 +1410,9 @@ class WaipWeb extends utils.Adapter {
1228
1410
  }
1229
1411
 
1230
1412
  try {
1231
- await this.setStateAsync('status.alarmAktiv', true, true);
1413
+ await this.setStateAsync('einsatz.alarmAktiv', true, true);
1232
1414
  } catch (e) {
1233
- this.safeWarn('status.alarmAktiv.setState', e);
1415
+ this.safeWarn('einsatz.alarmAktiv.setState', e);
1234
1416
  }
1235
1417
 
1236
1418
  // flache Felder setzen und gleichzeitig im Snapshot mitführen
@@ -1256,6 +1438,8 @@ class WaipWeb extends utils.Adapter {
1256
1438
  }
1257
1439
 
1258
1440
  await this.persistEinsatzSnapshot();
1441
+ await this.writeJsonArrayState('einsatz.json.emAlarmiert', this.currentEinsatzSnapshot.emAlarmiert);
1442
+ await this.writeJsonArrayState('einsatz.json.emWeitere', this.currentEinsatzSnapshot.emWeitere);
1259
1443
  } catch (e) {
1260
1444
  // Ein Alarm-Event konnte nicht verarbeitet werden -> echter Datenverlust.
1261
1445
  this.safeLog('error', 'handleAlarm', e);
@@ -1296,6 +1480,7 @@ class WaipWeb extends utils.Adapter {
1296
1480
  }
1297
1481
 
1298
1482
  await this.persistEinsatzSnapshot();
1483
+ await this.writeJsonArrayState('einsatz.json.rueckmeldungen', this.currentEinsatzSnapshot.rueckmeldungen);
1299
1484
  await this.updateRueckmeldungCounts();
1300
1485
  } catch (e) {
1301
1486
  // Eine Rückmeldung konnte nicht verarbeitet werden -> echter Datenverlust.
@@ -1306,7 +1491,7 @@ class WaipWeb extends utils.Adapter {
1306
1491
  /* Handler für Standby (io.standby) - Einsatz beendet / Monitor im Ruhezustand.
1307
1492
  Analog zum offiziellen Frontend (client_waip.js leert dabei Stichwort, Ortsdaten,
1308
1493
  Besonderheiten etc. und setzt die Karte zurück): der abgeschlossene Einsatz wird
1309
- zuerst archiviert (einsatz.history10), danach werden alle auf den aktuellen Einsatz
1494
+ zuerst archiviert (einsatz.json.history10), danach werden alle auf den aktuellen Einsatz
1310
1495
  bezogenen States geleert - so bleibt alarmAktiv ein verlässlicher Schalter dafür, ob
1311
1496
  einsatz.* gerade echte Live-Daten enthält, statt still den letzten (beendeten)
1312
1497
  Einsatz weiter anzuzeigen. */
@@ -1315,9 +1500,9 @@ class WaipWeb extends utils.Adapter {
1315
1500
  this.log.info('Standby empfangen - Einsatz beendet bzw. Monitor im Ruhezustand');
1316
1501
  this.appendMonitorAudit({ ts: new Date().toISOString(), event: 'standby' }).catch(() => {});
1317
1502
  try {
1318
- await this.setStateAsync('status.alarmAktiv', false, true);
1503
+ await this.setStateAsync('einsatz.alarmAktiv', false, true);
1319
1504
  } catch (e) {
1320
- this.safeWarn('status.alarmAktiv.setState', e);
1505
+ this.safeWarn('einsatz.alarmAktiv.setState', e);
1321
1506
  }
1322
1507
  await this.pushEinsatzToHistory();
1323
1508
  await this.clearCurrentEinsatzStates();
@@ -1328,14 +1513,18 @@ class WaipWeb extends utils.Adapter {
1328
1513
  }
1329
1514
 
1330
1515
  /* Leert alle States, die sich auf den aktuellen (jetzt beendeten) Einsatz beziehen -
1331
- flache einsatz.*-Felder, einsatz.json sowie alle abgeleiteten Zähler. Wird nach
1332
- pushEinsatzToHistory() aufgerufen, der abgeschlossene Einsatz bleibt also weiterhin
1333
- über einsatz.history10 abrufbar. */
1516
+ flache einsatz.*-Felder, einsatz.json.current/.routen/.rueckmeldungen/.emAlarmiert/
1517
+ .emWeitere sowie alle abgeleiteten Zähler. Wird nach pushEinsatzToHistory() aufgerufen,
1518
+ der abgeschlossene Einsatz bleibt also weiterhin über einsatz.json.history10 abrufbar. */
1334
1519
  async clearCurrentEinsatzStates() {
1335
1520
  const tasks = this.ALLOWED_EINSATZ_FIELDS.map(k => this.setStateAsync(`einsatz.${k}`, null, true));
1336
1521
  tasks.push(this.setStateAsync('einsatz.latitude', null, true));
1337
1522
  tasks.push(this.setStateAsync('einsatz.longitude', null, true));
1338
- tasks.push(this.setStateAsync('einsatz.json', null, true));
1523
+ tasks.push(this.setStateAsync('einsatz.json.current', null, true));
1524
+ tasks.push(this.writeJsonArrayState('einsatz.json.routen', []));
1525
+ tasks.push(this.writeJsonArrayState('einsatz.json.rueckmeldungen', []));
1526
+ tasks.push(this.writeJsonArrayState('einsatz.json.emAlarmiert', []));
1527
+ tasks.push(this.writeJsonArrayState('einsatz.json.emWeitere', []));
1339
1528
  tasks.push(this.setStateAsync('einsatz.routenGesamt', 0, true));
1340
1529
  const results = await Promise.allSettled(tasks);
1341
1530
  for (const r of results) {
@@ -1418,6 +1607,10 @@ class WaipWeb extends utils.Adapter {
1418
1607
  this.currentEinsatzSnapshot.routen = Array.isArray(data) ? data : data ? [data] : [];
1419
1608
 
1420
1609
  await this.persistEinsatzSnapshot();
1610
+ await this.writeJsonArrayState(
1611
+ 'einsatz.json.routen',
1612
+ this.currentEinsatzSnapshot.routen.map(r => this.flattenRoutenEntry(r)),
1613
+ );
1421
1614
  try {
1422
1615
  await this.setStateAsync('einsatz.routenGesamt', this.currentEinsatzSnapshot.routen.length, true);
1423
1616
  } catch (e) {
@@ -1430,26 +1623,16 @@ class WaipWeb extends utils.Adapter {
1430
1623
  }
1431
1624
 
1432
1625
  /* Handler für TTS-Events (io.playtts). Payload ist laut client_waip.js nur eine URL
1433
- (direkt als audio.src verwendet), daher kein Einsatzbezug/keine weiteren Felder. */
1626
+ (direkt als audio.src verwendet) - im Browser funktioniert das auch als relativer
1627
+ Pfad, weil er implizit gegen die aktuelle Seiten-Origin aufgelöst wird. Für uns nicht
1628
+ (VIS/Automationen haben nicht zwangsläufig dieselbe Origin wie der WAIP-Server),
1629
+ daher wird hier explizit zu einer vollständigen absoluten URL aufgelöst. */
1434
1630
  async handleTTS(incoming) {
1435
1631
  try {
1436
1632
  const data = normalizeData(incoming || {});
1437
1633
  const ts = new Date().toISOString();
1438
- await this.setField('tts.last', data);
1439
- await this.setField('tts.lastTimestamp', ts);
1440
-
1441
- if (!Array.isArray(this.ttsHistory)) {
1442
- this.ttsHistory = [];
1443
- }
1444
- this.ttsHistory.unshift({ zeitstempel: ts, url: data });
1445
- if (this.ttsHistory.length > this.HISTORY_SIZE) {
1446
- this.ttsHistory = this.ttsHistory.slice(0, this.HISTORY_SIZE);
1447
- }
1448
- try {
1449
- await this.setStateAsync('tts.history10', JSON.stringify(this.ttsHistory), true);
1450
- } catch (e) {
1451
- this.safeWarn('tts.history10.setState', e);
1452
- }
1634
+ await this.setField('einsatz.tts.last', this.resolveTtsUrl(data));
1635
+ await this.setField('einsatz.tts.lastTimestamp', ts);
1453
1636
  } catch (e) {
1454
1637
  // Ein TTS-Event konnte nicht verarbeitet werden -> echter Datenverlust.
1455
1638
  this.safeLog('error', 'handleTTS', e);
@@ -1778,7 +1961,7 @@ class WaipWeb extends utils.Adapter {
1778
1961
  if (this._lastRestzeit !== rest) {
1779
1962
  this._lastRestzeit = rest;
1780
1963
  try {
1781
- await this.setStateAsync('status.restzeit', rest, true);
1964
+ await this.setStateAsync('einsatz.restzeit', rest, true);
1782
1965
  } catch {
1783
1966
  /* ignore */
1784
1967
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.waip-web",
3
- "version": "0.7.13",
3
+ "version": "0.7.15",
4
4
  "description": "Wachalarm IP-Web (WAIP) Adapter für ioBroker – bindet Alarmierungen, Rückmeldungen, Routen und TTS-Ansagen per Socket.IO an ioBroker an.",
5
5
  "author": {
6
6
  "name": "rnc11",
@@ -48,7 +48,7 @@
48
48
  "@iobroker/eslint-config": "^2.2.0",
49
49
  "@iobroker/testing": "^5.2.2",
50
50
  "@tsconfig/node22": "^22.0.6",
51
- "@types/node": ">=22"
51
+ "@types/node": "^22.0.0"
52
52
  },
53
53
  "scripts": {
54
54
  "test:package": "mocha test/package.js --exit",