Skip to content

Commit f274b23

Browse files
authored
Fix Windows battery remaining capacity percent not reflecting IOCTL (#3124)
values
1 parent 6bb1fd9 commit f274b23

2 files changed

Lines changed: 18 additions & 34 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
[#3120](https://github.com/oshi/oshi/pull/3120),
77
[#3121](https://github.com/oshi/oshi/pull/3121): Port Linux JNA classes to FFM - [@dbwiddis](https://github.com/dbwiddis).
88
* [#3122](https://github.com/oshi/oshi/pull/3122): Refactor USB device classes to reduce duplication - [@dbwiddis](https://github.com/dbwiddis).
9+
* [#3124](https://github.com/oshi/oshi/pull/3124): Fix Windows battery remaining capacity percent not reflecting IOCTL values - [@dbwiddis](https://github.com/dbwiddis).
910

1011
# 6.11.0 (2026-04-04)
1112

oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsPowerSource.java

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2022 The OSHI Project Contributors
2+
* Copyright 2016-2026 The OSHI Project Contributors
33
* SPDX-License-Identifier: MIT
44
*/
55
package oshi.hardware.platform.windows;
@@ -14,7 +14,6 @@
1414
import com.sun.jna.Platform;
1515
import com.sun.jna.platform.win32.Guid.GUID;
1616
import com.sun.jna.platform.win32.Kernel32;
17-
import com.sun.jna.platform.win32.PowrProf.POWER_INFORMATION_LEVEL;
1817
import com.sun.jna.platform.win32.SetupApi;
1918
import com.sun.jna.platform.win32.WinBase;
2019
import com.sun.jna.platform.win32.WinError;
@@ -27,14 +26,12 @@
2726
import oshi.hardware.common.AbstractPowerSource;
2827
import oshi.jna.ByRef.CloseableIntByReference;
2928
import oshi.jna.Struct.CloseableSpDeviceInterfaceData;
30-
import oshi.jna.platform.windows.PowrProf;
3129
import oshi.jna.platform.windows.PowrProf.BATTERY_INFORMATION;
3230
import oshi.jna.platform.windows.PowrProf.BATTERY_MANUFACTURE_DATE;
3331
import oshi.jna.platform.windows.PowrProf.BATTERY_QUERY_INFORMATION;
3432
import oshi.jna.platform.windows.PowrProf.BATTERY_QUERY_INFORMATION_LEVEL;
3533
import oshi.jna.platform.windows.PowrProf.BATTERY_STATUS;
3634
import oshi.jna.platform.windows.PowrProf.BATTERY_WAIT_STATUS;
37-
import oshi.jna.platform.windows.PowrProf.SystemBatteryState;
3835
import oshi.util.Constants;
3936

4037
/**
@@ -102,30 +99,10 @@ private static WindowsPowerSource getPowerSource(String name) {
10299
String psSerialNumber = Constants.UNKNOWN;
103100
double psTemperature = 0d;
104101

105-
// windows PowerSource information comes from two sources: the PowrProf's
106-
// CallNTPowerInformation function which returns information for a single
107-
// object, and DeviceIoControl with each battery's (if more than one) handle
108-
// (which, in theory, return an array of objects but in most cases should return
109-
// one).
102+
// Note: CallNtPowerInformation(SystemBatteryState) was previously used as a
103+
// first pass here but no longer returns accurate data as of Windows 11 23H2.
104+
// All battery information is now sourced exclusively via DeviceIoControl.
110105
//
111-
// We start by fetching the PowrProf information, which will be replicated
112-
// across all IOCTL entries if there are more than one.
113-
114-
try (SystemBatteryState batteryState = new SystemBatteryState()) {
115-
if (0 == PowrProf.INSTANCE.CallNtPowerInformation(POWER_INFORMATION_LEVEL.SystemBatteryState, null, 0,
116-
batteryState.getPointer(), batteryState.size()) && batteryState.batteryPresent > 0) {
117-
if (batteryState.acOnLine == 0 && batteryState.charging == 0 && batteryState.discharging > 0) {
118-
psTimeRemainingEstimated = batteryState.estimatedTime;
119-
} else if (batteryState.charging > 0) {
120-
psTimeRemainingEstimated = -2d;
121-
}
122-
psMaxCapacity = batteryState.maxCapacity;
123-
psCurrentCapacity = batteryState.remainingCapacity;
124-
psRemainingCapacityPercent = Math.min(1d, (double) psCurrentCapacity / psMaxCapacity);
125-
psPowerUsageRate = batteryState.rate;
126-
}
127-
}
128-
129106
// Enumerate batteries and ask each one for information
130107
// Ported from:
131108
// https://docs.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
@@ -211,6 +188,7 @@ private static WindowsPowerSource getPowerSource(String name) {
211188
}
212189
if (0 != (bs.PowerState & BATTERY_CHARGING)) {
213190
psCharging = true;
191+
psTimeRemainingEstimated = -2d;
214192
}
215193
psCurrentCapacity = bs.Capacity;
216194
psVoltage = bs.Voltage > 0 ? bs.Voltage / 1000d
@@ -219,6 +197,8 @@ private static WindowsPowerSource getPowerSource(String name) {
219197
if (psVoltage > 0) {
220198
psAmperage = psPowerUsageRate / psVoltage;
221199
}
200+
psRemainingCapacityPercent = Math.min(1d,
201+
(double) psCurrentCapacity / psMaxCapacity);
222202
}
223203
}
224204

@@ -276,13 +256,16 @@ private static WindowsPowerSource getPowerSource(String name) {
276256
psTimeRemainingInstant = tr.getValue();
277257
}
278258
}
279-
// Fallback
280-
if (psTimeRemainingInstant < 0 && psPowerUsageRate != 0) {
281-
psTimeRemainingInstant = (psMaxCapacity - psCurrentCapacity)
282-
* 3600d / psPowerUsageRate;
283-
if (psTimeRemainingInstant < 0) {
284-
psTimeRemainingInstant *= -1;
285-
}
259+
// Fallback if BatteryEstimatedTime query failed
260+
if (psTimeRemainingInstant <= 0 && psPowerUsageRate != 0) {
261+
psTimeRemainingInstant = psDischarging
262+
? psCurrentCapacity * 3600d
263+
/ Math.abs(psPowerUsageRate)
264+
: (psMaxCapacity - psCurrentCapacity) * 3600d
265+
/ Math.abs(psPowerUsageRate);
266+
}
267+
if (psDischarging && psTimeRemainingInstant > 0) {
268+
psTimeRemainingEstimated = psTimeRemainingInstant;
286269
}
287270
// Exit loop
288271
batteryFound = true;

0 commit comments

Comments
 (0)