From dc9792fc9f6bc4b8af240e23e7c5fc050e713913 Mon Sep 17 00:00:00 2001 From: Mathieu GRENET Date: Wed, 31 Jul 2024 17:25:54 +0200 Subject: [PATCH] Add NZXT kraken X (X42, X52, X62 or X72) Support (#1143) * Add NZXT kraken X (X42, X52, X62 or X72) Support * Cleanup and removed overriding hard values * code style * more code style * Clean up PR and Kraken V3 --------- Co-authored-by: PhyxionNL <7643972+PhyxionNL@users.noreply.github.com> --- .../Hardware/Controller/Nzxt/KrakenV2.cs | 186 ++++++++++++++++++ .../Hardware/Controller/Nzxt/KrakenV3.cs | 20 +- .../Hardware/Controller/Nzxt/NzxtGroup.cs | 11 ++ 3 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV2.cs diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV2.cs b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV2.cs new file mode 100644 index 0000000..66b091d --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV2.cs @@ -0,0 +1,186 @@ +using System; +using HidSharp; + +namespace LibreHardwareMonitor.Hardware.Controller.Nzxt; + +/// +/// Support for the Kraken X (X42, X52, X62 or X72) devices. +/// +internal sealed class KrakenV2 : Hardware +{ + private static readonly byte[] _getFirmwareInfo = [0x10, 0x01]; + + private readonly HidDevice _device; + private readonly Sensor _fan; + private readonly byte _fanChannel; + private readonly bool _fanControl; + private readonly Sensor _fanRpm; + private readonly TimeSpan _interval = TimeSpan.FromMilliseconds(5000); + private readonly Sensor _liquidTemperature; + private readonly Sensor _pump; + private readonly byte _pumpChannel; + private readonly Sensor _pumpRpm; + private readonly byte[] _rawData = new byte[64]; + private readonly string _supportedFirmware; + + private DateTime _lastUpdate = DateTime.MinValue; + + public KrakenV2(HidDevice dev, ISettings settings) : base("Nzxt Kraken X", new Identifier("nzxt", "krakenx", dev.GetSerialNumber().TrimStart('0')), settings) + { + _device = dev; + + switch (dev.ProductID) + { + case 0x170e: + default: + Name = "NZXT Kraken X"; + + _fanControl = true; + + _fanChannel = 0x00; + _pumpChannel = 0x40; + + _supportedFirmware = "6.2.0"; + break; + } + + try + { + using HidStream stream = dev.Open(); + + stream.Write(_getFirmwareInfo); + + int tries = 0; + + while (FirmwareVersion == null && tries++ < 10) + { + stream.Read(_rawData); + + if (_rawData[0] == 0x11 && _rawData[1] == 0x01) + FirmwareVersion = $"{_rawData[0x11]}.{_rawData[0x12]}.{_rawData[0x13]}"; + } + + if (FirmwareVersion == null) + return; + + // Liquid temperature + _liquidTemperature = new Sensor("Liquid", 0, SensorType.Temperature, this, [], settings); + ActivateSensor(_liquidTemperature); + + // Pump Control + _pump = new Sensor("Pump Control", 0, SensorType.Control, this, [], settings); + Control pumpControl = new(_pump, settings, 60, 100); + _pump.Control = pumpControl; + pumpControl.ControlModeChanged += c => ControlValueChanged(_pump, c); + pumpControl.SoftwareControlValueChanged += c => ControlValueChanged(_pump, c); + ControlValueChanged(_pump, pumpControl); + ActivateSensor(_pump); + + // Pump RPM + _pumpRpm = new Sensor("Pump", 0, SensorType.Fan, this, [], settings); + ActivateSensor(_pumpRpm); + + if (_fanControl) + { + // Fan Control + _fan = new Sensor("Fans Control", 1, SensorType.Control, this, [], settings); + Control fanControl = new(_fan, settings, 0, 100); + _fan.Control = fanControl; + fanControl.ControlModeChanged += c => ControlValueChanged(_fan, c); + fanControl.SoftwareControlValueChanged += c => ControlValueChanged(_fan, c); + ControlValueChanged(_fan, fanControl); + ActivateSensor(_fan); + + // Fan RPM + _fanRpm = new Sensor("Fans", 1, SensorType.Fan, this, [], settings); + ActivateSensor(_fanRpm); + } + + IsValid = true; + } + catch + { } + } + + public string FirmwareVersion { get; } + + public override HardwareType HardwareType => HardwareType.Cooler; + + public bool IsValid { get; } + + public string Status => FirmwareVersion != _supportedFirmware ? $"Status: Untested firmware version {FirmwareVersion}! Please consider updating to version {_supportedFirmware}" : "Status: OK"; + + private void ControlValueChanged(Sensor sensor, IControl control) + { + try + { + if (control.ControlMode == ControlMode.Software) + { + //value will be updated at next Update() + sensor.Value = control.SoftwareValue; + _lastUpdate = DateTime.MinValue; + Update(); + } + else + { + //will let the device handle the value + sensor.Value = null; + } + } + catch (ObjectDisposedException) + { + // Could be unplugged, or the app is stopping... + } + } + + public override void Update() + { + try + { + using HidStream stream = _device.Open(); + + stream.Read(_rawData); + + // if not 0x04, it is not temperature data + if (_rawData[0] != 0x04) + return; + + // some packet may have 0 as temperature, don't know why just ignore it + if (_rawData[1] == 0x00) + return; + + _liquidTemperature.Value = _rawData[1] + (_rawData[2] / 10.0f); + _fanRpm.Value = (_rawData[3] << 8) | _rawData[4]; + _pumpRpm.Value = (_rawData[5] << 8) | _rawData[6]; + + // if we don't have control over the fan or pump, we don't need to update + if (!_pump.Value.HasValue && (!_fanControl || !_fan.Value.HasValue)) + return; + + //control value need to be updated every 5 seconds or it falls back to default + if (DateTime.Now - _lastUpdate < _interval) + return; + + if (_fanControl && _fan.Value.HasValue) + SetDuty(stream, _fanChannel, (byte)_fan.Value); + + if (_fanControl && _pump.Value.HasValue) + SetDuty(stream, _pumpChannel, (byte)_pump.Value); + } + catch (ObjectDisposedException) + { + // Could be unplugged, or the app is stopping... + } + } + + private void SetDuty(HidStream stream, byte channel, byte duty) + { + SetDuty(stream, channel, 0, duty); + } + + private void SetDuty(HidStream stream, byte channel, byte temperature, byte duty) + { + stream.Write([0x2, 0x4d, channel, temperature, duty]); + _lastUpdate = DateTime.Now; + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV3.cs b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV3.cs index 6007de1..4ebc9d1 100644 --- a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV3.cs +++ b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenV3.cs @@ -3,15 +3,15 @@ using HidSharp; namespace LibreHardwareMonitor.Hardware.Controller.Nzxt; -/** - * Support for the KrakenZ devices from NZXT - */ +/// +/// Support for the KrakenZ devices. +/// internal sealed class KrakenV3 : Hardware { - private static readonly byte[] _getFirmwareInfo = { 0x10, 0x01 }; + private static readonly byte[] _getFirmwareInfo = [0x10, 0x01]; private static readonly byte[] _setFanTarget = new byte[64]; private static readonly byte[] _setPumpTarget = new byte[64]; - private static readonly byte[] _statusRequest = { 0x74, 0x01 }; + private static readonly byte[] _statusRequest = [0x74, 0x01]; private readonly Sensor _fan; private readonly bool _fanControl; @@ -81,11 +81,11 @@ internal sealed class KrakenV3 : Hardware return; // Liquid temperature - _temperature = new Sensor("Liquid", 0, SensorType.Temperature, this, Array.Empty(), settings); + _temperature = new Sensor("Liquid", 0, SensorType.Temperature, this, [], settings); ActivateSensor(_temperature); // Pump Control - _pump = new Sensor("Pump Control", 0, SensorType.Control, this, Array.Empty(), settings); + _pump = new Sensor("Pump Control", 0, SensorType.Control, this, [], settings); Control pumpControl = new(_pump, settings, 20, 100); _pump.Control = pumpControl; pumpControl.ControlModeChanged += PumpSoftwareControlValueChanged; @@ -94,13 +94,13 @@ internal sealed class KrakenV3 : Hardware ActivateSensor(_pump); // Pump RPM - _pumpRpm = new Sensor("Pump", 0, SensorType.Fan, this, Array.Empty(), settings); + _pumpRpm = new Sensor("Pump", 0, SensorType.Fan, this, [], settings); ActivateSensor(_pumpRpm); if (_fanControl) { // Fan Control - _fan = new Sensor("Fans Control", 1, SensorType.Control, this, Array.Empty(), settings); + _fan = new Sensor("Fans Control", 1, SensorType.Control, this, [], settings); Control fanControl = new(_fan, settings, 20, 100); _fan.Control = fanControl; fanControl.ControlModeChanged += FanSoftwareControlValueChanged; @@ -109,7 +109,7 @@ internal sealed class KrakenV3 : Hardware ActivateSensor(_fan); // Fan RPM - _fanRpm = new Sensor("Fans", 1, SensorType.Fan, this, Array.Empty(), settings); + _fanRpm = new Sensor("Fans", 1, SensorType.Fan, this, [], settings); ActivateSensor(_fanRpm); } diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs index 267d168..379993b 100644 --- a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs +++ b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs @@ -25,6 +25,17 @@ internal class NzxtGroup : IGroup switch (dev.ProductID) { + case 0x170E: // NZXT Kraken X (X42, X52, X62 or X72) + var krakenV2 = new KrakenV2(dev, settings); + _report.AppendLine($"Device name: {productName}"); + _report.AppendLine($"Firmware version: {krakenV2.FirmwareVersion}"); + _report.AppendLine($"{krakenV2.Status}"); + _report.AppendLine(); + + if (krakenV2.IsValid) + _hardware.Add(krakenV2); + + break; case 0x2007: // Kraken X3 original pid case 0x2014: // Kraken X3 new pid case 0x3008: // Kraken Z3