# How OpenLogi Handles Device Enumeration for Logitech Bolt and Unifying Receivers

> OpenLogi enumerates Logitech Bolt and Unifying receivers by detecting HID++ devices and querying registers for device metadata. Learn how OpenLogi handles peripheral detection.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: how-to-guide
- Published: 2026-09-13

---

**OpenLogi enumerates paired peripherals by detecting HID++ capable USB receivers, instantiating protocol-specific handler objects for Bolt or Unifying hardware, and querying internal HID++ registers `0xB5/0x5N` and `0xB5/0x6N` to retrieve device metadata.**

OpenLogi implements a Rust-based open source stack for managing Logitech input devices through the proprietary HID++ protocol. The **OpenLogi device enumeration** pipeline spans three primary crates—`openlogi-hid`, `openlogi-hidpp`, and `openlogi-device`—to discover receivers and list their paired peripherals.

## HID Transport Layer Enumeration

The lowest level of the enumeration stack resides in [`crates/openlogi-hid/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hid/src/transport.rs). This module scans the host's USB bus for HID nodes belonging to the Logitech HID++ collection (`0xFF00/0x0002`). When a compatible node is found, the transport layer wraps it as an `async_hid::Device` object and returns it to the caller.

To optimize performance, [`crates/openlogi-hid/src/host.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hid/src/host.rs) provides an `enumerator()` function that caches these probe results. This caching layer ensures that high-level code receives a stable snapshot of available receivers without repeatedly querying the USB subsystem.

## Receiver Type Detection and Abstraction

Once the HID backend identifies a candidate node, control passes to [`crates/openlogi-hidpp/src/receiver.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/receiver.rs). This file defines the `Receiver` enum, which exposes two variants:

- `Receiver::Bolt`
- `Receiver::Unifying`

The `Receiver::new()` constructor inspects the USB/HID node's *receiver protocol* field to determine which variant to instantiate. This abstraction allows the rest of the codebase to treat Bolt and Unifying hardware through a unified interface while delegating protocol specifics to the appropriate implementation.

## Bolt Receiver Enumeration

The Bolt-specific logic lives in [`crates/openlogi-hidpp/src/receiver/bolt.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/receiver/bolt.rs). This implementation communicates with the receiver's internal registers to discover paired devices.

### Register-Based Device Discovery

The Bolt receiver stores pairing metadata in indexed registers. The enumeration routine reads the device-index registers `0xB5/0x5N` and `0xB5/0x6N` in a loop, where `N` represents the slot index. For each occupied slot, the code constructs a `DeviceInfo` struct containing the peripheral's metadata. Upon successfully parsing a device entry, the implementation emits a `BoltEvent::DeviceConnection` notification, as defined in [`crates/openlogi-hidpp/src/receiver/bolt/event.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/receiver/bolt/event.rs).

## Unifying Receiver Enumeration

Unifying receiver support is implemented in [`crates/openlogi-hidpp/src/receiver/unifying.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/receiver/unifying.rs). This module follows the same register-reading pattern as the Bolt implementation, accessing `0xB5/0x5N` and `0xB5/0x6N` to list paired devices.

However, the Unifying protocol uses a distinct *device-kind* encoding scheme. While the register addresses match Bolt hardware, the interpretation of device type values diverges at indices 5 and above, requiring separate parsing logic to correctly identify peripheral categories.

## Public Inventory API

The high-level entry point for device enumeration is `openlogi_device::inventory::enumerate`, located in [`crates/openlogi-device/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/lib.rs). This function orchestrates the entire pipeline:

1. Invokes the HID backend to enumerate all HID++ compatible nodes.
2. Instantiates a `Receiver` for each discovered node.
3. Calls the receiver-specific `enumerate_devices()` method to query paired peripherals.
4. Aggregates the results into `DeviceInventory` objects for transmission over IPC channels to GUI or overlay processes.

The following Rust example demonstrates the complete enumeration flow:

```rust
use openlogi_hidpp::receiver::Receiver;
use openlogi_hid::transport::enumerate_devices;

// Enumerate all HID++ nodes on the host
let nodes = enumerate_devices().await?;

// For each node, instantiate the appropriate receiver type
for node in nodes {
    if let Some(receiver) = Receiver::new(node).await? {
        match receiver {
            Receiver::Bolt(bolt) => {
                let devices = bolt.enumerate_devices().await?;
                println!("Bolt receiver has {} devices", devices.len());
            }
            Receiver::Unifying(unify) => {
                let devices = unify.enumerate_devices().await?;
                println!("Unifying receiver has {} devices", devices.len());
            }
        }
    }
}

```

## Summary

- **Detection**: [`openlogi-hid/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hid/src/transport.rs) filters USB nodes by the HID++ collection ID `0xFF00/0x0002`.
- **Abstraction**: [`openlogi-hidpp/src/receiver.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hidpp/src/receiver.rs) provides a unified `Receiver` enum that dispatches to Bolt or Unifying implementations based on protocol inspection.
- **Enumeration**: Both receiver types read registers `0xB5/0x5N` and `0xB5/0x6N`, but interpret the device-kind field differently.
- **Events**: The Bolt implementation emits `BoltEvent::DeviceConnection` notifications during the enumeration process.
- **API**: [`openlogi-device/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-device/src/lib.rs) exposes the public `enumerate` function that coordinates the entire discovery pipeline.

## Frequently Asked Questions

### What specific HID++ registers does OpenLogi read to enumerate paired devices?

OpenLogi reads the device-index register pair `0xB5/0x5N` and `0xB5/0x6N`, where `N` represents the device slot index. These registers contain the connection status and metadata for each paired peripheral on both Bolt and Unifying receivers.

### How does OpenLogi distinguish between Bolt and Unifying receivers?

During initialization, `Receiver::new()` in [`crates/openlogi-hidpp/src/receiver.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/receiver.rs) inspects the USB node's receiver protocol identifier. Based on this value, it instantiates either `Receiver::Bolt` or `Receiver::Unifying`, ensuring that subsequent register parsing uses the correct protocol interpretation.

### Where does the low-level USB enumeration occur in the OpenLogi codebase?

The USB enumeration logic resides in [`crates/openlogi-hid/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hid/src/transport.rs), which scans for HID devices matching the Logitech HID++ collection. The `enumerator()` function in [`crates/openlogi-hid/src/host.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hid/src/host.rs) adds a caching layer to stabilize results for higher-level consumption.

### What events are emitted during Bolt device enumeration?

As the Bolt receiver implementation parses each slot from registers `0xB5/0x5N` and `0xB5/0x6N`, it emits `BoltEvent::DeviceConnection` events defined in [`crates/openlogi-hidpp/src/receiver/bolt/event.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/receiver/bolt/event.rs). These events signal the discovery of a paired device and carry the associated `DeviceInfo` metadata.