> For the complete documentation index, see [llms.txt](https://docs.anthriq.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.anthriq.com/bxi-studio/developer/overview/get-started/device-info.md).

# Read device information

Two `system` operations report what a device is and what it can do. Both are available on every device type.

## List what the device supports

`get_capabilities` returns the feature catalogue. The SDK fetches it during `initialize()` and caches it, so reading the cache costs nothing.

{% tabs %}
{% tab title="Node.js" %}

```typescript
const capabilities = client.getCachedCapabilities();

for (const [name, feature] of Object.entries(capabilities.features)) {
  console.log(name, feature.operations.join(", "));
}
```

{% endtab %}

{% tab title="Python" %}

```python
capabilities = client.get_cached_capabilities()

for name, feature in capabilities.features.items():
    print(name, ", ".join(feature.operations))
```

{% endtab %}
{% endtabs %}

Re-query when a device may have changed, such as after a firmware update:

{% tabs %}
{% tab title="Node.js" %}

```typescript
const fresh = await client.getCapabilities();
```

{% endtab %}

{% tab title="Python" %}

```python
fresh = await client.get_capabilities()
```

{% endtab %}
{% endtabs %}

Each feature carries three parts:

| Part         | Contains                                                   |
| ------------ | ---------------------------------------------------------- |
| `operations` | The operation names the feature accepts                    |
| `limits`     | Device-declared bounds, such as maximum stream count       |
| `metadata`   | Device-declared values, such as contact quality thresholds |

What comes back depends on the device:

{% tabs %}
{% tab title="Anthriq Instinct" %}
Seven features: `system`, `registers`, `motors`, `eeg`, `impedance`, `firmware`, `auth`.

`impedance` metadata carries the contact quality thresholds the SDK classifies against, so read them from here rather than hardcoding them.
{% endtab %}

{% tab title="xBud" %}
Two features: `system` and `analog_input`.

`system` additionally declares `scan_devices`, which Anthriq headsets do not.
{% endtab %}
{% endtabs %}

`metadata` is where a device parameterises SDK behaviour rather than the SDK assuming it. See [Impedance measurement](/bxi-studio/developer/overview/concepts/impedance.md).

## Branch on capability, not on device type

Checking the capability keeps code working when a device gains a feature.

{% tabs %}
{% tab title="Node.js" %}

```typescript
const capabilities = client.getCachedCapabilities();

if (capabilities.features.impedance) {
  await checkElectrodeContact();
}

if (capabilities.features.firmware?.operations.includes("start_update")) {
  await offerFirmwareUpdate();
}
```

{% endtab %}

{% tab title="Python" %}

```python
capabilities = client.get_cached_capabilities()

if "impedance" in capabilities.features:
    await check_electrode_contact()

firmware = capabilities.features.get("firmware")
if firmware and "start_update" in firmware.operations:
    await offer_firmware_update()
```

{% endtab %}
{% endtabs %}

The SDK performs the same check on every call. An undeclared operation returns a failed response listing what is available, and never reaches the device. See [Device support](/bxi-studio/developer/overview/concepts/device-support.md).

## Read the current state

`get_state` reports the connection state and device metadata.

{% tabs %}
{% tab title="Node.js" %}

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

if (response.success) {
  console.log(response.data.statusMessage);
  console.log(response.data.details);
}
```

{% endtab %}

{% tab title="Python" %}

```python
response = await client.execute_operation("system", "get_state", {})

if response.success:
    print(response.data["statusMessage"])
    print(response.data.get("details"))
```

{% endtab %}
{% endtabs %}

| Field           | Type                     | Description                                           |
| --------------- | ------------------------ | ----------------------------------------------------- |
| `statusMessage` | `string`                 | Human-readable state                                  |
| `details`       | `Record<string, string>` | Device metadata; absent when disconnected or in error |
| `lastUpdate`    | `string`                 | Timestamp of the last state change                    |

`details` is populated only when the device is connected and healthy, so read it defensively rather than assuming it is present.

## Watch for state changes

Subscribe rather than polling. The device pushes an update whenever its state changes.

{% tabs %}
{% tab title="Node.js" %}

```typescript
const subscription = await client.features.subscribeToOperation(
  "system",
  "get_state",
  (update) => {
    if (!update.success) return;
    const message = update.data.statusMessage ?? "";
    if (message.toLowerCase().includes("error")) {
      console.error("Device error:", message);
    }
  }
);

const subscriptionId = subscription.data.subscriptionId;
```

{% endtab %}

{% tab title="Python" %}

```python
def on_state(update):
    if not update.success:
        return
    message = update.data.get("statusMessage", "")
    if "error" in message.lower():
        print("Device error:", message)

subscription = await client.subscribe_to_operation("system", "get_state", on_state)
subscription_id = subscription.subscription_id
```

{% endtab %}
{% endtabs %}

Unsubscribe with the same feature and operation used to subscribe:

{% tabs %}
{% tab title="Node.js" %}

```typescript
await client.features.unsubscribeFromOperation("system", "get_state", { subscriptionId });
```

{% endtab %}

{% tab title="Python" %}

```python
await client.unsubscribe_from_operation(
    "system", "get_state", {"subscriptionId": subscription_id}
)
```

{% endtab %}
{% endtabs %}

A state subscription is the cheapest way to notice a device dropping out mid-session, which otherwise surfaces as a timeout on whatever operation runs next.

## Enumerate xBud hardware

xBud adds `scan_devices`, which lists attached hardware. `anthriq-instinct` does not declare it.

{% tabs %}
{% tab title="Node.js" %}

```typescript
const response = await client.features.executeOperation("system", "scan_devices", {});
```

{% endtab %}

{% tab title="Python" %}

```python
response = await client.execute_operation("system", "scan_devices", {})
```

{% endtab %}
{% endtabs %}

See [Acquire analog input from xBud](/bxi-studio/developer/overview/devices/xbud.md) for querying one device in detail.

## Next steps

* [Configure channels](/bxi-studio/developer/overview/features/channels.md)
* [Device support](/bxi-studio/developer/overview/concepts/device-support.md)
