Tuesday, 1 May 2018

Keep the monitor display brightness the same when the power source is changed to battery/charging

When using a laptop you usually have to adjust the screen's brightness based on the ambient lighting conditions. However, when plugging/unplugging the power supply Windows will change the brightness again. This can be a poor user experience, for example the reason for plugging it in was just to charge it, not to change the brightness.

Here is a PowerShell v3.0+ script and Windows Task Scheduler config file for fixing this issue. The schedule will run at system startup, and just runs the script. The PowerShell script will register a script block to some Windows events. One is the brightness change event, so when the user manually changes the screen brightness it will save that value to a temp file. The other event is when the power supply is changed to battery/charging, it will change the value back from the saved temp file.

Here's the PowerShell script, just copy this into a Notepad and save as "C:\Battery power display brightness.ps1".




<#
    .SYNOPSIS
    Monitor brightness fixer.

    .DESCRIPTION
    Ensures monitor brightness remains at the same level when plugging/unplugging battery charger.
    Requires PowerShell 3.0 or higher.
#>

function Unregister-MyEvent($eventId) {
    # Clean up previous event handlers.
    $measureEvents = Get-EventSubscriber | Where-Object -Property SourceIdentifier -eq $eventId | Measure-Object
    if ($measureEvents.Count -gt 0) {
        Unregister-Event -SourceIdentifier $eventId
    }
}

function Get-MyBrightness {
    $monitorControl = Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightness
    $brightnessLevel = $monitorControl.CurrentBrightness
    return $brightnessLevel
}

function Set-MyBrightness($brightnessLevel) {
    $monitorControl = Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods
    $monitorControl.WmiSetBrightness(0, $brightnessLevel)
}

# Create temp file to save brightness value.
$global:savedBrightnessFile = (New-TemporaryFile).FullName
$currentBrightness = Get-MyBrightness
$currentBrightness > $global:savedBrightnessFile

$global:jobId = "$PSCommandPath>MonitorBrightnessJob"

# Win32_PowerManagementEvent
#   |> EventType ("Win32API|Power Management Events")
#        |> Power Status Change (10)
#             Indicates a change in the power status of the computer, such as a switch from
#             battery power to AC, or from AC to an uninterruptible power supply.
#             The system also broadcasts this event when remaining battery power slips
#             below the threshold specified by the user or if the battery power changes
#             by a specified percentage.
# Official documentation found here:
# https://msdn.microsoft.com/en-us/library/aa394362(v=vs.85).aspx
$powerEventQuery = "select * from Win32_PowerManagementEvent within 5 where EventType=10"
$eventId = "$PSCommandPath>PowerManagementEvent"

Unregister-MyEvent $eventId

# Register the event handler.
Register-CimIndicationEvent -Query $powerEventQuery -SourceIdentifier $eventId -Action {
    Stop-Job -Name $global:jobId
    $currentBrightness = Get-MyBrightness
    $savedBrightness = Get-Content -Path $global:savedBrightnessFile
    if ($currentBrightness -ne $savedBrightness) {
        Set-MyBrightness($savedBrightness)
    }
} | Out-Null

# WmiMonitorBrightnessEvent
# Official documentation here:
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa394537(v=vs.85).aspx
$brightnessEventName = "WmiMonitorBrightnessEvent"
$eventId = "$PSCommandPath>MonitorBrightnessEvent"

Unregister-MyEvent $eventId

# Register the event handler.
Register-CimIndicationEvent -Namespace root/wmi -ClassName $brightnessEventName -SourceIdentifier $eventId -Action {
    $measureJobs = Get-Job -Name $global:jobId -ErrorAction Ignore | Measure-Object
    if ($measureJobs.Count -gt 0) {
        Stop-Job -Name $global:jobId
        Remove-Job -Name $global:jobId
    }
    Start-Job -Name $global:jobId -ArgumentList $global:savedBrightnessFile -ScriptBlock {
        param($savedBrightnessFile)
        # Wait for a while to allow the power monitor event to cancel this instruction,
        # if not cancelled then continue on to saving the new brightness level.
        Start-Sleep -Milliseconds 2500
        $currentBrightness = (Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightness).CurrentBrightness
        $savedBrightness = Get-Content -Path $savedBrightnessFile
        if ($currentBrightness -ne $savedBrightness) {
            $currentBrightness > $savedBrightnessFile
        }
    } | Out-Null
} | Out-Null


Here's the Task Schedule config, just copy this into a Notepad and save as "C:\Battery power display brightness - Task Schedule.xml".



<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2018-05-01T22:30:29.7451345</Date>
    <Author>intrepidis</Author>
    <Description>Keep the monitor display brightness the same when the power source is changed to/from battery/charging.</Description>
    <URI>\Battery power display brightness</URI>
  </RegistrationInfo>
  <Triggers>
    <BootTrigger>
      <Enabled>true</Enabled>
    </BootTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>S-1-5-18</UserId>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>true</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
      <Arguments>-ExecutionPolicy Bypass -File "C:\Battery power display brightness.ps1"</Arguments>
    </Exec>
  </Actions>
</Task>



To install, run "Windows Task Scheduler" and Import the XML config file above. Now you can either reboot the machine, or just right click on the Schedule that was just imported and choose "Run".

Please note, there is a delay of a few seconds from when you manually change the brightness to when it's saved to the temp file.

2 comments:

  1. Thank you, it works after few tweaks.


    1) Powershell 5.0 is needed because command (New-TemporaryFile) is not supported in 3.0.
    You can eventually change this command to powershell 3.0 variant.

    2) "IF" condition should be removed from "# Register the event handler" code

    from:

    # Register the event handler.
    Register-CimIndicationEvent -Query $powerEventQuery -SourceIdentifier $eventId -Action {
    Stop-Job -Name $global:jobId
    $currentBrightness = Get-MyBrightness
    $savedBrightness = Get-Content -Path $global:savedBrightnessFile
    if ($currentBrightness -ne $savedBrightness) {
    Set-MyBrightness($savedBrightness)
    }
    } | Out-Null

    to:

    # Register the event handler.
    Register-CimIndicationEvent -Query $powerEventQuery -SourceIdentifier $eventId -Action {
    Stop-Job -Name $global:jobId
    $currentBrightness = Get-MyBrightness
    $savedBrightness = Get-Content -Path $global:savedBrightnessFile
    Set-MyBrightness($savedBrightness)
    } | Out-Null


    3) "-noexit" parameter should be added to Task Scheduler action to keep the script running:
    -noexit -ExecutionPolicy Bypass -File "C:\Battery power display brightness.ps1"

    4) It may be needed to add another trigger for computer wake up if you use sleep function:
    https://social.technet.microsoft.com/Forums/windows/en-US/0462579d-780a-460a-befd-90f755e902aa/how-to-schedule-a-task-on-resume-from-sleep?forum=itprovistaapps

    5) It may be needed to modify user account in Task properties after import

    ReplyDelete
    Replies
    1. Thanks for all the tweaks. I wonder whether you still use this script?

      Delete