Add support for Lian Li SL Infinity fan controller
[temp2RGB.git] / src / lian_li_sl_infinity.rs
diff --git a/src/lian_li_sl_infinity.rs b/src/lian_li_sl_infinity.rs
new file mode 100644 (file)
index 0000000..d16a4a3
--- /dev/null
@@ -0,0 +1,87 @@
+use crate::rgb::RGB;
+
+const LIANLI_VID: u16 = 0x0CF2;
+const LIANLI_UNI_HUB_SLINF_PID: u16 = 0xA102;
+
+const UNIHUB_SLINF_LED_MODE_STATIC_COLOR: u8 = 0x01;
+const UNIHUB_SLINF_LED_SPEED_000: u8 = 0x02;
+const UNIHUB_SLINF_LED_DIRECTION_LTR: u8 = 0x00;
+const UNIHUB_SLINF_LED_BRIGHTNESS_100: u8 = 0x00;
+
+const UNIHUB_SLINF_TRANSACTION_ID: u8 = 0xE0;
+
+const BUFFER_SIZE: usize = 353;
+
+const NB_LEDS_PER_FAN: u8 = 8;
+const NB_LEDS_PER_SIDE: u8 = 12;
+
+// Specific hardcoded values (should be given in the constructor).
+const CHANNEL_COUNT: u8 = 4; // 2 Channel per line of fans: one for fan itself and one for sides.
+const NB_FAN_PER_CHANNEL: u8 = 2; // 2 fans per channel.
+
+pub struct Device {
+    device: hidapi::HidDevice,
+}
+
+impl Device {
+    pub fn new(api: &hidapi::HidApi) -> Self {
+        let device = Device {
+            device: api.open(LIANLI_VID, LIANLI_UNI_HUB_SLINF_PID).unwrap(),
+        };
+
+        device
+    }
+
+    fn send_start_action(&self, channel_id: u8) {
+        let mut buffer = [0u8; 5];
+        buffer[0x00] = UNIHUB_SLINF_TRANSACTION_ID;
+        buffer[0x01] = 0x10;
+        buffer[0x02] = 0x60;
+        buffer[0x03] = 1 + channel_id / 2;
+        buffer[0x04] = NB_FAN_PER_CHANNEL;
+
+        let n_write = self.device.write(&buffer).unwrap();
+        assert_eq!(n_write, BUFFER_SIZE);
+    }
+
+    fn send_commit_data(&self, channel_id: u8) {
+        let mut buffer = [0u8; 6];
+        buffer[0x00] = UNIHUB_SLINF_TRANSACTION_ID;
+        buffer[0x01] = 0x10 + channel_id;
+        buffer[0x02] = UNIHUB_SLINF_LED_MODE_STATIC_COLOR;
+        buffer[0x03] = UNIHUB_SLINF_LED_SPEED_000;
+        buffer[0x04] = UNIHUB_SLINF_LED_DIRECTION_LTR;
+        buffer[0x05] = UNIHUB_SLINF_LED_BRIGHTNESS_100;
+
+        let n_write = self.device.write(&buffer).unwrap();
+        assert_eq!(n_write, BUFFER_SIZE);
+    }
+
+    pub fn set_color(&self, color: &RGB) {
+        for channel_id in 0..CHANNEL_COUNT {
+            self.send_start_action(channel_id);
+
+            let mut buffer = [0u8; BUFFER_SIZE];
+            buffer[0x00] = UNIHUB_SLINF_TRANSACTION_ID;
+            buffer[0x01] = 0x30 + channel_id;
+
+            let nb_leds = if channel_id % 2 == 0 {
+                NB_LEDS_PER_FAN * NB_FAN_PER_CHANNEL
+            } else {
+                NB_LEDS_PER_SIDE * NB_FAN_PER_CHANNEL
+            };
+
+            for i in 0..(26 as usize) {
+                let pos = i * 3 + 2;
+                buffer[pos] = color.red;
+                buffer[pos + 1] = color.blue;
+                buffer[pos + 2] = color.green;
+            }
+
+            let n_write = self.device.write(&buffer).unwrap();
+            assert_eq!(n_write, buffer.len());
+
+            self.send_commit_data(channel_id);
+        }
+    }
+}