API-Anforderungen für Telemetrie-Abruf (GET)
Version: 1.4
Stand: Juni 2026
Herausgeber: Dexa Solutions GmbH
Produkt: Safe Fire House (SFH)
1. Übersicht
Dieses Dokument beschreibt den REST-API-Endpoint, über den ein externes System die aktuellen Telemetriedaten der Safe Fire House Brandwarnanlage aktiv abruft (Pull). Die Zentrale ist hier der Server, das abrufende System der Client. Die Antwort enthält den Datenbaum aus Wache, Fahrzeugen und Rauchsensoren.
| Parameter | Wert |
|---|---|
| Method | GET |
| Pfad | /api/health (mit oder ohne abschließenden /) |
| Accept | application/json |
| Response-Type | application/json; charset=utf-8 |
| Frequenz | On-Demand (Client-gesteuert) |
| Rate Limit | 5 Requests/Sekunde pro Client-IP, Burst 10 (sonst 429) |
| Caching | Antwort bis zu 20 s serverseitig gecacht (siehe Abschnitt 9) |
1.1 Authentifizierung
Der Endpoint erfordert ein Bearer-Token im Authorization-Header. Das Token wird je Zentrale vergeben und vertraulich an das abrufende System übergeben.
| Methode | Header / Mechanismus | Beispiel |
|---|---|---|
| Bearer Token | Authorization: Bearer |
Authorization: Bearer bffdc50d5a1173159... |
Fehlt der Header oder ist das Token ungültig, antwortet der Endpoint mit 401. Ist serverseitig kein Token konfiguriert, antwortet er mit 503 (fail-closed, keine Datenausgabe).
2. Endpoint
GET http(s)://<zentrale-host>/api/health
<zentrale-host>= IP oder Hostname der Zentrale im lokalen Netz (z. B.192.168.).189.1651.16- Erreichbar über den Reverse-Proxy der Zentrale; der
Authorization-Header wird unverändert durchgereicht. - Der abschließende Slash ist optional (
/api/healthund/api/health/sind gleichwertig).
3. Request
Es wird kein Request-Body gesendet. Erforderlicher Header:
| Header | Pflicht | Wert |
|---|---|---|
Authorization |
ja | Bearer <token> |
Beispiel:
curl --location 'http://192.168.1.16/api/health/' \
--header 'Authorization: Bearer <token>'
4. Payload-Struktur (Response)
Root
├── timestamp
├── fireStation
├── deviceId
└── objects[]
├── vehicleId
├── sign
├── callSign
├── vehicleType
└── smokeDetectors[]
├── name
├── address
├── type
└── ...
Hinweis zu Wertetypen: Alle Schlüssel sind camelCase. Alle skalaren Werte werden als JSON-String ausgegeben (auch Zahlen und Flags, z. B.
"rssiDevice": "-71","battery": "false","alarmState": "0").objectsundsmokeDetectorssind echte JSON-Arrays.
5. Root-Objekt
| Key | Description | Type | Constraints |
|---|---|---|---|
timestamp |
Zeitstempel der Erstellung | string |
ISO 8601 UTC (YYYY-MM-DDTHH:mm:ssZ) |
fireStation |
Wache (Name, Adresse) | string |
Max. 150 Zeichen |
deviceId |
Seriennummer der Zentrale | string |
14 Zeichen, hexadezimal |
objects |
Auflistung der Objekte (Fahrzeug/Raum/Halle) | array |
Array von Objekt-Einträgen (siehe Abschnitt 6) |
Beispiel:
{
"timestamp": "2026-06-09T11:24:13Z",
"fireStation": "Feuerwehr Feuerstadt, Hauptstr. 112, 01234 Feuerstadt",
"deviceId": "001A2B3C4D5E6F",
"objects": [ ... ]
}
6. Vehicle-Objekt
| Key | Description | Type | Constraints |
|---|---|---|---|
vehicleId |
Fahrzeug-Identifikationsnummer (VIN) | string |
17 Zeichen; "n.a." falls nicht hinterlegt (siehe 8.1) |
sign |
Kennzeichen | string |
Max. 10 Zeichen; "n.a." falls nicht hinterlegt (siehe 8.1) |
callSign |
Funkrufname | string |
Max. 50 Zeichen |
vehicleType |
Fahrzeugtyp | string |
Max. 50 Zeichen; "n.a." falls nicht hinterlegt (siehe 8.1) |
smokeDetectors |
Auflistung der Rauchsensoren | array |
Array von SmokeDetector-Objekten; [] falls keine Melder zugeordnet (siehe 8.2) |
Beispiel:
{
"vehicleId": "WVWZZZ3CZWE123456",
"sign": "FS-FW 112",
"callSign": "1-HLF20-1",
"vehicleType": "HLF20",
"smokeDetectors": [ ... ]
}
7. SmokeDetector-Objekt
Alle Werte sind Strings (siehe Hinweis in Abschnitt 4). Fehlt ein einzelner Datapoint, wird ein typ-konformer Default geliefert (nie null) — siehe Abschnitt 8.3.
| Key | Description | Type | Constraints |
|---|---|---|---|
name |
Rauchsensorbezeichnung | string |
Max. 30 Zeichen |
address |
Rauchsensoradresse | string |
14 Zeichen, hexadezimal |
type |
Rauchsensortyp | string |
Konstant "SFHSS02" |
version |
Hardware-Version | string |
numerisch, ≥ 1 |
group |
Gruppierung | string |
0–9 oder leer |
teams |
Reserviert | string |
i. d. R. leer (siehe Abschnitt 11) |
firmware |
Firmware-Version | string |
Max. 9 Zeichen, Pattern [0-9.]+ |
rssiDevice |
Funkempfangswert Gerät (dBm) | string |
numerisch, −128 bis 128 |
rssiPeer |
Funkempfangswert Sender (dBm) | string |
numerisch, −128 bis 128 |
battery |
Flag: Batterieleistung niedrig | string |
"true" / "false" |
unreachState |
Flag: Gerät nicht erreichbar | string |
"true" / "false" |
unreachCumulative |
Kumulierte Nichterreichbarkeit (Tage) | string |
numerisch (0–9999) oder "n.a." (siehe 8.2) |
operationTime |
Betriebszeit (Tage) | string |
numerisch, 0–9999 |
dirtLevel |
Verschmutzungsgrad | string |
float-String (z. B. "0.000000") |
smokeLevel |
Raucherkennungsgrad | string |
float-String (z. B. "0.000000") |
alarmState |
Alarmstatus | string |
"0"–"3" (siehe Enum, Abschnitt 7.1) |
voltage |
Batteriespannung (V) | string |
float-String (0.0–3.2) |
chamber |
Flag: Rauchkammer verschmutzt | string |
"true" / "false" |
errorCode |
Fehlercode | string |
numerisch, 0–99 |
Beispiel:
{
"name": "1-HLF20-1 RM1",
"address": "00AABBCCDDEE11",
"type": "SFHSS02",
"version": "1",
"group": "",
"teams": "",
"firmware": "1.0.6",
"rssiDevice": "-65",
"rssiPeer": "0",
"battery": "false",
"unreachState": "false",
"unreachCumulative": "0",
"operationTime": "180",
"dirtLevel": "0.000000",
"smokeLevel": "0.000000",
"alarmState": "0",
"voltage": "3.000000",
"chamber": "false",
"errorCode": "0"
}
7.1 Enum: alarmState
| Wert | Bedeutung |
|---|---|
"0" |
Ruhezustand – Kein Rauch erkannt |
"1" |
Lokaler Alarm – Rauch erkannt |
"2" |
Reserviert |
"3" |
Broadcast Alarm – Anderer Sensor in Funkreichweite hat Rauch erkannt |
7.2 Flag-Logik
| Flag | Bedeutung wenn "true" |
Zusatzinfo |
|---|---|---|
chamber |
Rauchkammer verschmutzt | Siehe dirtLevel |
battery |
Batterieleistung niedrig | Siehe voltage (V) |
unreachState |
Gerät nicht erreichbar | Siehe unreachCumulative (Tage) |
8. Sonderfälle & Defaults
8.1 Fahrzeug-Metadaten nicht deklariert
vehicleId, sign und vehicleType werden je callSign aus der Fahrzeug-Stammdatenpflege der Zentrale gelesen. Verhalten pro Feld (einzeln):
| Situation | Ausgabe |
|---|---|
callSign fehlt in der Stammdatenpflege |
"n.a." |
| Eintrag vorhanden, Wert leer | "" (leerer String) |
| Eintrag + Wert vorhanden | der Wert |
callSign selbst stammt aus der Fahrzeugliste und ist immer gesetzt.
8.2 Gerät nicht erreichbar / nicht gepairt
| Fall | Verhalten |
|---|---|
| Melder gepairt, aber offline | erscheint im Baum; unreachState = "true"; unreachCumulative = Tage seit letztem Kontakt bzw. "n.a."; übrige Werte = zuletzt bekannter Stand (kein Live-Funk-Poll beim Abruf) |
| Melder nicht (mehr) gepairt | Melder fehlt im Array. Ein Fahrzeug ohne zugeordnete Melder liefert "smokeDetectors": [] |
unreachCumulative="n.a."bedeutet „nicht in der Erreichbarkeits-Historie der Zentrale geführt", nicht zwingend „erreichbar".
8.3 Fehlender Datapoint → typ-konformer Default
Existiert ein einzelner Sensor-Datapoint nicht (abweichendes Geräteprofil o. Ä.), wird statt null ein Default ausgegeben:
| Feld(er) | Default |
|---|---|
battery, unreachState, chamber |
"false" |
rssiDevice, rssiPeer, errorCode, operationTime, alarmState |
"0" |
voltage, smokeLevel, dirtLevel |
"0.000000" |
firmware, group, version, teams (keine Geräte-Metadaten) |
"" |
unreachCumulative (keine Historie) |
"n.a." |
9. Caching & Nebenläufigkeit
- TTL-Cache: Die Antwort wird serverseitig bis zu 20 s zwischengespeichert. Aufeinanderfolgende Abrufe innerhalb dieses Fensters liefern denselben (bis zu 20 s alten) Stand, ohne die Zentrale erneut abzufragen.
- Single-Flight: Pro Cache-Miss läuft höchstens eine Datenerhebung. Treffen mehrere Abrufe gleichzeitig ein, teilen sie sich das laufende Ergebnis; es werden keine parallelen Erhebungen gestartet.
- Die Werte spiegeln den zuletzt in der Zentrale bekannten Zustand der Sensoren wider (kein aktiver Funk-Poll der Geräte beim Abruf).
10. Response (HTTP)
10.1 Status Codes
| Code | Bedeutung |
|---|---|
200 OK |
Telemetrie erfolgreich geliefert (Body = Datenbaum) |
401 Unauthorized |
Fehlender oder ungültiger Authorization-Header |
429 Too Many Requests |
Rate Limit überschritten |
502 Bad Gateway |
Telemetrie nicht lesbar (Zentrale nicht erreichbar) |
503 Service Unavailable |
Serverseitig kein API-Token konfiguriert |
10.2 Success Response
Body ist der vollständige Telemetrie-Datenbaum (siehe Abschnitt 12).
10.3 Error Response
{
"error": "unauthorized"
}
{
"error": "telemetryUnavailable",
"detail": "telemetry source returned HTTP 500"
}
error |
HTTP | Bedeutung |
|---|---|---|
unauthorized |
401 |
Token fehlt/falsch |
telemetryUnavailable |
502 |
Datenerhebung fehlgeschlagen (detail) |
apiTokenNotConfigured |
503 |
Kein Token gesetzt |
11. Verhältnis zum Telemetrie-Upload
Dieser Endpoint ist eigenständig; das zyklische Telemetrie-Upload diente nur als fachliche Vorlage. Die Schlüssel sind durchgängig camelCase. Verbleibende Unterschiede zum Dokument „API-Anforderungen für Telemetrie-Upload":
| Aspekt | Upload-Dokument | Dieser Endpoint (Abruf) |
|---|---|---|
| Wurzel-Array | vehicles |
objects (enthält auch Räume/Hallen, nicht nur Fahrzeuge) |
| Detector-Keys (mehrwortig) | lowercase (operationtime, dirtlevel, smokelevel, alarmstate, errorcode) |
camelCase (operationTime, dirtLevel, smokeLevel, alarmState, errorCode) |
| Wertetypen | typisiert (integer/float/boolean/array) | alle skalaren Werte als String |
teams |
array ([]) |
string (i. d. R. leer) |
Beide Schemata können unabhängig weiterentwickelt werden.
12. Vollständiges Response-Beispiel
{
"timestamp": "2026-06-09T11:24:13Z",
"fireStation": "Feuerwehr Feuerstadt, Hauptstr. 112, 01234 Feuerstadt",
"deviceId": "001A2B3C4D5E6F",
"objects": [
{
"vehicleId": "WVWZZZ3CZWE123456",
"sign": "FS-FW 112",
"callSign": "1-HLF20-1",
"vehicleType": "HLF20",
"smokeDetectors": [
{
"name": "1-HLF20-1 RM1",
"address": "00AABBCCDDEE11",
"type": "SFHSS02",
"version": "1",
"group": "",
"teams": "",
"firmware": "1.0.6",
"rssiDevice": "-65",
"rssiPeer": "0",
"battery": "false",
"unreachState": "false",
"unreachCumulative": "0",
"operationTime": "180",
"dirtLevel": "0.000000",
"smokeLevel": "0.000000",
"alarmState": "0",
"voltage": "3.000000",
"chamber": "false",
"errorCode": "0"
},
{
"name": "1-HLF20-1 RM2",
"address": "00AABBCCDDEE22",
"type": "SFHSS02",
"version": "1",
"group": "",
"teams": "",
"firmware": "1.0.6",
"rssiDevice": "-72",
"rssiPeer": "0",
"battery": "false",
"unreachState": "false",
"unreachCumulative": "0",
"operationTime": "180",
"dirtLevel": "0.000000",
"smokeLevel": "0.000000",
"alarmState": "0",
"voltage": "3.000000",
"chamber": "false",
"errorCode": "0"
}
]
}
]
}