Initial Fedora brightness automation setup

This commit is contained in:
jan
2026-04-25 13:20:58 +02:00
commit dc932b8f64
12 changed files with 1092 additions and 0 deletions

14
bin/brightness-down-all Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
CONFIG="${BRIGHTNESS_AUTOMATION_CONFIG:-$HOME/.config/brightness-automation/env}"
if [[ -f "$CONFIG" ]]; then
# shellcheck disable=SC1090
source "$CONFIG"
fi
BACKLIGHT_DEVICE="${BRIGHTNESS_BACKLIGHT_DEVICE:-intel_backlight}"
STEP="${BRIGHTNESS_STEP:-10}"
brightnessctl -q -d "$BACKLIGHT_DEVICE" set "${STEP}%-"
"$HOME/.local/bin/brightness-osd" || true

204
bin/brightness-osd Executable file
View File

@@ -0,0 +1,204 @@
#!/usr/bin/env python3
import os
import signal
import sys
import time
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Adw, Gdk, GLib, Gtk
try:
gi.require_version("Gtk4LayerShell", "1.0")
from gi.repository import Gtk4LayerShell
except (ImportError, ValueError):
Gtk4LayerShell = None
BACKLIGHT_PATH = os.environ.get(
"BRIGHTNESS_OSD_BACKLIGHT", "/sys/class/backlight/intel_backlight"
)
PID_FILE = os.environ.get("BRIGHTNESS_OSD_PID", "/tmp/brightness-osd.pid")
VALUE_FILE = os.environ.get("BRIGHTNESS_OSD_VALUE", "/tmp/brightness-osd.value")
VISIBLE_MS = int(os.environ.get("BRIGHTNESS_OSD_VISIBLE_MS", "1400"))
def read_percent():
if len(sys.argv) > 1:
return max(0, min(100, int(round(float(sys.argv[1])))))
with open(os.path.join(BACKLIGHT_PATH, "brightness"), encoding="utf-8") as handle:
current = int(handle.read().strip())
with open(os.path.join(BACKLIGHT_PATH, "max_brightness"), encoding="utf-8") as handle:
maximum = int(handle.read().strip())
return max(0, min(100, round(current * 100 / maximum)))
def read_existing_pid():
try:
with open(PID_FILE, encoding="utf-8") as handle:
return int(handle.read().strip())
except (FileNotFoundError, ValueError):
return None
def process_exists(pid):
try:
os.kill(pid, 0)
return True
except ProcessLookupError:
return False
except PermissionError:
return True
def signal_existing(percent):
pid = None
for _attempt in range(5):
pid = read_existing_pid()
if pid and pid != os.getpid() and process_exists(pid):
break
time.sleep(0.03)
else:
return False
with open(VALUE_FILE, "w", encoding="utf-8") as handle:
handle.write(str(percent))
try:
os.kill(pid, signal.SIGUSR1)
return True
except ProcessLookupError:
return False
class BrightnessOsd(Adw.Application):
def __init__(self, percent):
super().__init__(application_id="local.brightness.osd")
self.percent = percent
self.label = None
self.bar = None
self.timeout_id = None
def update_percent(self, percent):
self.percent = max(0, min(100, percent))
if self.label:
self.label.set_label(f"{self.percent}%")
if self.bar:
self.bar.set_value(self.percent)
if self.timeout_id:
GLib.source_remove(self.timeout_id)
self.timeout_id = GLib.timeout_add(VISIBLE_MS, self.quit)
def handle_refresh(self, *_args):
try:
with open(VALUE_FILE, encoding="utf-8") as handle:
percent = int(handle.read().strip())
except (FileNotFoundError, ValueError):
percent = read_percent()
GLib.idle_add(self.update_percent, percent)
def do_activate(self):
with open(PID_FILE, "w", encoding="utf-8") as handle:
handle.write(str(os.getpid()))
window = Gtk.ApplicationWindow(application=self)
window.set_title("Brightness")
window.set_decorated(False)
window.set_resizable(False)
window.set_default_size(190, 58)
window.set_opacity(0.92)
window.set_hide_on_close(True)
if Gtk4LayerShell is not None:
Gtk4LayerShell.init_for_window(window)
Gtk4LayerShell.set_layer(window, Gtk4LayerShell.Layer.OVERLAY)
Gtk4LayerShell.set_anchor(window, Gtk4LayerShell.Edge.TOP, True)
Gtk4LayerShell.set_margin(window, Gtk4LayerShell.Edge.TOP, 28)
Gtk4LayerShell.set_keyboard_mode(window, Gtk4LayerShell.KeyboardMode.NONE)
css = Gtk.CssProvider()
css.load_from_data(
b"""
window {
background: transparent;
}
.osd {
background: alpha(@window_fg_color, 0.88);
color: @window_bg_color;
border-radius: 15px;
padding: 10px 14px;
box-shadow: 0 10px 34px alpha(black, 0.34);
}
.osd image {
color: @window_bg_color;
}
.percent {
font-size: 15px;
font-weight: 700;
}
levelbar trough {
min-height: 6px;
border-radius: 999px;
background: alpha(@window_bg_color, 0.24);
}
levelbar block.filled {
min-height: 6px;
border-radius: 999px;
background: @window_bg_color;
}
"""
)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(), css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
box.add_css_class("osd")
box.set_halign(Gtk.Align.CENTER)
box.set_valign(Gtk.Align.START)
box.set_margin_top(28)
icon = Gtk.Image.new_from_icon_name("display-brightness-symbolic")
icon.set_pixel_size(22)
self.label = Gtk.Label(label=f"{self.percent}%")
self.label.add_css_class("percent")
self.label.set_width_chars(4)
self.bar = Gtk.LevelBar.new_for_interval(0, 100)
self.bar.set_value(self.percent)
self.bar.set_size_request(96, -1)
self.bar.set_valign(Gtk.Align.CENTER)
box.append(icon)
box.append(self.bar)
box.append(self.label)
window.set_child(box)
signal.signal(signal.SIGUSR1, self.handle_refresh)
window.present()
self.timeout_id = GLib.timeout_add(VISIBLE_MS, self.quit)
def main():
percent = read_percent()
if signal_existing(percent):
return 0
with open(PID_FILE, "w", encoding="utf-8") as handle:
handle.write(str(os.getpid()))
app = BrightnessOsd(percent)
return app.run([])
if __name__ == "__main__":
raise SystemExit(main())

65
bin/brightness-sync-hg342pcb Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env bash
set -euo pipefail
CONFIG="${BRIGHTNESS_AUTOMATION_CONFIG:-$HOME/.config/brightness-automation/env}"
if [[ -f "$CONFIG" ]]; then
# shellcheck disable=SC1090
source "$CONFIG"
fi
MODEL="${BRIGHTNESS_DDCUTIL_MODEL:-HG342PCB}"
DISPLAY_NUM="${BRIGHTNESS_DDCUTIL_DISPLAY:-1}"
BUS_NUM="${BRIGHTNESS_DDCUTIL_BUS:-16}"
BACKLIGHT_PATH="${BRIGHTNESS_SYNC_BACKLIGHT:-/sys/class/backlight/intel_backlight}"
INTERVAL="${BRIGHTNESS_SYNC_INTERVAL:-1}"
MIN_PERCENT="${BRIGHTNESS_SYNC_MIN_PERCENT:-1}"
brightness_percent() {
local current max percent
current="$(<"${BACKLIGHT_PATH}/brightness")"
max="$(<"${BACKLIGHT_PATH}/max_brightness")"
percent="$(( (current * 100 + max / 2) / max ))"
if (( percent < MIN_PERCENT )); then
percent="$MIN_PERCENT"
elif (( percent > 100 )); then
percent=100
fi
printf '%s\n' "$percent"
}
run_ddcutil() {
if sudo -n ddcutil --bus "$BUS_NUM" getvcp 10 >/dev/null 2>&1; then
sudo -n ddcutil --bus "$BUS_NUM" "$@"
return
fi
if sudo -n ddcutil --display "$DISPLAY_NUM" getvcp 10 >/dev/null 2>&1; then
sudo -n ddcutil --display "$DISPLAY_NUM" "$@"
return
fi
if sudo -n ddcutil --model "$MODEL" getvcp 10 >/dev/null 2>&1; then
sudo -n ddcutil --model "$MODEL" "$@"
return
fi
echo "External ${MODEL} was not reachable via sudo -n ddcutil." >&2
return 1
}
last_percent=""
while true; do
percent="$(brightness_percent)"
if [[ "$percent" != "$last_percent" ]]; then
if run_ddcutil setvcp 10 "$percent" >/dev/null; then
last_percent="$percent"
fi
fi
sleep "$INTERVAL"
done

14
bin/brightness-up-all Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
CONFIG="${BRIGHTNESS_AUTOMATION_CONFIG:-$HOME/.config/brightness-automation/env}"
if [[ -f "$CONFIG" ]]; then
# shellcheck disable=SC1090
source "$CONFIG"
fi
BACKLIGHT_DEVICE="${BRIGHTNESS_BACKLIGHT_DEVICE:-intel_backlight}"
STEP="${BRIGHTNESS_STEP:-10}"
brightnessctl -q -d "$BACKLIGHT_DEVICE" set "+${STEP}%"
"$HOME/.local/bin/brightness-osd" || true