Last active
April 6, 2026 08:56
-
-
Save atiris/79bef04ae1472e26be1b4f5817ed894f to your computer and use it in GitHub Desktop.
Auto shutdown script for shutdown Windows PC in specified times (SK)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .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