npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@anthriq_dev/bxi-interface

v1.1.2

Published

Node.js SDK for BXI Interface - communicates with C++ bridge via ZMQ

Readme

BXI SDK Usage Guide

Complete guide to using the BXI SDK for Anthriq Instinct device operations.

Table of Contents

  1. Getting Started
  2. System Operations
  3. Register Operations
  4. Motor Operations
  5. EEG Streaming
  6. Impedance Streaming
  7. Error Handling
  8. Best Practices

Getting Started

Installation

npm install @anthriq_dev/bxi-interface

Basic Setup

import { BxiClient } from "@anthriq_dev/bxi-interface";

// Create a typed client for Anthriq Instinct
const client = new BxiClient<"anthriq-instinct">({
  debug: false,
  deviceType: "anthriq-instinct",
  timeout: 30000
});

// Initialize the client
await client.initialize("localhost"); // or "192.168.1.100" for remote devices

// Connect to the device (required after initialization)
await client.connect();

// Your device is now ready to use!

Cleanup

// Always disconnect and shutdown when done
await client.disconnect();
await client.shutdown();

System Operations

Get Capabilities

Retrieve device capabilities and available features.

const response = await client.features?.executeOperation("system", "get_capabilities", {});

if (response?.success) {
  const capabilities = response.data;
  console.log("Available features:", capabilities.features);
  console.log("Register types:", capabilities.registers);
}

Response Structure:

{
  features: {
    registers: [...],
    motors: {...},
    eeg: {...},
    impedance: {...}
  },
  registers: {
    synap: [...],
    nerv: [...],
    motor: [...],
    // ... other register types
  }
}

Get State

Get the current device state.

const response = await client.features?.executeOperation("system", "get_state", {});

if (response?.success) {
  const state = response.data;
  console.log("Status Message:", state.statusMessage);
  console.log("Details:", state.details); // Only present when connected
  console.log("Last Update:", state.lastUpdate);
}

Response Structure:

{
  statusMessage: string,
  details?: Record<string, string>, // Only when connected and not in error
  lastUpdate?: string
}

Subscribe to State Changes

Monitor device state changes in real-time.

const subscriptionResult = await client.features?.subscribeToOperation(
  "system",
  "get_state",
  (update) => {
    if (update.success) {
      const state = update.data;
      console.log("Status:", state.statusMessage);
      console.log("Details:", state.details);

      // Handle state changes based on status message or details
      if (state.statusMessage?.toLowerCase().includes("error")) {
        console.error("Device error:", state.statusMessage);
      }
    }
  }
);

if (subscriptionResult?.success) {
  const subscriptionId = subscriptionResult.data.subscriptionId;
  console.log("Subscribed with ID:", subscriptionId);

  // Wait for state updates
  await new Promise(resolve => setTimeout(resolve, 3000));

  // Later, unsubscribe (use same feature and operation as subscribe)
  await client.features?.unsubscribeFromOperation("system", "get_state", {
    subscriptionId
  });
}

Connect / Disconnect

Explicitly connect or disconnect from the device.

// Connect (required after initialize)
await client.connect();

// Disconnect (optional, called automatically in shutdown)
await client.disconnect();

Register Operations

Registers are device configuration registers that control various aspects of the device. All register operations use the "registers" feature with a type field in the payload.

Register Types

  • synap - Synap register (channel configuration)
  • nerv - Nerv register (ADC configuration)
  • motor - Motor register (motor configuration)
  • cms_drl - CMS/DRL register (common mode sense/driven right leg)
  • features - Features register (system features)
  • definition - Definition register (system definition)
  • routing - Routing register (signal routing)
  • bucket - Bucket register (data bucket configuration)

Read Register

Read a single register.

// Read synap register
const response = await client.features?.executeOperation("registers", "read", {
  type: "synap",
  synap_id: 0
});

if (response?.success) {
  const data = response.data.fields;
  console.log("Synap 0 enabled:", data.enabled);
  console.log("Synap type:", data.synap_type);
  console.log("Gain stage 1:", data.gain_stage_1);
  console.log("Gain stage 2:", data.gain_stage_2);
  console.log("LPF setting:", data.lpf_setting);
}

Synap Register Fields:

{
  synap_id: number; // Read-only
  enabled: number; // 0 = Disabled, 1 = Enabled
  synap_type: number; // 0 = Normal, 1 = REF
  ref_synap_id: number;
  connected_nerv_id: number;
  connected_nerv_channel_id: number;
  gain_stage_1: number; // 0 = 20G, 1 = 10G, 2 = 5G, 3 = 2G
  gain_stage_2: number; // 0 = 5G, 1 = 10G, 2 = 50G, 3 = 100G
  impedance_monitoring_enabled: number;
  lpf_setting: number; // 0 = 700Hz, 1 = 300Hz, 2 = 100Hz, 3 = 40Hz
  cms_drl_id: number;
  cms_contribution_ch: number;
  motor_associated: number;
  associated_motor_id: number;
}

Nerv Register Fields:

{
  nerv_id: number; // Read-only
  enabled: number; // 0 = Disabled, 1 = Enabled
  osr_for_exg: number; // 0-31
  osr_for_impedance: number; // 0-31
}

Motor Register Fields:

{
  motor_id: number; // Read-only
  enabled: number; // 0 = Disabled, 1 = Enabled
  current_position: number; // Read-only, 0-20
  displacement: number; // 0-20
  is_motor_group: number;
  calibrate: number; // 0-7
  operation: number; // 0 = Stop, 1 = Forward, 2 = Backward, 3 = Brake
  status: number; // Read-only
  stall_switch: number;
}

CMS/DRL Register Fields:

{
  cms_drl_id: number; // Read-only
  enabled: number; // 0 = Disabled, 1 = Enabled
  connected_nerv: number;
  cms_mon_ch: number;
  drl_mon_ch: number;
  drl_impedance_monitoring_enabled: number;
  cms_input_map: number; // 24-bit value
  cms_pga: number; // 0 = -1G, 1 = -2G, 2 = -4G, 3 = -8G, 4 = -16G, 5 = -32G, 6 = -64G
}

Write Register

Write to a register (partial writes supported).

// Enable synap register
const response = await client.features?.executeOperation("registers", "write", {
  type: "synap",
  synap_id: 0,
  fields: {
    enabled: 1,
    gain_stage_1: 0, // 20G
    gain_stage_2: 2, // 50G
    lpf_setting: 1   // 300Hz
  }
});

if (response?.success) {
  console.log("Register written successfully");

  // Verify by reading back
  await new Promise(resolve => setTimeout(resolve, 200)); // Wait for propagation

  const verifyResponse = await client.features?.executeOperation("registers", "read", {
    type: "synap",
    synap_id: 0
  });

  if (verifyResponse?.success) {
    const data = verifyResponse.data.fields;
    console.log("Verified - enabled:", data.enabled);
  }
}

Batch Register Operations

Read or write multiple registers in a single operation.

// Batch read
const batchReadResponse = await client.features?.executeOperation("registers", "read", {
  commands: [
    { type: "synap", synap_id: 0 },
    { type: "synap", synap_id: 1 },
    { type: "motor", motor_id: 0 },
    { type: "nerv", nerv_id: 0 }
  ]
});

if (batchReadResponse?.success) {
  const results = batchReadResponse.data.results;
  results.forEach((result: any) => {
    console.log(`Register ${result.type} ${result.id}:`, result.fields);
  });
}

// Batch write
const batchWriteResponse = await client.features?.executeOperation("registers", "write", {
  commands: [
    {
      type: "synap",
      synap_id: 0,
      fields: { enabled: 1 }
    },
    {
      type: "motor",
      motor_id: 0,
      fields: { enabled: 1 }
    }
  ]
});

if (batchWriteResponse?.success) {
  console.log("Batch write successful");
}

Motor Operations

Motor operations control the physical motors on the device.

Read Motor

Read motor status and position.

const response = await client.features?.executeOperation("motors", "read", {
  motor_ids: [0] // Array of motor IDs
});

if (response?.success) {
  const results = response.data.results;
  results.forEach((motor: any) => {
    console.log(`Motor ${motor.motor_id}:`);
    console.log("  Enabled:", motor.enabled);
    console.log("  Position:", motor.current_position);
  });
}

Response Structure:

{
  results: [
    {
      motor_id: number;
      enabled: number; // 0 = Disabled, 1 = Enabled
      current_position: number; // 0-20
    }
  ]
}

Move Motor

Move a motor forward or backward.

// Move forward
const moveResponse = await client.features?.executeOperation("motors", "move", {
  motor_id: 0,
  displacement: 5, // Steps to move (0-20)
  operation: "forward" // or "backward"
});

if (moveResponse?.success) {
  const motorData = moveResponse.data;
  console.log(`Motor ${motorData.motor_id} moved to position:`, motorData.current_position);
  console.log("Status:", motorData.status);

  // Verify position after a delay
  await new Promise(resolve => setTimeout(resolve, 500));

  const readResponse = await client.features?.executeOperation("motors", "read", {
    motor_ids: [0]
  });

  if (readResponse?.success) {
    const actualPosition = readResponse.data.results[0].current_position;
    console.log("Verified position:", actualPosition);
  }
}

Important Notes:

  • Motor position is clamped between 0 and 20
  • Moving forward at position 20 or backward at position 0 will not change position
  • Always verify position after movement for consistency

Stop Motor

Stop a motor immediately.

const stopResponse = await client.features?.executeOperation("motors", "stop", {
  motor_id: 0
});

if (stopResponse?.success) {
  const motorData = stopResponse.data;
  console.log(`Motor ${motorData.motor_id} stopped at position:`, motorData.current_position);
  console.log("Status:", motorData.status);

  // Verify position doesn't change after stop
  await new Promise(resolve => setTimeout(resolve, 500));

  const readResponse = await client.features?.executeOperation("motors", "read", {
    motor_ids: [0]
  });

  if (readResponse?.success) {
    const positionAfter = readResponse.data.results[0].current_position;
    if (motorData.current_position === positionAfter) {
      console.log("Position stable after stop");
    }
  }
}

Brake Motor

Apply brake to a motor.

const brakeResponse = await client.features?.executeOperation("motors", "brake", {
  motor_id: 0
});

if (brakeResponse?.success) {
  const motorData = brakeResponse.data;
  console.log(`Motor ${motorData.motor_id} brake applied at position:`, motorData.current_position);
  console.log("Status:", motorData.status);
}

EEG Streaming

EEG streaming provides real-time electroencephalography data from the device.

Stream Lifecycle

  1. Add Stream - Configure a stream
  2. Subscribe - Subscribe to receive data
  3. Start Stream - Begin data transmission
  4. Stop Stream - Stop data transmission
  5. Unsubscribe - Stop receiving data
  6. Remove Stream - Remove stream configuration

Add Stream

Configure an EEG stream.

const streamId = 20;
const port = 9020;

const addStreamResponse = await client.features?.executeOperation("eeg", "add_stream", {
  stream: {
    stream_id: streamId,
    protocol: "websocket",
    host: "127.0.0.1",
    port: port,
    elements_before_flush: 30 // Samples per batch (for 1kHz: 30 samples = 30ms)
  }
});

if (addStreamResponse?.success) {
  console.log("Stream added:", addStreamResponse.data.stream_id);
}

// Wait for WebSocket server to be ready
await new Promise(resolve => setTimeout(resolve, 1000));

Stream Configuration:

  • stream_id: Unique identifier for the stream (e.g., 20)
  • protocol: "websocket" or "ws"
  • host: Host address (usually "127.0.0.1" for local)
  • port: Port number for the stream server (e.g., 9020)
  • elements_before_flush: Number of samples per batch (30 for 1kHz EEG)

Note: source_id is automatically set by the SDK based on the feature type:

  • 0x2100 for EEG streams
  • 0x2200 for impedance streams

You should not provide source_id in the stream configuration.

Subscribe to Stream

Subscribe to receive EEG data.

const streamId = 20;
let eegDataReceived = 0;

const subscriptionResult = await client.features?.subscribeToOperation(
  "eeg",
  "stream",
  (update) => {
    if (update.success) {
      eegDataReceived++;
      const streamData = update.data;

      // Stream data structure
      if (streamData.frames?.[0]?.channels) {
        const channels = streamData.frames[0].channels;

        // Log first few updates
        if (eegDataReceived <= 3) {
          console.log(`EEG update #${eegDataReceived}: ${channels.length} channels`);
        }

        channels.forEach((channel: any) => {
          console.log(`Channel ${channel.channel_id}:`);
          console.log("  Channel ID:", channel.channel_id);
          console.log("  ADC Data:", channel.adc_data);
          console.log("  Has Error:", channel.has_error);
          if (channel.has_error) {
            console.log("  Error Info:", channel.error_info);
          }
        });
      }
    }
  },
  {
    stream_id: streamId
  }
);

if (subscriptionResult?.success) {
  const subscriptionId = subscriptionResult.data.subscriptionId;
  console.log("Subscribed with ID:", subscriptionId);

  // Store subscriptionId for later unsubscribe
  const eegSubscriptionId = subscriptionId;

  // Wait for WebSocket client to connect
  await new Promise(resolve => setTimeout(resolve, 2000));
}

Stream Data Structure:

{
  stream_id: number;
  frames: [
    {
      channels: [
        {
          channel_id: number; // Absolute channel ID (combined from adc_id and channel_id_local)
          adc_data: number; // 24-bit ADC data (signed integer)
          has_error: boolean;
          error_info: number; // Error info (8 bits)
        }
      ]
    }
  ]
}

Start Stream

Begin data transmission.

const streamId = 20;

const startResponse = await client.features?.executeOperation("eeg", "start_stream", {
  stream_id: streamId
});

if (startResponse?.success) {
  console.log("Stream started");

  // Wait for some data
  console.log("Collecting EEG data (3 seconds)...");
  await new Promise(resolve => setTimeout(resolve, 3000));
  console.log(`Received ${eegDataReceived} EEG data packets`);
}

Stop Stream

Stop data transmission.

const streamId = 20;
const dataCountBeforeStop = eegDataReceived;

const stopResponse = await client.features?.executeOperation("eeg", "stop_stream", {
  stream_id: streamId
});

if (stopResponse?.success) {
  console.log("Stream stopped");
}

// Wait a bit - some buffered packets may still arrive
// At 1kHz (30 samples/batch), we get ~33 batches/second
await new Promise(resolve => setTimeout(resolve, 1500));

// Verify no new data is received
const totalPacketsAfterStop = eegDataReceived - dataCountBeforeStop;
if (totalPacketsAfterStop === 0) {
  console.log("EEG stop_stream: Verified - no new data after stop");
} else if (totalPacketsAfterStop <= 60) {
  console.log(`EEG stop_stream: Received ${totalPacketsAfterStop} buffered packet(s) after stop (expected for 1kHz stream)`);
}

Unsubscribe

Stop receiving stream data. Use the same feature and operation name as subscribe.

const eegSubscriptionId = subscriptionResult.data.subscriptionId;

// Use unsubscribeFromOperation with same feature and operation as subscribe
const unsubscribeResult = await client.features?.unsubscribeFromOperation("eeg", "stream", {
  subscriptionId: eegSubscriptionId
});

if (unsubscribeResult?.success) {
  console.log("Successfully unsubscribed");
}

Remove Stream

Remove stream configuration.

const streamId = 20;

const removeResponse = await client.features?.executeOperation("eeg", "remove_stream", {
  stream_id: streamId
});

if (removeResponse?.success) {
  console.log("Stream removed");
}

Complete EEG Streaming Example

async function streamEEG() {
  const streamId = 20;
  const port = 9020;
  let eegDataReceived = 0;
  let eegSubscriptionId: string | null = null;

  // 1. Add stream
  const addStreamResult = await client.features?.executeOperation("eeg", "add_stream", {
    stream: {
      stream_id: streamId,
      protocol: "websocket",
      host: "127.0.0.1",
      port: port,
      elements_before_flush: 30
    }
  });

  if (!addStreamResult?.success) {
    throw new Error("Failed to add stream");
  }

  // 2. Wait for server to be ready
  await new Promise(resolve => setTimeout(resolve, 1000));

  // 3. Subscribe
  const subscriptionResult = await client.features?.subscribeToOperation(
    "eeg",
    "stream",
    (update) => {
      if (update.success) {
        eegDataReceived++;
        const data = update.data;
        if (data.frames?.[0]?.channels && eegDataReceived <= 3) {
          const channels = data.frames[0].channels;
          console.log(`EEG update #${eegDataReceived}: ${channels.length} channels`);
        }
      }
    },
    { stream_id: streamId }
  );

  if (subscriptionResult?.success) {
    eegSubscriptionId = subscriptionResult.data.subscriptionId;
  }

  // Wait for WebSocket client to connect
  await new Promise(resolve => setTimeout(resolve, 2000));

  // 4. Start stream
  await client.features?.executeOperation("eeg", "start_stream", {
    stream_id: streamId
  });

  // 5. Collect data for 10 seconds
  console.log("Collecting EEG data (10 seconds)...");
  await new Promise(resolve => setTimeout(resolve, 10000));
  console.log(`Received ${eegDataReceived} EEG data packets`);

  // 6. Stop stream
  const dataCountBeforeStop = eegDataReceived;
  await client.features?.executeOperation("eeg", "stop_stream", {
    stream_id: streamId
  });

  // Wait for buffered packets
  await new Promise(resolve => setTimeout(resolve, 1500));
  const totalPacketsAfterStop = eegDataReceived - dataCountBeforeStop;
  console.log(`Received ${totalPacketsAfterStop} buffered packet(s) after stop`);

  // 7. Unsubscribe (use same feature and operation as subscribe)
  if (eegSubscriptionId) {
    await client.features?.unsubscribeFromOperation("eeg", "stream", {
      subscriptionId: eegSubscriptionId
    });
  }

  // 8. Remove stream
  await client.features?.executeOperation("eeg", "remove_stream", {
    stream_id: streamId
  });

  console.log(`Total packets received: ${eegDataReceived}`);
}

Impedance Streaming

Impedance streaming provides real-time impedance measurements from the device.

Add Impedance Stream

const impedanceStreamId = 21;
const impedancePort = 9021;

const addStreamResponse = await client.features?.executeOperation("impedance", "add_stream", {
  stream: {
    stream_id: impedanceStreamId,
    protocol: "websocket",
    host: "127.0.0.1",
    port: impedancePort,
    elements_before_flush: 25 // Samples per batch (for 250Hz: 25 samples = 100ms)
  }
});

if (addStreamResponse?.success) {
  console.log("Impedance stream added");
}

// Wait for WebSocket server to be ready
await new Promise(resolve => setTimeout(resolve, 1000));

Impedance Stream Configuration:

  • stream_id: Unique identifier for the stream (e.g., 21)
  • port: Port number for the stream server (e.g., 9021)
  • elements_before_flush: 25 for 250Hz sampling rate

Note: source_id is automatically set to "0x2200" for impedance streams by the SDK.

Subscribe to Impedance Stream

const impedanceStreamId = 21;
let impedanceDataReceived = 0;

const subscriptionResult = await client.features?.subscribeToOperation(
  "impedance",
  "stream",
  (update) => {
    if (update.success) {
      impedanceDataReceived++;
      const streamData = update.data;

      if (streamData.frames?.[0]?.channels) {
        const channels = streamData.frames[0].channels;

        // Log first few updates
        if (impedanceDataReceived <= 3) {
          console.log(`Impedance update #${impedanceDataReceived}: ${channels.length} channels`);
        }

        channels.forEach((channel: any) => {
          console.log(`Channel ${channel.channel_id}:`);
          console.log("  ADC Data:", channel.adc_data);
          console.log("  Has Error:", channel.has_error);
          if (channel.has_error) {
            console.log("  Error Info:", channel.error_info);
          }
        });
      }
    }
  },
  {
    stream_id: impedanceStreamId
  }
);

if (subscriptionResult?.success) {
  const subscriptionId = subscriptionResult.data.subscriptionId;
  console.log("Subscribed with ID:", subscriptionId);

  // Wait for WebSocket client to connect
  await new Promise(resolve => setTimeout(resolve, 2000));
}

Impedance Data Structure:

{
  stream_id: number;
  frames: [
    {
      channels: [
        {
          channel_id: number; // Absolute channel ID (combined from adc_id and channel_id_local)
          adc_data: number; // 24-bit ADC data (signed integer)
          has_error: boolean;
          error_info: number; // Error info (8 bits)
        }
      ]
    }
  ]
}

Impedance Stream Operations

All operations are identical to EEG streaming:

const impedanceStreamId = 21;
const impedanceSubscriptionId = subscriptionResult.data.subscriptionId;

// Start
await client.features?.executeOperation("impedance", "start_stream", {
  stream_id: impedanceStreamId
});

// Collect data
console.log("Collecting Impedance data (3 seconds)...");
await new Promise(resolve => setTimeout(resolve, 3000));
console.log(`Received ${impedanceDataReceived} Impedance data packets`);

// Stop
const dataCountBeforeStop = impedanceDataReceived;
await client.features?.executeOperation("impedance", "stop_stream", {
  stream_id: impedanceStreamId
});

// Wait for buffered packets (at 250Hz, we get ~10 batches/second)
await new Promise(resolve => setTimeout(resolve, 2000));
const totalPacketsAfterStop = impedanceDataReceived - dataCountBeforeStop;
if (totalPacketsAfterStop <= 15) {
  console.log(`Impedance stop_stream: Received ${totalPacketsAfterStop} buffered packet(s) after stop (expected for 250Hz stream)`);
}

// Unsubscribe (use same feature and operation as subscribe)
await client.features?.unsubscribeFromOperation("impedance", "stream", {
  subscriptionId: impedanceSubscriptionId
});

// Remove
await client.features?.executeOperation("impedance", "remove_stream", {
  stream_id: impedanceStreamId
});

Error Handling

Response Structure

All operations return a SdkResponse:

{
  success: boolean;
  data?: T; // Response data (type depends on operation)
  error?: {
    code: string;
    message: string;
    details?: string;
  };
}

Error Handling Pattern

const response = await client.features?.executeOperation("motors", "read", {
  motor_ids: [0]
});

if (!response?.success) {
  const error = response.error;
  console.error("Operation failed:");
  console.error("  Code:", error?.code);
  console.error("  Message:", error?.message);
  console.error("  Details:", error?.details);

  // Handle specific error codes
  if (error?.code === "0x01") {
    console.error("Device not connected");
  }
} else {
  // Success - use response.data
  const motorData = response.data;
}

Common Error Codes

  • 0x01: Device not connected
  • 0x02: Invalid operation
  • 0x03: Invalid payload
  • 0x04: Operation timeout
  • 0x05: Stream not found
  • 0x06: Subscription not found

Timeout Handling

// Set timeout for operations
const client = new BxiClient<"anthriq-instinct">({
  timeout: 30000 // 30 seconds
});

// Operations will automatically timeout based on the client timeout setting
const streamId = 20;
try {
  const response = await client.features?.executeOperation("eeg", "start_stream", {
    stream_id: streamId
  });

  if (response?.success) {
    // Handle success
  } else {
    // Handle error
    console.error("Operation failed:", response.error);
  }
} catch (error) {
  console.error("Stream operation error:", error);
}

Best Practices

1. Always Initialize and Connect

// ✅ Good
const client = new BxiClient<"anthriq-instinct">();
await client.initialize("localhost");
await client.connect();

// ❌ Bad - missing connect()
await client.initialize("localhost");
// Operations will fail with "device not connected"

2. Clean Up Resources

try {
  // Use client
} finally {
  // Always cleanup
  if (client?.isConnected()) {
    await client.disconnect();
  }
  await client.shutdown();
}

3. Handle Stream Lifecycle Properly

// ✅ Good - complete lifecycle
await addStream();
await subscribe();
await startStream();
// ... collect data ...
await stopStream();
await unsubscribe();
await removeStream();

// ❌ Bad - missing cleanup
await addStream();
await subscribe();
await startStream();
// Stream remains active!

4. Verify Register Writes

// Write register
await client.features?.executeOperation("registers", "write", {
  type: "synap",
  synap_id: 0,
  fields: { enabled: 1 }
});

// Wait for propagation
await new Promise(resolve => setTimeout(resolve, 200));

// Verify
const readResponse = await client.features?.executeOperation("registers", "read", {
  type: "synap",
  synap_id: 0
});

if (readResponse?.success) {
  const enabled = readResponse.data.fields.enabled;
  if (enabled !== 1) {
    throw new Error("Write verification failed");
  }
}

5. Monitor Motor Position Consistency

// Read position before move
const positionBefore = await readMotorPosition(0);

// Move motor
await client.features?.executeOperation("motors", "move", {
  motor_id: 0,
  displacement: 5,
  operation: "forward"
});

// Wait for position update
await new Promise(resolve => setTimeout(resolve, 500));

// Verify position
const positionAfter = await readMotorPosition(0);
const expectedPosition = Math.min(20, positionBefore + 5); // Clamp to max

if (Math.abs(positionAfter - expectedPosition) > 1) {
  console.warn("Position inconsistency detected");
}

6. Handle Stream Buffering

// After stopping a stream, some buffered packets may still arrive
await client.features?.executeOperation("eeg", "stop_stream", {
  stream_id: 3
});

// Wait for buffered packets to clear
await new Promise(resolve => setTimeout(resolve, 1500));

// Monitor subscription callback - packets should stop arriving

7. Use TypeScript Types

// ✅ Good - typed client
const client = new BxiClient<"anthriq-instinct">();
// TypeScript knows all payload and response types

// ❌ Bad - untyped
const client = new BxiClient();
// No type safety

8. Subscribe to State Changes

// Monitor device state for errors
const stateSubscription = await client.features?.subscribeToOperation(
  "system",
  "get_state",
  (update) => {
    if (update.success && update.data.statusMessage?.toLowerCase().includes("error")) {
      console.error("Device error:", update.data.statusMessage);
      // Handle error (reconnect, retry, etc.)
    }
  }
);

// Later, unsubscribe (use same feature and operation as subscribe)
if (stateSubscription?.success) {
  await client.features?.unsubscribeFromOperation("system", "get_state", {
    subscriptionId: stateSubscription.data.subscriptionId
  });
}

9. Parallel Stream Operations

// When running multiple streams, use Promise.allSettled for resilience
const addStreamPromises = streams.map(stream =>
  client.features?.executeOperation("eeg", "add_stream", {
    stream: { ...stream }
  })
);

const results = await Promise.allSettled(addStreamPromises);
const successful = results.filter(r => r.status === 'fulfilled' && r.value?.success);
console.log(`${successful.length}/${streams.length} streams added`);

10. Error Recovery

async function retryOperation<T>(
  operation: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await operation();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
  throw new Error("Max retries exceeded");
}

// Usage
const response = await retryOperation(() =>
  client.features?.executeOperation("motors", "read", {
    motor_ids: [0]
  })
);

Complete Example

import { BxiClient } from "@anthriq_dev/bxi-interface";

async function completeExample() {
  const client = new BxiClient<"anthriq-instinct">({
    debug: false,
    deviceType: "anthriq-instinct",
    timeout: 30000
  });

  try {
    // Initialize and connect
    await client.initialize("localhost");
    await client.connect();
    console.log("✅ Connected");

    // Get capabilities
    const capabilities = await client.features?.executeOperation("system", "get_capabilities", {});
    console.log("Capabilities:", capabilities?.data);

    // Read a register
    const synapRead = await client.features?.executeOperation("registers", "read", {
      type: "synap",
      synap_id: 0
    });
    console.log("Synap 0:", synapRead?.data.fields);

    // Write a register
    await client.features?.executeOperation("registers", "write", {
      type: "synap",
      synap_id: 0,
      fields: { enabled: 1 }
    });

    // Read motor position
    const motorRead = await client.features?.executeOperation("motors", "read", {
      motor_ids: [0]
    });
    console.log("Motor position:", motorRead?.data.results[0].current_position);

    // Move motor
    await client.features?.executeOperation("motors", "move", {
      motor_id: 0,
      displacement: 5,
      operation: "forward"
    });

    // Setup EEG stream
    const streamId = 20;
    await client.features?.executeOperation("eeg", "add_stream", {
      stream: {
        stream_id: streamId,
        protocol: "websocket",
        host: "127.0.0.1",
        port: 9020,
        elements_before_flush: 30
      }
    });

    // Wait for server to be ready
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Subscribe to stream
    const subscription = await client.features?.subscribeToOperation(
      "eeg",
      "stream",
      (update) => {
        if (update.success) {
          console.log("EEG data received");
        }
      },
      { stream_id: streamId }
    );

    // Wait for WebSocket client to connect
    await new Promise(resolve => setTimeout(resolve, 2000));

    // Start stream
    await client.features?.executeOperation("eeg", "start_stream", {
      stream_id: streamId
    });

    // Collect data for 5 seconds
    await new Promise(resolve => setTimeout(resolve, 5000));

    // Stop and cleanup
    await client.features?.executeOperation("eeg", "stop_stream", {
      stream_id: streamId
    });

    // Wait for buffered packets
    await new Promise(resolve => setTimeout(resolve, 1500));

    // Unsubscribe (use same feature and operation as subscribe)
    await client.features?.unsubscribeFromOperation("eeg", "stream", {
      subscriptionId: subscription?.data.subscriptionId
    });

    await client.features?.executeOperation("eeg", "remove_stream", {
      stream_id: streamId
    });

  } catch (error) {
    console.error("Error:", error);
  } finally {
    // Always cleanup
    if (client.isConnected()) {
      await client.disconnect();
    }
    await client.shutdown();
  }
}

completeExample();

Additional Resources