LBrightness (lbright) is a lightweight, ultra-efficient, intelligent auto-brightness system for Linux. Written in high-performance Rust, it automatically adjusts screen brightness based on configurable time-of-day curves and Ambient Light Sensors (ALS), seamlessly learning your preferred levels when you make manual adjustments.
Built for microsecond-fast direct kernel sysfs I/O, robust external monitor DDC/CI control via ddcutil, and sub-5MB background memory footprint (< 4MB RSS typical) with zero idle CPU overhead.
- ⚡ Direct Sysfs Hardware Control: Reads and writes directly to
/sys/class/backlight/*with zero subprocess overhead and microsecond latency. - 🖥️ External Monitor Support (DDC/CI): Communicates with external monitors via DDC/CI (VCP
0x10) over an isolated worker thread with non-blocking timeouts to prevent system freezes. - 🧠 Adaptive Learning: Learns your preferred screen brightness when you make manual adjustments on internal panels.
- 📈 24-Hour Time Curves: Smooth, linear interpolation wrapping seamlessly across midnight.
- ☀️ Ambient Light Sensor (ALS) Integration: Smooths sensor readings with rolling averages and configurable hysteresis to eliminate screen brightness jitter.
- ⌨️ Interactive Terminal UI (TUI): Built-in ANSI menu (
lbright tui) to view live status, add/edit/delete curve points, test time targets, toggle ALS, pause/resume, and rescan displays. - 🪶 Zero-Overhead Background Daemon: Uses a predictable 100ms synchronous tick loop with < 4MB RSS and 0.0% idle CPU.
- 🔄 Non-Destructive Migration: Import legacy
profiles.confinto the new structured INI format withlbright migrate [--dry-run]. - 🔒 Advisory Config Locking: Prevents race conditions and write collisions between daemon learning and interactive configuration edits.
- Rust & Cargo (required to build from source):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - systemd (optional, recommended for user background service management)
- ddcutil (optional, required only for external DDC/CI monitors):
sudo apt install ddcutil i2c-tools(or equivalent for your distribution)
To allow lbright to control internal backlights and external DDC/CI monitors without requiring root privileges:
# Add your user to the video and i2c groups
sudo usermod -aG video,i2c $USER
# Ensure the i2c-dev kernel module is loaded for external monitor control
sudo modprobe i2c-dev
echo "i2c-dev" | sudo tee /etc/modules-load.d/i2c-dev.confNote
Log out and log back in (or restart) for group permission changes to take effect.
Clone the repository and run the included installer script:
git clone https://github.com/LeanBitLab/adaptive-brightness-linux.git
cd adaptive-brightness-linux
chmod +x install.sh
./install.shThe installer will:
- Compile the optimized release binary (
cargo build --release). - Install
lbrightto~/.local/bin/lbright. - Install and harden
lbright.serviceinto~/.config/systemd/user/. - Gracefully stop any legacy daemon and start
lbright.service.
To build and install a standard Debian package on Debian, Ubuntu, Linux Mint, or Pop!_OS:
# Build the package
./build-deb.sh
# Install the generated .deb package
sudo dpkg -i lbright_*.deb
# Enable and start the background service
systemctl --user enable --now lbright.service# Build release binary
cargo build --release
# Copy binary to user path
install -Dm755 target/release/lbright "$HOME/.local/bin/lbright"
# Install systemd user service
install -Dm644 lbright.service "$HOME/.config/systemd/user/lbright.service"
# Reload and start service
systemctl --user daemon-reload
systemctl --user enable --now lbright.serviceManage the background daemon using standard systemctl commands:
# Check daemon status and resource usage
systemctl --user status lbright.service
# Restart the daemon
systemctl --user restart lbright.service
# Stop the daemon
systemctl --user stop lbright.service
# View real-time daemon logs
journalctl --user -u lbright.service -flbright provides a full suite of CLI subcommands for control, status inspection, and configuration.
lbright <SUBCOMMAND> [OPTIONS]
| Command | Description | Example |
|---|---|---|
status |
Display detected hardware, current brightness, ALS sensor metrics, and pause state | lbright status |
tui |
Launch the interactive ANSI terminal menu and curve editor | lbright tui |
scan |
Scan and list all internal backlight panels and external DDC/CI monitors | lbright scan |
set <device> <0-100> |
Manually set target brightness percentage on a specific device | lbright set internal 65 |
pause <duration|off> |
Temporarily or indefinitely pause automatic adjustments | lbright pause 30m |
migrate [--dry-run] |
Import legacy ~/.config/auto-brightness/profiles.conf into config.ini |
lbright migrate --dry-run |
daemon [--foreground] |
Run background daemon process (used by systemd) | lbright daemon --foreground |
version |
Print current lbright version |
lbright version |
help |
Print help and CLI options | lbright help |
internal: Primary internal backlight panel (/sys/class/backlight/*).internal:<name>: Specific internal panel (e.g.internal:intel_backlight,internal:amdgpu_bl0).ddc:default: First detected external DDC/CI display.ddc:<index>: Specific DDC display index (e.g.ddc:1,ddc:2).ddc:bus=<bus>: Specific I2C bus number (e.g.ddc:bus=7).ddc:serial=<sn>: Specific monitor serial number (e.g.ddc:serial=ABC123456).ddc:model=<model>: Specific monitor model name (e.g.ddc:model=DELL_U2720Q).
# View live status
lbright status
# Manually set internal screen brightness to 60%
lbright set internal 60
# Set external DDC monitor #1 to 45%
lbright set ddc:1 45
# Pause adjustments for 1 hour (e.g. during gaming or movie playback)
lbright pause 1h
# Resume adjustments immediately
lbright pause off
# Check current pause timer status
lbright pause status
# Preview migration from old profiles.conf
lbright migrate --dry-runRun lbright tui from any terminal or SSH session to access an interactive management console:
==========================================
LBrightness Control Menu
==========================================
--- Main Menu --- [State: Active]
1) Live Status & Devices
2) Edit Internal Brightness Curve
3) Edit External Display Profiles
4) Toggle Ambient Sensor (Currently: Disabled)
5) Pause / Resume Adjustments
6) Rescan Displays
7) Migrate Old Config (profiles.conf)
8) Reload Daemon
9) Exit
- Interactive Curve Editor: Add, edit, remove, and simulate target brightness for specific timestamps (
HHMM). - Safe Advisory Locks: All edits made through the TUI automatically acquire advisory file locks, ensuring background learning won't overwrite your changes.
lbright controls external monitors using the industry-standard DDC/CI (Display Data Channel Command Interface) protocol via ddcutil.
- Direct Digital Connection (Required):
- Connect using direct HDMI-to-HDMI, DisplayPort-to-DisplayPort, USB-C (DP Alt Mode), or DVI-D.
[!WARNING] VGA to HDMI / HDMI to VGA adapters do NOT work. Active and passive analog VGA converters block bidirectional I2C communication on slave address
0x37, preventing software brightness adjustment. - Monitor OSD Menu Configuration:
- Open your physical monitor's built-in On-Screen Display (OSD) menu using its front or bottom buttons.
- Navigate to Settings / System / OSD Setup / Miscellaneous.
- Ensure DDC/CI is toggled to "On" or "Enabled".
- Software Prerequisites & Permissions:
# Install ddcutil and i2c-tools sudo apt install -y ddcutil i2c-tools # Add your user to the i2c group sudo usermod -aG i2c $USER # Ensure kernel i2c-dev module is loaded sudo modprobe i2c-dev echo "i2c-dev" | sudo tee /etc/modules-load.d/i2c-dev.conf
Test if your monitor responds to DDC/CI commands:
# 1. Direct hardware check
ddcutil detect
# 2. LBrightness hardware scan
lbright scan
# 3. Test changing brightness on external display #1 to 50%
lbright set ddc:1 50| Issue / Error Message | Cause | Solution |
|---|---|---|
Monitor does not support DDC/CI (address 0x37 unresponsive) |
DDC/CI disabled in monitor menu or blocked by video adapter. | 1. Enable DDC/CI in monitor physical OSD menu. 2. Replace VGA converter dongles with direct HDMI, DisplayPort, or DVI cables. |
Permission denied opening /dev/i2c-* |
Current user is not in i2c group. |
Run sudo usermod -aG i2c $USER, then log out and log back in (or run newgrp i2c). |
| External monitor not updating in background daemon | Display section disabled or daemon needs rescan. | Run lbright scan or open lbright tui -> Edit External Display Profiles to ensure the profile is enabled. |
The configuration file is located at ~/.config/lbrightness/config.ini. It uses an INI structure with a [general] section and individual device curve sections.
# LBrightness Configuration
version = 2
[general]
interval_min = 15
ambient = false
ambient_poll_sec = 5
ambient_average = 3
ambient_hysteresis_pct = 3
fade_internal = true
fade_external = false
learn_internal = true
learn_external = false
min_internal = 5
max_internal = 100
min_external = 0
max_external = 100
ddc_rescan_min = 5
ddc_cooldown_sec = 10
ddc_set_timeout_sec = 5
ddc_detect_timeout_sec = 10
[internal]
enabled = true
point = 00:00:18
point = 07:00:45
point = 12:00:65
point = 19:00:55
point = 23:00:25
[ddc:default]
enabled = true
point = 00:00:18
point = 07:00:45
point = 12:00:65
point = 19:00:55
point = 23:00:25| Option | Type | Default | Description |
|---|---|---|---|
interval_min |
Integer | 15 |
Periodic interval (in minutes) for recalculating time curves. |
ambient |
Boolean | false |
Enable or disable Ambient Light Sensor (ALS) integration. |
ambient_poll_sec |
Integer | 5 |
Sensor reading interval in seconds. |
ambient_average |
Integer | 3 |
Number of recent sensor samples used for rolling average smoothing. |
ambient_hysteresis_pct |
Integer | 3 |
Deadband percentage required before ALS triggers a brightness update (eliminates jitter). |
fade_internal |
Boolean | true |
Enables smooth multi-step brightness fading on internal sysfs panels. |
fade_external |
Boolean | false |
Multi-step fading on DDC displays (disabled by default due to I2C communication bus latency). |
learn_internal |
Boolean | true |
Automatically adjust 24h curve points when you change internal brightness manually. |
learn_external |
Boolean | false |
Automatically learn adjustments on external DDC displays. |
min_internal |
Integer (0-100) | 5 |
Minimum brightness floor for internal panel (prevents accidental black screen). |
max_internal |
Integer (0-100) | 100 |
Maximum brightness ceiling for internal panel. |
min_external |
Integer (0-100) | 0 |
Minimum brightness floor for external displays. |
max_external |
Integer (0-100) | 100 |
Maximum brightness ceiling for external displays. |
ddc_rescan_min |
Integer | 5 |
Periodic interval (in minutes) to rescan for attached/detached external monitors. |
ddc_cooldown_sec |
Integer | 10 |
Backoff cooldown period if a DDC communication error occurs. |
ddc_set_timeout_sec |
Integer | 5 |
Maximum execution timeout (in seconds) for ddcutil setvcp commands. |
ddc_detect_timeout_sec |
Integer | 10 |
Maximum execution timeout (in seconds) for ddcutil detect scans. |
enabled = true|false: Enables or disables brightness management for this display.point = HH:MM:PERCENT: Curve control point mapping 24-hour time to brightness percentage (0-100).lbrightuses smooth linear interpolation between adjacent points across midnight.
| Resource | Default Path | Environment Override |
|---|---|---|
| Active Config | ~/.config/lbrightness/config.ini |
LBRIGHT_CONFIG_PATH |
| State Directory | ~/.local/state/lbrightness/ |
LBRIGHT_STATE_DIR |
| Legacy Config | ~/.config/auto-brightness/profiles.conf |
LBRIGHT_LEGACY_CONFIG_PATH |
If you previously used the Python version of auto-brightness, import your existing profiles and time curves into lbright:
# Preview the converted configuration without writing
lbright migrate --dry-run
# Perform non-destructive migration
lbright migrateYour original ~/.config/auto-brightness/profiles.conf will remain untouched.
If you need to revert to the legacy Python service during testing:
# Disable lbright and enable legacy python service
systemctl --user disable --now lbright.service
systemctl --user enable --now auto-brightness.serviceTo switch back to lbright:
systemctl --user disable --now auto-brightness.service
systemctl --user enable --now lbright.serviceRun the uninstaller script to remove the binary and systemd service:
./uninstall.shTo automatically delete configuration and state files without prompting:
./uninstall.sh -yCheck out our other projects: 👉 LeanBitLab Projects

