Skip to content

Instantly share code, notes, and snippets.

@atiris
Last active April 6, 2026 08:56
Show Gist options
  • Select an option

  • Save atiris/79bef04ae1472e26be1b4f5817ed894f to your computer and use it in GitHub Desktop.

Select an option

Save atiris/79bef04ae1472e26be1b4f5817ed894f to your computer and use it in GitHub Desktop.
Auto shutdown script for shutdown Windows PC in specified times (SK)
<#
.SYNOPSIS
Naplanovane automaticke vypnutie pocitaca s GUI notifikaciami.
.DESCRIPTION
Skript sa spusti, zobrazi odpocet a v nastaveny cas vypne pocitac.
Pred vypnutim zobrazi notifikacie (Windows Forms dialog) v nastaveny pocet minut.
Notifikacia sa zobrazi na monitore, kde sa nachadza myskovy kurzor.
.PARAMETER Times
Zoznam casov vypnutia vo formate HH:MM. Default: 19:05
.PARAMETER NotifyMinutes
Minuty pred vypnutim, kedy sa zobrazi upozornenie. Default: 30,5
.PARAMETER DryRun
Iba simulacia — k skutocnemu vypnutiu nedojde.
.EXAMPLE
.\AutoShutdown.ps1 -Times "22:00","23:30" -NotifyMinutes 30,10,5
C:\Scripts\AutoShutdown.ps1 -Times "18:00" -NotifyMinutes 15,5 -DryRun
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "C:\Scripts\AutoShutdown.ps1"
pwsh -NoProfile -File C:\Scripts\AutoShutdown.ps1
'C:\Program Files\PowerShell\7\pwsh.exe' -NoProfile -File C:\Scripts\AutoShutdown.ps1 -Times "20:00" -NotifyMinutes 60,15
#>
param(
[string[]]$Times = @('19:05'),
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[Alias('Notify')]
$NotifyMinutes = @(30, 5),
[switch]$DryRun
)
# --- NORMALIZE NotifyMinutes (support int[], string[], comma-separated string) ---
if ($NotifyMinutes -is [string]) {
# Single string, possibly comma-separated
$NotifyMinutes = $NotifyMinutes -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -match '^-?\d+$' } | ForEach-Object { [int]$_ }
} elseif ($NotifyMinutes -is [array]) {
# Array: flatten, split any comma-strings, convert all to int
$NotifyMinutes = $NotifyMinutes | ForEach-Object {
if ($_ -is [string] -and $_.Contains(',')) {
$_ -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -match '^-?\d+$' } | ForEach-Object { [int]$_ }
} else {
try { [int]$_ } catch { $null }
}
}
$NotifyMinutes = $NotifyMinutes | Where-Object { $_ -ne $null }
}
# ─── FUNKCIE ──────────────────────────────────────────────────────────────────
function Show-NotificationAsync {
param(
[string]$Id,
[string]$ResultFile,
[string]$CloseFile,
[string]$Message
)
# Skript bezi v novom runspace na STA vlakne — ziadne konzolove okno
$safeMsg = $Message -replace '\\', '\\' -replace "'", "''"
$psScript = @"
Add-Type -AssemblyName System.Windows.Forms
[void][System.Windows.Forms.Application]::EnableVisualStyles()
`$rf = '$($ResultFile -replace "'","''")'
`$cf = '$($CloseFile -replace "'","''")'
`$msg = '$safeMsg'
# Centrum okna = monitor s kurzorom
`$cursor = [System.Windows.Forms.Cursor]::Position
`$screen = [System.Windows.Forms.Screen]::FromPoint(`$cursor)
`$bounds = `$screen.WorkingArea
`$form = New-Object System.Windows.Forms.Form
`$form.Text = 'Upozornenie'
`$form.Size = New-Object System.Drawing.Size(460, 230)
`$form.StartPosition = 'Manual'
`$form.TopMost = `$true
`$form.ShowInTaskbar = `$true
`$form.FormBorderStyle = 'FixedDialog'
`$form.MaximizeBox = `$false
`$form.MinimizeBox = `$false
`$form.Location = New-Object System.Drawing.Point(
(`$bounds.Left + [int]((`$bounds.Width - `$form.Width ) / 2)),
(`$bounds.Top + [int]((`$bounds.Height - `$form.Height) / 2))
)
`$label = New-Object System.Windows.Forms.Label
`$label.Text = `$msg
`$label.AutoSize = `$false
`$label.Size = New-Object System.Drawing.Size(420, 120)
`$label.Location = New-Object System.Drawing.Point(18, 12)
`$form.Controls.Add(`$label)
`$btnOK = New-Object System.Windows.Forms.Button
`$btnOK.Text = 'OK — rozumiem'
`$btnOK.Width = 120
`$btnOK.Location = New-Object System.Drawing.Point(50, 155)
`$btnOK.Add_Click({ 'OK' | Set-Content -Path `$rf -Encoding UTF8; `$form.Close() })
`$form.Controls.Add(`$btnOK)
`$btnCancel = New-Object System.Windows.Forms.Button
`$btnCancel.Text = 'Zrusit vypnutie'
`$btnCancel.Width = 130
`$btnCancel.Location = New-Object System.Drawing.Point(280, 155)
`$btnCancel.Add_Click({ 'CANCEL' | Set-Content -Path `$rf -Encoding UTF8; `$form.Close() })
`$form.Controls.Add(`$btnCancel)
`$closeTimer = New-Object System.Windows.Forms.Timer
`$closeTimer.Interval = 1000
`$closeTimer.Add_Tick({ if (Test-Path `$cf) { `$form.Close() } })
`$closeTimer.Start()
`$form.Add_Shown({ `$form.Activate() })
[System.Windows.Forms.Application]::Run(`$form)
`$closeTimer.Stop()
"@
# Zapíš formularovy skript do temp súboru a spusti ho v skrytom pwsh procese.
# -WindowStyle Hidden skryje konzolove okno, WinForms dialog zostane viditelny.
$formScript = Join-Path $env:TEMP "shutdown_form_$Id.ps1"
Set-Content -Path $formScript -Value $psScript -Encoding UTF8
$pwshExe = (Get-Command pwsh -ErrorAction SilentlyContinue)
$exe = if ($pwshExe) { $pwshExe.Source } else { 'powershell' }
Start-Process -FilePath $exe `
-ArgumentList @('-NoProfile', '-NonInteractive', '-WindowStyle', 'Hidden', '-File', "`"$formScript`"") `
-WindowStyle Hidden
return @{ Id = $Id; ResultFile = $ResultFile; CloseFile = $CloseFile }
}
function Close-Notification($info) {
if ($null -ne $info -and $info.CloseFile) {
'CLOSE' | Set-Content -Path $info.CloseFile -Encoding UTF8 -ErrorAction SilentlyContinue
}
}
# ─── INICIALIZACIA ─────────────────────────────────────────────────────────────
$now = Get-Date
$targetsList = foreach ($t in $Times) {
$parts = $t -split ':'
if ($parts.Count -ne 2) { continue }
$cand = Get-Date -Hour ([int]$parts[0]) -Minute ([int]$parts[1]) -Second 0
if ($now -gt $cand) { $cand = $cand.AddDays(1) }
$cand
}
$targets = @($targetsList | Sort-Object)
if ($targets.Count -eq 0) {
Write-Host 'Chyba: Ziadne validne casy. Ukoncujem.' -ForegroundColor Red
exit 1
}
$target = $targets[0]
if ($DryRun) {
$host.UI.RawUI.WindowTitle = "AutoShutdown SIM o $($Times -join ', ')"
} else {
$host.UI.RawUI.WindowTitle = "AutoShutdown o $($Times -join ', ')"
}
Write-Host "Skript spusteny : $($now.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Cyan
Write-Host "Casy vypnutia : $($Times -join ', ')" -ForegroundColor Cyan
Write-Host "Najblizsie : $($target.ToString('HH:mm:ss'))" -ForegroundColor Cyan
# Harmonogram upozorneni — zoznam objektov
$schedule = @(foreach ($n in ($NotifyMinutes | Sort-Object -Descending)) {
if ($n -ge 0) {
[pscustomobject]@{ Minutes = $n; FireAt = $target.AddMinutes(-$n); Sent = $false; Info = $null }
}
})
# Ak uz preslo nejake upozornenie, spusti najposlednejsie hned
$missed = @($schedule | Where-Object { $_.FireAt -le $now } | Sort-Object FireAt)
if ($missed.Count -gt 0) {
$entry = $missed[-1]
$fid = [guid]::NewGuid().ToString()
$rf = Join-Path $env:TEMP "shutdown_notify_$fid.result"
$cf = Join-Path $env:TEMP "shutdown_notify_$fid.close"
$msg = "Pozor, je $($now.ToString('HH:mm'))`nPocitac je nastaveny na vypnutie o $($target.ToString('HH:mm')) (za menej ako $($entry.Minutes) minut)`n`nAk nechcete aby sa pocitac v danom case vypol,`nstlacte tlacidlo Zrusit vypnutie na tomto upozorneni,`nalebo zatvorte terminalove okno v ktorom bezi AutoShutdown."
$entry.Info = Show-NotificationAsync -Id $fid -ResultFile $rf -CloseFile $cf -Message $msg
$entry.Sent = $true
}
$totalSec = [math]::Max(($target - $now).TotalSeconds, 1)
# ─── HLAVNA SLUCKA ─────────────────────────────────────────────────────────────
while ($true) {
$tick = Get-Date
$diff = $target - $tick
if ($diff.TotalSeconds -le 0) {
Write-Progress -Activity 'AutoShutdown' -Completed
if ($DryRun) {
Write-Host 'SIMULACIA — skript by teraz vypol pocitac.' -ForegroundColor Yellow
} else {
Write-Host 'Vypinam pocitac...' -ForegroundColor Red
Stop-Computer -Force
}
break
}
$elapsed = [math]::Max([math]::Min(($tick - $now).TotalSeconds, $totalSec), 0)
$pct = [int]($elapsed / $totalSec * 100)
$status = ' Do vypnutia zostava: {0:D2}h {1:D2}m {2:D2}s ' -f $diff.Hours, $diff.Minutes, $diff.Seconds
$act = if ($DryRun) { 'SIMULACIA VYPNUTIA' } else { 'AutoShutdown :' }
Write-Progress -Activity $act -Status $status -PercentComplete $pct
foreach ($entry in $schedule) {
if (-not $entry.Sent -and $tick -ge $entry.FireAt) {
# Zavri predchadzajuce notifikacie
foreach ($other in $schedule) {
if ($other -ne $entry -and $null -ne $other.Info) {
Close-Notification $other.Info
}
}
$fid = [guid]::NewGuid().ToString()
$rf = Join-Path $env:TEMP "shutdown_notify_$fid.result"
$cf = Join-Path $env:TEMP "shutdown_notify_$fid.close"
$msg = "Pozor, je $($tick.ToString('HH:mm'))`nPocitac je nastaveny na vypnutie o $($target.ToString('HH:mm')) (za $($entry.Minutes) minut)`n`nAk nechcete vypnutie, zatvorte tento skript`nalebo stlacte tlacidlo Zrusit vypnutie."
$entry.Info = Show-NotificationAsync -Id $fid -ResultFile $rf -CloseFile $cf -Message $msg
$entry.Sent = $true
}
}
foreach ($entry in $schedule) {
$info = $entry.Info
if ($null -ne $info -and (Test-Path $info.ResultFile)) {
$res = ((Get-Content $info.ResultFile -ErrorAction SilentlyContinue | Select-Object -First 1) + '').Trim()
if ($res -eq 'CANCEL') {
Write-Progress -Activity 'AutoShutdown' -Completed
Write-Host 'Pouzivatel zrusil vypnutie. Koniec.' -ForegroundColor Red
foreach ($e2 in $schedule) { Close-Notification $e2.Info }
exit 0
} elseif ($res -eq 'OK') {
Remove-Item $info.ResultFile -ErrorAction SilentlyContinue
}
}
}
Start-Sleep -Seconds 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment