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 with | Purpose |
|---|---|
src/device.ts | Implement command handling, state updates, and event logic |
package.json / README.md | Check 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
- Download and extract the package, then open its root directory.
- Check the generated
.env. Copy.env.exampleonly when it is absent, then complete connection settings. - Install the package dependencies with npm.
- Connect the real device or business service in
src/device.ts. - Start the program and verify it in the Device Agent workspace.
The minimum install and start commands are:
test -f .env || cp .env.example .env
npm install
npm run startFor 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:
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.
| Purpose | Interface |
|---|---|
| Validate and report state | await device.reportState(patch) |
| Validate and report modeled events | await device.emitRuntimeEvent(eventName, payload) |
| Handle modeled commands | device.onCommand(handler) or onRuntimeCommand() |
| Apply supported desired state | device.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:
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:
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.