@nolag/iot
IoT device telemetry and command dispatch with acknowledgment tracking.
Overview
Connect IoT devices and controllers over real-time channels. Devices push telemetry (sensor readings, status updates, or any structured measurement) while controllers dispatch commands with Promise-based acknowledgment tracking and configurable timeouts. Two roles participate in a device group: devices report data and acknowledge commands, and controllers monitor telemetry streams and issue commands. A single client can act as both. Groups organise devices by fleet, physical location, or function.
Key Features
- Real-time telemetry broadcast from devices to all group subscribers
- Promise-based command dispatch with configurable acknowledgment timeout
- Ephemeral telemetry for high-frequency data without storage overhead
- Ephemeral commands with fire-and-forget dispatch and ack tracking
- Ephemeral acknowledgment channel for command response tracking
- Device online/offline presence via lobby
- Multiple groups per connection; join by zone, building floor, vehicle type, etc.
How It Works
NoLagIoT wraps the @nolag/js-sdk client and manages a lobby that reflects device presence. Calling joinGroup(name) returns a DeviceGroup that subscribes to three topics: telemetry for ephemeral sensor readings, commands for ephemeral command payloads, and _cmd_ack for ephemeral acknowledgment messages. All topics are ephemeral (no server-side retention) to handle high-frequency telemetry without storage overhead. When a controller calls sendCommand() the SDK stores a pending Promise keyed on the command ID; when the target device calls ackCommand() the matching Promise resolves or rejects based on the reported status.
| Topic | Purpose | Replay |
|---|---|---|
telemetry | Sensor readings and device status updates | Ephemeral |
commands | Commands dispatched to devices | Ephemeral |
_cmd_ack | Command acknowledgment messages | Ephemeral |
Installation
npm install @nolag/iot @nolag/js-sdkQuick Start
import { NoLagIoT } from '@nolag/iot'
const client = new NoLagIoT('your-access-token')
await client.connect()
// Join a device group (organised by fleet, location, or function)
const group = await client.joinGroup('sensors-floor-3')
// ── Device side ───────────────────────────────────────────────────────────────
// Send a telemetry reading
group.sendTelemetry('temperature', 22.4, {
unit: 'celsius',
sensorId: 'tmp_01',
})
group.sendTelemetry('humidity', 61, { unit: 'percent' })
// Listen for commands sent by controllers
group.on('command', async ({ command }) => {
console.log('Received command:', command.name, command.params)
// Execute the command...
const result = await handleCommand(command)
// Acknowledge success
await group.ackCommand(command.commandId, 'success', result)
})
// ── Controller side ───────────────────────────────────────────────────────────
// Watch all telemetry from the group
group.on('telemetry', ({ deviceId, sensorId, value, opts, timestamp }) => {
console.log(`[${deviceId}] ${sensorId}: ${value}`)
})
// Retrieve the last known reading for a specific sensor
const reading = await group.getTelemetry('device-007', 'temperature')
console.log('Last temperature:', reading)
// Send a command (returns a Promise that resolves when the device acks)
try {
const cmd = await group.sendCommand('device-007', 'reboot', {
delay: 5000,
})
// Promise resolves once the device calls ackCommand
console.log('Command acked by device:', cmd.status)
} catch (err) {
// Throws if the device does not ack within the configured timeout
console.error('Command timed out or rejected:', err)
}
// Monitor ack events
group.on('commandAck', ({ commandId, deviceId, status, result }) => {
console.log(`Command ${commandId} acked by ${deviceId} with status ${status}`)
})
// Presence
client.on('deviceOnline', ({ deviceId }) => console.log('Online:', deviceId))
client.on('deviceOffline', ({ deviceId }) => console.log('Offline:', deviceId))API Reference
NoLagIoT
| Method | Returns | Description |
|---|---|---|
connect() | Promise<void> | Establish the WebSocket connection and join the lobby. |
disconnect() | void | Close the connection and leave the lobby. |
joinGroup(name) | Promise<DeviceGroup> | Subscribe to a device group. Returns the group instance. |
leaveGroup(name) | Promise<void> | Unsubscribe from a device group and release its resources. |
getOnlineDevices() | Device[] | Return all devices currently present in the lobby. |
NoLagIoT Events
| Event | Payload | Description |
|---|---|---|
connected | none | WebSocket connection established. |
disconnected | reason: string | Connection closed. |
reconnected | none | Connection restored; room membership and presence are restored automatically. |
error | error: Error | A transport or protocol error occurred. |
deviceOnline | device: Device | A device joined the lobby. |
deviceOffline | device: Device | A device left the lobby. |
DeviceGroup
| Method | Role | Returns | Description |
|---|---|---|---|
sendTelemetry(sensorId, value, opts?) | Device | void | Publish a sensor reading to the group. opts carries metadata such as unit or tags. |
getTelemetry(deviceId?, sensorId?) | Both | Promise<TelemetryReading[]> | Fetch the most recent telemetry. Omit arguments to get all devices and sensors. |
sendCommand(targetDeviceId, command, params?) | Controller | Promise<DeviceCommand> | Dispatch a named command to a device. Resolves when the device acknowledges, or rejects on timeout. |
ackCommand(commandId, status, result?) | Device | void | Send an acknowledgment for a received command. status is 'success' or 'error'. |
DeviceGroup Events
| Event | Payload | Description |
|---|---|---|
telemetry | { deviceId, sensorId, value, opts?, timestamp } | A telemetry reading was received from a device in the group. |
command | { command: DeviceCommand } | A command was dispatched to this device (device role only). |
commandAck | { commandId, deviceId, status, result? } | A device acknowledged a previously dispatched command. |
deviceJoined | device: Device | A device joined this group. |
deviceLeft | device: Device | A device left this group. |