How to get the remaining battery life in a Windows system?

Solution 1:

I assume you're talking about a laptop. No such API exist in Standard Java SE API. This information is available in the operating system platform native API only. You would need at least JNI or JNA (JavaWorld article) to be able to communicate with platform native API.

In Windows, you'd like to hook on SYSTEM_POWER_STATUS structure. It offers among others the BatteryLifePercent property which may be of your interest. I found a helpful JNA based code snippet in an old forums.sun.com topic which is currently not available anymore. So the credit actually goes to the Author Whose Name Shall Not Be Known.

To get it to work, first drop jna.jar in the classpath and then copy the following class unmodified into your project:

package com.stackoverflow.q3434719;

import java.util.ArrayList;
import java.util.List;

import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary;

public interface Kernel32 extends StdCallLibrary {

    public Kernel32 INSTANCE = Native.load("Kernel32", Kernel32.class);

    /**
     * @see https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-system_power_status
     */
    public class SYSTEM_POWER_STATUS extends Structure {
        public byte ACLineStatus;
        public byte BatteryFlag;
        public byte BatteryLifePercent;
        public byte Reserved1;
        public int BatteryLifeTime;
        public int BatteryFullLifeTime;

        @Override
        protected List<String> getFieldOrder() {
            ArrayList<String> fields = new ArrayList<String>();
            fields.add("ACLineStatus");
            fields.add("BatteryFlag");
            fields.add("BatteryLifePercent");
            fields.add("Reserved1");
            fields.add("BatteryLifeTime");
            fields.add("BatteryFullLifeTime");
            return fields;
        }

        /**
         * The AC power status
         */
        public String getACLineStatusString() {
            switch (ACLineStatus) {
                case (0): return "Offline";
                case (1): return "Online";
                default: return "Unknown";
            }
        }

        /**
         * The battery charge status
         */
        public String getBatteryFlagString() {
            switch (BatteryFlag) {
                case (1): return "High, more than 66 percent";
                case (2): return "Low, less than 33 percent";
                case (4): return "Critical, less than five percent";
                case (8): return "Charging";
                case ((byte) 128): return "No system battery";
                default: return "Unknown";
            }
        }

        /**
         * The percentage of full battery charge remaining
         */
        public String getBatteryLifePercent() {
            return (BatteryLifePercent == (byte) 255) ? "Unknown" : BatteryLifePercent + "%";
        }

        /**
         * The number of seconds of battery life remaining
         */
        public String getBatteryLifeTime() {
            return (BatteryLifeTime == -1) ? "Unknown" : BatteryLifeTime + " seconds";
        }

        /**
         * The number of seconds of battery life when at full charge
         */
        public String getBatteryFullLifeTime() {
            return (BatteryFullLifeTime == -1) ? "Unknown" : BatteryFullLifeTime + " seconds";
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("ACLineStatus: " + getACLineStatusString() + "\n");
            sb.append("Battery Flag: " + getBatteryFlagString() + "\n");
            sb.append("Battery Life: " + getBatteryLifePercent() + "\n");
            sb.append("Battery Left: " + getBatteryLifeTime() + "\n");
            sb.append("Battery Full: " + getBatteryFullLifeTime() + "\n");
            return sb.toString();
        }
    }

    /**
     * Fill the structure.
     */
    public int GetSystemPowerStatus(SYSTEM_POWER_STATUS result);
}

Here's how you can use it, e.g. in your main() method:

Kernel32.SYSTEM_POWER_STATUS batteryStatus = new Kernel32.SYSTEM_POWER_STATUS();
Kernel32.INSTANCE.GetSystemPowerStatus(batteryStatus);

System.out.println(batteryStatus); // Shows result of toString() method.

This prints successfully the following at my Latitude E5500:

ACLineStatus: Online
Battery Flag: High, more than 66 percent
Battery Life: 100%
Battery Left: Unknown
Battery Full: Unknown

And after plugging off the AC for 10 minutes:

ACLineStatus: Offline
Battery Flag: High, more than 66 percent
Battery Life: 82%
Battery Left: 2954 seconds
Battery Full: Unknown

(no, no bad battery, I am just compiling a movie and continuously transferring over wifi right now ;) )