Building a Battery Manager with PowerShell

Windows Management Instrumentation (WMI) never ceases to surprise me. I always find something new that I can manage via WMI and PowerShell. One particular piece of information that I personally needed focuses on the Win32_Battery class. Because my primary computers are laptops, it is important to know what is happening with battery life.

Yes, there is an icon in the system tray, but I have to grab the mouse and hover on it to get more detailed information. And more often than not, I’m so focused on the task at hand that I totally ignore the system tray. So allow me to walk you through my process for creating a battery management tool with WMI and PowerShell. You may not need to use my code samples, but they might prove useful as a model for your own scripting efforts.
Because I am going to run this locally, I’m going to stick with Get-WMIObject. Although I could use Get-CimInstance, there’s not much benefit that I can see since the local machine’s connection is made via DCOM. First, how did I know what class to use? I asked PowerShell.

Using Get-WMIObject in Windows PowerShell. (Image Credit: Jeff Hicks)
Using Get-WMIObject in Windows PowerShell. (Image Credit: Jeff Hicks)

Generally, for anything in the Root\Cimv2 namespace I will use a Win32 class, which leaves me with two options. If you refer to official MSDN documentation for the Win32_Batter class and Win32_PortableBattery class, you’ll notice that the classes look very similar. It also would seem that the Win32_PortableBattery would be a better choice. But then I decide to see exactly what each instance looks like on my laptop.

Get-WmiObject win32_portablebattery
Using Get-WmiObject with the Win32_PortableBattery class. (Image Credit: Jeff Hicks)
Using Get-WmiObject with the Win32_PortableBattery class. (Image Credit: Jeff Hicks)
Get-WmiObject win32_battery

Using Get-WmiObject with the Win32_Battery class. (Image Credit: Jeff Hicks)
Using Get-WmiObject with the Win32_Battery class. (Image Credit: Jeff Hicks)

It is apparent to me that this class provides more meaning information. I’ve highlighted a few properties that I want to take advantage of. I could run a query as simple as this:

Get-wmiobject win32_battery | select Availability,EstimatedRunTime,EstimatedChargeRemaining,BatteryStatus,PSComputername

As you can see it works.

Highlighting properties with the Win32_Batter class. (Image Credit: Jeff Hicks)
Highlighting properties with the Win32_Batter class. (Image Credit: Jeff Hicks)

The information here is not very meaningful. What is an Availability of 3 indicate? What does the value 1 for BatteryStatus mean? This is where you need to return to the MSDN documentation. If you look at information about Availability, then you will find a table of possible values and their meaning.
Property values for the official Win32_Battery class documentation. (Image Credit: Jeff Hicks)
Property values for the official Win32_Battery class documentation. (Image Credit: Jeff Hicks)

Whenever I have something like this, I use a Switch construct to “decode” the value.

Get-wmiobject win32_battery |
select @{Name="Availability";Expression={
 Switch ($_.Availability) {
 1  { "Other" ;break}
2  { "Unknown" ;break}
3  { "Running or Full Power";break}
4  { "Warning" ;break}
5  { "In Test";break}
6  { "Not Applicable";break}
7  { "Power Off";break}
8  { "Off Line";break}
9  { "Off Duty";break}
10  { "Degraded";break}
11  { "Not Installed";break}
12  { "Install Error";break}
13  { "Power Save - Unknown";break}
14  { "Power Save - Low Power Mode" ;break}
15  { "Power Save - Standby";break}
16  { "Power Cycle";break}
17  { "Power Save - Warning";break}
 }
 }},EstimatedRunTime,EstimatedChargeRemaining,BatteryStatus,PSComputername

Perhaps a bit tedious, but it works.

After using a switch statement, the Win32_Battery class information becomes much more meaningful. (Image Credit: Jeff Hicks)
After using a switch statement, the Win32_Battery class information becomes much more meaningful. (Image Credit: Jeff Hicks)

I could do something similar with BatteryStatus. But I don’t plan on typing all of this out everytime I want to check the battery. I’m going to build a tool in which this situation will take the form of a function.

#requires -version 3.0
#http://msdn.microsoft.com/en-us/library/aa394074%28v=vs.85%29.aspx
Function Measure-Battery {
[cmdletbinding()]
Param(
[Parameter(Position=0)]
[Alias('CN')]
[ValidateNotNullorEmpty()]
$computername = $env:computername)
$statusHash=@{
1 = "Discharging"
2 = "On A/C"
3 = "Fully Charged"
4 = "Low"
5 = "Critical"
6 = "Charging"
7 = "Charging High"
8 = "Charging Low"
9 = "Charging Critical"
10 = "Undefined"
11 = "Partially Charged"
}
$availHash=@{
1 = "Other"
2 =	"Unknown"
3 =	"Running or Full Power"
4 =	"Warning"
5 =	"In Test"
6 =	"Not Applicable"
7 =	"Power Off"
8 =	"Off Line"
9 =	"Off Duty"
10 = "Degraded"
11 = "Not Installed"
12 = "Install Error"
13 = "Power Save - Unknown"
14 = "Power Save - Low Power Mode"
15 = "Power Save - Standby"
16 = "Power Cycle"
17 = "Power Save - Warning"
}
Get-CimInstance -ClassName Win32_battery -ComputerName $computername |
Select PSComputername,
@{Name = "Runtime";Expression={New-TimeSpan -minutes $_.EstimatedRunTime}},
@{Name = "PctRemaining";Expression={$_.EstimatedChargeRemaining}},
@{Name = "MyAvailability";Expression={$availHash.Item($($_.Availability -as [int]))}},
@{Name = "BatteryStatus";Expression={ $statushash.Item($($_.batterystatus -as [int]))}}
} #end function
Set-Alias -Name gmb -Value Measure-Battery

My code even includes an alias for the function. Even though I had been using WMI, there may be times when I need to check a remote laptop in which case using Get-CIMInstance is the more modern approach. The syntax is identical except for the cmdlet name if your computers don’t support PowerShell v3 or later.

The other change I made was to define a hashtable of possible values for Availability and BatteryStatus. The numeric value is the key for each entry. In my Select-Object statement I can use the hashtable’s Item() method to retrieve the corresponding value. The parameter for the Item() method is the name of the key. The tricky part is that the property I get back from WMI is an unsigned integer ([uint]), which I can see in the documentation or from pipeing an instance to Get-Member. However, the keys in my decode hashtables are integers so I use the –AS operator to temporarily treat the Availability value as an integer. But now I can run a quick command and get all the information I need in plain English.

The function allows us to use a quick command to grab battery property information. (Image Credit: Jeff Hicks)
The function allows us to use a quick command to grab battery property information. (Image Credit: Jeff Hicks)

In an upcoming article we’ll look at some other ways to monitor settings such as battery life with PowerShell. But for now, let me leave you with a variation that takes advantage of the Write-Host cmdlet to display colorized information in the console.

#requires -version 3.0
#get battery information and display to host
Param(
[string]$computername = $env:computername,
[string]$char = "|"
)
$data = Get-CimInstance -ClassName Win32_battery -ComputerName $computername
#determine how much power remains
if ($data.EstimatedChargeRemaining -ge 80) {
    $color = "Green"
}
elseif ($data.EstimatedChargeRemaining -ge 40) {
    $color = "Yellow"
}
elseif ($data.EstimatedChargeRemaining -ge 20) {
    $color = "Magenta"
}
else {
    $color = "Red"
}
#write a blank line first
write "`r"
Write-Host $data.PSComputername -ForegroundColor cyan
Write-Host "Runtime    : " -NoNewline
Write-Host "$(New-TimeSpan -minutes $data.EstimatedRunTime)" -ForegroundColor $color
Write-Host "Remaining %: " -NoNewline
Write-host "$($data.EstimatedChargeRemaining)" -ForegroundColor $color
Write-Host "$($char*$($data.EstimatedChargeRemaining/2)) " -ForegroundColor $color
write "`r"

This little script will display a console-style bar graph indicating how much battery life remains. The bar color depends on the EstimatedChargeRemaining value and is set with an If/ElseIf statement. When you use something like this be sure to get your statements in the right order.

Using the Write-Host cmdlet in PowerShell to graph your battery levels. (Image Credit: Jeff Hicks)
Using the Write-Host cmdlet in PowerShell to graph your battery levels. (Image Credit: Jeff Hicks)

I am inserting a carriage return with `r before and after my graph.

The last thing I’ll warn you about is that when the battery is charging you might see some odd results.
You may see different results when your battery is actively charging. (Image Credit: Jeff Hicks)
You may see different results when your battery is actively charging. (Image Credit: Jeff Hicks)

But probably the only thing that matters is that the battery status indicates I am back on power. You can also tell because the runtime will be a ridiculously large number of days.
As with many of the articles I write on PowerShell, I hope you’ll pay closer attention to the techniques I used than the actual end result.