OpenLogi Device Kind Vocabularies: Asset Registry, HID++, and Pairing Registers Explained

OpenLogi normalizes three independent source vocabularies—asset registry strings, HID++ 0x0005 marketing types, and Bolt/Unifying pairing registers—into a canonical DeviceKind enum through dedicated mapping functions that respect a strict registry-first precedence chain.

Managing peripheral identities across different protocol layers requires reconciling disparate classification systems. In the AprilNEA/OpenLogi repository, the DeviceKind enum serves as the unified representation for all supported peripherals, consuming data from three distinct vocabularies that use different numeric schemes and string formats. Understanding how these vocabularies interact ensures accurate device classification in the UI and correct capability detection.

The Three Source Vocabularies

OpenLogi ingests device type information from three independent origins, each with its own encoding scheme and trust level.

Asset Registry Strings

The static asset manifest in openlogi-assets provides human-readable type identifiers. These free-form strings—such as "mouse" or "keyboard"—are parsed case-insensitively by DeviceKind::from_registry_type in [crates/openlogi-core/src/device.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-core/src/device.rs#L61-L84). This vocabulary typically originates from manufacturer metadata bundled with the application.

HID++ 0x0005 Marketing Type

Devices self-report their marketing category through the HID++ feature table using the hidpp::feature::device_type_and_name::DeviceType enum. Numeric values in this vocabulary start at 0 and map to physical device families like mice and keyboards. The conversion logic resides in map_device_type within [crates/openlogi-device/src/inventory/mappings.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-device/src/inventory/mappings.rs#L54-L69).

Bolt and Unifying Pairing Registers

Legacy and modern receivers store paired device metadata in protocol-specific registers. The Bolt protocol uses hidpp::receiver::bolt::DeviceKind (where Unknown=0), while the Unifying receiver uses hidpp::receiver::unifying::DeviceKind with a different numeric ordering. These are converted via map_kind (lines 22-37) and map_unifying_kind (lines 40-51) respectively.

Mapping Functions and Precedence Rules

OpenLogi never reinterprets raw bytes across vocabularies. Instead, each source feeds into dedicated mapper functions that return the canonical DeviceKind. The resolution follows a strict hierarchy implemented in effective_kind in [crates/openlogi-desktop/src/state/devices.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-desktop/src/state/devices.rs#L781-L792):

  1. Asset registry overrides all other sources when present.
  2. HID++ 0x0005 marketing type is trusted when the registry is absent and the probe yields a known variant.
  3. Pairing register serves as the fallback when both higher sources are unavailable or return Unknown.

This precedence chain guarantees that misclassified low-level registers (e.g., a Bolt register reporting Keyboard for a mouse) do not override explicit manufacturer metadata or the device's self-reported type.

The Canonical DeviceKind Enum

The unified enum is declared in [crates/openlogi-core/src/device.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-core/src/device.rs#L24-L58), encompassing variants for mouse, keyboard, numpad, presenter, remote, trackball, touchpad, tablet, gamepad, joystick, headset, camera, light, and an Unknown variant. This type-safe representation ensures consistent classification across the desktop application and core library, independent of the original vocabulary's numeric values.

Practical Code Examples

The following Rust code demonstrates how to parse each vocabulary and resolve the final device kind according to OpenLogi's precedence rules:

use openlogi_core::device::DeviceKind;
use openlogi_device::inventory::{map_kind, map_device_type, map_unifying_kind};
use hidpp::receiver::bolt::DeviceKind as BoltKind;
use hidpp::receiver::unifying::DeviceKind as UnifyingKind;
use hidpp::feature::device_type_and_name::DeviceType;

// 1. Parse asset registry string (case-insensitive)
let from_registry = DeviceKind::from_registry_type("MOUSE")
    .expect("Valid registry type");

// 2. Convert Bolt pairing register value (Unknown=0, Mouse=1, etc.)
let bolt_raw = BoltKind::Mouse;
let from_bolt = map_kind(bolt_raw);

// 3. Convert Unifying pairing register value
let unifying_raw = UnifyingKind::Keyboard;
let from_unifying = map_unifying_kind(unifying_raw);

// 4. Convert HID++ 0x0005 marketing type
let hid_type = DeviceType::Keyboard;
let from_hid = map_device_type(hid_type);

// Resolution follows precedence: registry > HID++ > register
assert_eq!(from_registry, DeviceKind::Mouse);

Summary

  • OpenLogi consumes three distinct vocabularies: asset registry strings, HID++ 0x0005 marketing types, and Bolt/Unifying pairing registers.
  • Each vocabulary uses different numeric schemes requiring dedicated mapping functions (map_kind, map_unifying_kind, map_device_type).
  • The registry string takes precedence, followed by HID++ marketing type, then pairing register fallback.
  • All sources normalize to the canonical DeviceKind enum defined in crates/openlogi-core/src/device.rs.
  • The UI ultimately depends on HID++ capability detection rather than the guessed kind to ensure accurate feature panels.

Frequently Asked Questions

What happens if the HID++ marketing type contradicts the pairing register?

The HID++ marketing type takes precedence over the pairing register, but both are overridden by the asset registry string. If the marketing type returns Unknown, the system falls back to the pairing register value according to the precedence chain in effective_kind.

Where is the DeviceKind enum defined in OpenLogi?

The canonical DeviceKind enum is defined in crates/openlogi-core/src/device.rs between lines 24 and 58, containing variants for all supported peripheral categories from mice to headsets.

How does OpenLogi handle case-sensitive registry strings?

The DeviceKind::from_registry_type function handles case-insensitive parsing of asset registry strings, normalizing inputs like "MOUSE", "mouse", or "Mouse" to the appropriate enum variant before applying the precedence rules.

Why does OpenLogi use three different vocabularies instead of one?

Each vocabulary originates from a different protocol layer: the asset registry provides static manufacturer metadata, HID++ 0x0005 reports the device's self-identified marketing category, and pairing registers store receiver-specific connection metadata. This multi-source approach ensures device identification survives partial protocol failures or missing features.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →