From 1ebc639e741c30372d70d6dbcbe33d31d2b0bac1 Mon Sep 17 00:00:00 2001
From: NeoAcheron <12508986+NeoAcheron@users.noreply.github.com>
Date: Tue, 2 Jun 2020 13:14:55 +0200
Subject: [PATCH] Added support for NZXT Kraken X3 devices. (#248)
Thanks to @NeoAcheron
---
.../LibreHardwareMonitor.csproj | 4 -
LibreHardwareMonitor/Resources/acicon.png | Bin 904 -> 0 bytes
LibreHardwareMonitor/Resources/bigng.png | Bin 702 -> 0 bytes
LibreHardwareMonitor/UI/HardwareTypeImage.cs | 15 +-
LibreHardwareMonitor/Utilities/HttpServer.cs | 10 +-
LibreHardwareMonitorLib/Hardware/Computer.cs | 4 +
.../Hardware/Controller/AeroCool/P7-H1.cs | 2 +-
.../Controller/AquaComputer/AquastreamXT.cs | 2 +-
.../Hardware/Controller/AquaComputer/MPS.cs | 2 +-
.../Controller/Heatmaster/Heatmaster.cs | 2 +-
.../Hardware/Controller/Nzxt/KrakenX3.cs | 159 ++++++++++++++++++
.../Hardware/Controller/Nzxt/NzxtGroup.cs | 70 ++++++++
.../Controller/TBalancer/TBalancer.cs | 2 +-
LibreHardwareMonitorLib/Hardware/IHardware.cs | 5 +-
.../LibreHardwareMonitorLib.csproj | 2 +
15 files changed, 246 insertions(+), 33 deletions(-)
delete mode 100644 LibreHardwareMonitor/Resources/acicon.png
delete mode 100644 LibreHardwareMonitor/Resources/bigng.png
create mode 100644 LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenX3.cs
create mode 100644 LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs
diff --git a/LibreHardwareMonitor/LibreHardwareMonitor.csproj b/LibreHardwareMonitor/LibreHardwareMonitor.csproj
index f2dd072..25a9fce 100644
--- a/LibreHardwareMonitor/LibreHardwareMonitor.csproj
+++ b/LibreHardwareMonitor/LibreHardwareMonitor.csproj
@@ -169,7 +169,6 @@
PortForm.cs
-
@@ -303,9 +302,6 @@
-
-
-
diff --git a/LibreHardwareMonitor/Resources/acicon.png b/LibreHardwareMonitor/Resources/acicon.png
deleted file mode 100644
index 26ae763be72d3a20157a5fa42ea181b302acd363..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 904
zcmV;319$w1P)ogJyS@Mb0~JX`
zK~y-)eUfcxRdpQ4KfnJu_niBD$JWeTW>>S?3aro>`obr2B{_tO0-=Y!NSI}?B9h6V
z7mbi82SZ0tSg(pQGR;;PVFb3!4aIP_+U~N4m2EYB&-)MjejmPa
zUB_!X=DYd@m98Cjs9GMbidBmz&!rm!KUupzZpplIXaY;5;A`_i&QS*@3sb{%-@y%U
z{D7=@cJGDp`rEH+DvfX^dn;Nj64fG&rE~sN=yc`Rv>rHl{`cx1zG(=J*^XgrRdf9A
z>*Jdb-h0hOS=s&G7wNFL`qR1%*rtj?qeUUrfCm33&ZqKNq7fJs*sX+PAg^j;Qu&O%
z!M4SuhLX!@j)pKg@l34)FBaeHR22BHrWWDeCOTRT$4LYGF9R2
z?BlqxfQk<$ssO@V1n>gA3Ba0d?d%$=Hf1IYsboWDg3#EUom$MEZ?9@PImN)17zc?|f{~Ela&Ax+Q@pFSS(-aO((GFF|3`3u6<5es<`{sQ
z+?FrcN?xi&)?WEC>Tnew^FIj8x{57eJ_AqyUVDCL!j>Kbp^vM3$B=%+dEosiENB84=ZTaNK6})Ycb(RroTR}_D=`V
eDX)Er5AY`;icWfw&Zb2G0000Px#24YJ`L;x57FaROJ(US52000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igG%
z5dk<8_CX>@2HM@dakSAh-}0006RNkl@3%2^<}xdnV=Hk(jmpa065Am<#15{ce+DIFL9q^9ItaTZv`YlR
zckGgO@*reV9U_PfO;|w;(Prf$X6+OIkWAY5zHf(5caYh0c;S7X_j#YoBaS3405c;o
zG9v*oBQX)1#K=qpNQ4ZCgbYXu7*ti6l+rhuuXR?r_tnR)-B>V;GsAHl`huMv0FVII
z{F_?Tm`Ew9Y_@ENE7>AOoU;pt0RTd3w|lO_1{y0p@s3_V(%2-SNlhxL>@YARm5QFd
z9PR4XgwR*VK!~EJ|J`FE(wOMsiCfLpUQKHD>j#AppfN}dKnUQ<(G}mg4FGo7w+)=T
z2PTU{@o2dD*xc;g`LOiCo02oQ`Y_0F_zLWcwb^YRiKTxx~#_RQzIV(b;;Obib
zXnU)p+<_5yLwVh$vh8>7P5t@4JepRFJk*saYLA}W
z-*o*oJAZml-OG();
RemoveType();
RemoveType();
+ RemoveType();
}
}
@@ -387,6 +390,7 @@ namespace LibreHardwareMonitor.Hardware
Add(new HeatmasterGroup(_settings));
Add(new AquaComputerGroup(_settings));
Add(new AeroCoolGroup(_settings));
+ Add(new NzxtGroup(_settings));
}
if (_storageEnabled)
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/AeroCool/P7-H1.cs b/LibreHardwareMonitorLib/Hardware/Controller/AeroCool/P7-H1.cs
index 5a49195..57cb955 100644
--- a/LibreHardwareMonitorLib/Hardware/Controller/AeroCool/P7-H1.cs
+++ b/LibreHardwareMonitorLib/Hardware/Controller/AeroCool/P7-H1.cs
@@ -41,7 +41,7 @@ namespace LibreHardwareMonitor.Hardware.Controller.AeroCool
public override HardwareType HardwareType
{
- get { return HardwareType.AeroCool; }
+ get { return HardwareType.Cooler; }
}
public int HubNumber { get; }
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/AquastreamXT.cs b/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/AquastreamXT.cs
index 94b5a75..c79d9bb 100644
--- a/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/AquastreamXT.cs
+++ b/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/AquastreamXT.cs
@@ -79,7 +79,7 @@ namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer
public override HardwareType HardwareType
{
- get { return HardwareType.AquaComputer; }
+ get { return HardwareType.Cooler; }
}
public string Status
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/MPS.cs b/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/MPS.cs
index 0d5b0b5..e76f007 100644
--- a/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/MPS.cs
+++ b/LibreHardwareMonitorLib/Hardware/Controller/AquaComputer/MPS.cs
@@ -47,7 +47,7 @@ namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer
public override HardwareType HardwareType
{
- get { return HardwareType.AquaComputer; }
+ get { return HardwareType.Cooler; }
}
public string Status
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Heatmaster/Heatmaster.cs b/LibreHardwareMonitorLib/Hardware/Controller/Heatmaster/Heatmaster.cs
index 15727b5..a575660 100644
--- a/LibreHardwareMonitorLib/Hardware/Controller/Heatmaster/Heatmaster.cs
+++ b/LibreHardwareMonitorLib/Hardware/Controller/Heatmaster/Heatmaster.cs
@@ -102,7 +102,7 @@ namespace LibreHardwareMonitor.Hardware.Controller.Heatmaster
public override HardwareType HardwareType
{
- get { return HardwareType.Heatmaster; }
+ get { return HardwareType.Cooler; }
}
public void Dispose()
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenX3.cs b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenX3.cs
new file mode 100644
index 0000000..2711ea9
--- /dev/null
+++ b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/KrakenX3.cs
@@ -0,0 +1,159 @@
+using HidSharp;
+using System;
+using System.Linq;
+using System.Threading;
+
+namespace LibreHardwareMonitor.Hardware.Controller.Nzxt
+{
+ /**
+ * Support for the Kraken X3 devices from NZXT
+ */
+ internal sealed class KrakenX3 : Hardware
+ {
+ // Some fixed messages to send to the pump for basic monitoring and control
+ private static readonly byte[] _getFirmareInfo = { 0x10, 0x01 };
+ private static readonly byte[] _initialize1 = { 0x70, 0x02, 0x01, 0xb8, 0x0b };
+ private static readonly byte[] _initialize2 = { 0x70, 0x01 };
+ private static readonly byte[][] _setPumpTargetMap = new byte[101][]; // Sacrifice memory to speed this up with a lookup instead of a copy operation
+
+ static KrakenX3()
+ {
+ byte[] set_pump_speed_header = { 0x72, 0x01, 0x00, 0x00 };
+
+ for (byte speed = 0; speed < _setPumpTargetMap.Length; speed++)
+ {
+ _setPumpTargetMap[speed] = set_pump_speed_header.Concat(Enumerable.Repeat(speed, 40).Concat(new byte[20])).ToArray();
+ }
+ }
+
+ private volatile bool _controlling = false;
+ private readonly Sensor _pump;
+ private readonly Control _pumpControl;
+ private readonly Sensor _pumpRpm;
+ private readonly byte[] _rawData = new byte[64];
+ private readonly HidStream _stream;
+ private readonly Sensor _temperature;
+
+ public string FirmwareVersion { get; private set; }
+
+ public KrakenX3(HidDevice dev, ISettings settings) : base("Nzxt Kraken X3", new Identifier("nzxt", "krakenx3", dev.GetSerialNumber().TrimStart('0')), settings)
+ {
+ if (dev.TryOpen(out _stream))
+ {
+ _stream.ReadTimeout = 5000; // The NZXT device returns with data that we need periodically without writing...
+ _stream.Write(_initialize1);
+ _stream.Write(_initialize2);
+
+ _stream.Write(_getFirmareInfo);
+ do
+ {
+ _stream.Read(_rawData);
+ if (_rawData[0] == 0x11 && _rawData[1] == 0x01)
+ {
+ FirmwareVersion = $"{_rawData[0x11]}.{_rawData[0x11]}.{_rawData[0x13]}";
+ }
+ } while (FirmwareVersion == null);
+
+ Name = $"Nzxt Kraken X3";
+
+ _pump = new Sensor("Pump Control", 0, SensorType.Control, this, new ParameterDescription[0], settings);
+ _pumpControl = new Control(_pump, settings, 0, 100);
+ _pump.Control = _pumpControl;
+ _pumpControl.ControlModeChanged += SoftwareControlValueChanged;
+ _pumpControl.SoftwareControlValueChanged += SoftwareControlValueChanged;
+ SoftwareControlValueChanged(_pumpControl);
+ ActivateSensor(_pump);
+
+ _pumpRpm = new Sensor("Pump", 0, SensorType.Fan, this, new ParameterDescription[0], settings);
+ ActivateSensor(_pumpRpm);
+
+ _temperature = new Sensor("Internal Water", 0, SensorType.Temperature, this, new ParameterDescription[0], settings);
+ ActivateSensor(_temperature);
+
+ ThreadPool.UnsafeQueueUserWorkItem(ContinuousRead, _rawData);
+ }
+ }
+
+ private void SoftwareControlValueChanged(Control control)
+ {
+ if (control.ControlMode == ControlMode.Software)
+ {
+ float value = control.SoftwareValue;
+ byte pump_speed_index = (byte)((value > 100) ? 100 : (value < 0) ? 0 : value); // Clamp the value, anything out of range will fail
+
+ _controlling = true;
+ _stream.Write(_setPumpTargetMap[pump_speed_index]);
+ _pump.Value = value;
+ }
+ else if (control.ControlMode == ControlMode.Default)
+ { // There isn't a "default" mode with this pump, but a safe setting is 40%
+ _stream.Write(_setPumpTargetMap[40]);
+ }
+ }
+
+ public override HardwareType HardwareType => HardwareType.Cooler;
+
+ public string Status => (FirmwareVersion != "2.1.0" ? $"Status: Untested Firmware Version {FirmwareVersion}! Please consider Updating to Version 2.1.0" : "Status: OK");
+
+ public override void Close()
+ {
+ base.Close();
+ _stream.Close();
+ }
+
+ private void ContinuousRead(object state)
+ {
+ byte[] buffer = new byte[_rawData.Length];
+ while (_stream.CanRead)
+ {
+ try
+ {
+ _stream.Read(buffer); // This is a blocking call, will wait for bytes to become available
+
+ lock (_rawData)
+ {
+ Array.Copy(buffer, _rawData, buffer.Length);
+ }
+ }
+ catch (TimeoutException ex)
+ {
+ // Don't care, just make sure the stream is still open
+ Thread.Sleep(500);
+ }
+ catch (ObjectDisposedException ex)
+ {
+ // Could be unplugged, or the app is stopping...
+ }
+ }
+ }
+
+ public override void Update()
+ {
+ // The NZXT Kraken X3 series sends updates periodically. We have to read it in a seperate thread, this call just reads that data.
+ lock (_rawData)
+ {
+
+ if (_rawData[0] == 0x75 && _rawData[1] == 0x02)
+ {
+ _temperature.Value = _rawData[15] + _rawData[16] / 10.0f;
+ _pumpRpm.Value = (_rawData[18] << 8) | _rawData[17];
+
+ // The following logic makes sure the pump is set to the controlling value. This pump sometimes sets itself to 0% when instructed to a value.
+ if (!_controlling)
+ {
+ _pump.Value = _rawData[19];
+ }
+ else if (_pump.Value != _rawData[19])
+ {
+ byte pump_speed_index = (byte)_pump.Value;
+ _stream.Write(_setPumpTargetMap[pump_speed_index]);
+ }
+ else
+ {
+ _controlling = false;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs
new file mode 100644
index 0000000..5c9eec5
--- /dev/null
+++ b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs
@@ -0,0 +1,70 @@
+// 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) LibreHardwareMonitor and Contributors.
+// All Rights Reserved.
+
+using System.Collections.Generic;
+using System.Text;
+using HidSharp;
+
+namespace LibreHardwareMonitor.Hardware.Controller.Nzxt
+{
+ internal class NzxtGroup : IGroup
+ {
+ private readonly List _hardware = new List();
+ private readonly StringBuilder _report = new StringBuilder();
+
+ public NzxtGroup(ISettings settings)
+ {
+ _report.AppendLine("Nzxt Hardware");
+ _report.AppendLine();
+
+ foreach (HidDevice dev in DeviceList.Local.GetHidDevices(0x1e71))
+ {
+ string productName = dev.GetProductName();
+
+ switch (dev.ProductID)
+ {
+ case 0x2007:
+ {
+ var device = new KrakenX3(dev, settings);
+ _report.AppendLine($"Device name: {productName}");
+ _report.AppendLine($"Firmware version: {device.FirmwareVersion}");
+ _report.AppendLine($"{device.Status}");
+ _report.AppendLine();
+ _hardware.Add(device);
+ break;
+ }
+ default:
+ {
+ _report.AppendLine($"Unknown Hardware PID: {dev.ProductID} Name: {productName}");
+ _report.AppendLine();
+ break;
+ }
+ }
+ }
+
+ if (_hardware.Count == 0)
+ {
+ _report.AppendLine("No Nzxt Hardware found.");
+ _report.AppendLine();
+ }
+ }
+
+ public IEnumerable Hardware => _hardware;
+
+ public void Close()
+ {
+ foreach (IHardware iHardware in _hardware)
+ {
+ if (iHardware is Hardware hardware)
+ hardware.Close();
+ }
+ }
+
+ public string GetReport()
+ {
+ return _report.ToString();
+ }
+ }
+}
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/TBalancer/TBalancer.cs b/LibreHardwareMonitorLib/Hardware/Controller/TBalancer/TBalancer.cs
index aff562d..c3d306f 100644
--- a/LibreHardwareMonitorLib/Hardware/Controller/TBalancer/TBalancer.cs
+++ b/LibreHardwareMonitorLib/Hardware/Controller/TBalancer/TBalancer.cs
@@ -90,7 +90,7 @@ namespace LibreHardwareMonitor.Hardware.Controller.TBalancer
public override HardwareType HardwareType
{
- get { return HardwareType.TBalancer; }
+ get { return HardwareType.Cooler; }
}
protected override void ActivateSensor(ISensor sensor)
diff --git a/LibreHardwareMonitorLib/Hardware/IHardware.cs b/LibreHardwareMonitorLib/Hardware/IHardware.cs
index 2ab1325..49d1af5 100644
--- a/LibreHardwareMonitorLib/Hardware/IHardware.cs
+++ b/LibreHardwareMonitorLib/Hardware/IHardware.cs
@@ -12,16 +12,13 @@ namespace LibreHardwareMonitor.Hardware
{
Motherboard,
SuperIO,
- AquaComputer,
Cpu,
Memory,
GpuNvidia,
GpuAmd,
- TBalancer,
- Heatmaster,
Storage,
Network,
- AeroCool
+ Cooler
}
public interface IHardware : IElement
diff --git a/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj b/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj
index 877f8a7..ea8f96f 100644
--- a/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj
+++ b/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj
@@ -43,6 +43,8 @@
+
+