Initial Fedora brightness automation setup
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
.DS_Store
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Jan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
152
README.md
Normal file
152
README.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# Fedora Brightness Automation Buttons OSD
|
||||||
|
|
||||||
|
Small Fedora/GNOME brightness setup for laptops with an internal Linux backlight and an external DDC/CI monitor.
|
||||||
|
|
||||||
|
The internal display is the master. Brightness keys change the internal panel, show a compact OSD, and a user service mirrors the same percentage to the external monitor with `ddcutil`.
|
||||||
|
|
||||||
|
This avoids drift from letting two outputs auto-adjust independently.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Internal display controlled with `brightnessctl`
|
||||||
|
- External monitor mirrored with `ddcutil` VCP code `10`
|
||||||
|
- Compact GTK4/libadwaita OSD for custom brightness shortcuts
|
||||||
|
- Optional Wayland top-edge positioning with `gtk4-layer-shell`
|
||||||
|
- systemd user service for continuous external sync
|
||||||
|
- Configurable backlight device, DDC bus, monitor model, step size, and sync interval
|
||||||
|
- Optional wluma config where only the internal display is managed
|
||||||
|
|
||||||
|
## Tested Setup
|
||||||
|
|
||||||
|
- Fedora 43
|
||||||
|
- GNOME Shell 49
|
||||||
|
- Internal panel: `intel_backlight`
|
||||||
|
- External monitor: DDC/CI via `ddcutil`
|
||||||
|
|
||||||
|
Other Fedora/GNOME setups should work if `brightnessctl` and `ddcutil` can control the displays.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install -y brightnessctl ddcutil python3-gobject gtk4 libadwaita gtk4-layer-shell
|
||||||
|
```
|
||||||
|
|
||||||
|
`gtk4-layer-shell` is recommended on GNOME Wayland. Without it, GNOME may place the OSD like a normal GTK window instead of pinning it at the top edge.
|
||||||
|
|
||||||
|
If your `ddcutil` command needs sudo, configure passwordless sudo for the specific command you use. Example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
jan ALL=(root) NOPASSWD: /usr/bin/ddcutil
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a stricter sudoers rule if you prefer limiting arguments.
|
||||||
|
|
||||||
|
## Quick Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitea.diehanis.de/Jan/Fedora_Brightness_Automation_Buttons_OSD.git
|
||||||
|
cd Fedora_Brightness_Automation_Buttons_OSD
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The installer writes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
~/.local/bin/brightness-osd
|
||||||
|
~/.local/bin/brightness-up-all
|
||||||
|
~/.local/bin/brightness-down-all
|
||||||
|
~/.local/bin/brightness-sync-hg342pcb
|
||||||
|
~/.config/systemd/user/external-brightness-sync.service
|
||||||
|
~/.config/brightness-automation/env
|
||||||
|
```
|
||||||
|
|
||||||
|
Then set your GNOME custom keyboard shortcuts to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/home/YOUR_USER/.local/bin/brightness-down-all
|
||||||
|
/home/YOUR_USER/.local/bin/brightness-up-all
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the full path in GNOME Settings.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Edit:
|
||||||
|
|
||||||
|
```text
|
||||||
|
~/.config/brightness-automation/env
|
||||||
|
```
|
||||||
|
|
||||||
|
Default config:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BRIGHTNESS_BACKLIGHT_DEVICE="intel_backlight"
|
||||||
|
BRIGHTNESS_SYNC_BACKLIGHT="/sys/class/backlight/intel_backlight"
|
||||||
|
BRIGHTNESS_STEP="10"
|
||||||
|
BRIGHTNESS_DDCUTIL_MODEL="HG342PCB"
|
||||||
|
BRIGHTNESS_DDCUTIL_DISPLAY="1"
|
||||||
|
BRIGHTNESS_DDCUTIL_BUS="16"
|
||||||
|
BRIGHTNESS_SYNC_INTERVAL="1"
|
||||||
|
BRIGHTNESS_SYNC_MIN_PERCENT="1"
|
||||||
|
BRIGHTNESS_OSD_VISIBLE_MS="1400"
|
||||||
|
```
|
||||||
|
|
||||||
|
After changing DDC or sync values:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user restart external-brightness-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Finding Your Values
|
||||||
|
|
||||||
|
Backlight device:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brightnessctl -l
|
||||||
|
ls /sys/class/backlight
|
||||||
|
```
|
||||||
|
|
||||||
|
External monitor:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ddcutil detect
|
||||||
|
sudo ddcutil getvcp 10
|
||||||
|
```
|
||||||
|
|
||||||
|
If `ddcutil detect` reports a bus like `/dev/i2c-16`, set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BRIGHTNESS_DDCUTIL_BUS="16"
|
||||||
|
```
|
||||||
|
|
||||||
|
## wluma
|
||||||
|
|
||||||
|
If you use `wluma`, let it manage only the internal display. The external monitor should follow through the sync service.
|
||||||
|
|
||||||
|
Optional install of the example wluma config:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
INSTALL_WLUMA_CONFIG=1 ./install.sh
|
||||||
|
systemctl --user restart wluma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Example config is in:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wluma/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status external-brightness-sync.service
|
||||||
|
journalctl --user -u external-brightness-sync.service -n 50 --no-pager
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstall
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./uninstall.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The uninstall script keeps `~/.config/brightness-automation/env` so local settings are not destroyed.
|
||||||
14
bin/brightness-down-all
Executable file
14
bin/brightness-down-all
Executable 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
204
bin/brightness-osd
Executable 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
65
bin/brightness-sync-hg342pcb
Executable 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
14
bin/brightness-up-all
Executable 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
|
||||||
522
docs/hp-firefly-fedora-brightness-notes.md
Normal file
522
docs/hp-firefly-fedora-brightness-notes.md
Normal file
@@ -0,0 +1,522 @@
|
|||||||
|
# HP Firefly G11 unter Fedora – finaler sauberer Stand
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
Saubere gemeinsame Helligkeitssteuerung für:
|
||||||
|
|
||||||
|
- internes Display über `intel_backlight`
|
||||||
|
- externen Monitor über `ddcutil`
|
||||||
|
|
||||||
|
sowie automatische Helligkeitsregelung über den vorhandenen Ambient-Light-Sensor mit `wluma`.
|
||||||
|
|
||||||
|
Finale Logik:
|
||||||
|
- **internes Display ist Master**
|
||||||
|
- `wluma` regelt nur noch `intel_backlight`
|
||||||
|
- der externe Monitor wird automatisch auf denselben Prozentwert nachgezogen
|
||||||
|
- die manuellen Helligkeitstasten zeigen ein GNOME/Adwaita-passendes OSD
|
||||||
|
- dadurch können intern und extern nicht mehr durch zwei unabhängige `wluma`-Regelungen auseinanderlaufen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Finaler Ist-Zustand
|
||||||
|
|
||||||
|
### Was aktuell funktioniert
|
||||||
|
- Das interne Display hängt als normales Backlight unter:
|
||||||
|
- `intel_backlight`
|
||||||
|
- Der externe Monitor `HG342PCB` am Thunderbolt-Dock ist per `ddcutil` erreichbar.
|
||||||
|
- Die gemeinsamen Button-Skripte regeln jetzt:
|
||||||
|
- intern per `brightnessctl`
|
||||||
|
- zeigen ein lokales GTK/libadwaita-OSD
|
||||||
|
- extern folgt automatisch über `external-brightness-sync.service`
|
||||||
|
- `wluma.service` läuft als User-Service nach dem Login.
|
||||||
|
- `wluma` nutzt nur noch das interne `intel_backlight`.
|
||||||
|
- `external-brightness-sync.service` spiegelt den internen Prozentwert automatisch auf den externen Monitor per `ddcutil`.
|
||||||
|
|
||||||
|
### Aktueller Nachweis
|
||||||
|
```bash
|
||||||
|
systemctl --user status wluma.service
|
||||||
|
systemctl --user status external-brightness-sync.service
|
||||||
|
brightnessctl -l
|
||||||
|
sudo ddcutil detect
|
||||||
|
sudo ddcutil getvcp 10
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwarteter sauberer Zustand:
|
||||||
|
in `brightnessctl -l`:
|
||||||
|
- `intel_backlight`
|
||||||
|
|
||||||
|
und in `sudo ddcutil detect`:
|
||||||
|
- `Display 1`
|
||||||
|
- `I2C bus: /dev/i2c-16`
|
||||||
|
- `Model: HG342PCB`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Warum dieser Stand der richtige ist
|
||||||
|
|
||||||
|
### 1. Internes und externes Display laufen jetzt über zwei stabile Wege
|
||||||
|
Das interne Panel läuft weiter über `intel_backlight`.
|
||||||
|
Der externe Monitor wird über das Thunderbolt-Dock direkt per `ddcutil` angesprochen.
|
||||||
|
|
||||||
|
Damit ist die gemeinsame Steuerung wieder stabil, auch wenn intern und extern technisch über unterschiedliche Wege laufen.
|
||||||
|
|
||||||
|
Wichtig:
|
||||||
|
Es gibt nur noch eine automatische Regelinstanz. `wluma` regelt das interne Panel, der Sync-Dienst übernimmt die Spiegelung nach extern.
|
||||||
|
|
||||||
|
### 2. Das Thunderbolt-Dock reicht DDC/CI sauber durch, `ddcci_backlight` war hier aber nicht der richtige Weg
|
||||||
|
Mit dem neuen Dock ist der Monitor nachweislich über `ddcutil` auf `card1-DP-5` / `/dev/i2c-16` erreichbar.
|
||||||
|
Die frühere `ddcci3`-Lösung über `/sys/class/backlight` war für dieses Setup dagegen nicht stabil nutzbar.
|
||||||
|
|
||||||
|
Der saubere Endstand ist deshalb:
|
||||||
|
|
||||||
|
- **intern: `brightnessctl`**
|
||||||
|
- **extern: `ddcutil`**
|
||||||
|
- **kein aggressives Hotplug-Refresh über `ddcci`**
|
||||||
|
|
||||||
|
### 3. GNOME Auto Brightness war nicht funktionsfähig
|
||||||
|
Der Ambient-Light-Sensor funktioniert eindeutig, aber GNOME hat die Helligkeit sichtbar nicht geregelt.
|
||||||
|
Deshalb ist `wluma` jetzt die saubere automatische Lösung auf Basis des Sensors.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aktive Komponenten
|
||||||
|
|
||||||
|
### Als User aktiv
|
||||||
|
|
||||||
|
#### wluma-Service
|
||||||
|
```text
|
||||||
|
~/.config/systemd/user/wluma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### wluma-Konfiguration
|
||||||
|
```text
|
||||||
|
~/.config/wluma/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Gemeinsame Helligkeitsskripte
|
||||||
|
```text
|
||||||
|
~/.local/bin/brightness-osd
|
||||||
|
~/.local/bin/brightness-ddcutil-hg342pcb
|
||||||
|
~/.local/bin/brightness-down-all
|
||||||
|
~/.local/bin/brightness-up-all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Externer Sync-Dienst
|
||||||
|
```text
|
||||||
|
~/.local/bin/brightness-sync-hg342pcb
|
||||||
|
~/.config/systemd/user/external-brightness-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Finaler Inhalt der wichtigen Dateien
|
||||||
|
|
||||||
|
### 1. `~/.local/bin/brightness-ddcutil-hg342pcb`
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MODEL="${BRIGHTNESS_DDCUTIL_MODEL:-HG342PCB}"
|
||||||
|
STEP="${BRIGHTNESS_DDCUTIL_STEP:-10}"
|
||||||
|
DISPLAY_NUM="${BRIGHTNESS_DDCUTIL_DISPLAY:-1}"
|
||||||
|
BUS_NUM="${BRIGHTNESS_DDCUTIL_BUS:-16}"
|
||||||
|
|
||||||
|
if [ "$#" -ne 1 ]; then
|
||||||
|
echo "Usage: $(basename "$0") up|down" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
up) delta="+" ;;
|
||||||
|
down) delta="-" ;;
|
||||||
|
*) echo "Usage: $(basename "$0") up|down" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if ddcutil --bus "$BUS_NUM" getvcp 10 >/dev/null 2>&1; then
|
||||||
|
ddcutil --bus "$BUS_NUM" "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ddcutil --display "$DISPLAY_NUM" getvcp 10 >/dev/null 2>&1; then
|
||||||
|
ddcutil --display "$DISPLAY_NUM" "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ddcutil --model "$MODEL" getvcp 10 >/dev/null 2>&1; then
|
||||||
|
ddcutil --model "$MODEL" "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "External HG342PCB was not reachable via ddcutil on bus ${BUS_NUM}." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_ddcutil setvcp 10 "$delta" "$STEP" >/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. `~/.local/bin/brightness-down-all`
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
brightnessctl -q -d intel_backlight set 10%-
|
||||||
|
/home/jan/.local/bin/brightness-osd || true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. `~/.local/bin/brightness-up-all`
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
brightnessctl -q -d intel_backlight set +10%
|
||||||
|
/home/jan/.local/bin/brightness-osd || true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. `~/.local/bin/brightness-osd`
|
||||||
|
```python
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
```
|
||||||
|
|
||||||
|
Dieses Skript zeigt ein kleines GTK4/libadwaita-OSD am oberen Bildschirmrand mit:
|
||||||
|
- Adwaita-Icon `display-brightness-symbolic`
|
||||||
|
- Prozentwert der internen Master-Helligkeit
|
||||||
|
- Fortschrittsbalken
|
||||||
|
|
||||||
|
Das OSD ist einzeilig und kompakt. Wenn mehrere Tastendrücke schnell hintereinander kommen, wird der laufende OSD-Prozess aktualisiert und der Ausblend-Timer verlängert; dadurch verschwindet es nicht mehr zwischen zwei Helligkeitsänderungen.
|
||||||
|
|
||||||
|
Für echte Positionierung am oberen Bildschirmrand unter GNOME Wayland wird dieses Fedora-Paket benötigt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install -y gtk4-layer-shell
|
||||||
|
```
|
||||||
|
|
||||||
|
Ohne `gtk4-layer-shell` darf GNOME/Wayland ein normales GTK-Fenster nicht zuverlässig oben positionieren; dann fällt das Skript auf die normale GTK-Fensterposition zurück.
|
||||||
|
|
||||||
|
### 5. `~/.config/wluma/config.toml`
|
||||||
|
```toml
|
||||||
|
[als.iio]
|
||||||
|
path = "/sys/bus/iio/devices"
|
||||||
|
thresholds = { 0 = "night", 20 = "dark", 80 = "dim", 250 = "normal", 500 = "bright", 800 = "outdoors" }
|
||||||
|
|
||||||
|
[[output.backlight]]
|
||||||
|
name = "eDP-1"
|
||||||
|
path = "/sys/class/backlight/intel_backlight"
|
||||||
|
capturer = "none"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. `~/.local/bin/brightness-sync-hg342pcb`
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. `~/.config/systemd/user/wluma.service`
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Adjust screen brightness automatically with wluma
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
After=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/wluma
|
||||||
|
Restart=always
|
||||||
|
PrivateNetwork=true
|
||||||
|
PrivateMounts=false
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical-session.target
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. `~/.config/systemd/user/external-brightness-sync.service`
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Mirror internal panel brightness to external HG342PCB
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
After=graphical-session.target wluma.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=%h/.local/bin/brightness-sync-hg342pcb
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical-session.target
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dienste – finaler Soll-Zustand
|
||||||
|
|
||||||
|
### Aktiv
|
||||||
|
```bash
|
||||||
|
systemctl --user status wluma.service
|
||||||
|
systemctl --user status external-brightness-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet:
|
||||||
|
- `wluma.service`: `active (running)`
|
||||||
|
- `external-brightness-sync.service`: `active (running)`
|
||||||
|
|
||||||
|
### Nicht mehr Teil der Lösung
|
||||||
|
Diese Komponenten gehören **nicht** mehr zur finalen Lösung:
|
||||||
|
|
||||||
|
- `ddcci-attach.service`
|
||||||
|
- `/usr/local/sbin/ddcci-attach-hg342pcb`
|
||||||
|
- `ddcci-refresh.service`
|
||||||
|
- `/usr/local/sbin/ddcci-refresh`
|
||||||
|
- `/etc/udev/rules.d/99-ddcci-hotplug.rules`
|
||||||
|
|
||||||
|
Der Grund:
|
||||||
|
Mit dem Thunderbolt-Dock funktioniert die externe Steuerung sauber über `ddcutil`; die `ddcci`-Backlight-Variante wird dafür nicht mehr benötigt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cleanup alter / falscher Komponenten
|
||||||
|
|
||||||
|
### Falls noch vorhanden: `ddcci-refresh` entfernen
|
||||||
|
```bash
|
||||||
|
sudo systemctl disable --now ddcci-refresh.service 2>/dev/null || true
|
||||||
|
sudo rm -f /etc/systemd/system/ddcci-refresh.service
|
||||||
|
sudo rm -f /usr/local/sbin/ddcci-refresh
|
||||||
|
sudo rm -f /etc/udev/rules.d/99-ddcci-hotplug.rules
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo udevadm control --reload-rules
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alte `ddcci`-Komponenten können entfernt werden
|
||||||
|
Die ältere `ddcci`-/`ddcci_backlight`-Variante ist **nicht mehr der finale Stand**.
|
||||||
|
Final ist jetzt die Hybrid-Variante über:
|
||||||
|
|
||||||
|
- `intel_backlight` für intern
|
||||||
|
- `ddcutil` für extern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bedienung im Alltag
|
||||||
|
|
||||||
|
### Manuell beide Displays heller / dunkler
|
||||||
|
```bash
|
||||||
|
/home/jan/.local/bin/brightness-down-all
|
||||||
|
/home/jan/.local/bin/brightness-up-all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reaktionsverhalten
|
||||||
|
Das interne Display reagiert praktisch sofort.
|
||||||
|
Der externe Monitor reagiert typischerweise leicht verzögert, oft ungefähr nach einer Sekunde.
|
||||||
|
|
||||||
|
Das ist bei `ddcutil` normal, weil zuerst DDC/CI-Kommunikation mit dem Monitor über das Dock stattfindet.
|
||||||
|
Etwas schneller könnte es eventuell mit `--noverify` werden, das ist aber weniger robust und daher aktuell nicht Teil des finalen Stands.
|
||||||
|
|
||||||
|
### GNOME-Shortcuts
|
||||||
|
Wenn dafür Benutzerkürzel in GNOME gesetzt werden, immer den **vollen Pfad** verwenden:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/home/jan/.local/bin/brightness-down-all
|
||||||
|
/home/jan/.local/bin/brightness-up-all
|
||||||
|
```
|
||||||
|
|
||||||
|
Nicht `~/.local/bin/...`, weil das im GNOME-Shortcut-Feld unzuverlässig sein kann.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## wluma und externer Sync – aktueller realer Zustand
|
||||||
|
|
||||||
|
### Wichtig
|
||||||
|
`wluma` läuft automatisch nach dem Login und steuert nur das interne Display:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status wluma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Der externe Monitor wird durch diesen User-Service nachgezogen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status external-brightness-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verhalten
|
||||||
|
`wluma` nutzt weiter den adaptiven Lernmodus, aber nur auf `intel_backlight`.
|
||||||
|
Der externe Sync-Dienst liest einmal pro Sekunde den internen Prozentwert aus:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/sys/class/backlight/intel_backlight/brightness
|
||||||
|
/sys/class/backlight/intel_backlight/max_brightness
|
||||||
|
```
|
||||||
|
|
||||||
|
und setzt denselben Prozentwert extern per:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -n ddcutil --bus 16 setvcp 10 <prozent>
|
||||||
|
```
|
||||||
|
|
||||||
|
Das bedeutet:
|
||||||
|
- `wluma` entscheidet die Helligkeit nur einmal, am internen Panel
|
||||||
|
- extern folgt automatisch
|
||||||
|
- manuelle Änderungen über die gemeinsamen Skripte bleiben ebenfalls synchron
|
||||||
|
- bei einem Replug des externen Monitors kann ein `systemctl --user restart external-brightness-sync.service` sinnvoll sein
|
||||||
|
|
||||||
|
### Praktische Folge
|
||||||
|
Wenn du möchtest, dass `wluma` besser wird, dann im Alltag in verschiedenen Lichtsituationen die Helligkeit mit deinen gemeinsamen Skripten nachregeln. Daraus lernt `wluma` die interne Master-Helligkeit; extern wird automatisch mitgezogen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sensorstatus
|
||||||
|
|
||||||
|
Der Ambient-Light-Sensor funktioniert sauber.
|
||||||
|
|
||||||
|
Nachgewiesen durch:
|
||||||
|
- `monitor-sensor` zeigt deutliche Lux-Änderungen
|
||||||
|
- `gsettings get org.gnome.settings-daemon.plugins.power ambient-enabled` war `true`
|
||||||
|
- `iio-sensor-proxy` läuft
|
||||||
|
|
||||||
|
Das Problem lag daher **nicht** am Sensor, sondern an GNOME Auto Brightness.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Was bei Problemen sinnvoll ist
|
||||||
|
|
||||||
|
### 1. Prüfen, ob intern und extern erreichbar sind
|
||||||
|
```bash
|
||||||
|
ls -l /sys/class/backlight
|
||||||
|
brightnessctl -l
|
||||||
|
sudo ddcutil --bus 16 getvcp 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Dienste prüfen
|
||||||
|
```bash
|
||||||
|
systemctl --user status wluma.service
|
||||||
|
systemctl --user status external-brightness-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Dienste neu starten
|
||||||
|
```bash
|
||||||
|
systemctl --user restart wluma.service
|
||||||
|
systemctl --user restart external-brightness-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Was aktuell bewusst **nicht** gemacht wird
|
||||||
|
|
||||||
|
- kein aggressives `delete_device` / `new_device` im laufenden Betrieb
|
||||||
|
- keine Hotplug-Automatik mit `ddcci-refresh`
|
||||||
|
- keine udev-Regel, die `ddcci` ständig neu erzeugt
|
||||||
|
- kein Verlassen auf GNOME Auto Brightness
|
||||||
|
- keine unabhängige `wluma`-Regelung für den externen Monitor
|
||||||
|
|
||||||
|
Der Grund ist reine Stabilität:
|
||||||
|
Interner Master plus externer DDC/CI-Sync vermeidet Drift zwischen den Displays.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kurzfazit
|
||||||
|
|
||||||
|
### Final sauber gelöst
|
||||||
|
- intern läuft über `intel_backlight`
|
||||||
|
- extern läuft über `ddcutil` auf Bus 16
|
||||||
|
- `wluma` läuft automatisch nach dem Login und steuert nur intern
|
||||||
|
- `external-brightness-sync.service` zieht den externen Monitor automatisch nach
|
||||||
|
- der ALS-Sensor funktioniert und wird von `wluma` genutzt
|
||||||
|
|
||||||
|
### Final bewusst verworfen
|
||||||
|
- externes Display direkt von `wluma` regeln lassen
|
||||||
|
- `ddcci-refresh`
|
||||||
|
- Hotplug-Delete/Recreate-Automatik
|
||||||
|
- GNOME Auto Brightness als eigentliche Endlösung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schnellbefehle
|
||||||
|
|
||||||
|
### Status
|
||||||
|
```bash
|
||||||
|
systemctl --user status wluma.service
|
||||||
|
systemctl --user status external-brightness-sync.service
|
||||||
|
ls /sys/class/backlight
|
||||||
|
brightnessctl -l
|
||||||
|
sudo ddcutil --bus 16 getvcp 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manuelle gemeinsame Steuerung
|
||||||
|
```bash
|
||||||
|
/home/jan/.local/bin/brightness-down-all
|
||||||
|
/home/jan/.local/bin/brightness-up-all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dienste neu starten
|
||||||
|
```bash
|
||||||
|
systemctl --user restart wluma.service
|
||||||
|
systemctl --user restart external-brightness-sync.service
|
||||||
|
```
|
||||||
59
install.sh
Executable file
59
install.sh
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
BACKLIGHT_DEVICE="${BRIGHTNESS_BACKLIGHT_DEVICE:-intel_backlight}"
|
||||||
|
BACKLIGHT_PATH="${BRIGHTNESS_SYNC_BACKLIGHT:-/sys/class/backlight/${BACKLIGHT_DEVICE}}"
|
||||||
|
DDC_MODEL="${BRIGHTNESS_DDCUTIL_MODEL:-HG342PCB}"
|
||||||
|
DDC_DISPLAY="${BRIGHTNESS_DDCUTIL_DISPLAY:-1}"
|
||||||
|
DDC_BUS="${BRIGHTNESS_DDCUTIL_BUS:-16}"
|
||||||
|
STEP="${BRIGHTNESS_STEP:-10}"
|
||||||
|
INTERVAL="${BRIGHTNESS_SYNC_INTERVAL:-1}"
|
||||||
|
MIN_PERCENT="${BRIGHTNESS_SYNC_MIN_PERCENT:-1}"
|
||||||
|
|
||||||
|
install -d "$HOME/.local/bin"
|
||||||
|
install -d "$HOME/.config/systemd/user"
|
||||||
|
install -d "$HOME/.config/brightness-automation"
|
||||||
|
|
||||||
|
install -m 0755 "$ROOT/bin/brightness-osd" "$HOME/.local/bin/brightness-osd"
|
||||||
|
install -m 0755 "$ROOT/bin/brightness-up-all" "$HOME/.local/bin/brightness-up-all"
|
||||||
|
install -m 0755 "$ROOT/bin/brightness-down-all" "$HOME/.local/bin/brightness-down-all"
|
||||||
|
install -m 0755 "$ROOT/bin/brightness-sync-hg342pcb" "$HOME/.local/bin/brightness-sync-hg342pcb"
|
||||||
|
install -m 0644 "$ROOT/systemd/user/external-brightness-sync.service" "$HOME/.config/systemd/user/external-brightness-sync.service"
|
||||||
|
|
||||||
|
if [[ ! -f "$HOME/.config/brightness-automation/env" ]]; then
|
||||||
|
cat >"$HOME/.config/brightness-automation/env" <<EOF
|
||||||
|
BRIGHTNESS_BACKLIGHT_DEVICE="$BACKLIGHT_DEVICE"
|
||||||
|
BRIGHTNESS_SYNC_BACKLIGHT="$BACKLIGHT_PATH"
|
||||||
|
BRIGHTNESS_STEP="$STEP"
|
||||||
|
BRIGHTNESS_DDCUTIL_MODEL="$DDC_MODEL"
|
||||||
|
BRIGHTNESS_DDCUTIL_DISPLAY="$DDC_DISPLAY"
|
||||||
|
BRIGHTNESS_DDCUTIL_BUS="$DDC_BUS"
|
||||||
|
BRIGHTNESS_SYNC_INTERVAL="$INTERVAL"
|
||||||
|
BRIGHTNESS_SYNC_MIN_PERCENT="$MIN_PERCENT"
|
||||||
|
BRIGHTNESS_OSD_VISIBLE_MS="1400"
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${INSTALL_WLUMA_CONFIG:-0}" == "1" ]]; then
|
||||||
|
install -d "$HOME/.config/wluma"
|
||||||
|
install -m 0644 "$ROOT/wluma/config.toml" "$HOME/.config/wluma/config.toml"
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now external-brightness-sync.service
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
Installed.
|
||||||
|
|
||||||
|
Shortcuts:
|
||||||
|
$HOME/.local/bin/brightness-down-all
|
||||||
|
$HOME/.local/bin/brightness-up-all
|
||||||
|
|
||||||
|
Config:
|
||||||
|
$HOME/.config/brightness-automation/env
|
||||||
|
|
||||||
|
Status:
|
||||||
|
systemctl --user status external-brightness-sync.service
|
||||||
|
EOF
|
||||||
13
systemd/user/external-brightness-sync.service
Normal file
13
systemd/user/external-brightness-sync.service
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Mirror internal panel brightness to external HG342PCB
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
After=graphical-session.target wluma.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=-%h/.config/brightness-automation/env
|
||||||
|
ExecStart=%h/.local/bin/brightness-sync-hg342pcb
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical-session.target
|
||||||
17
uninstall.sh
Executable file
17
uninstall.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
systemctl --user disable --now external-brightness-sync.service 2>/dev/null || true
|
||||||
|
rm -f "$HOME/.config/systemd/user/external-brightness-sync.service"
|
||||||
|
rm -f "$HOME/.local/bin/brightness-osd"
|
||||||
|
rm -f "$HOME/.local/bin/brightness-up-all"
|
||||||
|
rm -f "$HOME/.local/bin/brightness-down-all"
|
||||||
|
rm -f "$HOME/.local/bin/brightness-sync-hg342pcb"
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
Uninstalled binaries and user service.
|
||||||
|
|
||||||
|
Kept user config:
|
||||||
|
$HOME/.config/brightness-automation/env
|
||||||
|
EOF
|
||||||
8
wluma/config.toml
Normal file
8
wluma/config.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[als.iio]
|
||||||
|
path = "/sys/bus/iio/devices"
|
||||||
|
thresholds = { 0 = "night", 20 = "dark", 80 = "dim", 250 = "normal", 500 = "bright", 800 = "outdoors" }
|
||||||
|
|
||||||
|
[[output.backlight]]
|
||||||
|
name = "eDP-1"
|
||||||
|
path = "/sys/class/backlight/intel_backlight"
|
||||||
|
capturer = "none"
|
||||||
Reference in New Issue
Block a user