From e48908573a1f67943912591dcfb121d4bc79b0e8 Mon Sep 17 00:00:00 2001 From: Muhamad Visat <9150540+mvisat@users.noreply.github.com> Date: Sun, 27 Jul 2025 07:55:33 +0800 Subject: [PATCH] add support for the Lian Li UniHub SL v1.8 --- .../LianLiController/LianLiControllerDetect.cpp | 25 ++ .../LianLiUniHubSLController.cpp | 375 ++++++++++++++++++++ .../LianLiUniHubSLController.h | 119 +++++++ .../RGBController_LianLiUniHubSL.cpp | 381 +++++++++++++++++++++ .../RGBController_LianLiUniHubSL.h | 39 +++ 5 files changed, 939 insertions(+) create mode 100644 Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp create mode 100644 Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h create mode 100644 Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp create mode 100644 Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h diff --git a/Controllers/LianLiController/LianLiControllerDetect.cpp b/Controllers/LianLiController/LianLiControllerDetect.cpp index 925b463f..62e03a3a 100644 --- a/Controllers/LianLiController/LianLiControllerDetect.cpp +++ b/Controllers/LianLiController/LianLiControllerDetect.cpp @@ -20,6 +20,8 @@ #include "RGBController_LianLiStrimerLConnect.h" #include "LianLiUniHubController.h" #include "RGBController_LianLiUniHub.h" +#include "LianLiUniHubSLController.h" +#include "RGBController_LianLiUniHubSL.h" #include "LianLiUniHubALController.h" #include "RGBController_LianLiUniHubAL.h" #include "LianLiUniHub_AL10Controller.h" @@ -48,6 +50,7 @@ | Fan controller product IDs | \*-----------------------------------------------------*/ #define UNI_HUB_PID 0x7750 +#define UNI_HUB_SL_PID 0xA100 #define UNI_HUB_AL_PID 0xA101 #define UNI_HUB_SLINF_PID 0xA102 #define UNI_HUB_SLV2_PID 0xA103 @@ -159,6 +162,27 @@ void DetectLianLiUniHub_AL10() } } /* DetectLianLiUniHub_AL10() */ +void DetectLianLiUniHubSL(hid_device_info* info, const std::string& name) +{ + hid_device* device = hid_open_path(info->path); + if (!device) + { + return; + } + + LianLiUniHubSLController* controller = new LianLiUniHubSLController(device, info->path); + std::string version = controller->ReadVersion(); + + if (version != "v1.8") + { + delete controller; + return; + } + + RGBController_LianLiUniHubSL* rgb_controller = new RGBController_LianLiUniHubSL(controller, name); + ResourceManager::get()->RegisterRGBController(rgb_controller); +} /* DetectLianLiUniHubSL() */ + void DetectLianLiUniHubAL(hid_device_info* info, const std::string& name) { hid_device* dev = hid_open_path(info->path); @@ -271,6 +295,7 @@ void DetectLianLiUniversalScreen() REGISTER_DETECTOR("Lian Li Uni Hub", DetectLianLiUniHub); REGISTER_DETECTOR("Lian Li Universal Screen", DetectLianLiUniversalScreen); +REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - SL", DetectLianLiUniHubSL, ENE_USB_VID, UNI_HUB_SL_PID, 0x01, 0xFF72, 0xA1); REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - AL", DetectLianLiUniHubAL, ENE_USB_VID, UNI_HUB_AL_PID, 0x01, 0xFF72, 0xA1); REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - SL V2", DetectLianLiUniHubSLV2, ENE_USB_VID, UNI_HUB_SLV2_PID, 0x01, 0xFF72, 0xA1); REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - AL V2", DetectLianLiUniHubSLV2, ENE_USB_VID, UNI_HUB_ALV2_PID, 0x01, 0xFF72, 0xA1); diff --git a/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp new file mode 100644 index 00000000..5971b700 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp @@ -0,0 +1,375 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLController.cpp | +| | +| Driver for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "StringUtils.h" +#include "LianLiUniHubSLController.h" + +using namespace std::chrono_literals; + +LianLiUniHubSLController::LianLiUniHubSLController(hid_device *dev, const char *path) +{ + if(dev == nullptr) + { + return; + } + + device = dev; + location = "HID: " + std::string(path); + is_merged_mode = false; +} + +LianLiUniHubSLController::~LianLiUniHubSLController() +{ + if(device != nullptr) + { + hid_close(device); + device = nullptr; + } +} + +std::string LianLiUniHubSLController::ReadVersion() +{ + wchar_t buf[40]; + int ret = hid_get_product_string(this->device, buf, 40); + if(ret != 0) + { + return ""; + } + + /*------------------------------ -*\ + | Example: LianLi-UNI FAN-SL-v1.8 | + | We just want the v1.8 part | + | without trailing spaces | + \*--------------------------------*/ + std::string version = StringUtils::wstring_to_string(buf); + version = version.substr(version.find_last_of('-') + 1); + return version.substr(0, version.find_last_not_of(' ') + 1); +} + +std::string LianLiUniHubSLController::ReadSerial() +{ + wchar_t buf[20]; + int ret = hid_get_serial_number_string(this->device, buf, 20); + if(ret != 0) + { + return ""; + } + + std::string serial = StringUtils::wstring_to_string(buf); + return serial; +} + +void LianLiUniHubSLController::UpdateMode(const std::vector &zones, const mode &active) +{ + /*---------------------------------*\ + | Activate all channels | + \*---------------------------------*/ + for(size_t channel = 0; channel < zones.size(); channel++) + { + this->SendActivate(channel, zones[channel].leds_count); + } + + /*---------------------------------*\ + | Set merge mode if requested | + \*---------------------------------*/ + is_merged_mode = active.name.find("Merged") != std::string::npos; + this->SendMerge(); + + for (size_t channel = 0; channel < zones.size(); channel++) + { + UpdateZoneLEDs(channel, zones[channel], active); + } +} + +void LianLiUniHubSLController::UpdateZoneLEDs(size_t channel, const zone &z, const mode &active) +{ + /*---------------------------------*\ + | Handle per-LED color mode | + \*---------------------------------*/ + if(active.color_mode == MODE_COLORS_PER_LED) + { + this->SetPerLEDColor(channel, z, active); + return; + } + + /*-------------------------------------------------*\ + | In merged mode, only first channel takes control | + \*-------------------------------------------------*/ + if(is_merged_mode && channel > 0) + { + return; + } + + /*---------------------------------*\ + | Handle mode-specific color | + \*---------------------------------*/ + this->SetModeSpecificColor(channel, active); +} + + +void LianLiUniHubSLController::SetPerLEDColor(size_t channel, const zone &z, const mode &active) +{ + unsigned char color_buf[UNIHUB_SL_MAX_LED_PER_CHANNEL * 3]; + float brightness_scale = (float)(active.brightness) / active.brightness_max; + + memset(color_buf, 0, sizeof(color_buf)); + this->FillStaticColorBuffer(color_buf, z.colors, z.leds_count, brightness_scale); + this->SendColor(channel, color_buf, sizeof(color_buf)); + this->SendMode(channel, active); +} + +void LianLiUniHubSLController::SetModeSpecificColor(size_t channel, const mode &active) +{ + unsigned char color_buf[UNIHUB_SL_MAX_LED_PER_CHANNEL * 3]; + memset(color_buf, 0, sizeof(color_buf)); + + float brightness_scale = (float)(active.brightness) / active.brightness_max; + + switch(active.value) + { + case UNIHUB_SL_LED_MODE_RAINBOW: + case UNIHUB_SL_LED_MODE_RAINBOW_MORPH: + case UNIHUB_SL_LED_MODE_STACK_MULTI_COLOR: + case UNIHUB_SL_LED_MODE_NEON: + /*-------------------------*\ + | No need to set any value | + \*-------------------------*/ + break; + + case UNIHUB_SL_LED_MODE_STATIC: + case UNIHUB_SL_LED_MODE_BREATHING: + this->FillStaticColorBuffer(color_buf, active.colors.data(), active.colors.size(), brightness_scale); + break; + + case UNIHUB_SL_LED_MODE_COLOR_CYCLE: + case UNIHUB_SL_LED_MODE_RUNWAY: + case UNIHUB_SL_LED_MODE_STAGGERED: + case UNIHUB_SL_LED_MODE_TIDE: + case UNIHUB_SL_LED_MODE_METEOR: + case UNIHUB_SL_LED_MODE_MIXING: + case UNIHUB_SL_LED_MODE_STACK: + this->FillDynamicColorBuffer(color_buf, active.colors.data(), active.colors.size(), brightness_scale); + break; + + default: + LOG_WARNING("[Lian Li Uni Hub - SL] Unknown mode value: %d", active.value); + break; + } + + this->SendColor(channel, color_buf, sizeof(color_buf)); + this->SendMode(channel, active); +} + +void LianLiUniHubSLController::FillStaticColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale) +{ + size_t max_fans = (size_t)UNIHUB_SL_MAX_FAN_PER_CHANNEL; + size_t max_idx = num_colors < max_fans ? num_colors : max_fans; + + for(size_t fan_idx = 0; fan_idx < max_idx; fan_idx++) + { + RGBColor color = colors[fan_idx]; + unsigned char r = (unsigned char)(RGBGetRValue(color) * brightness_scale); + unsigned char g = (unsigned char)(RGBGetGValue(color) * brightness_scale); + unsigned char b = (unsigned char)(RGBGetBValue(color) * brightness_scale); + + for(size_t led_idx = 0; led_idx < UNIHUB_SL_LED_PER_FAN; led_idx++) + { + size_t absolute_led_idx = fan_idx * UNIHUB_SL_LED_PER_FAN + led_idx; + size_t idx = absolute_led_idx * 3; + + /*------------------------*\ + | The protocol uses R B G | + \*------------------------*/ + color_buf[idx + 0] = r; + color_buf[idx + 1] = b; + color_buf[idx + 2] = g; + } + } +} + +void LianLiUniHubSLController::FillDynamicColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale) +{ + size_t max_fans = (size_t)UNIHUB_SL_MAX_FAN_PER_CHANNEL; + size_t max_idx = num_colors < max_fans ? num_colors : max_fans; + + for(size_t color_idx = 0; color_idx < max_idx; color_idx++) + { + RGBColor color = colors[color_idx]; + unsigned char r = (unsigned char)(RGBGetRValue(color) * brightness_scale); + unsigned char g = (unsigned char)(RGBGetGValue(color) * brightness_scale); + unsigned char b = (unsigned char)(RGBGetBValue(color) * brightness_scale); + + size_t idx = (color_idx * 3); + + /*------------------------*\ + | The protocol uses R B G | + \*------------------------*/ + color_buf[idx + 0] = r; + color_buf[idx + 1] = b; + color_buf[idx + 2] = g; + } +} + +unsigned char LianLiUniHubSLController::ConvertBrightness(unsigned int brightness) +{ + switch(brightness) + { + case 0: + return UNIHUB_SL_LED_BRIGHTNESS_000; + + case 1: + return UNIHUB_SL_LED_BRIGHTNESS_025; + + case 2: + return UNIHUB_SL_LED_BRIGHTNESS_050; + + case 3: + return UNIHUB_SL_LED_BRIGHTNESS_075; + + case 4: + return UNIHUB_SL_LED_BRIGHTNESS_100; + + default: + return UNIHUB_SL_LED_BRIGHTNESS_100; + } +} + +unsigned char LianLiUniHubSLController::ConvertSpeed(unsigned int speed) +{ + switch(speed) + { + case 0: + return UNIHUB_SL_LED_SPEED_000; + + case 1: + return UNIHUB_SL_LED_SPEED_025; + + case 2: + return UNIHUB_SL_LED_SPEED_050; + + case 3: + return UNIHUB_SL_LED_SPEED_075; + + case 4: + return UNIHUB_SL_LED_SPEED_100; + + default: + return UNIHUB_SL_LED_SPEED_050; + } +} + +unsigned char LianLiUniHubSLController::ConvertDirection(unsigned int direction) +{ + switch(direction) + { + case MODE_DIRECTION_LEFT: + return UNIHUB_SL_LED_DIRECTION_LTR; + + case MODE_DIRECTION_RIGHT: + return UNIHUB_SL_LED_DIRECTION_RTL; + + default: + return UNIHUB_SL_LED_DIRECTION_LTR; + } +} + +void LianLiUniHubSLController::SendActivate(size_t channel, unsigned char num_fans) +{ + unsigned char buf[11]; + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x10; + buf[0x02] = 0x32; + buf[0x03] = 0x10 * channel + num_fans; + + this->LogBuffer("SendActivate", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLController::SendMerge() +{ + unsigned char buf[11]; + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x10; + if(is_merged_mode) + { + buf[0x02] = 0x33; + buf[0x03] = 0x00; + buf[0x04] = 0x01; + buf[0x05] = 0x02; + buf[0x06] = 0x03; + buf[0x07] = 0x08; + } + else + { + buf[0x02] = 0x34; + } + + this->LogBuffer("SendMerge", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLController::SendColor(size_t channel, const unsigned char *colors, size_t num_colors) +{ + unsigned char* buf = new unsigned char[2 + num_colors]; + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x30 + channel; // Channel 1: 0x30, Channel 2: 0x31, etc. + + memcpy(&buf[0x02], colors, num_colors); + + this->LogBuffer("SendColor", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); + + delete[] buf; +} + +void LianLiUniHubSLController::SendMode(size_t channel, const mode &active) +{ + unsigned char buf[11]; + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x10 + channel; // Channel 1: 0x10, Channel 2: 0x11, etc. + buf[0x02] = active.value; + buf[0x03] = this->ConvertSpeed(active.speed); + buf[0x04] = this->ConvertDirection(active.direction); + buf[0x05] = this->ConvertBrightness(active.brightness); + + this->LogBuffer("SendMode", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLController::LogBuffer(const char *operation, const unsigned char *buf, size_t buf_len) +{ + std::string hex_string; + for(size_t i = 0; i < buf_len; i++) + { + char hex_byte[3]; + snprintf(hex_byte, sizeof(hex_byte), "%02X", buf[i]); + hex_string += hex_byte; + } + LOG_DEBUG("[Lian Li Uni Hub - SL] %s buffer: %s", operation, hex_string.c_str()); +} diff --git a/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h new file mode 100644 index 00000000..0af69618 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h @@ -0,0 +1,119 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLController.h | +| | +| Driver for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + UNIHUB_SL_REPORT_ID = 0xE0, +}; + +enum +{ + UNIHUB_SL_MAX_CHANNEL = 4, + UNIHUB_SL_MAX_FAN_PER_CHANNEL = 4, + UNIHUB_SL_LED_PER_FAN = 16, + UNIHUB_SL_MAX_LED_PER_CHANNEL = 64, +}; + +enum +{ + UNIHUB_SL_LED_BRIGHTNESS_MIN = 0x00, // 0% + UNIHUB_SL_LED_BRIGHTNESS_MAX = 0x04, // 100% + UNIHUB_SL_LED_BRIGHTNESS_DEFAULT = 0x04, // 100% + + UNIHUB_SL_LED_BRIGHTNESS_000 = 0x08, // Black (turned off) + UNIHUB_SL_LED_BRIGHTNESS_025 = 0x03, // Dark + UNIHUB_SL_LED_BRIGHTNESS_050 = 0x02, // Medium + UNIHUB_SL_LED_BRIGHTNESS_075 = 0x01, // Bright + UNIHUB_SL_LED_BRIGHTNESS_100 = 0x00, // Brightest +}; + +enum +{ + UNIHUB_SL_LED_SPEED_MIN = 0x00, // Slowest + UNIHUB_SL_LED_SPEED_MAX = 0x04, // Fastest + UNIHUB_SL_LED_SPEED_DEFAULT = 0x00, // Slowest + + UNIHUB_SL_LED_SPEED_000 = 0x02, // Slowest + UNIHUB_SL_LED_SPEED_025 = 0x01, // Slow + UNIHUB_SL_LED_SPEED_050 = 0x00, // Medium + UNIHUB_SL_LED_SPEED_075 = 0xff, // Fast + UNIHUB_SL_LED_SPEED_100 = 0xfe, // Fastest +}; + +enum +{ + UNIHUB_SL_LED_DIRECTION_LTR = 0x00, // Left to right + UNIHUB_SL_LED_DIRECTION_RTL = 0x01, // Right to left +}; + +enum +{ + UNIHUB_SL_LED_MODE_RAINBOW = 0x05, + UNIHUB_SL_LED_MODE_RAINBOW_MORPH = 0x04, + UNIHUB_SL_LED_MODE_STATIC = 0x01, + UNIHUB_SL_LED_MODE_BREATHING = 0x02, + UNIHUB_SL_LED_MODE_COLOR_CYCLE = 0x23, + UNIHUB_SL_LED_MODE_RUNWAY = 0x1c, + UNIHUB_SL_LED_MODE_RUNWAY_MERGED = 0x1c, + UNIHUB_SL_LED_MODE_STAGGERED = 0x18, + UNIHUB_SL_LED_MODE_TIDE = 0x1a, + UNIHUB_SL_LED_MODE_METEOR = 0x24, + UNIHUB_SL_LED_MODE_METEOR_MERGED = 0x24, + UNIHUB_SL_LED_MODE_MIXING = 0x1e, + UNIHUB_SL_LED_MODE_STACK = 0x20, + UNIHUB_SL_LED_MODE_STACK_MULTI_COLOR = 0x21, + UNIHUB_SL_LED_MODE_NEON = 0x22, +}; + +class LianLiUniHubSLController +{ +public: + LianLiUniHubSLController(hid_device *device, const char *path); + ~LianLiUniHubSLController(); + + std::string ReadVersion(); + std::string ReadSerial(); + + std::string GetLocation() { return this->location; }; + + void UpdateMode(const std::vector &zones, const mode &active); + void UpdateZoneLEDs(size_t channel, const zone &z, const mode &active); + +private: + hid_device *device; + std::string location; + + bool is_merged_mode; + + void SetPerLEDColor(size_t channel, const zone &z, const mode &active); + void SetModeSpecificColor(size_t channel, const mode &active); + void FillStaticColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale); + void FillDynamicColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale); + + unsigned char ConvertBrightness(unsigned int brightness); + unsigned char ConvertSpeed(unsigned int speed); + unsigned char ConvertDirection(unsigned int direction); + + void SendActivate(size_t channel, unsigned char num_fans); + void SendMerge(); + void SendColor(size_t channel, const unsigned char *color_buf, size_t color_buf_len); + void SendMode(size_t channel, const mode &active); + + void LogBuffer(const char *operation, const unsigned char *buf, size_t len); +}; diff --git a/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp new file mode 100644 index 00000000..f22bc0c4 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp @@ -0,0 +1,381 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSL.cpp | +| | +| RGBController for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "RGBController_LianLiUniHubSL.h" + +/**------------------------------------------------------------------*\ + @name Lian Li Uni Hub - SL + @category Cooler + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiUniHubSL + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniHubSL::RGBController_LianLiUniHubSL(LianLiUniHubSLController *controller, std::string name) +{ + this->controller = controller; + this->name = name; + this->vendor = "Lian Li"; + this->description = "Lian Li Uni Hub - SL"; + this->version = controller->ReadVersion(); + this->serial = controller->ReadSerial(); + this->location = controller->GetLocation(); + this->type = DEVICE_TYPE_COOLER; + + initialized = false; + + mode Custom; + Custom.name = "Custom"; + Custom.value = UNIHUB_SL_LED_MODE_STATIC; + Custom.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Custom.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Custom.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Custom.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = UNIHUB_SL_LED_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Rainbow.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Rainbow.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Rainbow.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Rainbow.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Rainbow.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Rainbow.direction = UNIHUB_SL_LED_DIRECTION_LTR; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode RainbowMorph; + RainbowMorph.name = "Rainbow Morph"; + RainbowMorph.value = UNIHUB_SL_LED_MODE_RAINBOW_MORPH; + RainbowMorph.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowMorph.speed_min = UNIHUB_SL_LED_SPEED_MIN; + RainbowMorph.speed_max = UNIHUB_SL_LED_SPEED_MAX; + RainbowMorph.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + RainbowMorph.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + RainbowMorph.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + RainbowMorph.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + RainbowMorph.direction = UNIHUB_SL_LED_DIRECTION_LTR; + RainbowMorph.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowMorph); + + mode Static; + Static.name = "Static"; + Static.value = UNIHUB_SL_LED_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Static.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Static.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Static.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_SL_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Breathing.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Breathing.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Breathing.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Breathing.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Breathing.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = UNIHUB_SL_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + ColorCycle.speed_min = UNIHUB_SL_LED_SPEED_MIN; + ColorCycle.speed_max = UNIHUB_SL_LED_SPEED_MAX; + ColorCycle.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + ColorCycle.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + ColorCycle.colors_min = 3; + ColorCycle.colors_max = 3; + ColorCycle.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + ColorCycle.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + ColorCycle.direction = UNIHUB_SL_LED_DIRECTION_LTR; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(ColorCycle); + + mode Runway; + Runway.name = "Runway"; + Runway.value = UNIHUB_SL_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Runway.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Runway.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Runway.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Runway.colors_min = 2; + Runway.colors_max = 2; + Runway.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Runway.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Runway); + + mode RunwayMerged; + RunwayMerged.name = "Runway Merged"; + RunwayMerged.value = UNIHUB_SL_LED_MODE_RUNWAY; + RunwayMerged.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + RunwayMerged.speed_min = UNIHUB_SL_LED_SPEED_MIN; + RunwayMerged.speed_max = UNIHUB_SL_LED_SPEED_MAX; + RunwayMerged.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + RunwayMerged.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + RunwayMerged.colors_min = 2; + RunwayMerged.colors_max = 2; + RunwayMerged.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + RunwayMerged.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + RunwayMerged.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(RunwayMerged); + + mode Staggered; + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_SL_LED_MODE_STAGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Staggered.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Staggered.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Staggered.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Staggered.colors_min = 2; + Staggered.colors_max = 2; + Staggered.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Staggered.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Staggered); + + mode Tide; + Tide.name = "Tide"; + Tide.value = UNIHUB_SL_LED_MODE_TIDE; + Tide.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Tide.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Tide.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Tide.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Tide.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Tide.colors_min = 2; + Tide.colors_max = 2; + Tide.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Tide.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Tide.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Tide); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_SL_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Meteor.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Meteor.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Meteor.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Meteor.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Meteor.colors_min = 2; + Meteor.colors_max = 2; + Meteor.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Meteor.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Meteor.direction = UNIHUB_SL_LED_DIRECTION_LTR; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Meteor); + + mode MeteorMerged; + MeteorMerged.name = "Meteor Merged"; + MeteorMerged.value = UNIHUB_SL_LED_MODE_METEOR_MERGED; + MeteorMerged.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + MeteorMerged.speed_min = UNIHUB_SL_LED_SPEED_MIN; + MeteorMerged.speed_max = UNIHUB_SL_LED_SPEED_MAX; + MeteorMerged.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + MeteorMerged.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + MeteorMerged.colors_min = 2; + MeteorMerged.colors_max = 2; + MeteorMerged.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + MeteorMerged.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + MeteorMerged.direction = UNIHUB_SL_LED_DIRECTION_LTR; + MeteorMerged.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(MeteorMerged); + + mode Mixing; + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_SL_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Mixing.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Mixing.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Mixing.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Mixing.colors_min = 2; + Mixing.colors_max = 2; + Mixing.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Mixing.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Mixing); + + mode Stack; + Stack.name = "Stack"; + Stack.value = UNIHUB_SL_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Stack.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Stack.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Stack.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Stack.colors_min = 1; + Stack.colors_max = 1; + Stack.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Stack.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Stack.direction = UNIHUB_SL_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Stack); + + mode StackMultiColor; + StackMultiColor.name = "Stack Multi Color"; + StackMultiColor.value = UNIHUB_SL_LED_MODE_STACK_MULTI_COLOR; + StackMultiColor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + StackMultiColor.speed_min = UNIHUB_SL_LED_SPEED_MIN; + StackMultiColor.speed_max = UNIHUB_SL_LED_SPEED_MAX; + StackMultiColor.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + StackMultiColor.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + StackMultiColor.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + StackMultiColor.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + StackMultiColor.direction = UNIHUB_SL_LED_DIRECTION_LTR; + StackMultiColor.color_mode = MODE_COLORS_NONE; + modes.push_back(StackMultiColor); + + mode Neon; + Neon.name = "Neon"; + Neon.value = UNIHUB_SL_LED_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Neon.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Neon.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Neon.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Neon.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Neon.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + RGBColor default_colors[] = + { + ToRGBColor(255, 0, 0), // Red + ToRGBColor(0, 255, 0), // Green + ToRGBColor(0, 0, 255), // Blue + ToRGBColor(255, 255, 255), // White + }; + + for(size_t mode_idx = 0; mode_idx < modes.size(); mode_idx++) + { + mode &m = modes[mode_idx]; + m.colors.resize(m.colors_max); + for (unsigned int color_idx = 0; color_idx < m.colors_max; color_idx++) + { + m.colors[color_idx] = default_colors[color_idx % sizeof(default_colors)]; + } + } + + RGBController_LianLiUniHubSL::SetupZones(); +} + +RGBController_LianLiUniHubSL::~RGBController_LianLiUniHubSL() +{ + delete this->controller; +} + +void RGBController_LianLiUniHubSL::SetupZones() +{ + bool first_run = zones.size() == 0; + + leds.clear(); + colors.clear(); + if(first_run) + { + zones.resize(UNIHUB_SL_MAX_CHANNEL); + } + + for(size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + zones[zone_idx].name = "Channel "; + zones[zone_idx].name.append(std::to_string(zone_idx + 1)); + zones[zone_idx].type = ZONE_TYPE_LINEAR; + zones[zone_idx].matrix_map = NULL; + + zones[zone_idx].leds_min = 0; + zones[zone_idx].leds_max = UNIHUB_SL_MAX_FAN_PER_CHANNEL; + + if(first_run) + { + zones[zone_idx].leds_count = zones[zone_idx].leds_min; + } + + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = zones[zone_idx].name; + new_led.name.append(", Fan "); + new_led.name.append(std::to_string(led_idx + 1)); + new_led.value = zone_idx; + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_LianLiUniHubSL::ResizeZone(int zone, int new_size) +{ + if((size_t)zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHubSL::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_LianLiUniHubSL::UpdateZoneLEDs(int zone) +{ + if(!initialized) + { + return DeviceUpdateMode(); + } + controller->UpdateZoneLEDs(zone, zones[zone], modes[active_mode]); +} + +void RGBController_LianLiUniHubSL::UpdateSingleLED(int /* led */) +{ + DeviceUpdateMode(); +} + +void RGBController_LianLiUniHubSL::DeviceUpdateMode() +{ + if(active_mode == 0) + { + return; + } + + controller->UpdateMode(zones, modes[active_mode]); + initialized = true; +} + +void RGBController_LianLiUniHubSL::SetCustomMode() +{ + active_mode = 0; +} diff --git a/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h new file mode 100644 index 00000000..b63eb8ed --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSL.h | +| | +| RGBController for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LianLiUniHubSLController.h" + +class RGBController_LianLiUniHubSL : public RGBController +{ +public: + RGBController_LianLiUniHubSL(LianLiUniHubSLController *controller, std::string name); + ~RGBController_LianLiUniHubSL(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void SetCustomMode(); + +private: + LianLiUniHubSLController *controller; + bool initialized; +};