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 0x00 与 0xFF 为保留的列表结束哨兵值,调用时会被拒绝。
Rgb—— 每通道 8-bit 颜色,字段为red、green、blue。RgbZone——zone_id+color;哨兵 ID 在调用时即被拒绝。RgbZoneRange——first_zone_id、last_zone_id、color(闭区间)。ZonePresencePage——Zones0To111、Zones112To223、Zones224To255。FramePersistence——Volatile(仅写入 RAM)或VolatileAndNonVolatile(RAM + EEPROM)。
规格: Logitech HID++ 2.0 —— perKeyLighting。用于:
openlogi-hidpp中的类型化封装。
Function reference
The PerKeyLightingFeature wrapper (0x8081) exposes:
Methods
| Function | HID++ fn | Signature | Returns |
|---|---|---|---|
get_rgb_zone_presence | 0 | (page: ZonePresencePage) | [u8; 14] |
set_individual_rgb_zones | 1 | (zones: &[RgbZone]) | () |
set_consecutive_rgb_zones | 2 | (first_zone_id: u8, colors: [Rgb; 5]) | () |
set_consecutive_rgb_zones_delta_5bit | 3 | (first_zone_id: u8, packed: [u8; 15]) | () |
set_consecutive_rgb_zones_delta_4bit | 4 | (first_zone_id: u8, packed: [u8; 15]) | () |
set_range_rgb_zones | 5 | (ranges: &[RgbZoneRange]) | () |
set_rgb_zones_single_value | 6 | (color: Rgb, zone_ids: &[u8]) | () |
frame_end | 7 | (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.
| Field | Type | Description |
|---|---|---|
red | u8 | Red channel. |
green | u8 | Green channel. |
blue | u8 | Blue channel. |
RgbZone
A single zone and the color to apply to it.
| Field | Type | Description |
|---|---|---|
zone_id | u8 | Zone identifier (0 and 255 are reserved end-of-list sentinels). |
color | Rgb | Color to apply. |
RgbZoneRange
A contiguous range of zones to fill with one color.
| Field | Type | Description |
|---|---|---|
first_zone_id | u8 | First zone identifier in the range (inclusive). |
last_zone_id | u8 | Last zone identifier in the range (inclusive). |
color | Rgb | Color to apply across the range. |
ZonePresencePage
Which page of zone IDs a presence query covers.
| Variant | Value | Description |
|---|---|---|
Zones0To111 | 0 | Zone IDs 0..=111. |
Zones112To223 | 1 | Zone IDs 112..=223. |
Zones224To255 | 2 | Zone IDs 224..=255. |
FramePersistence
Storage persistence for frame_end.
| Variant | Value | Description |
|---|---|---|
Volatile | 0 | Volatile: applied to RAM only. |
VolatileAndNonVolatile | 1 | Applied 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):
| Bytes | Field | Notes |
|---|---|---|
| 2–15 | zone presence bitfield | 14 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.
| Bytes | Field | Notes |
|---|---|---|
slot*4 + 0 | zone_id | Zone identifier for slot (0–3) |
slot*4 + 1 | red | Red channel |
slot*4 + 2 | green | Green channel |
slot*4 + 3 | blue | Blue channel |
Response: none (ack only).
set_consecutive_rgb_zones (fn 2)
Request: 16-byte long payload for exactly 5 sequential zones.
| Bytes | Field | Notes |
|---|---|---|
| 0 | first_zone_id | Starting zone ID |
1 + i*3 | red of zone i | i = 0..4 |
2 + i*3 | green of zone i | |
3 + i*3 | blue 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.
| Bytes | Field | Notes |
|---|---|---|
| 0 | first_zone_id | Starting zone ID |
| 1–15 | packed | 15-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.
| Bytes | Field | Notes |
|---|---|---|
| 0 | first_zone_id | Starting zone ID |
| 1–15 | packed | 15-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.
| Bytes | Field | Notes |
|---|---|---|
slot*5 + 0 | first_zone_id | First zone ID in range (inclusive), slot 0–2 |
slot*5 + 1 | last_zone_id | Last zone ID in range (inclusive) |
slot*5 + 2 | red | Red channel |
slot*5 + 3 | green | Green channel |
slot*5 + 4 | blue | Blue 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.
| Bytes | Field | Notes |
|---|---|---|
| 0 | red | Red channel of the single color |
| 1 | green | Green channel |
| 2 | blue | Blue channel |
| 3–15 | zone IDs | Up 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.
| Bytes | Field | Notes |
|---|---|---|
| 0 | persistence | FramePersistence discriminant (0 = volatile, 1 = volatile + non-volatile) |
| 1 | current_frame high byte | Big-endian u16 |
| 2 | current_frame low byte | |
| 3 | frames_till_next_change high byte | Big-endian u16 |
| 4 | frames_till_next_change low byte | |
| 5–15 | — | Zeroed 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?;
}