OpenLogi

0x1C00 · persistentRemappableAction

Non-volatile control remapping — read or write per-control, per-host action assignments (keys, mouse buttons, consumer codes, internal functions) stored in device memory.

Persistently remaps a device control to a different HID action, writing the mapping to the device's non-volatile memory. Controls are identified by the same ControlIds as 0x1B04 (reprogControls); when both features are present, a live divert from 0x1B04 takes precedence over a persistent remap here.

get_feature_info reports which HID output categories the device supports; get_count returns the number of remappable controls and host slots. get_cid_info enumerates the control table; get_persistent_action / set_persistent_action read and write a per-control, per-host mapping. reset_persistent_action restores one mapping to its factory default; reset_to_factory_settings clears all mappings for a selected set of host slots.

  • ActionId — the action type a control produces: SendKeyboard, SendMouseButton, SendXDisplacement, SendYDisplacement, SendVerticalRoller, SendHorizontalRoller, SendConsumerControl, ExecuteInternalFunction, SendPowerKey.
  • RemappableCapabilities — bitflags advertising which ActionId types the device can produce; returned by get_feature_info.
  • ModifierMask — keyboard modifier flags (left/right Ctrl, Shift, Alt, GUI); applicable to SendKeyboard actions only.
  • HostMask — selects one or more host slots (HOST_1HOST_3) for reset_to_factory_settings.

Spec: Logitech HID++ 2.0 — persistentRemappableAction. Used by: Typed wrapper in openlogi-hidpp.

Function reference

The PersistentRemappableActionFeature wrapper (0x1c00) exposes:

Methods

FunctionHID++ fnSignatureReturns
get_feature_info0()RemappableCapabilities
get_count1()RemappableInfo
get_cid_info2(index: u8, host: HostIndex)ControlId
get_persistent_action3(cid: ControlId, host: HostIndex)PersistentAction
set_persistent_action4(cid: ControlId, host: HostIndex, config: PersistentActionConfig)()
reset_persistent_action5(cid: ControlId, host: HostIndex)()
reset_to_factory_settings6(hosts: HostMask)()

All methods are async and return Result<…, Hidpp20Error>.

Types

RemappableCapabilities

Bitflags advertising which HID output categories the device's persistent remapping can produce; returned by get_feature_info.

FlagBit/ValueDescription
KEYBOARD_REPORT1 << 0Can send keyboard/keypad keys.
MOUSE_BUTTONS1 << 1Can send mouse buttons.
X_DISPLACEMENT1 << 2Can send mouse X displacement.
Y_DISPLACEMENT1 << 3Can send mouse Y displacement.
VERTICAL_ROLLER1 << 4Can send vertical roller increments.
HORIZONTAL_ROLLER1 << 5Can send horizontal roller (AC pan) increments.
CONSUMER_CONTROL1 << 6Can send consumer-control codes.
INTERNAL_FUNCTION1 << 7Can execute internal functions.
POWER_KEY1 << 8Can send power keys.

RemappableInfo

Control-table sizing returned by get_count.

FieldTypeDescription
countu8Number of control IDs in the table.
host_countu8Number of hosts the device supports.

PersistentAction

The action mapped to a control, returned by get_persistent_action.

FieldTypeDescription
cidControlIdThe control the action belongs to.
hostHostIndexThe host slot the mapping applies to.
action_idActionIdThe action performed when triggered.
valueu16The HID usage code, displacement, or internal-function index sent.
modifier_maskModifierMaskKeyboard modifiers applied (keyboard actions only).
remappedboolWhether the control is remapped away from its default behaviour.

PersistentActionConfig

The action to assign via set_persistent_action.

FieldTypeDescription
action_idActionIdThe action to perform when triggered.
valueu16The HID usage code, displacement, or internal-function index to send.
modifier_maskModifierMaskKeyboard modifiers to apply (keyboard actions only).

ActionId

The action a control performs when triggered.

VariantValueDescription
SendKeyboard0x01Send a keyboard/keypad report (HID usage page 7).
SendMouseButton0x02Send a mouse-button report (usage page 9).
SendXDisplacement0x03Send mouse X displacement (usage page 1, code 0x30).
SendYDisplacement0x04Send mouse Y displacement (usage page 1, code 0x31).
SendVerticalRoller0x05Send vertical roller/wheel displacement (usage page 1, code 0x38).
SendHorizontalRoller0x06Send horizontal roller / AC pan displacement (usage page 12, code 0x0238).
SendConsumerControl0x07Send a consumer-control report (usage page 12).
ExecuteInternalFunction0x08Execute an internal function (the value is the function index).
SendPowerKey0x09Send a power-key report (usage page 1).

ModifierMask

Standard keyboard modifier keys for a remapped keyboard action. Modifiers only apply to keyboard reports.

FlagBit/ValueDescription
LEFT_CTRL1 << 0Left Control.
LEFT_SHIFT1 << 1Left Shift.
LEFT_ALT1 << 2Left Alt.
LEFT_GUI1 << 3Left GUI (Win/Command).
RIGHT_CTRL1 << 4Right Control.
RIGHT_SHIFT1 << 5Right Shift.
RIGHT_ALT1 << 6Right Alt.
RIGHT_GUI1 << 7Right GUI (Win/Command).

HostMask

A set of host slots for reset_to_factory_settings.

FlagBit/ValueDescription
HOST_11 << 0Host 1.
HOST_21 << 1Host 2.
HOST_31 << 2Host 3.

Wire format

Short getter requests carry a 3-byte zero payload; set_persistent_action uses a 16-byte long-payload request. All responses are read from the 16-byte long payload returned by .extend_payload().

get_feature_info (fn 0)

Request: [0x00, 0x00, 0x00]

Response (byte → field):

ByteFieldNotes
0–1RemappableCapabilitiesBig-endian u16 bitfield; see flag table above.

get_count (fn 1)

Request: [0x00, 0x00, 0x00]

Response (byte → field):

ByteFieldNotes
0countNumber of control IDs in the table.
1host_countNumber of host slots the device supports.

get_cid_info (fn 2)

Request: [index, host, 0x00]

Response (byte → field):

ByteFieldNotes
0–1ControlIdBig-endian u16 control identifier.

get_persistent_action (fn 3)

Request: [cid_hi, cid_lo, host]ControlId split into two bytes big-endian, followed by the HostIndex byte.

Response (byte → field):

ByteFieldNotes
0–1cidControlId as big-endian u16.
2hostHostIndex byte.
3action_idActionId discriminant (0x01–0x09).
4–5valueHID usage code, displacement, or function index as big-endian u16.
6modifier_maskModifierMask bitfield byte.
7remappedBit 0: 1 = remapped away from factory default.

set_persistent_action (fn 4)

Uses call_long: a 16-byte request payload; bytes 7–15 are zero-padded.

ByteFieldNotes
0–1cidControlId as big-endian u16.
2hostHostIndex byte.
3action_idActionId discriminant.
4–5valueHID usage / displacement / function index as big-endian u16.
6modifier_maskModifierMask bitfield byte.
7–15Reserved, zero.

No meaningful response body.

reset_persistent_action (fn 5)

Request: [cid_hi, cid_lo, host] — same layout as get_persistent_action request.

No meaningful response body.

reset_to_factory_settings (fn 6)

Request: [hosts_bits, 0x00, 0x00]HostMask bitfield in byte 0; bits 0/1/2 select HOST_1/HOST_2/HOST_3.

No meaningful response body.

Usage (Rust)

use hidpp::{
    device::Device,
    feature::{
        hosts_info::HostIndex,
        persistent_remappable_action::{
            ActionId, ModifierMask, PersistentActionConfig, PersistentRemappableActionFeature,
        },
        reprog_controls::ControlId,
    },
};

// device: &mut Device, already created via Device::new(channel, device_index)
device.enumerate_features().await?;
if let Some(feat) = device.get_feature::<PersistentRemappableActionFeature>() {
    // Query device capabilities and table size.
    let caps = feat.get_feature_info().await?;
    let info = feat.get_count().await?;
    println!("caps={caps:?}, controls={}, hosts={}", info.count, info.host_count);

    // Read the current mapping for control 0x00c4 on host 0.
    let host = HostIndex::from(0u8);
    let cid = ControlId::from(0x00c4u16);
    let action = feat.get_persistent_action(cid, host).await?;
    println!("current action: {:?}", action.action_id);

    // Remap the control to send Ctrl+C (usage 0x06 = HID keycode for 'c').
    let config = PersistentActionConfig {
        action_id: ActionId::SendKeyboard,
        value: 0x0006,
        modifier_mask: ModifierMask::LEFT_CTRL,
    };
    feat.set_persistent_action(cid, host, config).await?;
}

On this page