Weitere Schnittstellen
Neben der REST-Schnittstelle nimmt die Plattform Feld- und Zeitreihendaten über zwei Wege entgegen:
- MQTT – Geräte/Publisher senden Messwerte an einen MQTT-Broker; der
mqtt-clientder Plattform abonniert diesen und leitet die Daten weiter. - OPC-UA – die Plattform (
opc-ua-client) verbindet sich aktiv mit einem von Ihnen bereitgestellten OPC-UA-Server und liest dessen Variablen-Knoten.
Beide Wege münden in denselben internen Kafka-Ingestion-Topic und werden von dort in die Zeitreihen-Datenbank geschrieben:
MQTT-Publisher ──► MQTT-Broker ──► mqtt-client ──┐
├──► Kafka (Ingestion-Topic) ──► timeseries-ingestion-consumer ──► Zeitreihen-DB
OPC-UA-Server ◄── opc-ua-client ─────────────────┘
Die Anbindung erfolgt in Abstimmung mit dem Plattform-Betreiber: Broker-Zugang
und Topic-/Tenant-Zuordnung (MQTT) bzw. die Aktivierung der OPC-UA-Knoten werden
plattformseitig konfiguriert. Die internen Komponenten (mqtt-client,
opc-ua-client, Kafka, ts-service) validieren keine JWTs — sie sind nur im
internen Netz erreichbar. Sie als Entwickler stellen die Datenquelle bereit
(MQTT-Publisher bzw. OPC-UA-Server) im hier beschriebenen Format.
MQTT
Broker-Verbindung
| Parameter | Wert |
|---|---|
| Protokoll | MQTT v5 (Eclipse Paho) |
| Broker-URL | tcp://<host>:<port> (Standard-Port 1883) |
| Authentifizierung | Benutzername / Passwort |
| QoS | konfigurierbar (Standard 0) |
| Verschlüsselung | im Auslieferungszustand kein TLS (tcp://) |
Die Zugangsdaten und der/die abonnierten Topics werden vom Betreiber vergeben. Der
mqtt-client abonniert die pro Tenant konfigurierten Topics (Wildcards möglich,
z. B. int/#); das Topic bestimmt die Tenant-Zuordnung. Die Identität des
Messwerts (welches Signal) steckt nicht im Topic, sondern im Feld metric der
Payload.
Payload-Format
Jede MQTT-Nachricht enthält einen Datenpunkt als JSON:
{
"timestamp": "2026-07-22T09:15:00Z",
"metric": "water.north.level",
"value": 621.4
}
| Feld | Typ | Beschreibung |
|---|---|---|
timestamp | String | Zeitstempel im ISO-8601-Format (UTC/Offset, z. B. 2026-07-22T09:15:00Z). Muss strikt Instant-parsbar sein. |
metric | String | Frei wählbarer Metrik-Name (die externe Zeitreihen-ID). Wird pro Tenant beim ersten Datenpunkt automatisch als Metrik mit Herkunft MQTT angelegt; ein Signal verweist danach auf diese Metrik. |
value | Number | Numerischer Messwert (numerischer String wird ebenfalls akzeptiert; nicht-numerische Werte werden verworfen). |
Beispiel (Python)
Mit aiomqtt (pip install aiomqtt):
import asyncio
import json
from datetime import datetime, timezone
import aiomqtt
async def main() -> None:
async with aiomqtt.Client(
hostname="<broker-host>",
port=1883,
username="<mqtt-user>",
password="<mqtt-pass>",
) as client:
payload = json.dumps({
"timestamp": datetime.now(timezone.utc).isoformat(),
"metric": "water.north.level",
"value": 621.4,
})
# Das Topic muss unter einem abonnierten (Wildcard-)Topic liegen, z. B. int/#
await client.publish("int/local/test", payload)
if __name__ == "__main__":
asyncio.run(main())
OPC-UA
Anders als bei MQTT verbindet sich die Plattform aktiv mit Ihrem OPC-UA-Server und liest dessen Variablen-Knoten (überwiegend per Subscription, optional mit historischem Nachladen via HistoryRead).
Was Sie bereitstellen müssen
- Einen erreichbaren
opc.tcp://<host>:<port>-Endpunkt. - Variablen-Knoten mit numerischen Werten unterhalb der konfigurierten Root Nodes.
Der Client verbindet sich aktuell ausschließlich anonym und unverschlüsselt
(SecurityPolicy.None, AnonymousProvider). Ihr OPC-UA-Server muss daher einen
Endpunkt ohne Security-Policy und mit anonymem Zugriff anbieten. Für den Einsatz
über nicht-vertrauenswürdige Netze ist eine abgesicherte Transportstrecke (z. B.
VPN) einzuplanen.
Server-Konfiguration
Die anzubindenden Server pflegen Sie in der Plattform selbst, unter
Administration → OPC-UA-Server. Änderungen wirken ohne Neustart: der
opc-ua-client gleicht seine Verbindungen im Minutentakt mit der Konfiguration ab.
Je Server hinterlegen Sie:
| Feld | Beschreibung |
|---|---|
| Name | Bezeichnung innerhalb Ihres Mandanten, jederzeit änderbar |
| Adresse und Port | Der opc.tcp://<host>-Endpunkt und sein Port |
| Metrik-Muster | Vorlage für den Metrik-Namen. Platzhalter: NODE_ID/ID, BROWSE_NAME, DISPLAY_NAME, UUID (z. B. ergibt opcua.NODE_ID.BROWSE_NAME → opcua.<NodeId>.<BrowseName>). Pflichtangabe und danach nicht mehr änderbar, weil es die Metrik-Namen der Knoten bestimmt. Der Name entsteht erst, wenn ein Variablenknoten einem Signal zugeordnet wird — nicht beim Browsen; ein nicht zugeordneter Knoten hat keine Metrik. Einzige Ausnahme: ein aus einer älteren Fassung übernommener Server, dem noch keines hinterlegt ist — dort tragen Sie es einmalig nach, andernfalls sucht der Server nicht nach Knoten |
| In Betrieb | Außer Betrieb genommene Server werden getrennt; Knoten, Signale und Zeitreihen bleiben erhalten |
| Nach neuen Knoten suchen | Ob und wie oft der Adressraum unterhalb der Root Nodes durchsucht wird |
| Suchzeitplan | Wann gesucht wird, als CRON-Ausdruck mit sechs Feldern (Sekunde Minute Stunde Tag Monat Wochentag). Unterschiedliche Uhrzeiten entzerren mehrere Server gegeneinander |
| Historische Daten | Voreinstellung für das Nachladen per HistoryRead samt Startzeitpunkt. Als Startzeitpunkt sind ein ISO-8601-Zeitstempel (2026-01-01T00:00:00Z) oder das Schlüsselwort now zulässig; ohne Angabe gilt now |
| Nachladezeitplan | Wann fehlende Werte nachgeholt werden, ebenfalls als CRON-Ausdruck. Voreinstellung: stündlich um halb |
Je Server pflegen Sie außerdem die Root Nodes, ab denen die Traversierung des Adressraums startet. Sie tragen nur ihre Knoten-Kennung — ob, ab wann und wann historische Daten geholt werden, entscheiden Sie einmal am Server.
Lücken im Datenstrom
Steht Ihr Server still — Wartung, Netzausfall, nicht erreichbar —, liefert die Subscription die versäumten Werte später nicht nach. Ist der Abruf historischer Daten eingeschaltet, schließt die Plattform solche Lücken selbst: Sie merkt sich je Knoten, bis zu welchem Zeitpunkt die Historie abgeholt ist, und holt regelmäßig nach, was seither dazugekommen ist. Ein wiederholter Abruf richtet keinen Schaden an, weil bereits gespeicherte, unveränderte Werte verworfen werden.
Wann nachgeladen wird, bestimmen Sie je Server über den Nachladezeitplan — etwa nachts oder außerhalb einer Schicht, wenn die Anlage tagsüber nicht zusätzlich belastet werden soll. Ein Lauf ist zusätzlich in Menge und Dauer begrenzt; läuft er in diese Grenze, hört er zwischen zwei Zeitscheiben auf und der nächste setzt genau dort fort. Es geht dabei nichts verloren, ein Nachlauf wird nur später fertig.
Vier Eigenschaften sollten Sie dabei kennen:
- Der Nachlauf endet einige Minuten vor der Gegenwart — die neuesten Werte kommen ohnehin über die Subscription. Eine beendete Lücke ist daher wenige Stunden später geschlossen, nicht sofort.
- Nachgeladen werden kann nur, was Ihr OPC-UA-Server noch vorhält — und in der Auflösung, in der er es vorhält. Viele Server führen ihre Historie gröber als sie Werte über die Subscription meldet; eine geschlossene Lücke kann daher weniger Werte enthalten als ein gleich langer Zeitraum im Livebetrieb. In einer Messung an einer Anlage waren es rund 6 Werte je Stunde gegenüber 42 im Livebetrieb. Die Lücke ist damit gefüllt, aber nicht ununterscheidbar vom Normalbetrieb.
- Reicht die Aufbewahrung des Servers nicht so weit zurück wie die Lücke, bleibt sie bestehen. Aus demselben Grund reicht der Nachlauf von sich aus höchstens 60 Tage zurück (Betreibereinstellung): Was älter ist, holt die Plattform nicht mehr aus der Anlage, sondern erwartet es aus dem Archiv per Dateiimport.
- Je seltener der Zeitplan, desto später ist eine Lücke geschlossen. Ein nächtlicher Lauf schont die Anlage, hält eine tagsüber entstandene Lücke aber bis zur Nacht offen.
Ohne eingeschalteten historischen Abruf bleiben Lücken dauerhaft bestehen.
Ob eine Verbindung tatsächlich zustande kommt, sehen Sie in der Übersicht in der Spalte Verbindung — mit dem letzten Fehler und dem Zeitpunkt der letzten Knotensuche. Die Spalte Historie vollständig bis daneben zeigt, wie weit der Nachlauf gekommen ist: den ältesten Stand über alle Knoten des Servers, und im Tooltip den letzten Nachlauf, die Zahl der noch nie abgeholten Knoten und die der Knoten, für die der Server keine Historie führt. Je Knoten steht derselbe Stand in der Knotenliste in der Spalte Historie abgeholt bis; wer dort sortiert, sieht zuerst die Knoten, deren Lücke noch offen ist. Vor dem Speichern können Sie die eingegebenen Parameter über Verbindung testen prüfen.
Der opc-ua-client ist auf genau eine Instanz ausgelegt. Mehrere Instanzen
würden dieselben Knoten doppelt abonnieren und damit doppelte Werte erzeugen.
Die Aktivierung und Metrik-Zuordnung einzelner Knoten erfolgt anschließend über
die OPC-UA-Endpunkte der REST-Schnittstelle
(Tag opc-ua, JWT-geschützt, administrativ) — z. B. Server auflisten, Objekt-/
Variablen-Knoten lesen und Knoten aktiv schalten. Ein Signal wird einem Variablenknoten
zugeordnet, indem es mit opcUaNodeId (der Id des gebrowsten Knotens) angelegt wird;
die Plattform bildet dann aus dem Metrik-Muster des Servers den Metrik-Namen, legt die
Metrik mit Herkunft OPC_UA an und verknüpft Knoten und Signal damit. Diese Endpunkte
konfigurieren die Anbindung; sie sind kein Ingestion-Weg für Nutzdaten.
Das interne Kafka-Ingestion-Format
Sowohl mqtt-client als auch opc-ua-client erzeugen dieselbe JSON-Hülle und
senden sie an den Ingestion-Topic (Umgebungsvariable
KAFKA_TOPIC_TIMESERIES_INGESTION, Standard het.ingestion.timeseries). Von
dort schreibt der timeseries-ingestion-consumer die Punkte weg.
Kafka ist ein internes Transportformat ohne externe Authentifizierung. In der Regel produzieren Sie nicht direkt nach Kafka, sondern über MQTT/OPC-UA. Das Format ist hier zum Verständnis und zur Fehlersuche dokumentiert.
{
"tenantId": "customer",
"topic": "int/local/test",
"originalPayload": "{\"timestamp\":\"2026-07-22T09:15:00Z\",\"metric\":\"water.north.level\",\"value\":621.4}",
"payloads": [
{ "timestamp": "2026-07-22T09:15:00Z", "metric": "water.north.level", "value": "621.4" }
]
}
| Feld | Beschreibung |
|---|---|
tenantId | Ziel-Tenant (aus Topic-Zuordnung bzw. OPC-UA-Server-Konfiguration) |
topic | Ursprungs-Topic (nur informativ) |
origin | Herkunft der Daten: MQTT (mqtt-client), OPC_UA (opc-ua-client), IMPORT (data-fetcher). Optional — fehlt das Feld, gilt UNKNOWN. |
originalPayload | Die unveränderte Original-Nachricht (nur informativ) |
payloads[] | Ein oder mehrere Datenpunkte (timestamp, metric, value). MQTT liefert genau einen, OPC-UA batcht mehrere. |
Der Consumer legt pro Tenant und metric automatisch eine Metrik mit der gelieferten
Herkunft an und schreibt die Punkte über ts-service. Die Herkunft einer Metrik wird
einmal gesetzt; nur UNKNOWN wird beim ersten Datenpunkt mit bekannter Herkunft
nachgetragen, eine abweichende Herkunft wird protokolliert und ignoriert. Der value wird dabei zu einer Zahl (Double)
konvertiert; der timestamp muss strikt ISO-8601 sein.
Benachrichtigungen (Notifications)
Ein zweiter interner Kafka-Topic (KAFKA_TOPIC_NOTIFICATION, Standard
het.notification) transportiert Benachrichtigungen, die der
notification-consumer in die Datenbank schreibt. Die Nutzlast enthält ein
notification-Objekt (subject, text, entryTimestamp, acknowledgmentType,
…) sowie recipientUsernames. Produzenten sind interne Plattform-Komponenten;
für Anwendungen sind Benachrichtigungen über die
REST-Schnittstelle (Tag
notifications) zugänglich.
Nicht für externe Dateneinspeisung vorgesehen
Zur Klarstellung: Es existieren zwei REST-Schreibpfade für Zeitreihen, die intern bzw. für den hetida designer bestimmt sind und nicht als Geräte-Ingestion-API dienen:
ts-servicePOST /timeseries– interner Bulk-Insert (kein Auth, benötigt eine bereits existierendemetricId); wird ausschließlich vomtimeseries-ingestion-consumeraufgerufen.- core-backend
POST /api/adapter/timeseries– Bestandteil des hetida-designer-Adapters (JWT-geschützt, administrative Rollen); dient der Datenanbindung des Designers, nicht der Geräte-Einspeisung.
Für die Einspeisung von Feld-/Sensordaten nutzen Sie daher MQTT oder OPC-UA wie oben beschrieben.