OpenLogi
功能(Features)

0x8081 · perKeyLighting

逐键(逐区域)RGB 灯光控制——通过六种 setter 函数暂存最多 256 个区域的颜色,再由 frame_end 统一提交并可选择持久化到 EEPROM。

逐键(逐区域)RGB 灯光控制。区域颜色通过六个 setter 函数之一暂存,再由 frame_end 统一提交到显示层;frame_end 同时负责帧动画时序(current_frame / frames_till_next_change)以及是否将结果持久化到 EEPROM。get_rgb_zone_presence 分三页(每页 112 个区域)查询设备上实际存在的区域 ID。

六个 setter 在容量与寻址灵活性之间各有侧重:set_individual_rgb_zones 支持最多 4 个任意区域;set_consecutive_rgb_zones 以完整 24-bit 颜色设置 5 个连续区域;两个 delta 压缩变体分别将 8 个区域(5-bit 有符号差分)或 10 个区域(4-bit 有符号差分)压缩进单次请求;set_range_rgb_zones 用一种颜色填充最多 3 个闭区间范围;set_rgb_zones_single_value 用一种颜色涂抹最多 13 个任意区域。区域 ID 0x000xFF 为保留的列表结束哨兵值,调用时会被拒绝。

  • Rgb —— 每通道 8-bit 颜色,字段为 redgreenblue
  • RgbZone —— zone_id + color;哨兵 ID 在调用时即被拒绝。
  • RgbZoneRange —— first_zone_idlast_zone_idcolor(闭区间)。
  • ZonePresencePage —— Zones0To111Zones112To223Zones224To255
  • FramePersistence —— Volatile(仅写入 RAM)或 VolatileAndNonVolatile(RAM + EEPROM)。

规格: Logitech HID++ 2.0 —— perKeyLighting用于: openlogi-hidpp 中的类型化封装。

Function reference

The PerKeyLightingFeature wrapper (0x8081) exposes:

Methods

FunctionHID++ fnSignatureReturns
get_rgb_zone_presence0(page: ZonePresencePage)[u8; 14]
set_individual_rgb_zones1(zones: &[RgbZone])()
set_consecutive_rgb_zones2(first_zone_id: u8, colors: [Rgb; 5])()
set_consecutive_rgb_zones_delta_5bit3(first_zone_id: u8, packed: [u8; 15])()
set_consecutive_rgb_zones_delta_4bit4(first_zone_id: u8, packed: [u8; 15])()
set_range_rgb_zones5(ranges: &[RgbZoneRange])()
set_rgb_zones_single_value6(color: Rgb, zone_ids: &[u8])()
frame_end7(persistence: FramePersistence, current_frame: u16, frames_till_next_change: u16)()

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

Types

Rgb

An 8-bit-per-channel RGB color.

FieldTypeDescription
redu8Red channel.
greenu8Green channel.
blueu8Blue channel.

RgbZone

A single zone and the color to apply to it.

FieldTypeDescription
zone_idu8Zone identifier (0 and 255 are reserved end-of-list sentinels).
colorRgbColor to apply.

RgbZoneRange

A contiguous range of zones to fill with one color.

FieldTypeDescription
first_zone_idu8First zone identifier in the range (inclusive).
last_zone_idu8Last zone identifier in the range (inclusive).
colorRgbColor to apply across the range.

ZonePresencePage

Which page of zone IDs a presence query covers.

VariantValueDescription
Zones0To1110Zone IDs 0..=111.
Zones112To2231Zone IDs 112..=223.
Zones224To2552Zone IDs 224..=255.

FramePersistence

Storage persistence for frame_end.

VariantValueDescription
Volatile0Volatile: applied to RAM only.
VolatileAndNonVolatile1Applied to RAM and stored in EEPROM.

Wire format

Getter functions use a short (3-byte) request payload and return a long (16-byte) response payload read via extend_payload(). All setter functions (fn 1–7) use a long (16-byte) request payload and return no meaningful response bytes.

get_rgb_zone_presence (fn 0)

Request: [ 0x00, page, 0x00 ] — byte 0 = typeOfInfo (always 0x00), byte 1 = ZonePresencePage discriminant, byte 2 = padding.

Response (byte → field):

BytesFieldNotes
2–15zone presence bitfield14 bytes; bit i (LSB-first within each byte) = zone page_base + i exists

set_individual_rgb_zones (fn 1)

Request: 16-byte long payload, up to 4 slots of 4 bytes each. Unused slots are zeroed.

BytesFieldNotes
slot*4 + 0zone_idZone identifier for slot (0–3)
slot*4 + 1redRed channel
slot*4 + 2greenGreen channel
slot*4 + 3blueBlue channel

Response: none (ack only).

set_consecutive_rgb_zones (fn 2)

Request: 16-byte long payload for exactly 5 sequential zones.

BytesFieldNotes
0first_zone_idStarting zone ID
1 + i*3red of zone ii = 0..4
2 + i*3green of zone i
3 + i*3blue of zone i

Response: none (ack only).

set_consecutive_rgb_zones_delta_5bit (fn 3)

Request: 16-byte long payload — 8 consecutive zones encoded as 5-bit signed per-channel deltas.

BytesFieldNotes
0first_zone_idStarting zone ID
1–15packed15-byte verbatim delta payload (8×3 5-bit deltas, MSB-first, zone-then-channel)

Response: none (ack only).

set_consecutive_rgb_zones_delta_4bit (fn 4)

Request: 16-byte long payload — 10 consecutive zones encoded as 4-bit signed per-channel deltas.

BytesFieldNotes
0first_zone_idStarting zone ID
1–15packed15-byte verbatim delta payload (10×3 4-bit signed deltas, two per byte, high nibble first)

Response: none (ack only).

set_range_rgb_zones (fn 5)

Request: 16-byte long payload, up to 3 slots of 5 bytes each. Unused slots are zeroed.

BytesFieldNotes
slot*5 + 0first_zone_idFirst zone ID in range (inclusive), slot 0–2
slot*5 + 1last_zone_idLast zone ID in range (inclusive)
slot*5 + 2redRed channel
slot*5 + 3greenGreen channel
slot*5 + 4blueBlue channel

Response: none (ack only).

set_rgb_zones_single_value (fn 6)

Request: 16-byte long payload — one color applied to up to 13 individually addressed zones.

BytesFieldNotes
0redRed channel of the single color
1greenGreen channel
2blueBlue channel
3–15zone IDsUp to 13 zone IDs; unused bytes zeroed

Response: none (ack only).

frame_end (fn 7)

Request: 16-byte long payload — commits all staged zone changes.

BytesFieldNotes
0persistenceFramePersistence discriminant (0 = volatile, 1 = volatile + non-volatile)
1current_frame high byteBig-endian u16
2current_frame low byte
3frames_till_next_change high byteBig-endian u16
4frames_till_next_change low byte
5–15Zeroed padding

Response: none (ack only).

Usage (Rust)

use hidpp::{device::Device, feature::per_key_lighting::{
    FramePersistence, PerKeyLightingFeature, Rgb, RgbZone, ZonePresencePage,
}};

// mut device: Device, already created via Device::new(channel, index)
device.enumerate_features().await?;
if let Some(feat) = device.get_feature::<PerKeyLightingFeature>() {
    // Query which zone IDs are present on this device (first 112 zones)
    let presence = feat.get_rgb_zone_presence(ZonePresencePage::Zones0To111).await?;

    // Stage two individual zones: zone 1 = red, zone 2 = blue
    feat.set_individual_rgb_zones(&[
        RgbZone { zone_id: 1, color: Rgb { red: 0xff, green: 0x00, blue: 0x00 } },
        RgbZone { zone_id: 2, color: Rgb { red: 0x00, green: 0x00, blue: 0xff } },
    ]).await?;

    // Commit the staged changes to RAM only, one-shot (no animation)
    feat.frame_end(FramePersistence::Volatile, 0, 0).await?;
}

本页目录