WebSocket API Integration
Overview
This system uses a native WebSocket connection over WSS to stream real-time asset telemetry from the IoT Pro backend.
Socket URL: wss://iotpro.iotsolutions.com.mt/api/ws
Authentication is handled via a JWT token, which is required for both REST API access and WebSocket subscription.
Token TTL: ~2.5 hours
Refresh strategy: token is refreshed 5 minutes before expiry
Connection Flow
The client (iotpro-socket-client.js) available in Resources (at the bottom of this page) follows this sequence:
Authenticate with backend and obtain JWT
A dedicated account (provisioned for WebSocket connections) is used
Start JWT refresh scheduler
Fetch all assets that the authenticated user can read
A map of asset identifiers to asset names is created here
Fetch additional asset details based on asset profiles
Open a WebSocket, authenticate, and subscribe to all assets
Parse and handle incoming telemetry data
API Calls
Authentication
Log In
A JWT token is obtained using the login endpoint:
const response = await fetch(`https://iotpro.iotsolutions.com.mt/api/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify({
username: IOTPRO_USERNAME,
password: IOTPRO_PASSWORD
})
}
);The returned JWT is cached and used for subsequent API calls and WebSocket authentication.
Refresh Token
The token is refreshed using this endpoint:
const response = await fetch(`https://iotpro.iotsolutions.com.mt/api/auth/token`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
refreshToken: iotproRefreshToken
})
});Assets
Fetching Assets
The following endpoint is used to fetch all assets using pagination.
const res = await fetch(`https://iotpro.iotsolutions.com.mt/api/user/assets?page=${page}&pageSize=${PAGE_SIZE}`, {
method: "GET",
headers: {
"X-Authorization": `Bearer ${iotproJwt}`
}
}
);Assets are mapped into a local lookup table for fast resolution during streaming.
const data = await res.json();
for (const asset of data.data) {
const id = asset.id?.id;
const type = asset.type;
if (!id || !type) continue;
assets[id] = {
name: asset.name,
type
};
}Asset Details
Additional asset metadata is retrieved via telemetry attributes:
const res = await fetch(`https://iotpro.iotsolutions.com.mt/api/plugins/telemetry/ASSET/${uuid}/values/attributes/SERVER_SCOPE`, {
method: "GET",
headers: {
"X-Authorization": `Bearer ${iotproJwt}`
}
}
);Asset details can then be parsed as follows:
const data = await res.json();
const assetDetails = data.find(attr => attr.key === "assetDetails")?.value;WebSocket Connection
Creating a Connection
const socketUrl = "wss://iotpro.iotsolutions.com.mt/api/ws";
const ws = new WebSocket(socketUrl);Authentication and Subscription
ws.on("open", () => {
const msg = {
authCmd: {
cmdId: 0,
token: jwt
},
// NOTE commands are created according to the number of assets and PAGE_SIZE
cmds: [
{
cmdId: 1,
type: "ENTITY_DATA",
query: {
entityFilter: {
type: "entityType",
entityType: "ASSET"
},
pageLink: {
pageSize: 1000,
page: 0
},
entityFields: [
{ type: "ENTITY_FIELD", key: "name" }
],
latestValues: []
},
latestCmd: {
keys: [
// NOTE add relevant keys for required asset profiles
{ type: "TIME_SERIES", key: "device_id" },
{ type: "TIME_SERIES", key: "telemetry_param_latitude" },
{ type: "TIME_SERIES", key: "telemetry_param_longitude" }
]
}
}
]
};
ws.send(JSON.stringify(msg));
});Incoming Message Handling
Incoming WebSocket messages contain telemetry updates for subscribed assets. Each message is associated to a specific subscription via cmdId, which identifies the originating command in the initial WebSocket request.
All incoming messages follow the structure below:
{
"cmdId": 1,
"update": [
{
"entityId": { "id": "..." },
"latest": { "TIME_SERIES": { ... } }
}
]
}cmdId: Identifier of the subscription commandupdate: Array of entity updates returned for the subscriptionentityId.id: Unique asset identifierlatest.TIME_SERIES: Latest telemetry values for the asset
Incoming messages are parsed and mapped to cached asset metadata.
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString());
const updates = [];
for (const update of msg.update) {
const id = update.entityId?.id;
if (!id || !assetsLookup[id]) continue;
const timeSeries = update.latest?.TIME_SERIES;
if (!timeSeries || Object.keys(timeSeries).length === 0) continue;
updates.push({
name: assetsLookup[id].name,
profile: assetsLookup[id].type,
data: timeSeries
});
}
return updates;
});Connection Management
This section describes how authentication and WebSocket stability are maintained during runtime.
Token Refresh
JWT tokens are refreshed before expiry to maintain session validity.
const expiry = getJwtExpiry(iotproJwt);
const refreshInMs = Math.max(expiry - Date.now() - 5 * 60 * 1000, 1000);
tokenRefreshTimer = setTimeout(async () => {
try {
await refreshToken();
} catch {
await login();
}
startTokenRefreshTimer();
}, refreshInMs);WebSocket Reconnection
On a socket closed event, the client performs a throttled reconnection and ensures authentication is valid.
setTimeout(async () => {
try {
await refreshToken();
} catch {
await login();
}
startSocket();
}, 5000);