Skip to content

Node.js SDK ​

The Node.js SDK is for TypeScript or JavaScript device programs, gateways, edge services, and integrations with existing Node.js services or business APIs. The generated package uses @device-agent/device-sdk and RuntimeDevice; add real device behavior in src/device.ts.

Start withPurpose
src/device.tsImplement command handling, state updates, and event logic
package.json / README.mdCheck dependencies, scripts, and run commands

For .env, runtime-model.json, and device-spec.json, follow the shared project file and model rules.

Projects for the current adapter ​

The examples below use an SDK project for devices managed directly by Device Agent. Other adapters may use different callbacks; follow the package README. Where writable state is supported, implement its update callback as well as command handling, and report actual values after applying them to hardware.

Install and Start ​

  1. Download and extract the package, then open its root directory.
  2. Check the generated .env. Copy .env.example only when it is absent, then complete connection settings.
  3. Install the package dependencies with npm.
  4. Connect the real device or business service in src/device.ts.
  5. Start the program and verify it in the Device Agent workspace.

The minimum install and start commands are:

bash
test -f .env || cp .env.example .env
npm install
npm run start

For a Bun project, use bun install and bun run start.

src/index.ts loads .env and creates the device instance, while src/device.ts owns commands, state, and events. packages/device-sdk, packages/iot-platform, and packages/shared are local dependencies included in the package.

Implement Commands, State, and Events ​

The generated GeneratedDevice extends RuntimeDevice. After model validation, commands reach onRuntimeCommand() or a callback registered through onCommand(). Keep this execution path instead of overriding the lower-level handleCommand() and bypassing validation.

Extend the existing onRuntimeCommand() method in the GeneratedDevice class in src/device.ts, preserving the generated vision branch. The model must declare set_target_temperature(value) with a numeric value parameter and a target_temperature property, as in Define a Device Agent. Implement setTargetTemperature() in the same file to write to hardware and return actual state; throw on hardware failure:

ts
protected override async onRuntimeCommand(
  context: RuntimeCommandContext<GeneratedDeviceState>,
): Promise<DeviceResponseInit> {
  const { message, params } = context;
  if (this.shouldHandleVisionCommand(message.cmd)) {
    return this.handleVisionRecognitionCommand(message, params);
  }
  if (message.cmd !== "set_target_temperature") {
    return { code: 501, msg: "Command not implemented" };
  }
  const actualState = await setTargetTemperature(Number(params.value));
  await this.reportState(actualState);
  return { code: 0, msg: "ok" };
}

Keep the generated vision helper methods and add handlers for other required actions. A callback registered through onCommand() takes precedence over onRuntimeCommand() and must handle every required action. Return failure for unimplemented commands rather than treating a default acknowledgement as hardware success.

PurposeInterface
Validate and report stateawait device.reportState(patch)
Validate and report modeled eventsawait device.emitRuntimeEvent(eventName, payload)
Handle modeled commandsdevice.onCommand(handler) or onRuntimeCommand()
Apply supported desired statedevice.onUpdateDeviceState(handler)

Parameters, state, and event fields must match the current model. Desired-state callbacks apply hardware writes. Reported state represents the actual result; do not report success after a hardware failure.

Add Voice and Vision ​

Voice ​

Import VoiceClient from @device-agent/device-sdk, pass wsUrl explicitly, and connect microphone capture, speaker playback, and event listeners. VoiceClient requires a global WebSocket, available in Node.js 22 or later and Bun. The included example can read VOICE_WS_URL:

ts
import { VoiceClient } from "@device-agent/device-sdk";

const voice = new VoiceClient({
  wsUrl: process.env.VOICE_WS_URL ?? "ws://127.0.0.1:3001/ws/voice",
  deviceId: process.env.DEVICE_ID ?? "device-001",
  productId: process.env.PRODUCT_ID ?? "agent-001",
});

voice.on("agentReply", (text) => console.log(text));

await voice.connect();
voice.startListening("manual");
voice.sendAudio(pcmChunk);
voice.stopListening();

The complete example is in packages/device-sdk/examples/voice-chat.ts.

Vision ​

VOICE_CHAT_HOST configures vision upload and chat for the generated device. src/device.ts includes the generated package's preset single-photo recognition flow; see the package README for its trigger commands. Override captureLocalVisionImage() to return one image from a camera, screenshot, or image file:

ts
protected override async captureLocalVisionImage() {
  return {
    mimeType: "image/jpeg",
    imageBase64: await readCameraFrameAsBase64(),
    source: "sdk-camera",
  };
}

The generated code returns the recognition result as the command response.

See Voice Interaction and Camera and Vision for complete media settings. After startup, use the checks in SDK Access to verify online state, commands, state reports, and events.