From 60e4309eec7900b986d3ddb0df6937abc15ef4ef Mon Sep 17 00:00:00 2001 From: Luca Cesarano Date: Sat, 29 Aug 2026 05:54:48 +0000 Subject: [PATCH] Improve Govee LAN mode support --- .../GoveeController/GoveeController.cpp | 27 ++++ Controllers/GoveeController/GoveeController.h | 2 + .../GoveeController/RGBController_Govee.cpp | 146 +++++++++++++----- .../GoveeController/RGBController_Govee.h | 6 + 4 files changed, 145 insertions(+), 36 deletions(-) diff --git a/Controllers/GoveeController/GoveeController.cpp b/Controllers/GoveeController/GoveeController.cpp index c43c42fd9..36fa13a94 100644 --- a/Controllers/GoveeController/GoveeController.cpp +++ b/Controllers/GoveeController/GoveeController.cpp @@ -270,6 +270,33 @@ void GoveeController::SendRazerEnable() port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); } +void GoveeController::SetBrightness(unsigned int brightness) +{ + json command; + + command["msg"]["cmd"] = "brightness"; + command["msg"]["data"]["value"] = std::min(brightness, 100u); + + /*-----------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + +void GoveeController::SetPower(bool enabled) +{ + json command; + + command["msg"]["cmd"] = "turn"; + command["msg"]["data"]["value"] = enabled ? 1 : 0; + + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + void GoveeController::SendScan() { json command; diff --git a/Controllers/GoveeController/GoveeController.h b/Controllers/GoveeController/GoveeController.h index 0e13a7e67..599d5cff7 100644 --- a/Controllers/GoveeController/GoveeController.h +++ b/Controllers/GoveeController/GoveeController.h @@ -35,7 +35,9 @@ public: void SendScan(); + void SetBrightness(unsigned int brightness); void SetColor(unsigned char red, unsigned char green, unsigned char blue); + void SetPower(bool enabled); private: std::string firmware_version; diff --git a/Controllers/GoveeController/RGBController_Govee.cpp b/Controllers/GoveeController/RGBController_Govee.cpp index 1e3d23580..ed8798cfe 100644 --- a/Controllers/GoveeController/RGBController_Govee.cpp +++ b/Controllers/GoveeController/RGBController_Govee.cpp @@ -20,22 +20,33 @@ struct GoveeHardwareInfo { unsigned int led_count; unsigned int matrix_row_len; // 0 = linear + bool supports_razer; }; -const unsigned int GOVEE_FALLBACK_LED_COUNT = 20; +const unsigned int GOVEE_FALLBACK_LED_COUNT = 1; static std::map govee_hardware_info { - { "H6022", { 132, 12 } }, // Govee Smart Table Lamp 2 - { "H612F", { 12, 0 } }, // Govee Strip Light S (3m) - { "H619A", { 20, 0 } }, // Govee RGBIC Led Strip Lights - { "H70B1", { 20, 0 } }, // Govee LED Curtain Lights - { "H607C", { 174, 0 } }, // Govee Floor Lamp 2 + { "H6022", { 132, 12, true } }, // Govee Smart Table Lamp 2 + { "H612F", { 12, 0, true } }, // Govee Strip Light S (3m) + { "H619A", { 20, 0, true } }, // Govee RGBIC Led Strip Lights + { "H70B1", { 20, 0, true } }, // Govee LED Curtain Lights + { "H607C", { 174, 0, true } }, // Govee Floor Lamp 2 +}; + +enum +{ + GOVEE_MODE_STATIC = 0, + GOVEE_MODE_DIRECT = 1, }; RGBController_Govee::RGBController_Govee(GoveeController* controller_ptr) { - controller = controller_ptr; + controller = controller_ptr; + last_static_color = 0; + last_static_brightness = 0; + razer_supported = false; + static_initialized = false; name = "Govee " + controller->GetSku(); vendor = "Govee"; @@ -45,25 +56,32 @@ RGBController_Govee::RGBController_Govee(GoveeController* controller_ptr) version = controller->GetVersion(); mode Static; - Static.name = "Static"; - Static.value = 1; - Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; - Static.color_mode = MODE_COLORS_MODE_SPECIFIC; - Static.colors_min = 1; - Static.colors_max = 1; + Static.name = "Static"; + Static.value = GOVEE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.brightness_min = 0; + Static.brightness_max = 100; + Static.brightness = 100; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; Static.colors.resize(1); modes.push_back(Static); mode Direct; - Direct.name = "Direct"; - Direct.value = 0; - Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; - Direct.color_mode = MODE_COLORS_PER_LED; + Direct.name = "Direct"; + Direct.value = GOVEE_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 100; + Direct.color_mode = MODE_COLORS_PER_LED; modes.push_back(Direct); SetupZones(); keepalive_thread_run = 1; + last_update_time = std::chrono::steady_clock::now(); keepalive_thread = new std::thread(&RGBController_Govee::KeepaliveThread, this); } @@ -80,20 +98,19 @@ RGBController_Govee::~RGBController_Govee() void RGBController_Govee::SetupZones() { - GoveeHardwareInfo hw = { GOVEE_FALLBACK_LED_COUNT, 0 }; - bool resizable = true; + GoveeHardwareInfo hw = { GOVEE_FALLBACK_LED_COUNT, 0, false }; std::map::iterator it = govee_hardware_info.find(controller->GetSku()); if(it != govee_hardware_info.end()) { hw = it->second; - resizable = false; + razer_supported = hw.supports_razer; } zone strip; strip.leds_count = hw.led_count; - strip.leds_min = resizable ? 0 : hw.led_count; - strip.leds_max = resizable ? 255 : hw.led_count; + strip.leds_min = hw.led_count; + strip.leds_max = hw.led_count; if(hw.matrix_row_len == 0) { @@ -171,14 +188,40 @@ void RGBController_Govee::DeviceConfigureZone(int zone_idx) void RGBController_Govee::DeviceUpdateLEDs() { - last_update_time = std::chrono::steady_clock::now(); - - if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + if(colors.empty()) { - if(!colors.empty()) + return; + } + + const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + last_update_time = now; + + const unsigned int brightness = modes[active_mode].brightness; + + if(razer_supported) + { + controller->SendRazerData(&colors[0], (unsigned int)colors.size()); + controller->SetBrightness(brightness); + } + else + { + if(!static_initialized) { - controller->SendRazerData(&colors[0], (unsigned int)colors.size()); + controller->SetPower(true); } + + controller->SetColor + ( + RGBGetRValue(colors[0]), + RGBGetGValue(colors[0]), + RGBGetBValue(colors[0]) + ); + + controller->SetBrightness(brightness); + + last_static_color = colors[0]; + last_static_brightness = brightness; + static_initialized = true; } } @@ -194,28 +237,59 @@ void RGBController_Govee::DeviceUpdateSingleLED(int /*led*/) void RGBController_Govee::DeviceUpdateMode() { - if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + if(modes[active_mode].value == GOVEE_MODE_STATIC) { - unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); - unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); - unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); - controller->SetColor(red, grn, blu); + UpdateStatic(false); } - else + else if(modes[active_mode].value == GOVEE_MODE_DIRECT) { - controller->SendRazerEnable(); + if(razer_supported) + { + controller->SendRazerEnable(); + } + + last_update_time = std::chrono::steady_clock::now(); DeviceUpdateLEDs(); } } +void RGBController_Govee::UpdateStatic(bool force) +{ + last_update_time = std::chrono::steady_clock::now(); + + const RGBColor color = modes[active_mode].colors[0]; + const unsigned int brightness = modes[active_mode].brightness; + + if(force || !static_initialized || (color != last_static_color)) + { + controller->SetPower(true); + controller->SetColor(RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color)); + last_static_color = color; + } + + if(force || !static_initialized || (brightness != last_static_brightness)) + { + controller->SetBrightness(brightness); + last_static_brightness = brightness; + } + + static_initialized = true; +} + void RGBController_Govee::KeepaliveThread() { while(keepalive_thread_run.load()) { - if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::seconds(30)) + const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + if((now - last_update_time) > std::chrono::seconds(30)) { - DeviceUpdateLEDs(); + if(modes[active_mode].value == GOVEE_MODE_STATIC) + { + UpdateStatic(true); + } } + std::this_thread::sleep_for(10s); } } diff --git a/Controllers/GoveeController/RGBController_Govee.h b/Controllers/GoveeController/RGBController_Govee.h index 009c95d07..09fdb8055 100644 --- a/Controllers/GoveeController/RGBController_Govee.h +++ b/Controllers/GoveeController/RGBController_Govee.h @@ -32,8 +32,14 @@ public: void KeepaliveThread(); private: + void UpdateStatic(bool force); + GoveeController* controller; std::thread* keepalive_thread; std::atomic keepalive_thread_run; std::chrono::time_point last_update_time; + RGBColor last_static_color; + unsigned int last_static_brightness; + bool razer_supported; + bool static_initialized; };