This commit is contained in:
PhyxionNL
2025-10-01 10:42:02 +02:00
4 changed files with 248 additions and 0 deletions
@@ -22,6 +22,7 @@ using LibreHardwareMonitor.Hardware.Memory;
using LibreHardwareMonitor.Hardware.Motherboard;
using LibreHardwareMonitor.Hardware.Network;
using LibreHardwareMonitor.Hardware.Psu.Corsair;
using LibreHardwareMonitor.Hardware.Psu.Msi;
using LibreHardwareMonitor.Hardware.Storage;
namespace LibreHardwareMonitor.Hardware;
@@ -250,10 +251,12 @@ public class Computer : IComputer
if (value)
{
Add(new CorsairPsuGroup(_settings));
Add(new MsiPsuGroup(_settings));
}
else
{
RemoveType<CorsairPsuGroup>();
RemoveType<MsiPsuGroup>();
}
}
@@ -524,7 +527,10 @@ public class Computer : IComputer
Add(new NetworkGroup(_settings));
if (_psuEnabled)
{
Add(new CorsairPsuGroup(_settings));
Add(new MsiPsuGroup(_settings));
}
if (_batteryEnabled)
Add(new BatteryGroup(_settings));
@@ -0,0 +1,75 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) 2024 demorfi<demorfi@gmail.com>
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.
using System.Collections.Generic;
using System.Globalization;
using HidSharp;
namespace LibreHardwareMonitor.Hardware.Psu.Msi;
internal sealed class MsiPsu : Hardware
{
private readonly HidDevice _device;
private readonly List<PsuSensor> _sensors = [];
public MsiPsu(HidDevice device, ISettings settings, int index)
: base("MSI PSU", new Identifier("psu", "msi", index.ToString()), settings)
{
_device = device;
using HidStream stream = device.Open();
UsbApi.FirmwareInfo fwInfo = UsbApi.FwInfo(stream);
Name = $"{fwInfo.Vendor} {fwInfo.Product}";
AddSensors(settings);
}
public override HardwareType HardwareType => HardwareType.Psu;
public override void Update()
{
using HidStream stream = _device.Open();
float[] info = UsbApi.InfoList(stream);
_sensors.ForEach(s => s.Update(info));
}
private void AddSensors(ISettings settings)
{
int sensorIndex = 0;
_sensors.Add(new PsuSensor("Case", sensorIndex++, SensorType.Fan, this, settings, UsbApi.IndexInfo.FAN_RPM));
_sensors.Add(new PsuSensor("Case", sensorIndex++, SensorType.Temperature, this, settings, UsbApi.IndexInfo.TEMP));
_sensors.Add(new PsuSensor("+12V", sensorIndex++, SensorType.Voltage, this, settings, UsbApi.IndexInfo.VOLTS_12));
_sensors.Add(new PsuSensor("+12V", sensorIndex++, SensorType.Current, this, settings, UsbApi.IndexInfo.AMPS_12));
_sensors.Add(new PsuSensor("+5V", sensorIndex++, SensorType.Voltage, this, settings, UsbApi.IndexInfo.VOLTS_5));
_sensors.Add(new PsuSensor("+5V", sensorIndex++, SensorType.Current, this, settings, UsbApi.IndexInfo.AMPS_5));
_sensors.Add(new PsuSensor("+3.3V", sensorIndex++, SensorType.Voltage, this, settings, UsbApi.IndexInfo.VOLTS_3V3));
_sensors.Add(new PsuSensor("+3.3V", sensorIndex++, SensorType.Current, this, settings, UsbApi.IndexInfo.AMPS_3V3));
_sensors.Add(new PsuSensor("PSU Efficiency", sensorIndex++, SensorType.Level, this, settings, UsbApi.IndexInfo.EFFICIENCY));
_sensors.Add(new PsuSensor("PSU Out", sensorIndex++, SensorType.Power, this, settings, UsbApi.IndexInfo.PSU_OUT));
_sensors.Add(new PsuSensor("Total Runtime", sensorIndex++, SensorType.TimeSpan, this, settings, UsbApi.IndexInfo.RUNTIME, true));
}
private class PsuSensor : Sensor
{
private readonly UsbApi.IndexInfo _indexInfo;
public PsuSensor(string name, int index, SensorType type, MsiPsu hardware, ISettings settings, UsbApi.IndexInfo indexInfo, bool noHistory = false)
: base(name, index, false, type, hardware, null, settings, noHistory)
{
_indexInfo = indexInfo;
hardware.ActivateSensor(this);
}
public void Update(float[] info)
{
Value = info[(int)_indexInfo];
}
}
}
@@ -0,0 +1,64 @@
// This Source Code Form is subject to the terms of the GNU Public License, v. 2.0.
// Copyright (C) 2024 demorfi<demorfi@gmail.com>
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HidSharp;
namespace LibreHardwareMonitor.Hardware.Psu.Msi;
public class MsiPsuGroup : IGroup
{
private static readonly int[] _productIds =
{
0x56d4, // MEG Ai1300P
};
private static readonly ushort _vendorId = 0x0db0;
private readonly List<IHardware> _hardware;
private readonly StringBuilder _report;
public MsiPsuGroup(ISettings settings)
{
_report = new StringBuilder();
_report.AppendLine("MSI Ai series PSU Hardware");
_report.AppendLine();
_hardware = new List<IHardware>();
foreach (HidDevice dev in DeviceList.Local.GetHidDevices(_vendorId))
{
if (_productIds.Contains(dev.ProductID))
{
var device = new MsiPsu(dev, settings, _hardware.Count);
_hardware.Add(device);
_report.AppendLine($"Device name: {device.Name}");
_report.AppendLine();
}
}
if (_hardware.Count == 0)
{
_report.AppendLine("No MSI PSU Hardware found.");
_report.AppendLine();
}
}
public IReadOnlyList<IHardware> Hardware => _hardware;
public void Close()
{
foreach (IHardware iHardware in _hardware)
{
if (iHardware is Hardware hardware)
hardware.Close();
}
}
public string GetReport()
{
return _report.ToString();
}
}
@@ -0,0 +1,103 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) 2024 demorfi<demorfi@gmail.com>
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.
using System;
using System.Linq;
using System.Text;
using HidSharp;
namespace LibreHardwareMonitor.Hardware.Psu.Msi;
internal static class UsbApi
{
public enum IndexInfo : int
{
VOLTS_12V1 = 0,
VOLTS_12V2 = 2,
VOLTS_12V3 = 4,
VOLTS_12V4 = 6,
VOLTS_12V5 = 8,
VOLTS_12 = 10,
VOLTS_5 = 12,
VOLTS_3V3 = 14,
AMPS_12V1 = 1,
AMPS_12V2 = 3,
AMPS_12V3 = 5,
AMPS_12V4 = 7,
AMPS_12V5 = 9,
AMPS_12 = 11,
AMPS_5 = 13,
AMPS_3V3 = 15,
PSU_OUT = 16,
EFFICIENCY = 17,
TEMP = 18,
FAN_RPM = 19,
RUNTIME = 20
}
public static FirmwareInfo FwInfo(HidStream stream)
{
if (!Request(stream, new byte[2] { 0xFA, 0x51 }, out byte[] productArr, 1))
throw new ProtocolError(stream.Device, "Can't read product name");
static string ArrayToString(byte[] ar)
{
return Encoding.ASCII.GetString(ar.TakeWhile(x => x != 0).ToArray());
}
return new FirmwareInfo { Vendor = "MSI", Product = ArrayToString(productArr) };
}
private static float Linear11ToFloat32(ushort val)
{
int exp = (short)val >> 11;
int mant = ((short)(val & 0x7ff) << 5) >> 5;
return mant * (float)Math.Pow(2, exp);
}
private static bool Request(HidStream stream, byte[] command, out byte[] response, int offset = 0)
{
byte[] buffer = new byte[64];
Array.Copy(command, 0, buffer, 1, 2);
stream.Write(buffer);
byte[] reply = stream.Read();
response = new byte[42];
Array.Copy(reply, 2 + offset, response, 0, 42);
return reply[0] == buffer[0] && reply[1] == buffer[1];
}
public static float[] InfoList(HidStream stream)
{
int length = Enum.GetNames(typeof(IndexInfo)).Length;
float[] info = new float[length];
if (!Request(stream, new byte[2] { 0x51, 0xE0 }, out byte[] basic))
throw new ProtocolError(stream.Device, "Can't read basic info");
// basic has information only about the first 20 sensors
for (int i = 0; i < 20; i++)
{
byte[] replyData = new byte[4];
Array.Copy(basic, (i * 2) + 1, replyData, 0, 2);
info[i] = Linear11ToFloat32((ushort)BitConverter.ToInt32(replyData, 0));
}
// runtime info
Request(stream, new byte[2] { 0x51, 0xD1 }, out byte[] runtime);
info[(int)IndexInfo.RUNTIME] = BitConverter.ToInt32(runtime, 0) / 100;
return info;
}
public struct FirmwareInfo
{
public string Vendor;
public string Product;
}
}