functions/ConsoleCountdown.ps1
|
#functions for displaying a countdown timer in a PowerShell console session #region Exported functions function Start-ConsoleCountdown { [cmdletbinding(SupportsShouldProcess)] [OutputType('none')] [Alias('stcd')] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'The number of seconds for the countdown.' )] [ValidateScript({ $_ -gt 0 })] [int32]$Seconds, [Parameter( ValueFromPipelineByPropertyName, HelpMessage = 'Specify the text to display after the countdown completes in 25 characters or less.' )] [ValidateLength(1, 25)] [string]$PostCountdownText, [Parameter(HelpMessage = 'Specify an ANSI style or console color for the clock display.')] [PSDefaultValue(Help = 'Yellow')] [ValidateNotNullOrEmpty()] [string]$DisplayColor = 'Yellow', [Parameter(HelpMessage = 'Add a line border to the display.')] [switch]$Border, [Parameter(HelpMessage = 'Specify an ANSI or console color for the border. This parameter has no effect unless used with -Border')] [ValidateNotNullOrEmpty()] [string]$BorderColor = 'Green' ) begin { _verbose ($strings.Starting -f $MyInvocation.MyCommand) if ($MyInvocation.CommandOrigin -eq 'Runspace') { _verbose ($strings.Running -f $PSVersionTable.PSVersion) _verbose ($strings.UsingModule -f $modVersion) _verbose ($strings.Detected -f $Host.Name) } Write-Information $PSBoundParameters -Tags runtime #the action scriptblock needs to be self-contained or able to reference globally scoped items $action = { #if the global settings hashtable is removed, do not run and stop the countdown if (-not $global:consoleCountdownSettings) { Stop-ConsoleCountdown return } #dot source the helper functions from the PSClock module if required $modPath = (Get-Module PSTimers).path | Split-Path -Parent $funPath = Join-Path -Path $modPath -ChildPath 'functions\ConsoleTimerHelpers.ps1' . $funPath if ($global:consoleCountdownSettings.Border) { #add an offset if there is a Border $offSet = 5 } else { $offset = 1 } if (($global:consoleCountdownSettings.target -gt $global:consoleCountdownSettings.Timer.Elapsed)) { $global:consoleCountdownSettings.IsRunning = $True [string]$dtCountdown = '{0:hh\:mm\:ss}' -f ($global:consoleCountdownSettings.Target - $global:consoleCountdownSettings.Timer.Elapsed) } elseif ($global:consoleCountdownSettings.Timer.Elapsed -gt $global:consoleCountdownSettings.target) { $global:consoleCountdownSettings.IsRunning = $False $global:consoleCountdownSettings.Timer.Stop() if ($global:consoleCountdownSettings.PostText) { [string]$dtCountdown = $global:consoleCountdownSettings.PostText } else { [string]$dtCountdown = "00:00:00" Stop-ConsoleCountdown Return } } $fmtCountDown = "{0}$dtCountdown{1}" -f (ConvertTo-AnsiColor $global:consoleCountdownSettings.DisplayColor), "$([char]27)[0m" #define the cursor position in console $here = $host.ui.RawUI.CursorPosition $x = $Host.UI.RawUI.WindowSize.Width - $dtCountdown.length - $offSet $consolePosition = [System.Management.Automation.Host.Coordinates]::new($x, 0) $host.ui.RawUI.CursorPosition = $consolePosition if ($global:consoleCountdownSettings.Border) { Format-BorderBox -Text $fmtCountDown -BorderColor (ConvertTo-AnsiColor $global:consoleCountdownSettings.BorderColor) -Position $consolePosition } else { Write-Host $fmtCountDown -NoNewline } #reset the cursor position $host.ui.RawUI.CursorPosition = $here } #close action Write-Information $action } #begin process { if ($host.name -eq 'ConsoleHost') { _verbose ($strings.StartConsoleCountdown -f (New-TimeSpan -Seconds $Seconds) ) Write-Information -MessageData $myInvocation -Tags process Write-Information -MessageData $PSBoundParameters -Tags process #create $global:consoleCountdownSettings $consoleCountdownParams = @{ Name = 'consoleCountdownSettings' Scope = 'global' Description = 'This is used by the PSTimers module. Do not manually remove.' Value = [ordered]@{ Timer = [System.Diagnostics.Stopwatch]::new() Target = New-TimeSpan -Seconds $Seconds PostText = $PostCountdownText DisplayColor = ConvertTo-AnsiColor $DisplayColor Border = $Border BorderColor = ConvertTo-AnsiColor $BorderColor IsRunning = $False Note = 'This is used by the PSTimers module. Do not manually remove.' } } Set-Variable @consoleCountdownParams Write-Information $global:consoleCountdownSettings -Tags runtime if ($global:consoleCountdownSettings.IsRunning) { Write-Warning $strings.ExistingConsoleCountdown } else { _verbose $strings.RegisterConsoleCountdown #Register-EngineEvent doesn't natively support -WhatIf so I have to do it. if ($PSCmdlet.ShouldProcess('Start-ConsoleCountdown')) { #hide the subscription Register-EngineEvent -SourceIdentifier PowerShell.OnIdle -Action $action -SupportEvent $script:countdownEvent = Get-EventSubscriber -Force | Sort-Object SubscriptionId | Select-Object -Last 1 #add to the module array for event subscriptions $subs.Add($script:countdownEvent) _verbose ($strings.UsingSubscription -f $script:countdownEvent.SubscriptionID) $global:consoleCountdownSettings.Timer.Start() $global:consoleCountdownSettings.IsRunning = $True } #WhatIf } #no existing countdown in the console } #if console host else { Write-Warning $strings.InvalidHost } } #process end { _verbose ($strings.Ending -f $MyInvocation.MyCommand) } #end } #close Start-ConsoleCountdown function Stop-ConsoleCountdown { [cmdletbinding(SupportsShouldProcess)] [OutputType('none')] param( ) begin { _verbose ($strings.Starting -f $MyInvocation.MyCommand) if ($MyInvocation.CommandOrigin -eq 'Runspace') { _verbose ($strings.Running -f $PSVersionTable.PSVersion) _verbose ($strings.UsingModule -f $modVersion) _verbose ($strings.Detected -f $Host.Name) } } #begin process { _verbose $strings.StoppingConsoleCountdown if ($script:countdownEvent) { _verbose ($strings.Unregister -f $($script:countdownEvent.SubscriptionId)) Unregister-Event -SubscriptionId $script:countdownEvent.SubscriptionId -Force Remove-Variable -Name countdownEvent -Scope Script -Force -ErrorAction SilentlyContinue Remove-Variable -Name consoleCountdownSettings -Scope Global -Force -ErrorAction SilentlyContinue } else { Write-Warning $strings.NoConsoleCountdown } } #process end { _verbose ($strings.Ending -f $MyInvocation.MyCommand) } #end } #close Stop-ConsoleCountdown #endregion |