From 8d8d84af225513a58bb7bb1f42ff408eba54a14a Mon Sep 17 00:00:00 2001 From: EMN-CSharp <84488929+EMN-CSharp@users.noreply.github.com> Date: Fri, 11 Feb 2022 15:41:56 -0300 Subject: [PATCH] Add battery remaining time sensor (#660) --- .../Hardware/Battery/Battery.cs | 31 +++++++++++++++++++ .../Hardware/Battery/BatteryGroup.cs | 7 ++++- LibreHardwareMonitorLib/Interop/Kernel32.cs | 23 +++++++++++--- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/LibreHardwareMonitorLib/Hardware/Battery/Battery.cs b/LibreHardwareMonitorLib/Hardware/Battery/Battery.cs index 87079a1..c617671 100644 --- a/LibreHardwareMonitorLib/Hardware/Battery/Battery.cs +++ b/LibreHardwareMonitorLib/Hardware/Battery/Battery.cs @@ -23,6 +23,7 @@ namespace LibreHardwareMonitor.Hardware.Battery private readonly Sensor _designedCapacity; private readonly Sensor _fullChargedCapacity; private readonly Sensor _remainingCapacity; + private readonly Sensor _remainingTime; private readonly Sensor _voltage; public Battery @@ -98,6 +99,9 @@ namespace LibreHardwareMonitor.Hardware.Battery _degradationPercentage = new Sensor("Degradation Level", 0, SensorType.Level, this, settings); ActivateSensor(_degradationPercentage); + + _remainingTime = new Sensor("Remaining Time (Estimated)", 0, SensorType.TimeSpan, this, settings); + ActivateSensor(_remainingTime); } public float ChargeDischargeCurrent { get; private set; } @@ -120,6 +124,8 @@ namespace LibreHardwareMonitor.Hardware.Battery public float RemainingCapacity { get; private set; } + public uint RemainingTime { get; private set; } + public float Voltage { get; private set; } public override void Update() @@ -184,6 +190,31 @@ namespace LibreHardwareMonitor.Hardware.Battery _degradationPercentage.Value = 100f - (_fullChargedCapacity.Value * 100f / _designedCapacity.Value); } + + uint estimatedRunTime = 0; + Kernel32.BATTERY_QUERY_INFORMATION bqi = default; + bqi.BatteryTag = _batteryTag; + bqi.InformationLevel = Kernel32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryEstimatedTime; + if (Kernel32.DeviceIoControl(_batteryHandle, + Kernel32.IOCTL.IOCTL_BATTERY_QUERY_INFORMATION, + ref bqi, + Marshal.SizeOf(bqi), + ref estimatedRunTime, + Marshal.SizeOf(), + out _, + IntPtr.Zero)) + { + RemainingTime = estimatedRunTime; + if (estimatedRunTime != Kernel32.BATTERY_UNKNOWN_TIME) + { + ActivateSensor(_remainingTime); + _remainingTime.Value = estimatedRunTime; + } + else + { + DeactivateSensor(_remainingTime); + } + } } public override void Close() diff --git a/LibreHardwareMonitorLib/Hardware/Battery/BatteryGroup.cs b/LibreHardwareMonitorLib/Hardware/Battery/BatteryGroup.cs index bb7c173..bf99c9f 100644 --- a/LibreHardwareMonitorLib/Hardware/Battery/BatteryGroup.cs +++ b/LibreHardwareMonitorLib/Hardware/Battery/BatteryGroup.cs @@ -26,7 +26,7 @@ namespace LibreHardwareMonitor.Hardware.Battery IntPtr hdev = SetupApi.SetupDiGetClassDevs(ref SetupApi.GUID_DEVICE_BATTERY, IntPtr.Zero, IntPtr.Zero, SetupApi.DIGCF_PRESENT | SetupApi.DIGCF_DEVICEINTERFACE); if (hdev != SetupApi.INVALID_HANDLE_VALUE) { - for (uint i = 0;; i++) + for (uint i = 0; ; i++) { SetupApi.SP_DEVICE_INTERFACE_DATA did = default; did.cbSize = (uint)Marshal.SizeOf(typeof(SetupApi.SP_DEVICE_INTERFACE_DATA)); @@ -198,6 +198,11 @@ namespace LibreHardwareMonitor.Hardware.Battery .Append(" Charge Level: ").AppendFormat("{0:F2}", bat.RemainingCapacity * 100f / bat.FullChargedCapacity).AppendLine(" %") .Append(" Voltage: ").AppendFormat("{0:F3}", bat.Voltage).AppendLine(" V"); + if (bat.RemainingTime != Kernel32.BATTERY_UNKNOWN_TIME) + { + reportBuilder.Append(" Remaining Time (Estimated): ").AppendFormat("{0:g}", TimeSpan.FromSeconds(bat.RemainingTime)).AppendLine(); + } + switch (bat.ChargeDischargeRate) { case > 0: diff --git a/LibreHardwareMonitorLib/Interop/Kernel32.cs b/LibreHardwareMonitorLib/Interop/Kernel32.cs index 44c50ee..abb29f1 100644 --- a/LibreHardwareMonitorLib/Interop/Kernel32.cs +++ b/LibreHardwareMonitorLib/Interop/Kernel32.cs @@ -29,6 +29,8 @@ namespace LibreHardwareMonitor.Interop internal const uint LPTR = 0x0000 | 0x0040; + internal const uint BATTERY_UNKNOWN_TIME = 0xFFFFFFFF; + private const string DllName = "kernel32.dll"; [Flags] @@ -258,6 +260,19 @@ namespace LibreHardwareMonitor.Interop out uint lpBytesReturned, IntPtr lpOverlapped); + [DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool DeviceIoControl + ( + SafeFileHandle hDevice, + IOCTL dwIoControlCode, + ref BATTERY_QUERY_INFORMATION lpInBuffer, + int nInBufferSize, + ref uint lpOutBuffer, + int nOutBufferSize, + out uint lpBytesReturned, + IntPtr lpOverlapped); + [DllImport(DllName, CallingConvention = CallingConvention.Winapi)] internal static extern IntPtr LocalAlloc(uint uFlags, ulong uBytes); @@ -275,7 +290,7 @@ namespace LibreHardwareMonitor.Interop [DllImport(DllName, ExactSpelling = true)] internal static extern IntPtr GetProcAddress(IntPtr module, string methodName); - + [DllImport(DllName)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool FreeLibrary(IntPtr module); @@ -285,7 +300,7 @@ namespace LibreHardwareMonitor.Interop [DllImport(DllName, CallingConvention = CallingConvention.Winapi)] internal static extern IntPtr GetCurrentThread(); - + [DllImport(DllName, CallingConvention = CallingConvention.Winapi)] public static extern ushort GetActiveProcessorGroupCount(); @@ -303,9 +318,9 @@ namespace LibreHardwareMonitor.Interop ( SafeFileHandle device, IOControlCode ioControlCode, - [MarshalAs(UnmanagedType.AsAny)] [In] object inBuffer, + [MarshalAs(UnmanagedType.AsAny)][In] object inBuffer, uint inBufferSize, - [MarshalAs(UnmanagedType.AsAny)] [Out] object outBuffer, + [MarshalAs(UnmanagedType.AsAny)][Out] object outBuffer, uint nOutBufferSize, out uint bytesReturned, IntPtr overlapped);