So I had some fun with my post last week on taking a nap with PowerShell. I got some great feedback on Twitter and a new comments on the blog. My initial effort was a relatively simple PowerShell script which certainly got the job done. But I there were a number of areas where I could expand and improve the script and they would be terrific teaching aids. So I did.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
The function is defined in a script you can find in Github.
Let's look at some of the changes I made. First off, I turned this into a function complete with comment based help. You'll need to dot source the script file into your PowerShell session or profile script to make the command available.
I made just about every option a parameter and added a few parameter aliases as well. So even though I made the Minutes parameter positional so that you don't need to use the parameter name, you could use –Nap or –Time. You'll notice I also made the wakeup message a parameter. Feel free to set your own default value. Otherwise, you can set a different message at different times.
I also realized that if you are napping, someone might still drop by your desk. So I included an option to display a progress bar using Write-Progress. This is a cmdlet that doesn't get the love it should.
I defined an array of messages:
#an array of status messages if using a progress bar $ops = "I'm solving a PowerShell problem","I'm chasing cmdlets", "Brilliance at work","Re-initializing my pipeline", "Go away","I'm checking eyelid integrity","It can wait...", "Don't you dare!","Spawning a new runspace","I'm multitasking", "Nothing is that important","Unless you have beer for me, go away", "I'm testing the new PSNap provider","I need this", "I'm downloading my new matrix","Resource recyling in progress", "Synaptic synch in progress","Neural network rebooting", "If you can read this you shouldn't be here", "$($env:username) has left the building"
The messages will be used as the Status property for Write-Progress. I like using a hashtable of parameters to splat when using Write-Progress.
$progHash = @{ Activity = "Ssssshhh..." Status = $ops[0] SecondsRemaining = $remainingSeconds }
If I use the Progress bar, it is displayed using the seconds remaining.
if ($ProgressBar ) { Write-Progress @proghash #tick down $remainingseconds $proghash.SecondsRemaining = $remainingSeconds-- #pick a new random status if remaining seconds is divisible by 10 if ($remainingSeconds/10 -is [int]) { $proghash.status = $ops | Get-Random } } #if
And every 10 seconds I set the status to another randomly selected message. The result is something like this:
The last major change I made per a suggestion was to use the text to speech feature to have a Windows voice "say" the wake up message. I added a parameter for you to specify a voice name which in the US will most likely be David or Zira. If you don't know the names, you can specify a bogus value like 'foo' and the function will display the available names. This works because I added a validation script to the Voice parameter.
[ValidateScript({ #get voices Add-Type -AssemblyName System.speech $installed = [System.Speech.Synthesis.SpeechSynthesizer]::new().GetInstalledVoices().voiceinfo.Name if ($installed -match $_ ) { $True } else { [regex]$rx= "Microsoft\s+(?\w+)\s+" #build a list of voices assuming the voice name is something like Microsoft David Desktop $choices = (($rx.Matches($installed)).foreach({$_.groups["name"].value})) -join "," Throw "Can't find an installed voice for $_. Possible values are: $choices" } })] [Parameter(ParameterSetName = "voice")] [string]$Voice,
This is probably a bit more involved than most validation scripts. The main takeaway is that if you use a validation script it has to return either True or False, or throw an exception as I'm doing here. But it works.
By adding a voice option I decided the function could either display the message using Write-Host or speak it. The chime happens in either event.
If ($Voice) { Add-Type -AssemblyName System.speech $speech = New-Object System.Speech.Synthesis.SpeechSynthesizer #find the matching voice object $selected = [System.Speech.Synthesis.SpeechSynthesizer]::new().GetInstalledVoices().voiceinfo.Name | where {$_ -match $voice} $speech.SelectVoice($selected) $speech.Rate = $Rate $speech.SpeakAsync($message) | Out-Null #write a blank line to get a new prompt Write-Host "`n" } else { Write-Host "`n$Message" -ForegroundColor Yellow }
What this meant was that I had to differentiate the parameters which I did with parameter sets. I specified the default in the cmdletbinding attribute.
[cmdletbinding(DefaultParameterSetName = "host")]
Then I needed to specify a parameter set name for each parameter. If you don't specify parameter set name, then the parameter will belong to all sets. Or you can do as I did and be explicit. If you do it properly it should be reflected in the help.
You can see that there are 2 ways to use this command. I'll let you grab a copy and try out the new additions.
Certainly this isn't a production oriented script but I hope it serves up some interesting examples of different scripting techniques and cmdlets.
As always, comments sincerely welcomed.
Enjoy!
Hilarious script, but also very clever and lots of new things for me to discover. Thanks for sharing
Thanks. These types of articles are intended to be fun and entertaining and even teach a few things without you realizing it!