From 9ea49c8d02af72fe065d2f805337da28b567fa48 Mon Sep 17 00:00:00 2001
From: CobyBE <edwin.mietz@gmail.com>
Date: Wed, 22 Apr 2020 14:42:30 +0200
Subject: [PATCH] Add support for Razer Firefly V2

---
 README.md                                     |   1 +
 daemon/openrazer_daemon/hardware/mouse_mat.py |  15 ++
 driver/razeraccessory_driver.c                |  29 +++-
 driver/razeraccessory_driver.h                |   1 +
 ...io.github.openrazer.openrazer.metainfo.xml |   1 +
 install_files/udev/99-razer.rules             |   2 +-
 .../openrazer/_fake_driver/razerfireflyv2.cfg |  17 +++
 scripts/driver/firefly_v2/test.py             | 140 ++++++++++++++++++
 8 files changed, 203 insertions(+), 3 deletions(-)
 create mode 100644 pylib/openrazer/_fake_driver/razerfireflyv2.cfg
 create mode 100644 scripts/driver/firefly_v2/test.py

diff --git a/README.md b/README.md
index bc655a41..b7a1147a 100644
--- a/README.md
+++ b/README.md
@@ -134,6 +134,7 @@ The devices below are fully feature supported by OpenRazer, which means all avai
 | Razer Firefly                                 |  1532:0C00  |
 | Razer Goliathus                               |  1532:0C01  |
 | Razer Goliathus Extended                      |  1532:0C02  |
+| Razer Firefly v2                              |  1532:0C04  |
 
 ### Headsets
 | Device                                        | USB VID:PID |
diff --git a/daemon/openrazer_daemon/hardware/mouse_mat.py b/daemon/openrazer_daemon/hardware/mouse_mat.py
index bebaa262..0ca0ec22 100644
--- a/daemon/openrazer_daemon/hardware/mouse_mat.py
+++ b/daemon/openrazer_daemon/hardware/mouse_mat.py
@@ -19,6 +19,21 @@ class RazerFirefly(__RazerDeviceBrightnessSuspend):
     DEVICE_IMAGE = "https://assets.razerzone.com/eeimages/support/products/594/594_firefly_500x500.png"
 
 
+class RazerFireflyV2(__RazerDeviceBrightnessSuspend):
+    """
+    Class for the Razer Firefly V2
+    """
+    USB_VID = 0x1532
+    USB_PID = 0x0C04
+    HAS_MATRIX = True
+    MATRIX_DIMS = [1, 19]
+    METHODS = ['get_device_type_mousemat', 'set_wave_effect', 'set_static_effect', 'set_spectrum_effect',
+               'set_none_effect', 'set_breath_random_effect', 'set_breath_single_effect', 'set_breath_dual_effect',
+               'set_custom_effect', 'set_key_row', 'trigger_reactive_effect']
+
+    DEVICE_IMAGE = "https://assets.razerzone.com/eeimages/support/products/1594/1594_firefly_v2.png"
+
+
 class RazerFireflyHyperflux(__RazerDeviceBrightnessSuspend):
     """
     Class for the Razer Firefly Hyperflux (2018)
diff --git a/driver/razeraccessory_driver.c b/driver/razeraccessory_driver.c
index 1067e1ce..35af9669 100644
--- a/driver/razeraccessory_driver.c
+++ b/driver/razeraccessory_driver.c
@@ -120,6 +120,10 @@ static ssize_t razer_attr_read_device_type(struct device *dev, struct device_att
         device_type = "Razer Firefly\n";
         break;
 
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
+        device_type = "Razer Firefly V2\n";
+        break;
+
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
         device_type = "Razer Firefly Hyperflux\n";
         break;
@@ -215,6 +219,7 @@ static ssize_t razer_attr_write_mode_spectrum(struct device *dev, struct device_
         break;
 
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
     case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -258,6 +263,7 @@ static ssize_t razer_attr_write_mode_reactive(struct device *dev, struct device_
 
         switch (device->usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
             report = razer_chroma_extended_matrix_effect_reactive(VARSTORE, ZERO_LED, speed, (struct razer_rgb *)&buf[1]);
@@ -292,6 +298,7 @@ static ssize_t razer_attr_write_mode_reactive_trigger(struct device *dev, struct
 
     switch (device->usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
         // TODO: Fix reactive trigger for Goliathus
@@ -330,6 +337,7 @@ static ssize_t razer_attr_write_mode_none(struct device *dev, struct device_attr
         break;
 
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
     case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -406,6 +414,7 @@ static ssize_t razer_attr_write_mode_custom(struct device *dev, struct device_at
         break;
 
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
     case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -454,6 +463,7 @@ static ssize_t razer_attr_write_mode_static(struct device *dev, struct device_at
             break;
 
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
         case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -508,6 +518,7 @@ static ssize_t razer_attr_write_mode_wave(struct device *dev, struct device_attr
         break;
 
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_CHROMA_HDK:
     case USB_DEVICE_ID_RAZER_CHROMA_BASE:
     case USB_DEVICE_ID_RAZER_NOMMO_PRO:
@@ -546,6 +557,7 @@ static ssize_t razer_attr_write_mode_breath(struct device *dev, struct device_at
 
     switch (device->usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
     case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -682,6 +694,7 @@ static ssize_t razer_attr_write_set_key_row(struct device *dev, struct device_at
             break;
 
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
         case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -734,6 +747,7 @@ static ssize_t razer_attr_read_get_serial(struct device *dev, struct device_attr
 
     case USB_DEVICE_ID_RAZER_FIREFLY:
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
     case USB_DEVICE_ID_RAZER_CORE:
@@ -891,6 +905,7 @@ static ssize_t razer_attr_write_set_brightness(struct device *dev, struct device
 
     switch (device->usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
         report = razer_chroma_extended_matrix_brightness(VARSTORE, ZERO_LED, brightness);
@@ -944,6 +959,7 @@ static ssize_t razer_attr_read_set_brightness(struct device *dev, struct device_
 
     switch (device->usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
     case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
     case USB_DEVICE_ID_RAZER_KRAKEN_KITTY_EDITION:
@@ -1067,6 +1083,7 @@ static bool razer_accessory_match(struct hid_device *hdev, bool ignore_special_d
     struct usb_device *usb_dev = interface_to_usbdev(intf);
 
     switch (usb_dev->descriptor.idProduct) {
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_KRAKEN_KITTY_EDITION:
     case USB_DEVICE_ID_RAZER_MOUSE_BUNGEE_V3_CHROMA:
     case USB_DEVICE_ID_RAZER_BASE_STATION_V2_CHROMA:
@@ -1097,14 +1114,13 @@ static int razer_accessory_probe(struct hid_device *hdev, const struct hid_devic
         goto exit;
     }
 
-
-
     // Init data
     razer_accessory_init(dev, intf, hdev);
 
     switch(usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_CORE:
     case USB_DEVICE_ID_RAZER_KRAKEN_KITTY_EDITION:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_MOUSE_BUNGEE_V3_CHROMA:
     case USB_DEVICE_ID_RAZER_BASE_STATION_V2_CHROMA:
         expected_protocol = 0;
@@ -1143,6 +1159,7 @@ static int razer_accessory_probe(struct hid_device *hdev, const struct hid_devic
 
         switch(usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
             // Device initial brightness is always 100% anyway
@@ -1152,6 +1169,7 @@ static int razer_accessory_probe(struct hid_device *hdev, const struct hid_devic
 
         switch(usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_CORE:
         case USB_DEVICE_ID_RAZER_NOMMO_CHROMA:
         case USB_DEVICE_ID_RAZER_NOMMO_PRO:
@@ -1168,6 +1186,7 @@ static int razer_accessory_probe(struct hid_device *hdev, const struct hid_devic
 
         switch(usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_CORE:
         case USB_DEVICE_ID_RAZER_CHROMA_MUG:
         case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -1182,6 +1201,7 @@ static int razer_accessory_probe(struct hid_device *hdev, const struct hid_devic
 
         switch(usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
         case USB_DEVICE_ID_RAZER_FIREFLY:
@@ -1248,6 +1268,7 @@ static void razer_accessory_disconnect(struct hid_device *hdev)
 
     switch(usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_CORE:
+    case USB_DEVICE_ID_RAZER_FIREFLY_V2:
     case USB_DEVICE_ID_RAZER_KRAKEN_KITTY_EDITION:
     case USB_DEVICE_ID_RAZER_MOUSE_BUNGEE_V3_CHROMA:
     case USB_DEVICE_ID_RAZER_BASE_STATION_V2_CHROMA:
@@ -1287,6 +1308,7 @@ static void razer_accessory_disconnect(struct hid_device *hdev)
 
         switch(usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_CORE:
         case USB_DEVICE_ID_RAZER_NOMMO_CHROMA:
         case USB_DEVICE_ID_RAZER_NOMMO_PRO:
@@ -1303,6 +1325,7 @@ static void razer_accessory_disconnect(struct hid_device *hdev)
 
         switch(usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_CORE:
         case USB_DEVICE_ID_RAZER_CHROMA_MUG:
         case USB_DEVICE_ID_RAZER_CHROMA_HDK:
@@ -1317,6 +1340,7 @@ static void razer_accessory_disconnect(struct hid_device *hdev)
 
         switch(usb_dev->descriptor.idProduct) {
         case USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX:
+        case USB_DEVICE_ID_RAZER_FIREFLY_V2:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA:
         case USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED:
         case USB_DEVICE_ID_RAZER_FIREFLY:
@@ -1371,6 +1395,7 @@ static int razer_raw_event(struct hid_device *hdev, struct hid_report *report, u
 static const struct hid_device_id razer_devices[] = {
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_FIREFLY) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_FIREFLY_HYPERFLUX) },
+    { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_FIREFLY_V2) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_CORE) },
diff --git a/driver/razeraccessory_driver.h b/driver/razeraccessory_driver.h
index edc0dcc7..52df6476 100644
--- a/driver/razeraccessory_driver.h
+++ b/driver/razeraccessory_driver.h
@@ -19,6 +19,7 @@
 #define USB_DEVICE_ID_RAZER_FIREFLY 0x0C00
 #define USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA 0x0C01
 #define USB_DEVICE_ID_RAZER_GOLIATHUS_CHROMA_EXTENDED 0x0C02
+#define USB_DEVICE_ID_RAZER_FIREFLY_V2 0x0C04
 #define USB_DEVICE_ID_RAZER_CHROMA_MUG 0x0F07
 #define USB_DEVICE_ID_RAZER_CHROMA_BASE 0x0F08
 #define USB_DEVICE_ID_RAZER_CHROMA_HDK 0x0F09
diff --git a/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml b/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml
index d24d097d..84948470 100644
--- a/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml
+++ b/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml
@@ -138,6 +138,7 @@
     <modalias>usb:v1532p0C00d*</modalias>
     <modalias>usb:v1532p0C01d*</modalias>
     <modalias>usb:v1532p0C02d*</modalias>
+    <modalias>usb:v1532p0C04d*</modalias>
     <modalias>usb:v1532p0F07d*</modalias>
     <modalias>usb:v1532p0F08d*</modalias>
     <modalias>usb:v1532p0F09d*</modalias>
diff --git a/install_files/udev/99-razer.rules b/install_files/udev/99-razer.rules
index f416e3f7..01f29aaa 100644
--- a/install_files/udev/99-razer.rules
+++ b/install_files/udev/99-razer.rules
@@ -20,7 +20,7 @@ ATTRS{idProduct}=="0501|0504|0506|0510|0527", \
     ENV{ID_RAZER_CHROMA}="1", ENV{RAZER_DRIVER}="razerkraken"
 
 # Accessories (Speakers, Mousemats, Razer Core, etc)
-ATTRS{idProduct}=="0068|0215|0517|0518|0c00|0c01|0c02|0f07|0f08|0f09|0f19|0f1d|0f20", \
+ATTRS{idProduct}=="0068|0215|0517|0518|0c00|0c01|0c02|0c04|0f07|0f08|0f09|0f19|0f1d|0f20", \
     ATTRS{idVendor}=="1532", \
     ENV{ID_RAZER_CHROMA}="1", ENV{RAZER_DRIVER}="razeraccessory"
 
diff --git a/pylib/openrazer/_fake_driver/razerfireflyv2.cfg b/pylib/openrazer/_fake_driver/razerfireflyv2.cfg
new file mode 100644
index 00000000..e5303197
--- /dev/null
+++ b/pylib/openrazer/_fake_driver/razerfireflyv2.cfg
@@ -0,0 +1,17 @@
+[device]
+dir_name = 0003:1532:0C04.0001
+name = Razer Firefly V2
+files = r,device_serial,XX0000000C04
+        r,device_type,%(name)s
+        r,firmware_version,v1.0
+        rw,matrix_brightness,0
+        w,matrix_custom_frame
+        w,matrix_effect_breath
+        w,matrix_effect_custom
+        w,matrix_effect_none
+        w,matrix_effect_reactive
+        w,matrix_effect_spectrum
+        w,matrix_effect_static
+        w,matrix_effect_wave
+        w,matrix_reactive_trigger
+        r,version,1.0.0
diff --git a/scripts/driver/firefly_v2/test.py b/scripts/driver/firefly_v2/test.py
new file mode 100644
index 00000000..0d9caf75
--- /dev/null
+++ b/scripts/driver/firefly_v2/test.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python3
+import argparse
+import glob
+import os
+import time
+import random
+
+
+COLOURS = (b'\xFF\x00\x00', b'\x00\xFF\x00', b'\x00\x00\xFF', b'\xFF\xFF\x00', b'\xFF\x00\xFF', b'\x00\xFF\xFF')
+
+
+def write_binary(driver_path, device_file, payload):
+    with open(os.path.join(driver_path, device_file), 'wb') as open_file:
+        open_file.write(payload)
+
+
+def read_string(driver_path, device_file):
+    with open(os.path.join(driver_path, device_file), 'r') as open_file:
+        return open_file.read().rstrip('\n')
+
+
+def write_string(driver_path, device_file, payload):
+    with open(os.path.join(driver_path, device_file), 'w') as open_file:
+        open_file.write(payload)
+
+
+def find_devices(vid, pid):
+    driver_paths = glob.glob(os.path.join('/sys/bus/hid/drivers/razeraccessory', '*:{0:04X}:{1:04X}.*'.format(vid, pid)))
+
+    for driver_path in driver_paths:
+        device_type_path = os.path.join(driver_path, 'device_type')
+
+        if os.path.exists(device_type_path):
+            yield driver_path
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--skip-standard', action='store_true')
+    parser.add_argument('--skip-custom', action='store_true')
+    parser.add_argument('--skip-game-led', action='store_true')
+    parser.add_argument('--skip-macro-led', action='store_true')
+
+    return parser.parse_args()
+
+
+if __name__ == '__main__':
+    args = parse_args()
+
+    found_chroma = False
+
+    for index, driver_path in enumerate(find_devices(0x1532, 0x0C04), start=1):
+        found_chroma = True
+
+        print("Razer Firefly_hyperflux {0}\n".format(index))
+
+        print("Driver version: {0}".format(read_string(driver_path, 'version')))
+        print("Driver firmware version: {0}".format(read_string(driver_path, 'firmware_version')))
+        print("Device serial: {0}".format(read_string(driver_path, 'device_serial')))
+        print("Device type: {0}".format(read_string(driver_path, 'device_type')))
+        print("Device mode: {0}".format(read_string(driver_path, 'device_mode')))
+
+        # Set to static red so that we have something standard
+        write_binary(driver_path, 'matrix_effect_static', b'\xFF\x00\x00')
+
+        if not args.skip_standard:
+            print("Starting brightness test. Press enter to begin.")
+            input()
+            print("Max brightness...", end='')
+            write_string(driver_path, 'matrix_brightness', '255')
+            time.sleep(1)
+            print("brightness ({0})".format(read_string(driver_path, 'matrix_brightness')))
+            time.sleep(1)
+            print("Half brightness...", end='')
+            write_string(driver_path, 'matrix_brightness', '128')
+            time.sleep(1)
+            print("brightness ({0})".format(read_string(driver_path, 'matrix_brightness')))
+            time.sleep(1)
+            print("Zero brightness...", end='')
+            write_string(driver_path, 'matrix_brightness', '0')
+            time.sleep(1)
+            print("brightness ({0})".format(read_string(driver_path, 'matrix_brightness')))
+            time.sleep(1)
+            write_string(driver_path, 'matrix_brightness', '255')
+
+            print("Starting other colour effect tests. Press enter to begin.")
+            input()
+            print("Green Static")
+            write_binary(driver_path, 'matrix_effect_static', b'\x00\xFF\x00')
+            time.sleep(5)
+            print("Cyan Static")
+            write_binary(driver_path, 'matrix_effect_static', b'\x00\xFF\xFF')
+            time.sleep(5)
+            print("Spectrum")
+            write_binary(driver_path, 'matrix_effect_spectrum', b'\x00')
+            time.sleep(10)
+            print("None")
+            write_binary(driver_path, 'matrix_effect_none', b'\x00')
+            time.sleep(5)
+            print("Wave Left")
+            write_string(driver_path, 'matrix_effect_wave', '1')
+            time.sleep(5)
+            print("Wave Right")
+            write_string(driver_path, 'matrix_effect_wave', '2')
+            time.sleep(5)
+            print("Breathing random")
+            write_binary(driver_path, 'matrix_effect_breath', b'\x00')
+            time.sleep(10)
+            print("Breathing red")
+            write_binary(driver_path, 'matrix_effect_breath', b'\xFF\x00\x00')
+            time.sleep(10)
+            print("Breathing blue-green")
+            write_binary(driver_path, 'matrix_effect_breath', b'\x00\xFF\x00\x00\x00\xFF')
+            time.sleep(10)
+
+        if not args.skip_custom:
+            # Custom LEDs all rows
+            payload_all = b'\x00\x00\x12'
+            for i in range(0, 19):  # 19 colours 0x00-0x12
+                payload_all += random.choice(COLOURS)
+
+            payload_m1_5 = b''
+            for led in (0x00, 0x12):
+                led_byte = led.to_bytes(1, byteorder='big')
+                payload_m1_5 += b'\x00' + led_byte + led_byte + b'\xFF\xFF\xFF'
+
+            print("Custom LED matrix colours test. Press enter to begin.")
+            input()
+            write_binary(driver_path, 'matrix_custom_frame', payload_all)
+            write_binary(driver_path, 'matrix_effect_custom', b'\x00')
+            print("Custom LED matrix partial colours test. First and last led to white. Press enter to begin.")
+            input()
+            write_binary(driver_path, 'matrix_custom_frame', payload_m1_5)
+            write_binary(driver_path, 'matrix_effect_custom', b'\x00')
+            time.sleep(0.5)
+
+        print("Finished")
+
+    if not found_chroma:
+        print("No Fireflies V2 found")
-- 
GitLab