ARM64_Lab

Windows batteryreport measured 94.1% full-charge capacity health

In this article
  1. Extracting only capacity from XML
  2. Capacity obtained and history not obtained
  3. The usage history could not be extracted
  4. The degradation calculation worked
  5. What to fix next

The full-charge capacity from powercfg /batteryreport /XML was 48450 mWh, or 94.1% of the 51480 mWh design capacity.

Battery life is an important selling point for an ARM64 machine. While using a Surface Pro 11th Edition, there are situations where how much battery remains matters more than boot time or emulation speed. I feel that especially on days when I work away from an outlet. This time, instead of relying on how it felt, I used the XML that Windows provides through batteryreport to turn the degradation into a number. The measurement date was July 12, 2026.

It was slightly different from what I expected that I did not need to read the HTML report visually. Adding /XML exposes DesignCapacity and FullChargeCapacity as tags. Extracting them with regular expressions avoids manually transcribing the numbers into a note and reduces the risk of misreading a digit. The daily usage history did not come out in the same way. entry_count was 0 and entries_sample was empty. I will return to that below.

Extracting only capacity from XML

The command I actually tried was powercfg /batteryreport /XML, with the output directed to an XML file. Opening the HTML and looking at its table is also possible, but for an article it is better to be able to retrieve the same values mechanically.

For this extraction, I read the entire XML as a string and used regular expressions to pick out only the required tags. The capacity tags were more straightforward than expected. Taking a shortcut here led directly to the failure on the history side.

$xml = Get-Content .\batteryreport.xml -Raw
$design = [int]([regex]::Match($xml, '<DesignCapacity>(\d+)</DesignCapacity>').Groups[1].Value)
$full = [int]([regex]::Match($xml, '<FullChargeCapacity>(\d+)</FullChargeCapacity>').Groups[1].Value)
$health = [math]::Round($full / $design * 100, 1)

This removes the steps of opening the battery report in a browser, finding the table, and copying the values without misreading a digit. Generating the XML itself took only 6.53 seconds on this machine. That is sufficient for an article measurement.

Capacity obtained and history not obtained

Here are the values as they were obtained. I used only numbers that remained in the JSON.

Item Value
XML generation time 6.53 seconds
XML size 154155 bytes
Design capacity DesignCapacity 51480 mWh
Full-charge capacity FullChargeCapacity 48450 mWh
Difference 3030 mWh
Health 94.1%
Usage history entry_count 0
WMI retrieval time 1.13 seconds
EstimatedChargeRemaining 80%
DesignVoltage 8221 mV
powerplan retrieval time 0.32 seconds

The calculation is simple: 48450 / 51480 * 100, rounded to one decimal place, is 94.1%. The difference is 51480 mWh minus 48450 mWh, or 3030 mWh. In other words, the full-charge container is 3030 mWh smaller than when new. Up to this point, the batteryreport XML is sufficient by itself.

On the WMI side, the stdout contained a JSON string from Win32_Battery. It had Name set to SurfaceBattery, EstimatedChargeRemaining set to 80, DesignVoltage set to 8221, and BatteryStatus set to 2. This is a separate confirmation from the batteryreport capacity: it provides the current charge and design voltage.

The power plan was 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced). The machine was using the default Balanced plan, so the batteryreport was not generated while it was set to a high-performance plan.

The usage history could not be extracted

This part failed. In short, the result was zero entries.

entry_count was 0 and entries_sample was an empty array. I had planned to take the start time, active time, and energy consumed from each day's usage history and calculate consumption in mWh per hour, but that plan was blocked. If it had worked, I could have divided 48450 mWh by the consumption and estimated "how many hours from a full charge." Why were the capacity tags available while the history was empty?

Honestly, the most likely explanation is that the attribute names assumed by the regular expression did not match the actual XML shape. Rather than conclude that batteryreport has no history, it is more natural to conclude that the extraction script was looking too narrowly.

This failure is worth preserving in the article. Battery health needs only DesignCapacity and FullChargeCapacity. To inspect usage history, however, the reader must match not just tag names but also attributes and hierarchy to the actual XML. A table in the HTML does not guarantee the same names in the XML. I realized here that I had been too quick to talk about runtime from health alone.

The degradation calculation worked

The health measurement worked. Running powercfg /batteryreport /XML, extracting the two capacity tags, and reducing them to 94.1% is easy to reproduce. ARM64 Windows did not require a special procedure; this portion was almost surprisingly short.

I still marked the verdict as "conditional" because the usage history contained zero entries. To discuss battery life, "full-charge capacity is 48450 mWh" is not enough. Only after measuring how many mWh are actually consumed in one hour can we say how many hours a particular workload may last.

On this machine, the most useful role for this is a small health-check tool. It can be used immediately after buying a used machine, to check a device that has been used for a long time, or to see whether full-charge capacity changed suddenly after an OS update. For those checks, the XML should be enough without opening the HTML.

Personally, I think ARM64 machines marketed for battery life should have this number checked occasionally. If the battery seems to drain faster today, I want to distinguish whether full-charge capacity fell, the workload was heavier, or the power plan changed.

What to fix next

Next time I will inspect the XML node structure before trying the usage-history extraction instead of forcing the whole thing through regular expressions. With entry_count still at zero, I cannot proceed to estimating runtime from energy consumption, so the first goal is to retrieve even one history entry.

Even this small check established something useful. On July 12, 2026, the SurfaceBattery full-charge capacity was 48450 mWh, the difference from design capacity was 3030 mWh, and health was 94.1%. The power plan was Balanced. Those values were obtained mechanically.

The surprising part was how little code was needed to check battery health. The difficult part was the history, which remains unfinished. The next attempt will follow the actual XML shape and start by extracting a single usage-history entry.

Hardware Operations
a
arm64lab — Independent publisher

Personal test notes from a Surface Pro 11th Edition with Snapdragon X Elite, used as a daily machine since May 2025. Results are based on direct measurements and do not represent any company or organization.