MSEndpointMgr

Proactive Battery Replacement with Endpoint Analytics

Battery health is of course something that over time diminishes, and tends to creep up on you when you least expect it, yes, we have all been caught out with a laptop dying in the middle of a meeting..

Typically IT departments react when the machine is thrown through the door of the IT department with a note saying please fix, and all too often it is a case that the battery itself is long since out of warranty. So wouldn’t it be cool if both those managing environments, along with the end-user were made aware of battery health issues before they became critical?

Endpoint Analytics FTW

Extending on Configuration Baselines I would have created in the past, I have ported a solution which monitors for the battery falling outside of a set threshold, typically this is less than 30-40%. OEM warranty replacements often use around these figures within the warranty period in order to validate a replacement is required, and having booked more than a fair share of battery replacements in my admin days, it is also clear that many of them use the built in “PowerCFG.exe /BatteryReport” details when validating a replacement.

Monitoring & Toast Notifications

With Endpoint Analytics we have the opportunity to not only monitor for what ever we opt to call via our script, but we can also run a remediation action. Obviously when your battery is well on its way to being goosed, there isn’t anything you can do in terms of remediation, however, you can of course do something like generate a toast style notification for the end-user.

So in the below scripts I am simply querying WMI to determine the manufacturer specifications and the current fully charged capacity value for the battery, then should it fail to reach a predetermined value which you can specify (40% being the recommended value), a notification will be invoked where the battery falls below that value:

Kind of cool I thought, but obviously don’t go overboard and run the detection every hour, otherwise you might invoke the Get-Laptop -Transform Frisbee -Target “Company\IT Department” -AlternativeTarget “OpenWindow” command.

Creating The Proactive Remediation Task

  • Launch the Microsoft Endpoint Portal – https://endpoint.microsoft.com
  • Click on Reports
  • Click on Endpoint Analytics (Assuming you have already set this up)
  • Click on Proactive Remediations
  • Click on Create Script Package
  • Use the following script for your Detection Script:
function CheckBatteryHealth {
	# Check for presence of battery and check where present
	If (Get-WmiObject win32_battery) {
		# Check machine type and other info
		[string]$SerialNumber = (Get-WmiObject win32_bios).SerialNumber
		
		# Maximum Acceptable Health Perentage
		$MinHealth = "40"

        # Multiple Battery handling
        $BatteryInstances = Get-WmiObject -Namespace "ROOT\WMI" -Class "BatteryStatus" | Select-Object -ExpandProperty InstanceName
		
        ForEach($BatteryInstance in $BatteryInstances){

            # Set Variables for health check

            $BatteryDesignSpec = Get-WmiObject -Namespace "ROOT\WMI" -Class "BatteryStaticData" | Where-Object -Property InstanceName -EQ $BatteryInstance | Select-Object -ExpandProperty DesignedCapacity
            $BatteryFullCharge = Get-WmiObject -Namespace "ROOT\WMI" -Class "BatteryFullChargedCapacity" | Where-Object -Property InstanceName -EQ $BatteryInstance | Select-Object -ExpandProperty FullChargedCapacity

            # Fall back WMI class for Microsoft Surface devices
            if ($BatteryDesignSpec -eq $null -or $BatteryFullCharge -eq $null -and ((Get-WmiObject -Class Win32_BIOS | Select-Object -ExpandProperty Manufacturer) -match "Microsoft")) {
	
                # Attempt to call WMI provider
	            if (Get-WmiObject -Class MSBatteryClass -Namespace "ROOT\WMI") {
		            $MSBatteryInfo = Get-WmiObject -Class MSBatteryClass -Namespace "root\wmi" | Where-Object -Property InstanceName -EQ $BatteryInstance | Select-Object FullChargedCapacity, DesignedCapacity
		
		            # Set Variables for health check
		            $BatteryDesignSpec = $MSBatteryInfo.DesignedCapacity
		            $BatteryFullCharge = $MSBatteryInfo.FullChargedCapacity
	            }
            }
		
		    if ($BatteryDesignSpec -gt $null -and $BatteryFullCharge -gt $null) {
			    # Determine battery replacement required
			    [int]$CurrentHealth = ($BatteryFullCharge/$BatteryDesignSpec) * 100
			    if ($CurrentHealth -le $MinHealth) {
				    $ReplaceBattery = $true
				
				    # Generate Battery Report
				    $ReportingPath = Join-Path -Path $env:SystemDrive -ChildPath "Reports"
				    if (-not (Test-Path -Path $ReportingPath)) {
					    New-Item -Path $ReportingPath -ItemType Dir | Out-Null
				    }
				    $ReportOutput = Join-Path -Path $ReportingPath -ChildPath $('\Battery-Report-' + $SerialNumber + '.html')
				
				    # Run Windows battery health report
				    Start-Process PowerCfg.exe -ArgumentList "/BatteryReport /OutPut $ReportOutput" -Wait -WindowStyle Hidden
				
				    # Output replacement message and flag for remediation step
				    Write-Output "Battery replacement required - $CurrentHealth% of manufacturer specifications"
				    exit 1
				
			    } else {
				    # Output replacement not required values
				    $ReplaceBattery = $false
				    Write-Output "Battery status healthy: $($CurrentHealth)% of manufacturer specifications"
				    # Not exiting here so that second battery can be checked
			    }
		    } else {
			# Output battery not present
			Write-Output "Battery not present in system."
			exit 0
		    }
            }
	    } else {
        # Output battery value condition check error
        Write-Output "Unable to obtain battery health information from WMI"
        exit 0
    }
}
CheckBatteryHealth
  • Now use the following code for your Toast notification (making sure to include your own image paths)
function Display-ToastNotification() {
	$Load = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
	$Load = [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime]
	# Load the notification into the required format
	$ToastXML = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
	$ToastXML.LoadXml($Toast.OuterXml)
	
	# Display the toast notification
	try {
		[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($App).Show($ToastXml)
	} catch {
		Write-Output -Message 'Something went wrong when displaying the toast notification' -Level Warn
		Write-Output -Message 'Make sure the script is running as the logged on user' -Level Warn
	}
}

# Check for required entries in registry for when using Powershell as application for the toast
# Register the AppID in the registry for use with the Action Center, if required
$RegPath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings'
$App = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'

# Multiple Battery handling
$BatteryInstances = Get-WmiObject -Namespace "ROOT\WMI" -Class "BatteryStatus" | Select-Object -ExpandProperty InstanceName


ForEach($BatteryInstance in $BatteryInstances){

    # Set Variables for health check

    $BatteryDesignSpec = Get-WmiObject -Namespace "ROOT\WMI" -Class "BatteryStaticData" | Where-Object -Property InstanceName -EQ $BatteryInstance | Select-Object -ExpandProperty DesignedCapacity
    $BatteryFullCharge = Get-WmiObject -Namespace "ROOT\WMI" -Class "BatteryFullChargedCapacity" | Where-Object -Property InstanceName -EQ $BatteryInstance | Select-Object -ExpandProperty FullChargedCapacity

    # Fall back WMI class for Microsoft Surface devices
    if ($BatteryDesignSpec -eq $null -or $BatteryFullCharge -eq $null -and ((Get-WmiObject -Class Win32_BIOS | Select-Object -ExpandProperty Manufacturer) -match "Microsoft")) {
	
        # Attempt to call WMI provider
	    if (Get-WmiObject -Class MSBatteryClass -Namespace "ROOT\WMI") {
		    $MSBatteryInfo = Get-WmiObject -Class MSBatteryClass -Namespace "root\wmi" | Where-Object -Property InstanceName -EQ $BatteryInstance | Select-Object FullChargedCapacity, DesignedCapacity
		
		    # Set Variables for health check
		    $BatteryDesignSpec = $MSBatteryInfo.DesignedCapacity
		    $BatteryFullCharge = $MSBatteryInfo.FullChargedCapacity
	    }
    }

    # Determine battery replacement required
    [int]$CurrentHealth = ($BatteryFullCharge/$BatteryDesignSpec) * 100

    # Setting image variables
    $LogoImageUri = "YOURLOGIMAGEURLHERE"
    $HeroImageUri = "YOURHEROIMAGEURLHERE"
    $LogoImage = "$env:TEMP\ToastLogoImage.png"
    $HeroImage = "$env:TEMP\ToastHeroImage.png"
    $MinHealth = 40
    

    If($CurrentHealth -le $MinHealth){

        #Fetching images from uri
        Invoke-WebRequest -Uri $LogoImageUri -OutFile $LogoImage
        Invoke-WebRequest -Uri $HeroImageUri -OutFile $HeroImage

        #Defining the Toast notification settings
        #ToastNotification Settings
        $Scenario = 'reminder' # <!-- Possible values are: reminder | short | long -->

        # Load Toast Notification text
        $AttributionText = "MSEndpointMgr"
        $HeaderText = "Battery Replacement Required"
        $TitleText = "Your device battery health is currently operating at $CurrentHealth% of manufacturers specifications."
        $BodyText1 = "It is recommended that your battery is replaced as soon as possible."
        $BodyText2 = "Please contact IT and request a battery replacement. Thank you in advance."


        # Creating registry entries if they don't exists
                if (-NOT (Test-Path -Path "$RegPath\$App")) {
	    New-Item -Path "$RegPath\$App" -Force
	    New-ItemProperty -Path "$RegPath\$App" -Name 'ShowInActionCenter' -Value 1 -PropertyType 'DWORD'
        }

        # Make sure the app used with the action center is enabled
            if ((Get-ItemProperty -Path "$RegPath\$App" -Name 'ShowInActionCenter' -ErrorAction SilentlyContinue).ShowInActionCenter -ne '1') {
	    New-ItemProperty -Path "$RegPath\$App" -Name 'ShowInActionCenter' -Value 1 -PropertyType 'DWORD' -Force
        }


# Formatting the toast notification XML
[xml]$Toast = @"
<toast scenario="$Scenario">
    <visual>
    <binding template="ToastGeneric">
        <image placement="hero" src="$HeroImage"/>
        <image id="1" placement="appLogoOverride" hint-crop="circle" src="$LogoImage"/>
        <text placement="attribution">$AttributionText</text>
        <text>$HeaderText</text>
        <group>
            <subgroup>
                <text hint-style="title" hint-wrap="true" >$TitleText</text>
            </subgroup>
        </group>
        <group>
            <subgroup>     
                <text hint-style="body" hint-wrap="true" >$BodyText1</text>
            </subgroup>
        </group>
        <group>
            <subgroup>     
                <text hint-style="body" hint-wrap="true" >$BodyText2</text>
            </subgroup>
        </group>
    </binding>
    </visual>
    <actions>
        <action activationType="system" arguments="dismiss" content="$DismissButtonContent"/>
    </actions>
</toast>
"@

        #Send the notification
        Display-ToastNotification
        Exit 0
    }
else{$CurrentHealth}
}
  • At this point you should have a screen similar to the below:
  • Assign the Proactive Remediation and let it do its thing..

Endpoint Analytics Reporting

From an Intune admin perspective, compliance then can be tracked through the Endpoint Analytics reporting node:

Drilling into the device status, and then adding the “Pre-remediation detection output” column, you can then see the output from the script, examples:

Battery Report

To assist with any calls to an OEM for a battery replacement, the script will also generate a battery report using the before mentioned PowerCFG.exe command. Below are snippets from this report, which will be dropped into a “Reports” folder on the C:\



Over time of course the full charge capacity will diminish, so the above screenshots are not a true reflection on this as the machine in question is only around 6 months old, but you get the idea.

Conclusion

So this is just another quick way that Endpoint Analytics can be used, I hope you have enjoyed reading this post. Thanks goes out to Martin Bengtsson and Jan Ketil Skanke for inspiration/code for the toast notification.

Update: Special shout out to Jason Cody, PFE with Microsoft, for giving the heads up and code addition for Surface devices with multiple batteries #CommunityRocks

Maurice Daly

Maurice has been working in the IT industry for the past 20 years and currently working in the role of Senior Cloud Architect with CloudWay. With a focus on OS deployment through SCCM/MDT, group policies, active directory, virtualisation and office 365, Maurice has been a Windows Server MCSE since 2008 and was awarded Enterprise Mobility MVP in March 2017. Most recently his focus has been on automation of deployment tasks, creating and sharing PowerShell scripts and other content to help others streamline their deployment processes.

Add comment

Sponsors

Categories

MSEndpointMgr.com use cookies to ensure that we give you the best experience on our website.