|
<# |
|
.SYNOPSIS |
|
Controls monitor power state via PowerShell. |
|
|
|
.DESCRIPTION |
|
Use -TurnOff to place all displays into standby. Use -TurnOn to wake them. |
|
Works like the normal Windows display idle timeout (standby), not a black screensaver. |
|
|
|
.PARAMETER TurnOff |
|
Put all attached displays into standby. |
|
|
|
.PARAMETER TurnOn |
|
Wake displays by setting them to on. A simulated key press is also sent for compatibility with systems that may not respond reliably. |
|
|
|
.EXAMPLE |
|
.\ScreenController.ps1 -TurnOff |
|
|
|
.EXAMPLE |
|
.\ScreenController.ps1 -TurnOn |
|
|
|
.NOTES |
|
Author: asheroto |
|
URL: https://gist.github.com/asheroto/48337e8451f5a5a01ba2c270426a5350 |
|
Requires: Windows 10/11, PowerShell 5.1+ |
|
Important: Must run in the interactive user session. If using Task Scheduler or NinjaOne, set Run only when user is logged on and run as the current logged-in user. |
|
#> |
|
|
|
#requires -version 5.1 |
|
param( |
|
[switch]$TurnOn, |
|
[switch]$TurnOff |
|
) |
|
|
|
Set-StrictMode -Version 2.0 |
|
$ErrorActionPreference = 'Stop' |
|
|
|
function Assert-UserContext { |
|
<# |
|
.SYNOPSIS |
|
Ensures the script is running in an interactive user session. |
|
#> |
|
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent() |
|
$sid = $id.User.Value |
|
$name = $id.Name |
|
$isSystem = ($sid -eq 'S-1-5-18') -or ($name -eq 'NT AUTHORITY\SYSTEM') -or ($name -eq 'SYSTEM') |
|
|
|
if ($isSystem -or -not [Environment]::UserInteractive) { |
|
Write-Output "[Error] No logged-in user session detected. Run as the current logged-in user." |
|
Write-Output "[Hint] If scheduling, set 'Run only when user is logged on' and choose the user account." |
|
exit 3 |
|
} |
|
} |
|
|
|
# user32 interop |
|
Add-Type -TypeDefinition @" |
|
using System; |
|
using System.Runtime.InteropServices; |
|
public static class Native { |
|
[DllImport("user32.dll", SetLastError=true)] |
|
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult); |
|
[DllImport("user32.dll", SetLastError=true)] |
|
public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); |
|
} |
|
"@ | Out-Null |
|
|
|
function Set-MonitorPower { |
|
<# |
|
.SYNOPSIS |
|
Set displays to on, low-power, or standby. |
|
#> |
|
param([ValidateSet(0,1,2)][int]$State) |
|
$HWND_BROADCAST = [IntPtr]0xffff |
|
$WM_SYSCOMMAND = 0x0112 |
|
$SC_MONITORPOWER= 0xF170 |
|
$SMTO_NORMAL = 0x0000 |
|
[IntPtr]$res = [IntPtr]::Zero |
|
[void][Native]::SendMessageTimeout($HWND_BROADCAST, $WM_SYSCOMMAND, [IntPtr]$SC_MONITORPOWER, [IntPtr]$State, $SMTO_NORMAL, 2000, [ref]$res) |
|
|
|
switch ($State) { |
|
0 { Write-Output "Displays turned on." } |
|
1 { Write-Output "Displays set to low-power mode." } |
|
2 { Write-Output "Displays turned off (standby mode)." } |
|
} |
|
} |
|
|
|
function Send-WakeInput { |
|
<# |
|
.SYNOPSIS |
|
Sends a harmless key event to wake displays. |
|
#> |
|
$VK_SHIFT = 0x10 |
|
[Native]::keybd_event([byte]$VK_SHIFT, 0, 0, 0) |
|
Start-Sleep -Milliseconds 50 |
|
[Native]::keybd_event([byte]$VK_SHIFT, 0, 2, 0) |
|
Write-Output "Wake input sent." |
|
} |
|
|
|
# Arg validation |
|
if (($TurnOn -and $TurnOff) -or (-not $TurnOn -and -not $TurnOff)) { |
|
Write-Output "Specify exactly one of -TurnOn or -TurnOff." |
|
exit 1 |
|
} |
|
|
|
# Ensure interactive user context |
|
Assert-UserContext |
|
|
|
if ($TurnOff) { |
|
Set-MonitorPower -State 2 |
|
exit 0 |
|
} |
|
|
|
if ($TurnOn) { |
|
Send-WakeInput |
|
Set-MonitorPower -State 0 |
|
exit 0 |
|
} |