This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

2configs/desktops/sway: add power profile widget to eww

- Click power graph to cycle through low/normal/high profiles
- Tooltip shows current TDP and profile name
- Add sudoers rule for passwordless ryzenadj
- Use state file since ryzenadj -i doesn't work on Strix Point

+101 -21
-3
tools/power-profile/flake-module.nix
··· 5 5 { 6 6 packages.power-profile = pkgs.writeShellApplication { 7 7 name = "power-profile"; 8 - runtimeInputs = [ 9 - pkgs.ryzenadj 10 - ]; 11 8 text = builtins.readFile ./power-profile.sh; 12 9 }; 13 10 };
+13 -6
tools/power-profile/power-profile.sh
··· 7 7 PROFILE_NORMAL=15 8 8 PROFILE_HIGH=28 9 9 10 + # State file for eww integration (ryzenadj -i doesn't work on all CPUs) 11 + STATE_FILE="/tmp/power-profile-state" 12 + 10 13 usage() { 11 14 cat <<EOF 12 15 Usage: power-profile <profile|watts> ··· 32 35 } 33 36 34 37 set_tdp() { 35 - local stapm=$1 38 + local name=$1 39 + local stapm=$2 36 40 # Calculate burst limits (using integer math, *10 then /10 to simulate decimals) 37 41 local slow=$(( (stapm * 12) / 10 )) 38 42 local fast=$(( (stapm * 15) / 10 )) ··· 45 49 echo " STAPM (sustained): ${stapm}W" 46 50 echo " Slow limit: ${slow}W" 47 51 echo " Fast limit: ${fast}W" 48 - ryzenadj --stapm-limit="$stapm_mw" --slow-limit="$slow_mw" --fast-limit="$fast_mw" 52 + /run/wrappers/bin/sudo /run/current-system/sw/bin/ryzenadj --stapm-limit="$stapm_mw" --slow-limit="$slow_mw" --fast-limit="$fast_mw" 53 + 54 + # Write state for eww integration 55 + echo "$name $stapm" > "$STATE_FILE" 49 56 echo "Done." 50 57 } 51 58 ··· 56 63 57 64 case "$1" in 58 65 low) 59 - set_tdp "$PROFILE_LOW" 66 + set_tdp "low" "$PROFILE_LOW" 60 67 ;; 61 68 normal) 62 - set_tdp "$PROFILE_NORMAL" 69 + set_tdp "normal" "$PROFILE_NORMAL" 63 70 ;; 64 71 high) 65 - set_tdp "$PROFILE_HIGH" 72 + set_tdp "high" "$PROFILE_HIGH" 66 73 ;; 67 74 -h|--help|help) 68 75 usage ··· 70 77 ;; 71 78 *) 72 79 if [[ "$1" =~ ^[0-9]+$ ]]; then 73 - set_tdp "$1" 80 + set_tdp "custom" "$1" 74 81 else 75 82 echo "Error: Unknown profile '$1'" 76 83 usage
+88 -12
2configs/desktops/sway/eww.nix
··· 1 1 { 2 2 pkgs, 3 3 lib, 4 + config, 5 + self, 4 6 ... 5 7 }: 6 8 let ··· 20 22 (defpoll cpu-load :interval "2s" :initial "0.00" "cut -d' ' -f1 /proc/loadavg") 21 23 (defpoll temperature :interval "2s" :initial '{"temp": 0, "fan": 0}' "${lib.getExe tempScript}") 22 24 (defpoll power :interval "2s" :initial '{"watts": 0, "charging": false}' "${lib.getExe powerScript}") 25 + (defpoll power-profile :interval "2s" :initial '{"name": "unknown", "tdp": 0}' "${lib.getExe powerProfileStatusScript}") 23 26 (defpoll battery :interval "10s" :initial '{"capacity": "100", "icon": "󰁹", "watts": "0", "time_remaining": "--", "status": "Full"}' "${lib.getExe batteryScript}") 24 27 (defpoll volume :interval "1s" :initial '{"level": "0", "icon": "󰕿"}' "${lib.getExe volumeScript}") 25 28 (defpoll network :interval "5s" :initial "󰖪 ..." "${lib.getExe networkScript}") ··· 83 86 (label :text "''${battery.capacity}% ''${battery.icon}"))) 84 87 85 88 (defwidget power-graph-widget [] 86 - (box :class {power.charging ? "graph-module power charging" : "graph-module power"} :orientation "h" :space-evenly false :spacing 4 87 - :tooltip "''${battery.time_remaining} ''${battery.status == 'Charging' ? 'to full' : 'remaining'}" 88 - (graph :class "graph" 89 - :value {power.watts} 90 - :thickness 3 91 - :time-range "60s" 92 - :min 0 93 - :max 45 94 - :dynamic false 95 - :line-style "round" 96 - :width 75) 97 - (label :text "''${round(power.watts, 1)}W ''${power.charging ? '󰂄' : '󰁹'}"))) 89 + (eventbox :onclick "${lib.getExe powerProfileCycleScript}" 90 + (box :class {power.charging ? "graph-module power charging" : "graph-module power"} :orientation "h" :space-evenly false :spacing 4 91 + :tooltip "TDP: ''${power-profile.tdp}W (''${power-profile.name}) | ''${battery.time_remaining} ''${battery.status == 'Charging' ? 'to full' : 'remaining'}" 92 + (graph :class "graph" 93 + :value {power.watts} 94 + :thickness 3 95 + :time-range "60s" 96 + :min 0 97 + :max 45 98 + :dynamic false 99 + :line-style "round" 100 + :width 75) 101 + (label :text "''${round(power.watts, 1)}W ''${power.charging ? '󰂄' : '󰁹'}")))) 98 102 99 103 (defwidget volume-widget [] 100 104 (box :class "volume" :orientation "h" :space-evenly false ··· 512 516 ''; 513 517 }; 514 518 519 + # Power profile state file (written by power-profile tool, read by eww) 520 + powerProfileStateFile = "/tmp/power-profile-state"; 521 + 522 + powerProfileStatusScript = pkgs.writeShellApplication { 523 + name = "eww-power-profile-status"; 524 + runtimeInputs = [ pkgs.jq ]; 525 + text = '' 526 + state_file="${powerProfileStateFile}" 527 + if [ -f "$state_file" ]; then 528 + read -r name tdp < "$state_file" 529 + else 530 + name="unknown" 531 + tdp="?" 532 + fi 533 + jq -n --arg name "$name" --arg tdp "$tdp" '{name: $name, tdp: $tdp}' 534 + ''; 535 + }; 536 + 537 + powerProfileCycleScript = pkgs.writeShellApplication { 538 + name = "eww-power-profile-cycle"; 539 + runtimeInputs = [ pkgs.libnotify ]; 540 + text = '' 541 + state_file="${powerProfileStateFile}" 542 + 543 + # Profile names (must match power-profile tool) 544 + profiles=("low" "normal" "high") 545 + 546 + # Read current profile from state file 547 + current="unknown" 548 + if [ -f "$state_file" ]; then 549 + read -r current _ < "$state_file" 550 + fi 551 + 552 + # Find next profile (cycle through) 553 + next_idx=0 554 + for i in "''${!profiles[@]}"; do 555 + if [ "''${profiles[$i]}" = "$current" ]; then 556 + next_idx=$(( (i + 1) % ''${#profiles[@]} )) 557 + break 558 + fi 559 + done 560 + 561 + next="''${profiles[$next_idx]}" 562 + 563 + # Use the power-profile tool (uses sudo internally, writes state file) 564 + if /run/current-system/sw/bin/power-profile "$next" >/dev/null 2>&1; then 565 + notify-send -t 2000 "Power Profile" "Set to $next" 566 + else 567 + notify-send -t 2000 "Power Profile" "Failed to set profile" 568 + fi 569 + ''; 570 + }; 571 + 515 572 ewwConfigDir = pkgs.runCommand "eww-config" { } '' 516 573 mkdir -p $out 517 574 cp ${ewwYuck} $out/eww.yuck ··· 520 577 521 578 in 522 579 { 580 + # ryzenadj and power-profile for power profile control 581 + environment.systemPackages = [ 582 + pkgs.ryzenadj 583 + self.packages.${pkgs.system}.power-profile 584 + ]; 585 + 586 + # Allow user to run ryzenadj without password (for power profile switching) 587 + security.sudo.extraRules = [ 588 + { 589 + users = [ config.users.users.mainUser.name ]; 590 + commands = [ 591 + { 592 + command = "/run/current-system/sw/bin/ryzenadj"; 593 + options = [ "NOPASSWD" ]; 594 + } 595 + ]; 596 + } 597 + ]; 598 + 523 599 # Make RAPL energy counters readable for power monitoring 524 600 # Works on both Intel and AMD Zen CPUs (via intel_rapl_msr module) 525 601 services.udev.extraRules = ''