1#![deny(missing_docs)]
2
3use std::error;
4use std::fmt;
5
6const NZXT_VENDOR_ID: u16 = 0x1e71;
7const NZXT_KRAKEN_X_PRODUCT_ID: u16 = 0x170e;
8
9#[derive(Debug)]
11pub struct KrakenData {
12 pub liquid_temp: u8,
14 pub fan_speed: u16,
16 pub pump_speed: u16,
18 pub firmware_version: (u8, u16, u8),
20}
21
22#[derive(Debug)]
24pub enum KrakenError {
25 FanSpeedOutOfRange,
27 PumpSpeedOutOfRange,
29 Comms,
31 UsbError(hidapi::HidError),
33}
34
35impl fmt::Display for KrakenError {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 match *self {
38 KrakenError::FanSpeedOutOfRange => write!(f, "Fan speed must be between 25% and 100%"),
39 KrakenError::PumpSpeedOutOfRange => {
40 write!(f, "Pump speed must be between 60% and 100%")
41 }
42 KrakenError::Comms => write!(f, "Did not receive enough data from the device"),
43 KrakenError::UsbError(ref e) => e.fmt(f),
44 }
45 }
46}
47
48impl error::Error for KrakenError {
49 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
50 match *self {
51 KrakenError::UsbError(ref e) => Some(e),
52 _ => None,
53 }
54 }
55}
56
57impl From<KrakenError> for std::io::Error {
58 fn from(error: KrakenError) -> Self {
59 std::io::Error::new(std::io::ErrorKind::Other, error)
60 }
61}
62
63pub struct Kraken {
65 device: hidapi::HidDevice,
66}
67
68impl Kraken {
69 pub fn open() -> Result<Kraken, KrakenError> {
74 let api = match hidapi::HidApi::new() {
75 Ok(r) => r,
76 Err(e) => return Err(KrakenError::UsbError(e)),
77 };
78 let device = match api.open(NZXT_VENDOR_ID, NZXT_KRAKEN_X_PRODUCT_ID) {
79 Ok(r) => r,
80 Err(e) => return Err(KrakenError::UsbError(e)),
81 };
82
83 Ok(Kraken { device })
84 }
85
86 pub fn read(&self) -> Result<KrakenData, KrakenError> {
91 let mut buf = [0u8; 64];
92 let res = match self.device.read_timeout(&mut buf, 1000) {
93 Ok(r) => r,
94 Err(e) => return Err(KrakenError::UsbError(e)),
95 };
96
97 if res < 0x0f {
98 return Err(KrakenError::Comms);
100 }
101
102 Ok(KrakenData {
103 liquid_temp: buf[1],
104 fan_speed: (buf[3] as u16) << 8 | buf[4] as u16,
105 pump_speed: (buf[5] as u16) << 8 | buf[6] as u16,
106 firmware_version: (
107 buf[0x0b],
108 (buf[0x0c] as u16) << 8 | buf[0x0d] as u16,
109 buf[0x0e],
110 ),
111 })
112 }
113
114 pub fn set_fan_speed(&self, fan_speed: u8) -> Result<(), KrakenError> {
119 if fan_speed < 25 || fan_speed > 100 {
120 return Err(KrakenError::FanSpeedOutOfRange);
121 }
122
123 let mut buf = [0u8; 5];
124 buf[0] = 0x02;
125 buf[1] = 0x4d;
126 buf[2] = 0x00;
127 buf[3] = 0x00;
128 buf[4] = fan_speed;
129
130 let res = match self.device.write(&buf) {
131 Ok(r) => r,
132 Err(e) => return Err(KrakenError::UsbError(e)),
133 };
134
135 if res != buf.len() {
136 return Err(KrakenError::Comms);
137 }
138
139 Ok(())
140 }
141
142 pub fn set_pump_speed(&self, pump_speed: u8) -> Result<(), KrakenError> {
147 if pump_speed < 60 || pump_speed > 100 {
148 return Err(KrakenError::PumpSpeedOutOfRange);
149 }
150
151 let mut buf = [0u8; 5];
152 buf[0] = 0x02;
153 buf[1] = 0x4d;
154 buf[2] = 0x00;
155 buf[3] = 0x00;
156 buf[4] = pump_speed;
157
158 let res = match self.device.write(&buf) {
159 Ok(r) => r,
160 Err(e) => return Err(KrakenError::UsbError(e)),
161 };
162
163 if res != buf.len() {
164 return Err(KrakenError::Comms);
165 }
166
167 Ok(())
168 }
169}