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

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 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 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 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. 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 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:

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
}
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
}
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.
  • Feature 0x8080 (RgbEffectsFeature) handles hardware effects like breathing via set_rgb_cluster_effect in rgb_effects.rs.
  • Both features are registered in registry.rs and wrapped by the high-level KeyboardLighting API in 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 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. 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →