DocumentationDevice interactionBeginner's guidesExchange data with the device

Exchange data with the device

In this guide, you’ll learn how to send commands to a connected Ledger device and process the responses using the Device Management Kit (DMK). By the end, you’ll understand multiple ways to communicate with your hardware device.

What you’ll learn

  • How to send low-level APDU commands to a device
  • How to use pre-defined commands for common operations
  • How to work with device actions for complex flows
  • How to handle user interactions during device communication

Prerequisites

Before starting this tutorial, make sure you’ve completed:

You should have a working DMK instance and a connected device with a valid sessionId.

Step 1: Sending an APDU command

APDU (Application Protocol Data Unit) commands are the low-level protocol used to communicate with Ledger devices. While it’s generally recommended to use higher-level abstractions, understanding APDUs can be helpful for advanced use cases.

import {
  ApduBuilder,
  ApduParser,
  CommandUtils,
} from "@ledgerhq/device-management-kit";
 
// Build the APDU to open the Bitcoin app
const openAppApduArgs = {
  cla: 0xe0,
  ins: 0xd8,
  p1: 0x00,
  p2: 0x00,
};
const apdu = new ApduBuilder(openAppApduArgs)
  .addAsciiStringToData("Bitcoin")
  .build();
 
// Send the APDU to the device
const apduResponse = await dmk.sendApdu({ sessionId, apdu });
 
// Parse the result
const parser = new ApduParser(apduResponse);
 
// Check if the command was successful
if (!CommandUtils.isSuccessResponse(apduResponse)) {
  throw new Error(
    `Unexpected status word: ${parser.encodeToHexaString(
      apduResponse.statusCode,
    )}`,
  );
}

Step 2: Using pre-defined commands

For most common operations, the DMK provides pre-defined commands that are easier to use and maintain. The sendCommand method handles building the APDU, sending it to the device, and parsing the response.

❗️ Error Handling

Most commands will reject with an error if:

  • The device is locked - check the device session state with dmk.getDeviceSessionState({ sessionId }) first
  • The response status word is not 0x9000 (success)

Opening an app

import { OpenAppCommand } from "@ledgerhq/device-management-kit";
 
// Create the command to open the Bitcoin app
const command = new OpenAppCommand("Bitcoin");
 
// Send the command and wait for the user to confirm on the device
await dmk.sendCommand({ sessionId, command });

Closing an app

import { CloseAppCommand } from "@ledgerhq/device-management-kit";
 
// Create the command to close the current app
const command = new CloseAppCommand();
 
// Send the command
await dmk.sendCommand({ sessionId, command });

Getting OS information

import { GetOsVersionCommand } from "@ledgerhq/device-management-kit";
 
// Create the command to get OS version information
const command = new GetOsVersionCommand();
 
// Send the command and get the response
const { seVersion, mcuSephVersion, mcuBootloaderVersion } =
  await dmk.sendCommand({ sessionId, command });
 
console.log(`OS Version: ${seVersion}`);

ℹ️ Tip: This information is also available in the device session state, which you can access with dmk.getDeviceSessionState({ sessionId }).

Getting app information

import { GetAppAndVersionCommand } from "@ledgerhq/device-management-kit";
 
// Create the command to get app information
const command = new GetAppAndVersionCommand();
 
// Send the command and get the response
const { name, version } = await dmk.sendCommand({ sessionId, command });
 
console.log(`Running app: ${name} v${version}`);

Step 3: Working with Device Actions

Device actions define a sequence of commands to be sent to the device. They’re useful for complex operations that require user interaction, like opening an app and performing operations within it.

Device actions return an observable that emits different states during execution, allowing your application to respond to intermediate steps.

Opening an app with a Device Action

import {
  OpenAppDeviceAction,
  OpenAppDAState,
  DeviceActionStatus,
  UserActionRequiredType,
} from "@ledgerhq/device-management-kit";
 
// Create the device action
const openAppDeviceAction = new OpenAppDeviceAction({ appName: "Bitcoin" });
 
// Execute the device action
const { observable, cancel } = await dmk.executeDeviceAction({
  sessionId,
  deviceAction: openAppDeviceAction,
});
 
// Subscribe to state changes
observable.subscribe({
  next: (state: OpenAppDAState) => {
    switch (state.status) {
      case DeviceActionStatus.NotStarted:
        console.log("Action not started yet");
        break;
      case DeviceActionStatus.Pending:
        const {
          intermediateValue: { userActionRequired },
        } = state;
        switch (userActionRequired) {
          case UserActionRequiredType.None:
            console.log("No user action required");
            break;
          case UserActionRequiredType.ConfirmOpenApp:
            console.log(
              "The user should confirm the app opening on the device",
            );
            break;
          case UserActionRequiredType.UnlockDevice:
            console.log("The user should unlock the device");
            break;
          default:
            // Always handle all possible user actions
            throw new Error("Unhandled user action required");
        }
        break;
      case DeviceActionStatus.Stopped:
        console.log("Action has been cancelled");
        break;
      case DeviceActionStatus.Completed:
        const { output } = state;
        console.log("App opened successfully", output);
        break;
      case DeviceActionStatus.Error:
        const { error } = state;
        console.error("An error occurred during the action", error);
        break;
    }
  },
  error: (error) => {
    console.error("Subscription error:", error);
  },
  complete: () => {
    console.log("Device action complete");
  },
});
 
// If you need to cancel the action
// cancel();

What you’ve learned

In this tutorial, you’ve learned how to:

  • Send low-level APDU commands to a Ledger device
  • Use pre-defined commands for common operations
  • Work with device actions for complex flows
  • Handle user interactions during device communication

Next steps

  • Check out the sample app a comprehensive demonstration of the Device Management Kit’s capabilities in a React application.
  • Learn how to build custom commands for specific applications
  • Explore how to handle errors during device communication
Ledger
Copyright © Ledger SAS. All rights reserved. Ledger, Ledger Nano S, Ledger Vault, Ledger OS are registered trademarks of Ledger SAS