MaxKK

PABS - Powerplan And Brightness Selector

Apr 18th, 2026 (edited)
188
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PowerShell 9.32 KB | Software | 0 0
  1. # ==============================================================================
  2. # PABS: Powerplan And Brightness Selector (v1.1)
  3. # A lightweight utility to automate Windows Power Plans and Brightness levels.
  4. # written by MaxK https://www.reddit.com/user/Glad-Journalist-4807/
  5. # on ThinkPad X390/T14s gen 1 hybrid
  6. # Please keep the credits intact :)
  7. #
  8. # Changes:
  9. # 1. PS 7 compatibility corrections: replaced ContextMenu with ContextMenuStrip, MenuItems -> Items and MenuItem ->ToolStripMenuItem.
  10. # ==============================================================================
  11.  
  12. # CONFIGURATION
  13.  
  14. # list of defined power plans mapped to their system GUIDs
  15. $PowerPlans = @{
  16.     "Ultra"    = "e9a42b02-d5df-448d-aa00-03f14749eb61"
  17.     "High"     = "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"
  18.     "Balanced" = "381b4222-f694-41f0-9685-ff5bb260df2e"
  19.     "PwrSaver" = "a1841308-3541-4fab-bc81-f71556f20b4a"
  20. }
  21.  
  22. # Default plan when on AC and no specific rules from the watchlist match
  23. $DefaultPPAC      = "Balanced"
  24.  
  25. # Default plan when on Battery and no specific rules from the watchlist match
  26. $DefaultPPBattery = "PwrSaver"
  27.  
  28. # When set to 1, the current brightness level is preserved and set after switching plans
  29. $AutoInheritBrightness = 1
  30.  
  31. # AC Watchlist - Priority is TOP-DOWN (first matching directory wins)
  32. $WatchListAC = @(
  33.     @{ Path = "C:\Games\*";                          Plan = "Ultra" }
  34.     @{ Path = "c:\Tools\OpenShot Video Editor\*";    Plan = "Ultra" }
  35.     @{ Path = "C:\Program Files\Mozilla Firefox\*";  Plan = "High" }
  36.     @{ Path = "c:\Program Files\DAUM\PotPlayer\*";   Plan = "PwrSaver" }
  37. )
  38.  
  39. # Battery Watchlist - Again the priority is TOP-DOWN (first matching directory wins)
  40. $WatchListBattery = @(
  41.     @{ Path = "C:\Games\*";          Plan = "Balanced" }
  42.     @{ Path = "C:\Program Files\*";  Plan = "PwrSaver" }
  43. )
  44.  
  45. # Interval for checking running processes and switching plans (in seconds)
  46. $ProcessCheckInterval = 5
  47.  
  48. # Interval for polling keyboard state (Ctrl+Shift+B) in milliseconds
  49. $KeyCheckInterval = 50
  50.  
  51. # Standard Windows GUID for the Video/Display subgroup
  52. $DisplaySubGroup = "7516b95f-f776-4464-8c53-06167f40cc99"
  53.  
  54. # end of CONFIGURATION
  55.  
  56. #WIN32 API&UTILS
  57.  
  58. $WinFuncs = @"
  59. [DllImport("user32.dll")] public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
  60. [DllImport("user32.dll")] public static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
  61. [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
  62. [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
  63. [DllImport("user32.dll")] public static extern short GetAsyncKeyState(int vKey);
  64. [DllImport("powrprof.dll")] public static extern uint PowerGetActiveScheme(IntPtr UserRootPowerKey, out IntPtr ActivePolicyGuiPtr);
  65. "@
  66. $Win32 = Add-Type -MemberDefinition $WinFuncs -Name "Win32Utils" -Namespace Win32 -PassThru
  67.  
  68. # get console window
  69. $ConsolePtr = (Get-Process -Id $PID).MainWindowHandle
  70.  
  71. # disable the "X" (Close) button to prevent accidental exit
  72. function Disable-CloseButton {
  73.     $hMenu = [Win32.Win32Utils]::GetSystemMenu($ConsolePtr, $false)
  74.     [void][Win32.Win32Utils]::EnableMenuItem($hMenu, 0xF060, 0x00000001)
  75.     Write-Host "Disabling X"
  76. }
  77.  
  78. # start the script hidden and lock the close button
  79. [void][Win32.Win32Utils]::ShowWindowAsync($ConsolePtr, 0) # 0 = SW_HIDE
  80. Disable-CloseButton
  81.  
  82. # find the GUID of the "Display brightness"
  83. $BrightGUID = $null
  84. $query = powercfg /q SCHEME_CURRENT SUB_VIDEO
  85. foreach ($line in $query) {
  86.     if ($line -match "GUID:\s+([\w-]+).+\(Display brightness\)") {
  87.         $BrightGUID = $matches[1].Trim(); break
  88.     }
  89. }
  90.  
  91. # tray icon
  92.  
  93. Add-Type -AssemblyName System.Windows.Forms, System.Drawing
  94. $TrayIcon = New-Object System.Windows.Forms.NotifyIcon
  95. $TrayIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -Id $PID).Path)
  96. $TrayIcon.Visible = $true
  97.  
  98. $ContextMenu = New-Object System.Windows.Forms.ContextMenuStrip
  99. $StatusItem = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Enabled = $false; Text = "Plan: Init..." }
  100. # dashboard
  101. $ToggleItem = New-Object System.Windows.Forms.ToolStripMenuItem
  102. $ToggleItem.Text = "Show Dashboard"
  103. $ToggleItem.add_Click({
  104.     if ([Win32.Win32Utils]::IsWindowVisible($ConsolePtr)) {
  105.         [void][Win32.Win32Utils]::ShowWindowAsync($ConsolePtr, 0) # Hide
  106.         $ToggleItem.Text = "Show Dashboard"
  107.     } else {
  108.         [void][Win32.Win32Utils]::ShowWindowAsync($ConsolePtr, 5) # Show
  109.         Disable-CloseButton
  110.         $ToggleItem.Text = "Minimize to tray"
  111.     }
  112. })
  113.  
  114. $ExitItem = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = "Exit" }
  115. $ExitItem.add_Click({ $TrayIcon.Visible = $false; [void](Stop-Process -Id $PID) })
  116.  
  117. [void]$ContextMenu.Items.Add($StatusItem)
  118. [void]$ContextMenu.Items.Add("-")
  119. [void]$ContextMenu.Items.Add($ToggleItem)
  120. [void]$ContextMenu.Items.Add($ExitItem)
  121. $TrayIcon.ContextMenuStrip = $ContextMenu
  122.  
  123. function Write-Log ($msg, $color = "White") {
  124.     $timestamp = Get-Date -Format "HH:mm:ss"
  125.     Write-Host "[$timestamp] $msg" -ForegroundColor $color
  126. }
  127.  
  128.  
  129. # get currebnt brightness
  130. function Get-CurrentBrightness {
  131.     $bObj = Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightness | Select-Object -First 1
  132.     return $bObj.CurrentBrightness
  133. }
  134.  
  135. # set brightness level for both AC and DC modes for a target plan
  136. function Set-PlanBrightness ($targetGuid, $brightness) {
  137.     if ($null -eq $BrightGUID) { return }
  138.     [void](Start-Process "powercfg.exe" -ArgumentList "/setacvalueindex $targetGuid $DisplaySubGroup $BrightGUID $brightness" -WindowStyle Hidden -Wait)
  139.     [void](Start-Process "powercfg.exe" -ArgumentList "/setdcvalueindex $targetGuid $DisplaySubGroup $BrightGUID $brightness" -WindowStyle Hidden -Wait)
  140. }
  141.  
  142. function Get-IsOnAC {
  143.     $battery = Get-CimInstance -ClassName Win32_Battery -ErrorAction SilentlyContinue
  144.     if ($null -eq $battery) { return $true }
  145.     return $battery.BatteryStatus -eq 2
  146. }
  147.  
  148. function Get-ActivePlanGuid {
  149.     $ptr = [IntPtr]::Zero
  150.     [void][Win32.Win32Utils]::PowerGetActiveScheme([IntPtr]::Zero, [ref]$ptr)
  151.     if ($ptr -ne [IntPtr]::Zero) {
  152.         return ([System.Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [type][Guid])).ToString().ToLower()
  153.     }
  154.     return ""
  155. }
  156.  
  157. # main loop
  158.  
  159. $lastProcessCheck = [DateTime]::MinValue
  160. Write-Log "PABS v1.0 Started" "Cyan"
  161.  
  162. while ($true) {
  163.     [System.Windows.Forms.Application]::DoEvents()
  164.  
  165.     # Ctrl+Shift+B monitoring
  166.     # set the current brightness level to all power plans defined in $PowerPlans
  167.     if (($Win32::GetAsyncKeyState(17) -band 0x8000) -and ($Win32::GetAsyncKeyState(16) -band 0x8000) -and ($Win32::GetAsyncKeyState(66) -band 0x8000)) {
  168.         $currB = Get-CurrentBrightness
  169.         Write-Log "Syncing brightness: $currB% to ALL plans" "Magenta"
  170.         foreach ($guid in $PowerPlans.Values) { Set-PlanBrightness $guid $currB }
  171.         [void](powercfg /setactive (Get-ActivePlanGuid))
  172.         Start-Sleep -Milliseconds 500
  173.     }
  174.  
  175.     # monitoring and AC/battery logic
  176.     if ([DateTime]::Now -gt $lastProcessCheck.AddSeconds($ProcessCheckInterval)) {
  177.         $IsOnAC = Get-IsOnAC
  178.         $CurrentWL = if($IsOnAC){$WatchListAC}else{$WatchListBattery}
  179.         $TargetAlias = if($IsOnAC){$DefaultPPAC}else{$DefaultPPBattery}
  180.        
  181.         # get list of running processes with a file path
  182.         $procs = Get-Process | Where-Object { $_.Path } | Select-Object -Property Path
  183.  
  184.         $TriggeringPath = "Default (No match)"
  185.         # priority matching loop
  186.         :OuterLoop foreach ($item in $CurrentWL) {
  187.             foreach ($p in $procs) {
  188.                 if ($p.Path -like $item.Path) {
  189.                     $TargetAlias = $item.Plan
  190.                     $TriggeringPath = $p.Path # Store the path that caused the match
  191.                     break OuterLoop
  192.                 }
  193.             }
  194.         }
  195.        
  196.         # if no match then default
  197.         if ($TriggeringPath -eq "Default (No match)") {
  198.             $TriggeringPath = "No watched process running"
  199.         }
  200.        
  201.        
  202.         if ($PowerPlans.ContainsKey($TargetAlias)) {
  203.             $TargetGUID = $PowerPlans[$TargetAlias].ToLower()
  204.             $ActiveGUID = Get-ActivePlanGuid
  205.             $mode = if($IsOnAC){"AC"}else{"BAT"}
  206.            
  207.             $StatusItem.Text = "Plan: $TargetAlias [$mode]"
  208.             $TrayIcon.Text = "PABS: $TargetAlias ($mode)"
  209.  
  210.            
  211.             if ($ActiveGUID -ne $TargetGUID) {
  212.                 # inherit current brightness to the target plan if enabled
  213.                 if ($AutoInheritBrightness -eq 1) {
  214.                     $currB = Get-CurrentBrightness
  215.                     Write-Log "Inheriting brightness: $currB% for $TargetAlias" "Gray"
  216.                     Set-PlanBrightness $TargetGUID $currB
  217.                 }
  218.                 # apply the target power plan
  219.                 [void](Start-Process "powercfg.exe" -ArgumentList "/setactive $TargetGUID" -WindowStyle Hidden -Wait)
  220.                 Write-Log "Switching to $TargetAlias ($mode). Triggered by: $TriggeringPath" "Yellow"
  221.             }
  222.         }
  223.         $lastProcessCheck = [DateTime]::Now
  224.        
  225.         # ensure Close button remains disabled if the dashboard is visible
  226.         if ([Win32.Win32Utils]::IsWindowVisible($ConsolePtr)) { Disable-CloseButton }
  227.     }
  228.    
  229.     Start-Sleep -Milliseconds $KeyCheckInterval
  230. }
Advertisement
Add Comment
Please, Sign In to add comment