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

> Explore OpenLogi's device kind vocabularies: asset registry, HID++ marketing types, and pairing registers. Learn how OpenLogi normalizes these into a canonical DeviceKind enum.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: deep-dive
- Published: 2026-08-29

---

**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/main/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/main/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](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-device/src/inventory/mappings.rs#L22-L37)) and `map_unifying_kind` ([lines 40-51](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-device/src/inventory/mappings.rs#L40-L51)) 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/main/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/main/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:

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/device.rs) between [lines 24 and 58](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-core/src/device.rs#L24-L58), 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.