kraken_rs/
kraken.rs

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/// This struct tracks the details returned by the device when we query it.
10#[derive(Debug)]
11pub struct KrakenData {
12    /// The temp of the liquid in degrees C.
13    pub liquid_temp: u8,
14    /// The current speed of the fan as RPM.
15    pub fan_speed: u16,
16    /// The current speed of the pump as RPM.
17    pub pump_speed: u16,
18    /// The current firmware version as (Major, Minor, Revision).
19    pub firmware_version: (u8, u16, u8),
20}
21
22/// A common error enum for errors returned from our API.
23#[derive(Debug)]
24pub enum KrakenError {
25    /// If the fan speed supplied is outside of the 25-100% range.
26    FanSpeedOutOfRange,
27    /// If the pump speed supplied is outside of the 60-100% range.
28    PumpSpeedOutOfRange,
29    /// If the device did not return enough data to us.
30    Comms,
31    /// Wrapper error for any underlying USB errors thrown by the hidapi.
32    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
63/// Representation of the Kraken device containing the underlying USB device.
64pub struct Kraken {
65    device: hidapi::HidDevice,
66}
67
68impl Kraken {
69    /// Attempts to open the Kraken device.
70    ///
71    /// This will usually require superuser priviledges on the machine, and will
72    /// return an error if attempted without them.
73    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    /// Reads the current state of the Kraken device.
87    ///
88    /// This will populate a `KrakenData` struct with the current values of the
89    /// device.
90    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            // We don't have enough data to extract the values we need - something went wrong.
99            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    /// Sets the fan speed of the device.
115    ///
116    /// A fan speed provided outside of the range 25-100% will result in a
117    /// `FanSpeedOutOfRange` Error.
118    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    /// Sets the pump speed of the device.
143    ///
144    /// A pump speed provided outside of the range 60-100% will result in a
145    /// `PumpSpeedOutOfRange` Error.
146    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}