Physical Device Keys vs Model Keys in OpenLogi v3 Migration: Complete Technical Guide
OpenLogi's v3 migration replaces model keys (shared across identical devices) with physical device keys (unique per hardware unit) to prevent configuration collisions when multiple identical peripherals are connected.
The OpenLogi project underwent a significant architectural change in version 3 regarding how input devices are identified and configured. Understanding the distinction between physical device keys and model keys in OpenLogi v3 migration is essential for developers maintaining device configurations or migrating legacy integrations. This shift from model-scoped to device-scoped indexing ensures that per-device settings like DPI and SmartShift apply to the correct physical unit rather than being ambiguously shared across all identical models.
Understanding Model Keys in OpenLogi
A model key is a stable identifier that groups all devices of the same model type. According to the OpenLogi source code, this value is generated by DeviceRecord::model_key as a hash derived from the device's model name string (e.g., "2b35a").
Before v3, configuration maps, asset lookups, and legacy settings relied on these model keys. However, this approach created a critical limitation: connecting two identical devices resulted in key collisions, making it impossible to store distinct settings for each physical unit. Model keys remain in use today solely for asset lookup and diagnostic purposes, not for runtime configuration storage.
Understanding Physical Device Keys
In contrast, a physical device key uniquely identifies a single piece of hardware. The DeviceRecord::device_key method generates this identifier from the device's stable serial number. When serial numbers are unavailable, OpenLogi generates a UUID at first discovery to ensure uniqueness.
Starting with v3, the main configuration map Config::devices uses this physical key instead of the model key. This change eliminates collisions when multiple identical devices are present, ensuring that per-device settings apply to the specific unit intended, even when several identical models are connected simultaneously.
The v3 Migration Implementation
The migration is explicitly documented in crates/openlogi-core/src/config.rs, where a comment explains the breaking change: "v3 changes the device map from model keys to physical-device keys. No v2 device entries are migrated because model-scoped settings cannot be assigned safely when two identical devices exist."
To enforce type safety and prevent accidental use of model keys in per-device maps, OpenLogi introduced the DeviceKey wrapper type in crates/openlogi-desktop/src/state/device_key.rs. This struct wraps the physical config key, ensuring that plain String types cannot be passed to device-specific maps by accident. The UI state tables in crates/openlogi-desktop/src/state/devices.rs now use DeviceKey as their primary index for all runtime configuration data.
Code Implementation Patterns
The following patterns demonstrate how to work with the new key system in Rust.
Creating and using DeviceKey instances:
// Creating a DeviceKey from the physical identifier (e.g., a serial number)
let phys_key = DeviceKey::from("usb-1234567890");
// Using the physical key to store per-device settings
let mut dpi_map: BTreeMap<DeviceKey, DpiSetting> = BTreeMap::new();
dpi_map.insert(phys_key.clone(), DpiSetting::new(800));
// Looking up a setting with a plain string works thanks to Borrow impl
assert_eq!(dpi_map.get("usb-1234567890"), Some(&DpiSetting::new(800)));
Migrating from legacy model-key maps to physical-key maps:
// Old map keyed by model (may contain collisions)
let mut old_map: BTreeMap<String, DeviceConfig> = ...;
// New map keyed by DeviceKey (unique per physical device)
let mut new_map: BTreeMap<DeviceKey, DeviceConfig> = BTreeMap::new();
for (model_key, cfg) in old_map {
// Resolve the physical key for each device instance
let phys_key = resolve_physical_key(&model_key);
new_map.insert(phys_key, cfg);
}
Summary
- Model keys identify device types via a hash of the model name and are shared across all identical devices, now relegated to asset lookup and diagnostics only.
- Physical device keys uniquely identify individual hardware units via serial numbers or UUIDs and serve as the primary index for runtime configuration since the v3 migration.
- The migration intentionally abandons v2 settings because model-scoped configurations cannot be safely distributed across multiple identical physical devices.
- The
DeviceKeywrapper type incrates/openlogi-desktop/src/state/device_key.rsenforces compile-time safety by distinguishing physical keys from plain strings and preventing accidental misuse.
Frequently Asked Questions
Why did OpenLogi v3 stop using model keys for configuration?
Model keys caused collisions when users connected multiple identical devices, such as two of the same mouse model. Since the model key is identical for all devices of that type, the configuration system could not distinguish which settings belonged to which physical unit. Physical device keys solve this by providing a unique identifier for each piece of hardware based on serial numbers or generated UUIDs.
How is a physical device key generated when a device lacks a serial number?
When a device does not provide a stable serial number, DeviceRecord::device_key generates a UUID at the time of first discovery. This UUID persists for that specific device instance, ensuring consistent identification across application restarts even without hardware-level serial numbers.
Does OpenLogi v3 migrate existing v2 configuration data automatically?
No. As noted in crates/openlogi-core/src/config.rs, v2 device entries are not migrated because model-scoped settings cannot be safely assigned when two identical devices exist. The system cannot determine which physical device should inherit the previous model-wide settings, requiring users to reconfigure their devices after upgrading to v3.
What prevents developers from accidentally using model keys in device-specific maps?
The DeviceKey wrapper type in crates/openlogi-desktop/src/state/device_key.rs enforces type safety at compile time. By wrapping the physical config key in a dedicated struct, the API prevents plain String values (which might contain model keys) from being inserted into per-device configuration maps, eliminating an entire class of configuration errors where the wrong key type could corrupt device settings.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →