docs: spec for fan-profile auto mode
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
# Fan-Profile "Auto" Mode Design
|
||||
|
||||
**Date:** 2026-05-01
|
||||
**Status:** Approved
|
||||
|
||||
## Summary
|
||||
|
||||
Add a virtual `auto` strategy to the waybar fan-profile module. When auto is on, `scripts/fan-profile.sh` keeps `fw-fanctrl` in sync with the active `power-profiles-daemon` profile — `power-saver` → `lazy`, `balanced` → `medium`, `performance` → `agile`. Reconciliation rides waybar's existing 5 s poll; no new daemon, no udev hooks, no fw-fanctrl strategy edits.
|
||||
|
||||
## Current Behaviour
|
||||
|
||||
`scripts/fan-profile.sh` is invoked by waybar's `custom/fan-profile` module on a 5 s interval and on click:
|
||||
|
||||
- **Status emit:** runs `fw-fanctrl print`, parses out the active strategy and current speed, prints a JSON line with an icon and tooltip.
|
||||
- **`--menu`:** runs `fw-fanctrl print list`, pipes the strategy names through wofi, calls `fw-fanctrl use <choice>`, signals `pkill -RTMIN+9 waybar` so the bar refreshes.
|
||||
|
||||
The strategy list is whatever `fw-fanctrl print list` returns: `laziest, lazy, medium, agile, very-agile, deaf, aeolus`. There is currently no notion of an automatic mode.
|
||||
|
||||
## Target Behaviour
|
||||
|
||||
### Wofi menu
|
||||
|
||||
A new entry `auto` is prepended to the strategy list before passing it to wofi. Selecting it:
|
||||
|
||||
1. Touches the state file `~/.local/state/fan-profile-auto`.
|
||||
2. Resolves the current power profile and calls `fw-fanctrl use <mapped>` once.
|
||||
3. Signals waybar to refresh.
|
||||
|
||||
Selecting any non-`auto` strategy:
|
||||
|
||||
1. Removes the state file (if it exists).
|
||||
2. Calls `fw-fanctrl use <choice>` (current behaviour, unchanged).
|
||||
3. Signals waybar to refresh.
|
||||
|
||||
### Status emit (every 5 s)
|
||||
|
||||
```
|
||||
state file exists?
|
||||
├── yes → read powerprofilesctl get
|
||||
│ ├── ok → resolve mapped strategy
|
||||
│ │ ├── differs from fw-fanctrl's active → fw-fanctrl use <mapped>
|
||||
│ │ └── matches → no-op
|
||||
│ │ emit JSON: text="auto", tooltip="auto → <mapped>", class="auto"
|
||||
│ └── error → emit JSON: text="auto (?)", tooltip="auto: power-profiles-daemon unreachable", class="auto"
|
||||
└── no → existing behaviour: emit fw-fanctrl's active strategy
|
||||
```
|
||||
|
||||
### Mapping
|
||||
|
||||
| power-profiles-daemon | fw-fanctrl |
|
||||
|---|---|
|
||||
| `power-saver` | `lazy` |
|
||||
| `balanced` | `medium` |
|
||||
| `performance` | `agile` |
|
||||
|
||||
Hardcoded in the script — small, stable, doesn't need a config file. If a profile name comes back unrecognized (e.g. a custom profile), the script falls into the error branch above (`auto (?)`).
|
||||
|
||||
## Implementation
|
||||
|
||||
### `scripts/fan-profile.sh` (only file changed)
|
||||
|
||||
Three additions to the existing script:
|
||||
|
||||
1. **State path constant** at top:
|
||||
```bash
|
||||
STATE_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto"
|
||||
```
|
||||
|
||||
2. **Mapping function**:
|
||||
```bash
|
||||
map_profile_to_strategy() {
|
||||
case "$1" in
|
||||
power-saver) echo lazy ;;
|
||||
balanced) echo medium ;;
|
||||
performance) echo agile ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
```
|
||||
|
||||
3. **Menu branch** — prepend `auto` and route the choice:
|
||||
```bash
|
||||
if [ "$1" = "--menu" ]; then
|
||||
STRATS=$(fw-fanctrl print list 2>/dev/null | grep "^-" | sed 's/^- //')
|
||||
CHOICE=$(printf 'auto\n%s\n' "$STRATS" | wofi --dmenu --prompt "Fan Strategy:" \
|
||||
--width 260 --height 320 --hide-scroll --no-actions --insensitive)
|
||||
[ -z "$CHOICE" ] && exit
|
||||
if [ "$CHOICE" = "auto" ]; then
|
||||
mkdir -p "$(dirname "$STATE_FILE")"
|
||||
touch "$STATE_FILE"
|
||||
PROFILE=$(powerprofilesctl get 2>/dev/null) && \
|
||||
MAPPED=$(map_profile_to_strategy "$PROFILE") && \
|
||||
fw-fanctrl use "$MAPPED"
|
||||
else
|
||||
rm -f "$STATE_FILE"
|
||||
fw-fanctrl use "$CHOICE"
|
||||
fi
|
||||
pkill -RTMIN+9 waybar
|
||||
exit
|
||||
fi
|
||||
```
|
||||
|
||||
4. **Status branch** — wrap the existing emit with auto-mode handling:
|
||||
```bash
|
||||
if [ -f "$STATE_FILE" ]; then
|
||||
PROFILE=$(powerprofilesctl get 2>/dev/null)
|
||||
MAPPED=$(map_profile_to_strategy "$PROFILE" 2>/dev/null)
|
||||
if [ -n "$MAPPED" ]; then
|
||||
ACTIVE=$(fw-fanctrl print 2>/dev/null | awk -F"'" '/^Strategy:/{print $2}')
|
||||
[ "$ACTIVE" != "$MAPPED" ] && fw-fanctrl use "$MAPPED" >/dev/null 2>&1
|
||||
printf '{"text": " auto", "tooltip": "Auto fan strategy\\nProfile: %s → %s\\nClick to change", "class": "auto"}\n' "$PROFILE" "$MAPPED"
|
||||
else
|
||||
printf '{"text": " auto (?)", "tooltip": "Auto: power-profiles-daemon unreachable or unknown profile\\nClick to change", "class": "auto"}\n'
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
# ... existing status emit (fw-fanctrl print → icon/level → JSON) ...
|
||||
```
|
||||
|
||||
### Reconciliation cadence
|
||||
|
||||
Waybar polls `custom/fan-profile` every 5 s (`"interval": 5` in `waybar/config.jsonc`). Each poll runs the script, which reads the state file and reconciles. No additional daemon, signal, or hook is needed. Worst-case desync: 5 s after a power-profile change.
|
||||
|
||||
The existing `pkill -RTMIN+9 waybar` from `power-profile.sh --menu` will trigger an immediate refresh when the user changes profile via the bar — so in the common case, the fan strategy switches within waybar's render cycle, not 5 s later.
|
||||
|
||||
### Persistence
|
||||
|
||||
State file lives at `$XDG_STATE_HOME/fan-profile-auto` (default `~/.local/state/fan-profile-auto`). XDG state dir survives reboot, so auto stays on across sessions. Removing the file (or selecting any non-auto strategy) disables auto mode.
|
||||
|
||||
## Files Touched
|
||||
|
||||
- `scripts/fan-profile.sh` — three additions (state constant, mapping function, two new branches in menu and status handlers).
|
||||
|
||||
No CSS, no waybar config, no install.sh changes.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Syncing the auto-on state across machines (would require moving the state file into `~/.config/` and tracking it in the dotfiles repo).
|
||||
- Mapping to strategies other than `lazy`/`medium`/`agile`. The user can edit the mapping function inline if they want different curves later.
|
||||
- Reacting to anything other than power-profiles-daemon (CPU temp, AC vs battery, etc.). Those were rejected during brainstorming in favour of the cleaner power-profile coupling.
|
||||
- A separate icon for auto mode. Reuses the existing `` glyph; the `auto` text in the bar is the differentiator.
|
||||
- Tracking `/etc/fw-fanctrl/config.json` in the dotfiles repo (raised during clarifications, not adopted — auto doesn't need any new fw-fanctrl strategies).
|
||||
Reference in New Issue
Block a user