govee 0.8.4

Async Rust library for controlling Govee smart lighting devices via cloud and local LAN APIs
Documentation

govee

CI Release crates.io docs.rs codecov

A Rust library for controlling Govee smart lighting devices. Provides idiomatic async access to both the Govee cloud API (v1 and v2) and the local LAN API over UDP, a unified backend abstraction, device registry with name/alias resolution, and a scene system for multi-device presets.

Designed as a foundation for higher-level consumers — it has no opinion about how it is invoked.

Status

Development complete. See the development plan for completed milestones.

Ecosystem

Crate Description Repo Status
govee Core library — backends, registry, scenes wkusnierczyk/govee Done
govee-cli Command-line interface wkusnierczyk/govee-cli Pending
govee-mcp Model Context Protocol server for AI agents wkusnierczyk/govee-mcp Pending
govee-server HTTP/WebSocket server for remote control wkusnierczyk/govee-server Pending
govee-workflow Workflow engine — timed command sequences, choreography wkusnierczyk/govee-workflow Pending

Getting started

Install

Add to your Cargo.toml:

[dependencies]
govee = "0.8"
tokio = { version = "1", features = ["full"] }

Or from source:

git clone https://github.com/wkusnierczyk/govee.git
cd govee
cargo build

Quick example

use govee::config::Config;
use govee::registry::DeviceRegistry;
use govee::scene::SceneTarget;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::load(std::path::Path::new("config.toml"))?;
    let registry = DeviceRegistry::start(config).await?;

    // Control a device by name or alias
    let id = registry.resolve("kitchen")?;
    registry.set_brightness(&id, 75).await?;

    // Apply a scene to a group
    registry.apply_scene("warm", SceneTarget::Group("upstairs".into())).await?;

    Ok(())
}

For detailed API configuration and usage, see the sections below.

Usage

Device registry

The DeviceRegistry is the primary entry point. It merges devices from cloud and local backends, provides name/alias resolution, per-device backend routing, optimistic state caching, and command delegation.

use govee::config::Config;
use govee::registry::DeviceRegistry;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::load(std::path::Path::new("config.toml"))?;
    let registry = DeviceRegistry::start(config).await?;

    // Resolve by name or alias
    let id = registry.resolve("kitchen")?;
    let state = registry.get_state(&id).await?;
    println!("brightness: {}", state.brightness);

    // Control
    registry.set_brightness(&id, 75).await?;
    registry.set_color(&id, govee::types::Color::new(255, 128, 0)).await?;

    // Group commands (concurrent)
    registry.group_set_power("upstairs", true).await?;

    Ok(())
}

Group commands execute concurrently via join_all. Designed for typical Govee setups with fewer than ~20 devices per group. Larger groups may trigger cloud API rate limits.

Scenes

Built-in and user-defined lighting presets. Each scene sets brightness and either an RGB color or a color temperature.

use govee::scene::SceneTarget;

// Apply a built-in scene to a single device
registry.apply_scene("warm", SceneTarget::DeviceName("kitchen".into())).await?;

// Apply to a group
registry.apply_scene("focus", SceneTarget::Group("office".into())).await?;

// Apply to all devices
registry.apply_scene("night", SceneTarget::All).await?;

Built-in scenes: warm (2700K/40%), focus (5500K/80%), night (red/10%), movie (2200K/20%), bright (6500K/100%).

User-defined scenes are loaded from the config file and can override built-ins:

[scenes.reading]
color_temp = 4000
brightness = 60

[scenes.night]  # overrides built-in
color = { r = 128, g = 0, b = 0 }
brightness = 5

apply_scene sends 2 commands per device (color/temp then brightness). On partial failure, some devices may be in an intermediate state — no rollback is attempted.

Cloud backend

For direct backend access without the registry:

use govee::backend::cloud::CloudBackend;
use govee::backend::GoveeBackend;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("GOVEE_API_KEY")
        .expect("set GOVEE_API_KEY env var");
    let backend = CloudBackend::new(api_key, None, None)?;
    let devices = backend.list_devices().await?;
    let state = backend.get_state(&devices[0].id).await?;
    backend.set_brightness(&devices[0].id, 75).await?;
    Ok(())
}

The API key is obtained from the Govee Home mobile app. Store it in an environment variable or a .env file — never hardcode it in source or commit it to version control. HTTPS is enforced for all remote URLs.

v2 API features

The cloud backend uses the Govee v2 API for device list, state, and all control commands. It also exposes higher-level capabilities only available through v2:

// Preset light scenes (device firmware scenes)
let scenes = backend.list_scenes(&id).await?;
backend.set_scene(&id, &scenes[0]).await?;

// DIY scenes (user-created in the Govee Home app)
let diy = backend.list_diy_scenes(&id).await?;
backend.set_diy_scene(&id, &diy[0]).await?;

// Segmented color and brightness (devices with addressable segments)
backend.set_segment_color(&id, &[0, 1, 2], Color::new(255, 0, 0)).await?;
backend.set_segment_brightness(&id, &[0, 1], 80).await?;

// Work modes (device-specific modes with optional sub-mode value)
let modes = backend.list_work_modes(&id).await?;
backend.set_work_mode(&id, modes[0].id, None).await?;

list_scenes, list_diy_scenes, and list_work_modes require the device's capability list to be cached (fetched during list_devices). Calling them before list_devices returns GoveeError::NotImplemented. The local backend returns NotImplemented for these methods — they are cloud-only.

Local LAN backend

use govee::backend::local::LocalBackend;
use govee::backend::GoveeBackend;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let backend = LocalBackend::new(Duration::from_secs(2), 60).await?;
    let devices = backend.list_devices().await?;
    backend.set_color(&devices[0].id, govee::types::Color::new(255, 0, 128)).await?;
    Ok(())
}

No API key required. Requires the device to be on the same LAN segment. Port 4002 must be available (not used by Home Assistant, govee2mqtt, etc.).

Configuration

Create a config file (conventionally ~/.config/govee/config.toml):

backend = "auto"            # auto | cloud | local
discovery_interval_secs = 60

[aliases]
kitchen = "H6076 Kitchen Strip"
bedroom = "H6078 Bedroom Light"

[groups]
upstairs = ["bedroom"]
all = ["kitchen", "bedroom"]

[scenes.reading]
color_temp = 4000
brightness = 60

The API key should be provided via the GOVEE_API_KEY environment variable or a .env file — not in the config file, which may be committed to version control.

See Config for all available options.

Security

The Govee LAN protocol is unauthenticated plaintext UDP. Any device on the local network can discover, control, and impersonate Govee devices. This is a fundamental property of the protocol — the library mitigates where possible (e.g., using UDP source IP over payload IP) but cannot fully prevent LAN-based attacks.

The cloud backend uses HTTPS with system CA verification (no certificate pinning). The API key is sent in every request header. Config redacts the API key in Debug and Serialize output, but the key is stored in memory in plaintext.

Scene names are restricted to alphanumeric characters, -, and _ to prevent log injection. Color temperature is capped at 10000K to avoid undefined firmware behavior.

See SECURITY.md for the full threat model.

Development

See CONTRIBUTING.md for build, test, lint, and git hooks setup.

Development plan

Milestone Scope Status
M1 — Scaffold & CI/CD Cargo project, module stubs, GitHub Actions for CI (fmt + clippy, build, test) and two-step immutable release on tag v0.1.0
M2 — Core types & configuration DeviceId, Device, DeviceState, Color, GoveeError, Config with TOML parsing, input validation v0.2.0
M3 — Cloud backend (v1) GoveeBackend trait, CloudBackend (list, state, control), rate limit handling, User-Agent, timeouts v0.3.0
M4 — Local LAN backend LocalBackend with UDP multicast discovery, unicast control, state queries, port conflict detection, TTL-based device cache v0.4.0
M5 — Device registry DeviceRegistry: cloud+local merge, name/alias resolution, backend auto-selection, optimistic state cache, groups v0.5.0
M6 — Scenes Built-in + user-defined scene presets, apply_scene with device/group/all targeting, scene validation v0.6.0
M7 — SRE & hardening Structured tracing, retry/backoff, graceful degradation, security audit, integration test suite, threat model docs v0.7.0
M8 — API v2 v2 API transport, capability model, preset/DIY scenes, segmented control, work modes, Codecov integration v0.8.0