API
API
optiCLOUD ofrece varias interfaces técnicas para conectar dispositivos, leer datos y automatizar procesos. En el día a día son relevantes sobre todo dos tipos de acceso:
- la REST API, para autenticación, gestión de dispositivos, acceso a archivos y consultas mediante HTTP
- la MQTT API, para el intercambio de datos directamente entre el dispositivo y la plataforma
Esta página resume los flujos básicos de la API y sirve como introducción técnica para integraciones y scripts.
Contexto
Las dos API cumplen funciones distintas:
- La REST API es adecuada para scripts de backend, evaluaciones, descargas de archivos, búsqueda de dispositivos y operaciones de administración.
- La MQTT API es adecuada para dispositivos, gateways y sistemas embebidos que deban enviar o consultar telemetría o atributos directamente a la plataforma.
Además, desde el User Menu se puede acceder a la API Description. Allí se documentan de forma interactiva los endpoints REST disponibles.
Fundamentos de la REST API
Autenticación
Para los accesos REST se establece primero una sesión. El flujo típico consta de dos pasos:
POST /api/noauth/authorizationPOST /api/auth/login
En el primer paso se envían a la plataforma el nombre de usuario y la contraseña. Como respuesta se recibe una lista de los tenants a los que tiene acceso el usuario. A continuación, de la asignación de tenant deseada se toma el authorityId y se envía, junto con el nombre de usuario y la contraseña, al endpoint de inicio de sesión.
Como resultado, la plataforma devuelve un token que se incluye en las solicitudes posteriores en el encabezado X-Authorization: Bearer <token>.
Un posible ejemplo en Python:
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",
}
La selección concreta del tenant solo es necesaria si un usuario tiene acceso a varios tenants.
Obtener la lista de dispositivos de un tenant
Para listar los dispositivos de un tenant se puede utilizar el endpoint GET /api/tenants/{tenant_id}/devices. Normalmente se establecen para ello los parámetros page=0 y limit=-1, con el fin de obtener todos los dispositivos en una sola respuesta.
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"]
De este modo se obtienen, entre otros datos, los ID de dispositivo necesarios para otras llamadas a la API.
Consultar los archivos OSF de un dispositivo
Los archivos OSF procesados o ya conocidos por el servidor pueden obtenerse mediante GET /api/devices/{device_id}/data-files/original.
Parámetros típicos:
startTime, como marca de tiempo Unix en milisegundosendTime, como marca de tiempo Unix en milisegundoslimitpage
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"]
Consultar la lista completa de archivos y el estado de almacenamiento
Si no solo se necesitan los archivos disponibles en el servidor, sino todos los archivos conocidos, incluido su estado, puede utilizarse además el endpoint /API/filelist/list/....
Hay que tener en cuenta algunas particularidades:
- El endpoint devuelve XML en lugar de JSON.
- La URL utiliza
/API/en mayúsculas. - Aquí las indicaciones de tiempo se esperan en segundos, no en milisegundos.
Además, primero se necesita el token del dispositivo. Este puede leerse mediante GET /api/device/{deviceId}/credentials. Allí es relevante el campo 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"]
Valores de estado posibles:
0: solo en el dispositivo1: solicitado por el servidor2: solo en el servidor3: tanto en el dispositivo como en el servidor
Para este endpoint XML debe establecerse un encabezado Accept con compatibilidad con XML. Sin un encabezado adecuado, la plataforma puede responder con 406 Not Acceptable.
Para leer las credenciales de los dispositivos, la cuenta de usuario necesita los derechos adecuados, en particular para mostrar dispositivos y credenciales de dispositivos.
Descargar archivos OSF
La descarga de archivos se realiza normalmente en dos pasos:
POST /api/device/{device_id}/data-files/CLIENT_SCOPE/downloadGET /api/device/{device_id}/data-files/CLIENT_SCOPE/token/{download_token}
En el primer paso se solicita el archivo deseado mediante su nombre de archivo. La respuesta contiene un token de descarga. En el segundo paso se descarga el archivo propiamente dicho con ese token.
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
Si se solicitan varios archivos a la vez, la respuesta suele ser un archivo ZIP y no un único archivo .osfz.
Obtener los últimos datos de telemetría de un dispositivo
Para obtener valores actuales puede utilizarse GET /api/plugins/telemetry/DEVICE/{device_id}/values/timeseries. En muchos casos, startTs y endTs se establecen simplemente en el momento actual cuando solo se necesitan los valores más recientes.
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()
El resultado contiene los valores actuales de cada canal de datos junto con las marcas de tiempo.
Obtener la comunicación del dispositivo
Los eventos de comunicación de un dispositivo pueden consultarse mediante GET /api/devices/{deviceId}/communication/data. Según el dispositivo y la configuración de la plataforma, esto incluye, por ejemplo, entradas de PING, ATTN o devoluciones de archivos.
Parámetros necesarios:
startTimeendTimelimitpage
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()
Enviar comandos a un dispositivo
Algunos endpoints REST sirven para poner en cola comandos para un dispositivo. Normalmente, estos endpoints no establecen una conexión en línea directa con el dispositivo. En su lugar, el comando se procesa en la siguiente conexión del dispositivo.
Un ejemplo es el listado de archivos mediante LIST_FILES. Para ello se utiliza POST /api/devices/{deviceId}/request-list.
El cuerpo de la solicitud contiene normalmente:
startTimeen formato ISO 8601endTimeen formato ISO 8601tag, a menudo por ejemplopreview
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()
Fundamentos de la MQTT API
La interfaz MQTT está pensada para dispositivos que se comunican directamente con optiCLOUD. Las tareas típicas son:
- Enviar telemetría
- Cargar atributos de cliente
- Solicitar atributos shared o de cliente
- Suscribirse a atributos shared
La autenticación se realiza habitualmente mediante el Access Token del dispositivo como nombre de usuario MQTT.
Formato JSON admitido
La MQTT API utiliza de forma predeterminada un formato clave-valor basado en JSON. Las claves son cadenas de texto; los valores pueden ser, por ejemplo, cadenas de texto, valores booleanos, números o también objetos JSON anidados.
{
"stringKey": "value1",
"booleanKey": true,
"doubleKey": 42.0,
"longKey": 73,
"jsonKey": {
"someNumber": 42,
"someArray": [1, 2, 3],
"someNestedObject": {
"key": "value"
}
}
}
Enviar telemetría por MQTT
Para cargar datos de telemetría se publica en el topic v1/devices/me/telemetry.
Forma sencilla sin marca de tiempo propia:
{"temperature": 42}
Alternativamente también es posible un array de objetos:
[{"key1": "value1"}, {"key2": true}]
Si no se envía ninguna marca de tiempo, la plataforma utiliza la marca de tiempo del servidor.
Si el propio dispositivo puede registrar marcas de tiempo, normalmente se utiliza el siguiente formato:
{
"ts": 1451649600512,
"values": {
"temperature": 42,
"humidity": 55
}
}
Aquí, ts es una marca de tiempo Unix en milisegundos.
Un ejemplo con mosquitto_pub:
mosquitto_pub -d -q 1 \
-h "demo.opticloud.io" \
-t "v1/devices/me/telemetry" \
-u "$ACCESS_TOKEN" \
-m '{"temperature":42}'
Sustituya demo.opticloud.io por su host y $ACCESS_TOKEN por el Access Token del dispositivo.
Enviar atributos por MQTT
Los atributos de dispositivo del lado del cliente se publican en el topic v1/devices/me/attributes.
Ejemplo:
{
"attribute1": "value1",
"attribute2": true
}
Ejemplo con mosquitto_pub:
mosquitto_pub -d \
-h "demo.opticloud.io" \
-t "v1/devices/me/attributes" \
-u "$ACCESS_TOKEN" \
-m '{"attribute1":"value1","attribute2":true}'
Solicitar atributos al servidor
Los dispositivos también pueden solicitar activamente al servidor atributos de cliente y shared. Para ello se publica en un topic de solicitud:
v1/devices/me/attributes/request/$request_id
Al mismo tiempo, el dispositivo debe suscribirse al topic de respuesta correspondiente:
v1/devices/me/attributes/response/+
Como la publicación y la suscripción deben realizarse dentro de la misma sesión MQTT, esto se implementa con frecuencia con una biblioteca de cliente MQTT. Un ejemplo mínimo con 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();
});
Para iniciar el ejemplo:
export TOKEN=$ACCESS_TOKEN
node mqtt-js-attributes-request.js
Indicaciones para la práctica
Para las integraciones en producción son importantes sobre todo los siguientes puntos:
- REST y MQTT tienen responsabilidades distintas y deben combinarse de forma selectiva.
- Algunos endpoints trabajan con milisegundos y otros con segundos.
- Para los endpoints XML es importante un encabezado
Acceptadecuado. - Los comandos a dispositivos a menudo solo se registran en una cola y no se ejecutan de inmediato.
- Para endpoints sensibles, como las credenciales de dispositivos, se necesitan permisos adicionales.
Contexto general
La API constituye la base técnica para integraciones externas, software de dispositivos, scripts de automatización y evaluaciones específicas de clientes en torno a optiCLOUD.
Para el manejo a través de la interfaz son relevantes en particular las áreas Control Center, Dashboards y Automation. La API complementa estas áreas cuando se desea procesar datos o funciones fuera de la interfaz estándar.