Zum Hauptinhalt springen

API

API

optiCLOUD stellt mehrere technische Schnittstellen bereit, um Geräte anzubinden, Daten auszulesen und Prozesse zu automatisieren. Im Alltag sind dabei vor allem zwei Zugriffsarten relevant:

  • die REST API für Authentifizierung, Geräteverwaltung, Dateizugriff und Abfragen über HTTP
  • die MQTT API für den Datenaustausch direkt zwischen Gerät und Plattform

Diese Seite fasst die grundlegenden API-Abläufe zusammen und dient als technischer Einstieg für Integrationen und Skripte.

Einordnung

Die beiden APIs erfüllen unterschiedliche Aufgaben:

  • REST API eignet sich für Backend-Skripte, Auswertungen, Dateidownloads, Gerätesuche und Verwaltungsoperationen.
  • MQTT API eignet sich für Geräte, Gateways und Embedded-Systeme, die Telemetrie oder Attribute direkt an die Plattform senden oder abrufen sollen.

Zusätzlich ist über das User Menu die API Description erreichbar. Dort werden verfügbare REST-Endpunkte interaktiv dokumentiert.

REST API Grundlagen

Authentifizierung

Für REST-Zugriffe wird zunächst eine Sitzung aufgebaut. Der typische Ablauf besteht aus zwei Schritten:

  1. POST /api/noauth/authorization
  2. POST /api/auth/login

Im ersten Schritt werden Benutzername und Passwort an die Plattform gesendet. Als Antwort kommt eine Liste der Tenants zurück, auf die der Benutzer Zugriff hat. Anschließend wird aus der gewünschten Tenant-Zuordnung die authorityId entnommen und zusammen mit Benutzername und Passwort an den Login-Endpunkt gesendet.

Als Ergebnis liefert die Plattform ein Token zurück, das bei weiteren Anfragen im Header X-Authorization: Bearer <token> mitgegeben wird.

Ein mögliches Python-Beispiel:

import requests

BASEURL = "https://example.opticloud.io"
CREDENTIALS = {"username": "user@example.com", "password": "secret"}
TENANT = "MyTenant"

def login(baseurl, credentials, tenant):
auth_response = requests.post(
baseurl + "/api/noauth/authorization",
json=credentials,
verify=True,
)
auth_response.raise_for_status()

selected = None
for entry in auth_response.json():
try:
if entry["tenant"]["name"] == tenant:
selected = entry
break
except KeyError:
continue

if selected is None:
raise RuntimeError("Tenant not found")

login_body = {
"username": credentials["username"],
"password": credentials["password"],
"authorityId": selected["authorityId"]["id"],
}

login_response = requests.post(
baseurl + "/api/auth/login",
json=login_body,
verify=True,
)
login_response.raise_for_status()
token = login_response.json()["token"]
return {
"X-Authorization": f"Bearer {token}",
"Accept": "text/html, application/json, application/xhtml+xml, application/xml;q=0.9",
}
hinweis

Die konkrete Tenant-Auswahl ist nur dann notwendig, wenn ein Benutzer Zugriff auf mehrere Tenants besitzt.

Geräteliste eines Tenants abrufen

Um Geräte eines Tenants aufzulisten, kann der Endpunkt GET /api/tenants/{tenant_id}/devices verwendet werden. Typischerweise werden dafür die Parameter page=0 und limit=-1 gesetzt, um alle Geräte in einer Antwort abzurufen.

def get_devices(baseurl, header, tenant_id):
response = requests.get(
baseurl + f"/api/tenants/{tenant_id}/devices",
headers=header,
params={"page": 0, "limit": -1},
verify=True,
)
response.raise_for_status()
return response.json()["data"]

Damit erhält man unter anderem die Geräte-IDs, die für weitere API-Aufrufe benötigt werden.

OSF-Dateien eines Geräts abfragen

Verarbeitete oder vom Server bereits bekannte OSF-Dateien lassen sich über GET /api/devices/{device_id}/data-files/original abrufen.

Typische Parameter:

  • startTime als Unix-Zeitstempel in Millisekunden
  • endTime als Unix-Zeitstempel in Millisekunden
  • limit
  • page
def get_processed_osf_files(baseurl, header, device_id, start_time, end_time):
response = requests.get(
baseurl + f"/api/devices/{device_id}/data-files/original",
headers=header,
params={
"startTime": start_time,
"endTime": end_time,
"limit": -1,
"page": 0,
},
verify=True,
)
response.raise_for_status()
return response.json()["data"]

Vollständige Dateiliste und Speicherstatus abfragen

Wenn nicht nur die serverseitig verfügbaren Dateien benötigt werden, sondern alle bekannten Dateien inklusive Status, kann zusätzlich der Endpunkt /API/filelist/list/... verwendet werden.

Dabei sind einige Besonderheiten zu beachten:

  • Der Endpunkt liefert XML statt JSON.
  • Die URL verwendet /API/ in Großschreibung.
  • Zeitangaben werden hier in Sekunden erwartet, nicht in Millisekunden.

Zusätzlich wird zunächst der Geräte-Token benötigt. Dieser lässt sich über GET /api/device/{deviceId}/credentials auslesen. Relevant ist dort das Feld credentialsId.

import xmltodict

def get_listed_files(baseurl, header, device_id, start_time, end_time):
credentials_response = requests.get(
baseurl + f"/api/device/{device_id}/credentials",
headers=header,
verify=True,
)
credentials_response.raise_for_status()
credentials_id = credentials_response.json()["credentialsId"]

list_response = requests.get(
baseurl + f"/API/filelist/list/deviceid/{credentials_id}/StartTime/{start_time}/{end_time}",
headers=header,
verify=True,
)
list_response.raise_for_status()

list_dict = xmltodict.parse(list_response.content)
return list_dict["FileList"]["GeoLogFile"]

Mögliche Statuswerte:

  • 0 nur auf dem Gerät vorhanden
  • 1 vom Server angefragt
  • 2 nur auf dem Server vorhanden
  • 3 sowohl auf Gerät als auch auf Server vorhanden
info

Für diesen XML-Endpunkt sollte ein Accept-Header mit XML-Unterstützung gesetzt werden. Ohne passenden Header kann die Plattform mit 406 Not Acceptable antworten.

hinweis

Um Geräte-Anmeldeinformationen auszulesen, benötigt das Benutzerkonto die passenden Rechte, insbesondere für das Anzeigen von Geräten und Geräte-Anmeldeinformationen.

OSF-Dateien herunterladen

Der Download von Dateien erfolgt typischerweise in zwei Schritten:

  1. POST /api/device/{device_id}/data-files/CLIENT_SCOPE/download
  2. GET /api/device/{device_id}/data-files/CLIENT_SCOPE/token/{download_token}

Im ersten Schritt wird die gewünschte Datei per Dateinamen angefordert. Die Antwort enthält ein Download-Token. Im zweiten Schritt wird die eigentliche Datei mit diesem Token heruntergeladen.

import os

def download_processed_osf_file(baseurl, header, device_id, filename, savedir):
response = requests.post(
baseurl + f"/api/device/{device_id}/data-files/CLIENT_SCOPE/download",
headers=header,
json=[filename],
verify=True,
)
response.raise_for_status()
download_token = response.text

file_response = requests.get(
baseurl + f"/api/device/{device_id}/data-files/CLIENT_SCOPE/token/{download_token}",
headers=header,
verify=True,
)
file_response.raise_for_status()

target = os.path.join(savedir, filename.split("/")[-1])
with open(target, "wb") as handle:
handle.write(file_response.content)
return target
info

Wenn mehrere Dateien gleichzeitig angefordert werden, ist die Antwort in der Regel ein ZIP-Archiv und keine einzelne .osfz-Datei.

Letzte Telemetriedaten eines Geräts abrufen

Für den Abruf aktueller Werte kann GET /api/plugins/telemetry/DEVICE/{device_id}/values/timeseries verwendet werden. In vielen Fällen werden startTs und endTs einfach auf den aktuellen Zeitpunkt gesetzt, wenn nur die neuesten Werte benötigt werden.

import time

def get_latest_telemetry(baseurl, header, device_id):
now_ms = int(time.time() * 1000)
response = requests.get(
baseurl + f"/api/plugins/telemetry/DEVICE/{device_id}/values/timeseries",
headers=header,
params={"startTs": now_ms, "endTs": now_ms},
verify=True,
)
response.raise_for_status()
return response.json()

Das Ergebnis enthält die aktuellen Werte je Datenkanal zusammen mit Zeitstempeln.

Gerätekommunikation abrufen

Kommunikationsereignisse eines Geräts lassen sich über GET /api/devices/{deviceId}/communication/data abfragen. Dazu zählen je nach Gerät und Plattformkonfiguration beispielsweise Einträge zu PING, ATTN oder Dateirückgaben.

Benötigte Parameter:

  • startTime
  • endTime
  • limit
  • page
def get_device_communication(baseurl, header, device_id, start_ts, end_ts):
response = requests.get(
baseurl + f"/api/devices/{device_id}/communication/data",
headers=header,
params={
"startTime": start_ts,
"endTime": end_ts,
"limit": 2147483647,
"page": 0,
},
verify=True,
)
response.raise_for_status()
return response.json()

Kommandos an ein Gerät senden

Einige REST-Endpunkte dienen dazu, Kommandos für ein Gerät in die Warteschlange einzustellen. Diese Endpunkte bauen normalerweise keine direkte Online-Verbindung zum Gerät auf. Stattdessen wird das Kommando bei der nächsten Verbindung des Geräts verarbeitet.

Ein Beispiel ist die Dateiauflistung per LIST_FILES. Dafür wird POST /api/devices/{deviceId}/request-list verwendet.

Der Request-Body enthält typischerweise:

  • startTime im ISO-8601-Format
  • endTime im ISO-8601-Format
  • tag, häufig zum Beispiel preview
def request_list(baseurl, header, device_id, start_time, end_time, tag):
response = requests.post(
baseurl + f"/api/devices/{device_id}/request-list",
headers=header,
json={
"startTime": start_time,
"endTime": end_time,
"tag": tag,
},
verify=True,
)
response.raise_for_status()

MQTT API Grundlagen

Die MQTT-Schnittstelle ist für Geräte gedacht, die direkt mit optiCLOUD kommunizieren. Typische Aufgaben sind:

  • Telemetrie senden
  • Client-Attribute hochladen
  • Shared- oder Client-Attribute anfordern
  • Shared-Attribute abonnieren

Die Authentifizierung erfolgt üblicherweise über das Geräte-Access-Token als MQTT-Benutzername.

Unterstütztes JSON-Format

Die MQTT API verwendet standardmäßig ein Key-Value-Format auf JSON-Basis. Schlüssel sind Strings, Werte können zum Beispiel Strings, Boolean-Werte, Zahlen oder auch verschachtelte JSON-Objekte sein.

{
"stringKey": "value1",
"booleanKey": true,
"doubleKey": 42.0,
"longKey": 73,
"jsonKey": {
"someNumber": 42,
"someArray": [1, 2, 3],
"someNestedObject": {
"key": "value"
}
}
}

Telemetrie per MQTT senden

Zum Hochladen von Telemetriedaten wird auf das Topic v1/devices/me/telemetry publiziert.

Einfache Form ohne eigenen Zeitstempel:

{"temperature": 42}

Alternativ ist auch ein Array aus Objekten möglich:

[{"key1": "value1"}, {"key2": true}]

Wenn kein Zeitstempel mitgesendet wird, verwendet die Plattform den serverseitigen Zeitstempel.

Wenn das Gerät selbst Zeitstempel erfassen kann, wird meist folgendes Format verwendet:

{
"ts": 1451649600512,
"values": {
"temperature": 42,
"humidity": 55
}
}

Dabei ist ts ein Unix-Zeitstempel in Millisekunden.

Ein Beispiel mit mosquitto_pub:

mosquitto_pub -d -q 1 \
-h "demo.opticloud.io" \
-t "v1/devices/me/telemetry" \
-u "$ACCESS_TOKEN" \
-m '{"temperature":42}'
info

Ersetzen Sie demo.opticloud.io durch Ihren Host und $ACCESS_TOKEN durch das Access-Token des Geräts.

Attribute per MQTT senden

Client-seitige Geräteattribute werden auf das Topic v1/devices/me/attributes publiziert.

Beispiel:

{
"attribute1": "value1",
"attribute2": true
}

Beispiel mit mosquitto_pub:

mosquitto_pub -d \
-h "demo.opticloud.io" \
-t "v1/devices/me/attributes" \
-u "$ACCESS_TOKEN" \
-m '{"attribute1":"value1","attribute2":true}'

Attribute vom Server anfordern

Geräte können Client- und Shared-Attribute auch aktiv vom Server anfordern. Dafür wird auf ein Request-Topic publiziert:

v1/devices/me/attributes/request/$request_id

Gleichzeitig muss das Gerät auf das passende Antwort-Topic abonnieren:

v1/devices/me/attributes/response/+

Da Publish und Subscribe innerhalb derselben MQTT-Sitzung stattfinden müssen, wird das häufig mit einer MQTT-Client-Bibliothek umgesetzt. Ein minimales Beispiel mit mqtt.js:

var mqtt = require('mqtt');

var client = mqtt.connect('mqtt://demo.opticloud.io', {
username: process.env.TOKEN
});

client.on('connect', function () {
client.subscribe('v1/devices/me/attributes/response/+');
client.publish(
'v1/devices/me/attributes/request/1',
'{"clientKeys":"attribute1,attribute2","sharedKeys":"shared1,shared2"}'
);
});

client.on('message', function (topic, message) {
console.log('response.topic: ' + topic);
console.log('response.body: ' + message.toString());
client.end();
});

Zum Starten des Beispiels:

export TOKEN=$ACCESS_TOKEN
node mqtt-js-attributes-request.js

Hinweise für die Praxis

Für produktive Integrationen sind vor allem diese Punkte wichtig:

  • REST und MQTT haben unterschiedliche Zuständigkeiten und sollten gezielt kombiniert werden.
  • Einige Endpunkte arbeiten mit Millisekunden, andere mit Sekunden.
  • Für XML-Endpunkte ist ein passender Accept-Header wichtig.
  • Kommandos an Geräte werden häufig nur in eine Warteschlange eingetragen und nicht sofort ausgeführt.
  • Für sensible Endpunkte wie Geräte-Credentials werden zusätzliche Berechtigungen benötigt.

Einordnung

Die API bildet die technische Grundlage für externe Integrationen, Gerätesoftware, Automatisierungsskripte und kundenspezifische Auswertungen rund um optiCLOUD.

Für die Bedienung über die Oberfläche sind insbesondere die Bereiche Control Center, Dashboards und Automation relevant. Die API ergänzt diese Bereiche, wenn Daten oder Funktionen außerhalb der Standardoberfläche verarbeitet werden sollen.