diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/GridV3.cs b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/GridV3.cs
new file mode 100644
index 0000000..57782d8
--- /dev/null
+++ b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/GridV3.cs
@@ -0,0 +1,184 @@
+// 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;
+using System.Collections.Generic;
+using System.Threading;
+using HidSharp;
+
+namespace LibreHardwareMonitor.Hardware.Controller.Nzxt
+{
+ ///
+ /// Support for the NZXT GRID+ V3 devices.
+ ///
+ internal sealed class GridV3 : Hardware
+ {
+ // Some initialization messages to send to the controller. No visible effects but NZXT CAM send them.
+ private static readonly byte[] _initialize1 = { 0x01, 0x5c };
+ private static readonly byte[] _initialize2 = { 0x01, 0x5d };
+ private static readonly byte[] _initialize3 = { 0x01, 0x59 };
+
+ private const int FANS_COUNT = 6;
+
+ private readonly byte[] _setFanSpeedMsg;
+ private readonly HidStream _stream;
+ private readonly Dictionary _rawData = new Dictionary();
+
+ private readonly Sensor _noise;
+ private readonly Sensor[] _currents = new Sensor[FANS_COUNT];
+ private readonly Sensor[] _powers = new Sensor[FANS_COUNT];
+ private readonly Sensor[] _pwmControls = new Sensor[FANS_COUNT];
+ private readonly Sensor[] _rpmSensors = new Sensor[FANS_COUNT];
+ private readonly Sensor[] _voltages = new Sensor[FANS_COUNT];
+
+ private readonly Control[] _fanControls = new Control[FANS_COUNT];
+
+ public GridV3(HidDevice dev, ISettings settings) : base("NZXT GRID+ V3", new Identifier("nzxt", "gridv3", dev.GetSerialNumber().TrimStart('0')), settings)
+ {
+ if (dev.TryOpen(out _stream))
+ {
+ for (int fanID = 0; fanID < FANS_COUNT; fanID++)
+ {
+ _rawData[fanID] = new byte[21];
+ }
+
+ _setFanSpeedMsg = new byte[65];
+ _setFanSpeedMsg[0] = 0x02;
+ _setFanSpeedMsg[1] = 0x4d;
+ _setFanSpeedMsg[3] = 0x00;
+
+ _stream.Write(_initialize1);
+ _stream.Write(_initialize2);
+ _stream.Write(_initialize3);
+
+ do
+ {
+ _stream.Read(_rawData[0]);
+ if (_rawData[0][0] == 0x04)
+ {
+ FirmwareVersion = $"{_rawData[0][11]}.{_rawData[0][14]}";
+ }
+ }
+ while (FirmwareVersion == null);
+
+ Name = "NZXT GRID+ V3";
+
+ // Initialize all sensors and controls for all fans
+ for (int i = 0; i < FANS_COUNT; i++)
+ {
+ _rpmSensors[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Fan, this, Array.Empty(), settings);
+ _voltages[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Voltage, this, Array.Empty(), settings);
+ _currents[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Current, this, Array.Empty(), settings);
+ _powers[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Power, this, Array.Empty(), settings);
+ _pwmControls[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Control, this, Array.Empty(), settings);
+
+ _fanControls[i] = new Control(_pwmControls[i], settings, 0, 100);
+
+ _pwmControls[i].Control = _fanControls[i];
+ _fanControls[i].ControlModeChanged += SoftwareControlValueChanged;
+ _fanControls[i].SoftwareControlValueChanged += SoftwareControlValueChanged;
+ SoftwareControlValueChanged(_fanControls[i]);
+
+ ActivateSensor(_rpmSensors[i]);
+ ActivateSensor(_voltages[i]);
+ ActivateSensor(_currents[i]);
+ ActivateSensor(_powers[i]);
+ ActivateSensor(_pwmControls[i]);
+
+ // NZXT GRID does not report current PWM value. So we need to initialize it with some value to keep GUI and device values in sync.
+ _fanControls[i].SetDefault();
+ }
+ _noise = new Sensor($"GRID Noise", 0, SensorType.Noise, this, Array.Empty(), settings);
+ ActivateSensor(_noise);
+
+ Thread readGridReports = new Thread(ContinuousRead) { IsBackground = true };
+ readGridReports.Start(_rawData);
+ }
+ }
+
+ public string FirmwareVersion { get; }
+
+ public override HardwareType HardwareType => HardwareType.Cooler;
+
+ private void SoftwareControlValueChanged(Control control)
+ {
+ if (control.ControlMode == ControlMode.Software)
+ {
+ float value = control.SoftwareValue;
+ byte fanSpeed = (byte)(value > 100 ? 100 : (value < 0) ? 0 : value); // Clamp the value, anything out of range will fail
+
+ //_controlling = true;
+ _setFanSpeedMsg[2] = (byte)control.Sensor.Index;
+ _setFanSpeedMsg[4] = fanSpeed;
+
+ _stream.Write(_setFanSpeedMsg);
+
+ _pwmControls[control.Sensor.Index].Value = value;
+ }
+ else if (control.ControlMode == ControlMode.Default)
+ {
+ // There isn't a "default" mode, but let's say a safe setting is 40%
+ _setFanSpeedMsg[2] = (byte)control.Sensor.Index;
+ _setFanSpeedMsg[4] = 40;
+
+ _stream.Write(_setFanSpeedMsg);
+
+ _pwmControls[control.Sensor.Index].Value = 40;
+ }
+ }
+
+ public override void Close()
+ {
+ _stream?.Close();
+ base.Close();
+ }
+
+ private void ContinuousRead(object state)
+ {
+ byte[] buffer = new byte[_rawData[0].Length];
+ while (_stream.CanRead)
+ {
+ try
+ {
+ _stream.Read(buffer); // This is a blocking call, will wait for bytes to become available
+ if (buffer[0] == 0x04)
+ {
+ lock (_rawData)
+ {
+ int fanID = (buffer[15] >> 4) & 0x0f;
+ Array.Copy(buffer, _rawData[fanID], buffer.Length);
+ }
+ }
+ }
+ catch (TimeoutException)
+ {
+ // Don't care, just make sure the stream is still open
+ Thread.Sleep(500);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Could be unplugged, or the app is stopping...
+ return;
+ }
+ }
+ }
+
+ public override void Update()
+ {
+ // The NZXT GRID+ V3 series sends updates periodically. We have to read it in a seperate thread, this call just reads that data.
+ lock (_rawData)
+ {
+ for (int fanID = 0; fanID < FANS_COUNT; fanID++)
+ {
+ _rpmSensors[fanID].Value = (_rawData[fanID][3] << 8) | _rawData[fanID][4];
+ _voltages[fanID].Value = _rawData[fanID][7] + _rawData[fanID][8] / 100.0f;
+ _currents[fanID].Value = _rawData[fanID][9] + _rawData[fanID][10] / 100.0f;
+ _powers[fanID].Value = _currents[fanID].Value * _voltages[fanID].Value;
+ }
+ _noise.Value = _rawData[2][1];
+ }
+ }
+ }
+}
diff --git a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs
index 7b9efa7..1341aa2 100644
--- a/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs
+++ b/LibreHardwareMonitorLib/Hardware/Controller/Nzxt/NzxtGroup.cs
@@ -33,6 +33,13 @@ namespace LibreHardwareMonitor.Hardware.Controller.Nzxt
_report.AppendLine();
_hardware.Add(device);
break;
+ case 0x1711:
+ var gridv3 = new GridV3(dev, settings);
+ _report.AppendLine($"Device name: {productName}");
+ _report.AppendLine($"Firmware version: {gridv3.FirmwareVersion}");
+ _report.AppendLine();
+ _hardware.Add(gridv3);
+ break;
default:
_report.AppendLine($"Unknown Hardware PID: {dev.ProductID} Name: {productName}");