# How OpenLogi Controls RGB Lighting on Keyboards via HID++ (0x8070/0x8080)

> Discover how OpenLogi controls keyboard RGB lighting using HID++ 2.0 features 0x8070 and 0x8080. Learn about per-key zones and cluster effects with async APIs.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: how-to-guide
- Published: 2026-09-12

---

**OpenLogi implements keyboard RGB lighting control through HID++ 2.0 protocol features 0x8070 (per-key zones) and 0x8080 (cluster effects), exposing async APIs in the `openlogi-hidpp` and `openlogi-device` crates.**

The **AprilNEA/OpenLogi** project provides an open-source implementation of Logitech's HID++ protocol for Linux and other platforms. Its RGB lighting system targets gaming keyboards through two specific feature IDs that manage granular per-key illumination and macro-level lighting animations.

## Understanding HID++ RGB Features (0x8070 and 0x8080)

OpenLogi communicates with Logitech keyboards using the HID++ 2.0 protocol, specifically targeting two feature IDs for lighting control. Feature **0x8070** handles per-key RGB zone management, while **0x8080** manages cluster-wide lighting effects.

### Feature 0x8070: Per-Key Lighting

The `PerKeyLightingFeature` struct in [`crates/openlogi-hidpp/src/feature/per_key_lighting.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/feature/per_key_lighting.rs) implements the 0x8070 protocol. This feature allows setting colors for individual keys, consecutive ranges, or the entire keyboard through zone-based addressing.

Key methods include:

- `set_rgb_zones_single_value` – Applies one color to all zones
- `set_consecutive_rgb_zones` – Updates a range of zones with specific colors
- `set_individual_rgb_zones` – Targets specific zone indices with distinct colors

These methods accept `RgbZone` and `RgbZoneRange` structs and return `Result<(), Hidpp20Error>` via async dispatch.

### Feature 0x8080: RGB Cluster Effects

The `RgbEffectsFeature` struct in [`crates/openlogi-hidpp/src/feature/rgb_effects.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/feature/rgb_effects.rs) implements the 0x8080 protocol for hardware-accelerated effects. Instead of setting static colors, this feature triggers firmware-level animations like breathing or color cycling across the entire keyboard or defined clusters.

The primary method `set_rgb_cluster_effect` accepts an `RgbClusterEffect` enum variant (e.g., `Breathing`) and an optional color table. When `None` is provided for colors, the device uses its default palette.

## Feature Registration and Discovery

Before applications can control lighting, OpenLogi must discover available features on the connected device. The [`registry.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/registry.rs) file in `crates/openlogi-hidpp/src/feature/` registers both lighting features during device initialization.

When a keyboard connects, the device layer queries the firmware's feature set. If the response includes 0x8070 or 0x8080, the corresponding `PerKeyLightingFeature` or `RgbEffectsFeature` objects are instantiated and exposed to higher-level APIs.

## High-Level Device API

The `openlogi-device` crate provides ergonomic wrappers in [`crates/openlogi-device/src/write/lighting.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/write/lighting.rs). The `KeyboardLighting` struct exposes user-friendly methods that forward to the underlying HID++ features:

- `set_rgb_zones_single_value` wraps the 0x8070 single-value command
- `set_individual_rgb_zones` wraps the 0x8070 individual zone command
- `set_rgb_cluster_effect` wraps the 0x8080 effect command

These methods use the async transport layer (`openlogi-hidpp` → `openlogi-hid` → `async-hid`) to ensure UI responsiveness while commands queue for transmission. The UI layer in [`openlogi-desktop/src/features/lighting/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-desktop/src/features/lighting/device.rs) consumes these APIs to react to user actions.

## Practical Implementation Examples

The following examples demonstrate how to control RGB lighting using the high-level device API:

```rust
use openlogi_device::write::lighting::KeyboardLighting;
use openlogi_hidpp::feature::Rgb;

// Set a solid red color across the entire keyboard using per-key lighting
async fn set_solid_red(device: &KeyboardLighting) -> Result<(), Hidpp20Error> {
    let rgb = Rgb { r: 0xFF, g: 0x00, b: 0x00 };
    // 0x8070 feature: apply single value to all zones (0 = full keyboard)
    device.set_rgb_zones_single_value(rgb, 0).await
}

```

```rust
use openlogi_device::write::lighting::KeyboardLighting;
use openlogi_hidpp::feature::rgb_effects::RgbClusterEffect;

// Activate a breathing effect using cluster effects
async fn start_breathing(device: &KeyboardLighting) -> Result<(), Hidpp20Error> {
    // 0x8080 feature: BREATHING effect with default device colors
    let effect = RgbClusterEffect::Breathing;
    device.set_rgb_cluster_effect(effect, None).await
}

```

```rust
use openlogi_device::write::lighting::KeyboardLighting;
use openlogi_hidpp::feature::RgbZone;

// Set the "G" key (zone index 42) to blue
async fn highlight_g_key(device: &KeyboardLighting) -> Result<(), Hidpp20Error> {
    let zone = RgbZone {
        start: 42,  // HID++ zone index for the G key
        color: Rgb { r: 0x00, g: 0x00, b: 0xFF },
    };
    // 0x8070 feature: update specific zone
    device.set_individual_rgb_zones(&[zone]).await
}

```

## Summary

- OpenLogi uses **HID++ 2.0 features 0x8070 and 0x8080** to control keyboard RGB lighting through the `openlogi-hidpp` crate.
- **Feature 0x8070** (`PerKeyLightingFeature`) manages per-key zones via `set_individual_rgb_zones` and related methods in [`per_key_lighting.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/per_key_lighting.rs).
- **Feature 0x8080** (`RgbEffectsFeature`) handles hardware effects like breathing via `set_rgb_cluster_effect` in [`rgb_effects.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/rgb_effects.rs).
- Both features are registered in [`registry.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/registry.rs) and wrapped by the high-level `KeyboardLighting` API in [`lighting.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/lighting.rs).
- All lighting commands are **asynchronous**, using `async fn` signatures returning `Result<(), Hidpp20Error>` to maintain UI responsiveness.

## Frequently Asked Questions

### What is the difference between HID++ features 0x8070 and 0x8080?

Feature **0x8070** controls per-key RGB lighting, allowing software to set specific colors for individual keys or zones statically. Feature **0x8080** controls cluster effects, initiating hardware-managed animations like breathing or color waves that run independently on the keyboard's firmware without continuous software updates.

### How does OpenLogi handle async RGB commands?

All HID++ lighting methods are implemented as `async fn` returning `Result<(), Hidpp20Error>`. The calls traverse the async transport stack (`openlogi-hidpp` → `openlogi-hid` → `async-hid`), allowing the UI to remain responsive while commands queue and transmit to the device over USB or Bluetooth.

### Can I set different colors for individual keys using OpenLogi?

Yes. The `PerKeyLightingFeature` (0x8070) provides `set_individual_rgb_zones`, which accepts a slice of `RgbZone` structs. Each struct specifies a zone index and RGB values, enabling unique colors for every key. The `KeyboardLighting` wrapper in [`lighting.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/lighting.rs) exposes this functionality directly to applications.

### Where are the RGB lighting features registered in the codebase?

The features are registered in [`crates/openlogi-hidpp/src/feature/registry.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/feature/registry.rs). During device initialization, this registry checks the keyboard's reported feature set and instantiates `PerKeyLightingFeature` for 0x8070 and `RgbEffectsFeature` for 0x8080 when present, making them available to the device abstraction layer.