mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2025-12-23 23:37:48 -05:00
Initial support for Govee devices
This commit is contained in:
321
Controllers/GoveeController/GoveeController.cpp
Normal file
321
Controllers/GoveeController/GoveeController.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| GoveeController.cpp |
|
||||
| |
|
||||
| Driver for Govee wireless lighting devices |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "base64.hpp"
|
||||
#include "GoveeController.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
base64::byte CalculateXorChecksum(std::vector<base64::byte> packet)
|
||||
{
|
||||
base64::byte checksum = 0;
|
||||
|
||||
for(unsigned int i = 0; i < packet.size(); i++)
|
||||
{
|
||||
checksum ^= packet[i];
|
||||
}
|
||||
|
||||
return(checksum);
|
||||
}
|
||||
|
||||
GoveeController::GoveeController(std::string ip)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Fill in location string with device's IP address |
|
||||
\*-----------------------------------------------------*/
|
||||
ip_address = ip;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Register callback for receiving broadcasts |
|
||||
\*-----------------------------------------------------*/
|
||||
RegisterReceiveBroadcastCallback(this);
|
||||
|
||||
broadcast_received = false;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Request device information |
|
||||
\*-----------------------------------------------------*/
|
||||
SendScan();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Wait up to 5s for device information to be received |
|
||||
\*-----------------------------------------------------*/
|
||||
for(unsigned int wait_count = 0; wait_count < 500; wait_count++)
|
||||
{
|
||||
if(broadcast_received)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Open a UDP client sending to the Govee device IP, |
|
||||
| port 4003 |
|
||||
\*-----------------------------------------------------*/
|
||||
port.udp_client(ip_address.c_str(), "4003");
|
||||
}
|
||||
|
||||
GoveeController::~GoveeController()
|
||||
{
|
||||
UnregisterReceiveBroadcastCallback(this);
|
||||
}
|
||||
|
||||
std::string GoveeController::GetLocation()
|
||||
{
|
||||
return("IP: " + ip_address);
|
||||
}
|
||||
|
||||
std::string GoveeController::GetSku()
|
||||
{
|
||||
return(sku);
|
||||
}
|
||||
|
||||
std::string GoveeController::GetVersion()
|
||||
{
|
||||
return("BLE Hardware Version: " + bleVersionHard + "\r\n" +
|
||||
"BLE Software Version: " + bleVersionSoft + "\r\n" +
|
||||
"WiFi Hardware Version: " + wifiVersionHard + "\r\n" +
|
||||
"WiFI Software Version: " + wifiVersionSoft + "\r\n");
|
||||
}
|
||||
|
||||
void GoveeController::ReceiveBroadcast(char* recv_buf, int size)
|
||||
{
|
||||
if(broadcast_received)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Responses are not null-terminated, so add termination |
|
||||
\*-----------------------------------------------------*/
|
||||
recv_buf[size] = '\0';
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Convert null-terminated response to JSON |
|
||||
\*-----------------------------------------------------*/
|
||||
json response = json::parse(recv_buf);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Check if the response contains the method name |
|
||||
\*-----------------------------------------------------*/
|
||||
if(response.contains("msg"))
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Handle responses for scan command |
|
||||
| This command's response should contain a msg |
|
||||
| object containing a data member with ip, device, |
|
||||
| sku, among others. |
|
||||
\*-------------------------------------------------*/
|
||||
if(response["msg"].contains("cmd"))
|
||||
{
|
||||
if(response["msg"]["cmd"] == "scan")
|
||||
{
|
||||
if(response["msg"].contains("data"))
|
||||
{
|
||||
if(response["msg"]["data"].contains("ip"))
|
||||
{
|
||||
if(response["msg"]["data"]["ip"] == ip_address)
|
||||
{
|
||||
if(response["msg"]["data"].contains("sku"))
|
||||
{
|
||||
sku = response["msg"]["data"]["sku"];
|
||||
}
|
||||
|
||||
if(response["msg"]["data"].contains("bleVersionHard"))
|
||||
{
|
||||
bleVersionHard = response["msg"]["data"]["bleVersionHard"];
|
||||
}
|
||||
|
||||
if(response["msg"]["data"].contains("bleVersionSoft"))
|
||||
{
|
||||
bleVersionSoft = response["msg"]["data"]["bleVersionSoft"];
|
||||
}
|
||||
|
||||
if(response["msg"]["data"].contains("wifiVersionHard"))
|
||||
{
|
||||
wifiVersionHard = response["msg"]["data"]["wifiVersionHard"];
|
||||
}
|
||||
|
||||
if(response["msg"]["data"].contains("wifiVersionSoft"))
|
||||
{
|
||||
wifiVersionSoft = response["msg"]["data"]["wifiVersionSoft"];
|
||||
}
|
||||
|
||||
broadcast_received = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GoveeController::SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char brightness)
|
||||
{
|
||||
json command;
|
||||
|
||||
command["msg"]["cmd"] = "colorwc";
|
||||
command["msg"]["data"]["color"]["r"] = red;
|
||||
command["msg"]["data"]["color"]["g"] = green;
|
||||
command["msg"]["data"]["color"]["b"] = blue;
|
||||
command["msg"]["data"]["colorTemInKelvin"] = "0";
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Convert the JSON object to a string and write it |
|
||||
\*-----------------------------------------------------*/
|
||||
std::string command_str = command.dump();
|
||||
|
||||
port.udp_write((char *)command_str.c_str(), command_str.length() + 1);
|
||||
}
|
||||
|
||||
void GoveeController::SendRazerData(RGBColor* colors, unsigned int size)
|
||||
{
|
||||
std::vector<base64::byte> pkt = { 0xBB, 0x00, 0x00, 0xB0, 0x00, 0x00 };
|
||||
json command;
|
||||
|
||||
pkt[2] = 2 + (3 * size);
|
||||
pkt[5] = size;
|
||||
pkt.resize(6 + (3 * size));
|
||||
|
||||
for(std::size_t led_idx = 0; led_idx < size; led_idx++)
|
||||
{
|
||||
pkt[6 + (led_idx * 3)] = RGBGetRValue(colors[led_idx]);
|
||||
pkt[7 + (led_idx * 3)] = RGBGetGValue(colors[led_idx]);
|
||||
pkt[8 + (led_idx * 3)] = RGBGetBValue(colors[led_idx]);
|
||||
}
|
||||
|
||||
pkt.push_back(CalculateXorChecksum(pkt));
|
||||
|
||||
command["msg"]["cmd"] = "razer";
|
||||
command["msg"]["data"]["pt"] = base64::encode(pkt);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Convert the JSON object to a string and write it |
|
||||
\*-----------------------------------------------------*/
|
||||
std::string command_str = command.dump();
|
||||
|
||||
port.udp_write((char *)command_str.c_str(), command_str.length() + 1);
|
||||
}
|
||||
|
||||
void GoveeController::SendRazerDisable()
|
||||
{
|
||||
const std::vector<base64::byte> pkt = { 0xBB, 0x00, 0x01, 0xB1, 0x00, 0x0B };
|
||||
json command;
|
||||
|
||||
command["msg"]["cmd"] = "razer";
|
||||
command["msg"]["data"]["pt"] = base64::encode(pkt);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Convert the JSON object to a string and write it |
|
||||
\*-----------------------------------------------------*/
|
||||
std::string command_str = command.dump();
|
||||
|
||||
port.udp_write((char *)command_str.c_str(), command_str.length() + 1);
|
||||
}
|
||||
|
||||
void GoveeController::SendRazerEnable()
|
||||
{
|
||||
const std::vector<base64::byte> pkt = { 0xBB, 0x00, 0x01, 0xB1, 0x01, 0x0A };
|
||||
json command;
|
||||
|
||||
command["msg"]["cmd"] = "razer";
|
||||
command["msg"]["data"]["pt"] = base64::encode(pkt);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Convert the JSON object to a string and write it |
|
||||
\*-----------------------------------------------------*/
|
||||
std::string command_str = command.dump();
|
||||
|
||||
port.udp_write((char *)command_str.c_str(), command_str.length() + 1);
|
||||
}
|
||||
|
||||
void GoveeController::SendScan()
|
||||
{
|
||||
json command;
|
||||
|
||||
command["msg"]["cmd"] = "scan";
|
||||
command["msg"]["data"]["account_topic"] = "GA/123456789";
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Convert the JSON object to a string and write it |
|
||||
\*-----------------------------------------------------*/
|
||||
std::string command_str = command.dump();
|
||||
|
||||
broadcast_port.udp_write((char *)command_str.c_str(), command_str.length() + 1);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Static class members for shared broadcast receiver |
|
||||
\*---------------------------------------------------------*/
|
||||
net_port GoveeController::broadcast_port;
|
||||
std::vector<GoveeController*> GoveeController::callbacks;
|
||||
std::thread* GoveeController::ReceiveThread;
|
||||
std::atomic<bool> GoveeController::ReceiveThreadRun;
|
||||
|
||||
void GoveeController::ReceiveBroadcastThreadFunction()
|
||||
{
|
||||
char recv_buf[1024];
|
||||
|
||||
broadcast_port.set_receive_timeout(1, 0);
|
||||
|
||||
while(ReceiveThreadRun.load())
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Receive up to 1024 bytes from the device with a |
|
||||
| 1s timeout |
|
||||
\*-------------------------------------------------*/
|
||||
int size = broadcast_port.udp_listen(recv_buf, 1024);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| If data was received, loop through registered |
|
||||
| callback controllers and call the |
|
||||
| ReceiveBroadcast function for the controller |
|
||||
| matching the received data |
|
||||
| |
|
||||
| NOTE: As implemented, it doesn't actually match |
|
||||
| the intended controller and just calls all |
|
||||
| registered controllers. As they are all called |
|
||||
| sequence, this should work, but if parallel calls |
|
||||
| are ever needed, receives should be filtered by |
|
||||
| IP address |
|
||||
\*-------------------------------------------------*/
|
||||
if(size > 0)
|
||||
{
|
||||
for(std::size_t callback_idx = 0; callback_idx < callbacks.size(); callback_idx++)
|
||||
{
|
||||
GoveeController* controller = callbacks[callback_idx];
|
||||
|
||||
controller->ReceiveBroadcast(recv_buf, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GoveeController::RegisterReceiveBroadcastCallback(GoveeController* controller_ptr)
|
||||
{
|
||||
callbacks.push_back(controller_ptr);
|
||||
}
|
||||
|
||||
void GoveeController::UnregisterReceiveBroadcastCallback(GoveeController* controller_ptr)
|
||||
{
|
||||
for(std::size_t callback_idx = 0; callback_idx < callbacks.size(); callback_idx++)
|
||||
{
|
||||
if(callbacks[callback_idx] == controller_ptr)
|
||||
{
|
||||
callbacks.erase(callbacks.begin() + callback_idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Controllers/GoveeController/GoveeController.h
Normal file
71
Controllers/GoveeController/GoveeController.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| GoveeController.h |
|
||||
| |
|
||||
| Driver for Govee wireless lighting devices |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "RGBController.h"
|
||||
#include "net_port.h"
|
||||
|
||||
class GoveeController
|
||||
{
|
||||
public:
|
||||
GoveeController(std::string ip);
|
||||
~GoveeController();
|
||||
|
||||
std::string GetLocation();
|
||||
std::string GetSku();
|
||||
std::string GetVersion();
|
||||
|
||||
void ReceiveBroadcast(char* recv_buf, int size);
|
||||
|
||||
void SendRazerData(RGBColor* colors, unsigned int size);
|
||||
void SendRazerDisable();
|
||||
void SendRazerEnable();
|
||||
|
||||
void SendScan();
|
||||
|
||||
void SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char brightness);
|
||||
|
||||
private:
|
||||
std::string firmware_version;
|
||||
std::string ip_address;
|
||||
std::string module_name;
|
||||
std::string module_mac;
|
||||
|
||||
std::string sku;
|
||||
std::string bleVersionHard;
|
||||
std::string bleVersionSoft;
|
||||
std::string wifiVersionHard;
|
||||
std::string wifiVersionSoft;
|
||||
|
||||
bool broadcast_received;
|
||||
|
||||
net_port port;
|
||||
|
||||
public:
|
||||
/*-----------------------------------------------------*\
|
||||
| One receive thread is shared among all instances of |
|
||||
| GoveeController, so the receive thread function is |
|
||||
| static and the thread is initialized in the detector |
|
||||
| if any GoveeControllers are created. |
|
||||
\*-----------------------------------------------------*/
|
||||
static net_port broadcast_port;
|
||||
static std::vector<GoveeController*> callbacks;
|
||||
static std::thread* ReceiveThread;
|
||||
static std::atomic<bool> ReceiveThreadRun;
|
||||
|
||||
static void ReceiveBroadcastThreadFunction();
|
||||
static void RegisterReceiveBroadcastCallback(GoveeController* controller_ptr);
|
||||
static void UnregisterReceiveBroadcastCallback(GoveeController* controller_ptr);
|
||||
};
|
||||
92
Controllers/GoveeController/GoveeControllerDetect.cpp
Normal file
92
Controllers/GoveeController/GoveeControllerDetect.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| GoveeControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Govee wireless lighting devices |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include "Detector.h"
|
||||
#include "GoveeController.h"
|
||||
#include "RGBController.h"
|
||||
#include "RGBController_Govee.h"
|
||||
#include "SettingsManager.h"
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* DetectGoveeControllers *
|
||||
* *
|
||||
* Detect Govee devices *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
|
||||
void DetectGoveeControllers()
|
||||
{
|
||||
json govee_settings;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get Govee settings from settings manager |
|
||||
\*-----------------------------------------------------*/
|
||||
govee_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("GoveeDevices");
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If the Govee settings contains devices, process |
|
||||
\*-----------------------------------------------------*/
|
||||
if(govee_settings.contains("devices"))
|
||||
{
|
||||
GoveeController::ReceiveThreadRun = false;
|
||||
|
||||
if(govee_settings["devices"].size() > 0)
|
||||
{
|
||||
/*---------------------------------------------*\
|
||||
| Open a UDP client sending to and receiving |
|
||||
| from the Govee Multicast IP, send port 4001 |
|
||||
| and receive port 4002 |
|
||||
\*---------------------------------------------*/
|
||||
GoveeController::broadcast_port.udp_client("239.255.255.250", "4001", "4002");
|
||||
GoveeController::broadcast_port.udp_join_multicast_group("239.255.255.250");
|
||||
|
||||
/*---------------------------------------------*\
|
||||
| Start a thread to handle responses received |
|
||||
| from the Govee device |
|
||||
\*---------------------------------------------*/
|
||||
GoveeController::ReceiveThreadRun = true;
|
||||
GoveeController::ReceiveThread = new std::thread(&GoveeController::ReceiveBroadcastThreadFunction);
|
||||
}
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < govee_settings["devices"].size(); device_idx++)
|
||||
{
|
||||
if(govee_settings["devices"][device_idx].contains("ip"))
|
||||
{
|
||||
std::string govee_ip = govee_settings["devices"][device_idx]["ip"];
|
||||
|
||||
GoveeController* controller = new GoveeController(govee_ip);
|
||||
RGBController_Govee* rgb_controller = new RGBController_Govee(controller);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| All controllers have been created, the broadcast |
|
||||
| receiver thread is no longer needed and can be |
|
||||
| shut down |
|
||||
\*-------------------------------------------------*/
|
||||
if(GoveeController::ReceiveThreadRun)
|
||||
{
|
||||
GoveeController::ReceiveThreadRun = false;
|
||||
GoveeController::ReceiveThread->join();
|
||||
delete GoveeController::ReceiveThread;
|
||||
GoveeController::broadcast_port.tcp_close();
|
||||
}
|
||||
}
|
||||
|
||||
} /* DetectGoveeControllers() */
|
||||
|
||||
REGISTER_DETECTOR("Govee", DetectGoveeControllers);
|
||||
142
Controllers/GoveeController/RGBController_Govee.cpp
Normal file
142
Controllers/GoveeController/RGBController_Govee.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_Govee.cpp |
|
||||
| |
|
||||
| RGBController for Govee wireless lighting devices |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 27 Dec 2023 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <map>
|
||||
#include "RGBController_Govee.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static std::map<std::string, unsigned int> govee_led_counts
|
||||
{
|
||||
{ "H619A", 20 },
|
||||
{ "H70B1", 20 },
|
||||
};
|
||||
|
||||
RGBController_Govee::RGBController_Govee(GoveeController* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
name = "Govee " + controller->GetSku();
|
||||
vendor = "Govee";
|
||||
type = DEVICE_TYPE_LIGHT;
|
||||
description = "Govee Device";
|
||||
location = controller->GetLocation();
|
||||
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.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;
|
||||
modes.push_back(Direct);
|
||||
|
||||
SetupZones();
|
||||
|
||||
keepalive_thread_run = 1;
|
||||
keepalive_thread = new std::thread(&RGBController_Govee::KeepaliveThread, this);
|
||||
}
|
||||
|
||||
RGBController_Govee::~RGBController_Govee()
|
||||
{
|
||||
keepalive_thread_run = 0;
|
||||
keepalive_thread->join();
|
||||
delete keepalive_thread;
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_Govee::SetupZones()
|
||||
{
|
||||
unsigned int led_count = govee_led_counts[controller->GetSku()];
|
||||
|
||||
zone strip;
|
||||
strip.name = "Govee Strip";
|
||||
strip.type = ZONE_TYPE_LINEAR;
|
||||
strip.leds_count = led_count;
|
||||
strip.leds_min = led_count;
|
||||
strip.leds_max = led_count;
|
||||
strip.matrix_map = NULL;
|
||||
zones.push_back(strip);
|
||||
|
||||
for(std::size_t led_idx = 0; led_idx < strip.leds_count; led_idx++)
|
||||
{
|
||||
led strip_led;
|
||||
strip_led.name = "Govee LED";
|
||||
leds.push_back(strip_led);
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_Govee::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| This device does not support resizing zones |
|
||||
\*---------------------------------------------------------*/
|
||||
}
|
||||
|
||||
void RGBController_Govee::DeviceUpdateLEDs()
|
||||
{
|
||||
last_update_time = std::chrono::steady_clock::now();
|
||||
|
||||
if(modes[active_mode].color_mode == MODE_COLORS_PER_LED)
|
||||
{
|
||||
controller->SendRazerData(&colors[0], colors.size());
|
||||
}
|
||||
}
|
||||
|
||||
void RGBController_Govee::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_Govee::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_Govee::DeviceUpdateMode()
|
||||
{
|
||||
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
|
||||
{
|
||||
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, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
controller->SendRazerEnable();
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
}
|
||||
|
||||
void RGBController_Govee::KeepaliveThread()
|
||||
{
|
||||
while(keepalive_thread_run.load())
|
||||
{
|
||||
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::seconds(30))
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
std::this_thread::sleep_for(10s);
|
||||
}
|
||||
}
|
||||
39
Controllers/GoveeController/RGBController_Govee.h
Normal file
39
Controllers/GoveeController/RGBController_Govee.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_Govee.h |
|
||||
| |
|
||||
| RGBController for Govee wireless lighting devices |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "GoveeController.h"
|
||||
|
||||
class RGBController_Govee : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_Govee(GoveeController* controller_ptr);
|
||||
~RGBController_Govee();
|
||||
|
||||
void SetupZones();
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
void KeepaliveThread();
|
||||
|
||||
private:
|
||||
GoveeController* controller;
|
||||
std::thread* keepalive_thread;
|
||||
std::atomic<bool> keepalive_thread_run;
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
|
||||
};
|
||||
111
Controllers/GoveeController/base64.hpp
Normal file
111
Controllers/GoveeController/base64.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
|
||||
namespace base64
|
||||
{
|
||||
inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
inline static const char kPadCharacter = '=';
|
||||
|
||||
using byte = std::uint8_t;
|
||||
|
||||
inline std::string encode(const std::vector<byte>& input)
|
||||
{
|
||||
std::string encoded;
|
||||
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
|
||||
|
||||
std::uint32_t temp{};
|
||||
auto it = input.begin();
|
||||
|
||||
for(std::size_t i = 0; i < input.size() / 3; ++i)
|
||||
{
|
||||
temp = (*it++) << 16;
|
||||
temp += (*it++) << 8;
|
||||
temp += (*it++);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]);
|
||||
}
|
||||
|
||||
switch(input.size() % 3)
|
||||
{
|
||||
case 1:
|
||||
temp = (*it++) << 16;
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(2, kPadCharacter);
|
||||
break;
|
||||
case 2:
|
||||
temp = (*it++) << 16;
|
||||
temp += (*it++) << 8;
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
|
||||
encoded.append(1, kPadCharacter);
|
||||
break;
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
inline std::vector<byte> decode(const std::string& input)
|
||||
{
|
||||
if(input.length() % 4)
|
||||
throw std::runtime_error("Invalid base64 length!");
|
||||
|
||||
std::size_t padding{};
|
||||
|
||||
if(input.length())
|
||||
{
|
||||
if(input[input.length() - 1] == kPadCharacter) padding++;
|
||||
if(input[input.length() - 2] == kPadCharacter) padding++;
|
||||
}
|
||||
|
||||
std::vector<byte> decoded;
|
||||
decoded.reserve(((input.length() / 4) * 3) - padding);
|
||||
|
||||
std::uint32_t temp{};
|
||||
auto it = input.begin();
|
||||
|
||||
while(it < input.end())
|
||||
{
|
||||
for(std::size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
temp <<= 6;
|
||||
if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41;
|
||||
else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47;
|
||||
else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04;
|
||||
else if(*it == 0x2B) temp |= 0x3E;
|
||||
else if(*it == 0x2F) temp |= 0x3F;
|
||||
else if(*it == kPadCharacter)
|
||||
{
|
||||
switch(input.end() - it)
|
||||
{
|
||||
case 1:
|
||||
decoded.push_back((temp >> 16) & 0x000000FF);
|
||||
decoded.push_back((temp >> 8 ) & 0x000000FF);
|
||||
return decoded;
|
||||
case 2:
|
||||
decoded.push_back((temp >> 10) & 0x000000FF);
|
||||
return decoded;
|
||||
default:
|
||||
throw std::runtime_error("Invalid padding in base64!");
|
||||
}
|
||||
}
|
||||
else throw std::runtime_error("Invalid character in base64!");
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
decoded.push_back((temp >> 16) & 0x000000FF);
|
||||
decoded.push_back((temp >> 8 ) & 0x000000FF);
|
||||
decoded.push_back((temp ) & 0x000000FF);
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
}
|
||||
@@ -175,12 +175,14 @@ void PhilipsWizController::ReceiveThreadFunction()
|
||||
{
|
||||
char recv_buf[1025];
|
||||
|
||||
port.set_receive_timeout(1, 0);
|
||||
|
||||
while(ReceiveThreadRun.load())
|
||||
{
|
||||
/*-----------------------------------------------------------------*\
|
||||
| Receive up to 1024 bytes from the device with a 1s timeout |
|
||||
\*-----------------------------------------------------------------*/
|
||||
int size = port.udp_listen_timeout(recv_buf, 1024, 1, 0);
|
||||
int size = port.udp_listen(recv_buf, 1024);
|
||||
|
||||
if(size > 0)
|
||||
{
|
||||
|
||||
@@ -52,7 +52,12 @@ net_port::~net_port()
|
||||
}
|
||||
}
|
||||
|
||||
bool net_port::udp_client(const char * client_name, const char * port)
|
||||
bool net_port::udp_client(const char* client_name, const char * port)
|
||||
{
|
||||
return(udp_client(client_name, port, "0"));
|
||||
}
|
||||
|
||||
bool net_port::udp_client(const char * client_name, const char * send_port, const char * recv_port)
|
||||
{
|
||||
sockaddr_in myAddress;
|
||||
|
||||
@@ -73,7 +78,7 @@ bool net_port::udp_client(const char * client_name, const char * port)
|
||||
|
||||
myAddress.sin_family = AF_INET;
|
||||
myAddress.sin_addr.s_addr = inet_addr("0.0.0.0");
|
||||
myAddress.sin_port = htons(0);
|
||||
myAddress.sin_port = htons(atoi(recv_port));
|
||||
|
||||
if(bind(sock, (sockaddr*)&myAddress, sizeof(myAddress)) == SOCKET_ERROR)
|
||||
{
|
||||
@@ -85,7 +90,7 @@ bool net_port::udp_client(const char * client_name, const char * port)
|
||||
addrinfo hints = {};
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
if(getaddrinfo(client_name, port, &hints, &result_list) == 0)
|
||||
if(getaddrinfo(client_name, send_port, &hints, &result_list) == 0)
|
||||
{
|
||||
memcpy(&addrDest, result_list->ai_addr, result_list->ai_addrlen);
|
||||
freeaddrinfo(result_list);
|
||||
@@ -99,28 +104,32 @@ bool net_port::udp_client(const char * client_name, const char * port)
|
||||
}
|
||||
}
|
||||
|
||||
void net_port::udp_join_multicast_group(const char * group_name)
|
||||
{
|
||||
struct ip_mreq group;
|
||||
|
||||
group.imr_multiaddr.s_addr = inet_addr(group_name);
|
||||
group.imr_interface.s_addr = inet_addr("0.0.0.0");
|
||||
|
||||
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group));
|
||||
}
|
||||
|
||||
int net_port::udp_listen(char * recv_data, int length)
|
||||
{
|
||||
return(recvfrom(sock, recv_data, length, 0, NULL, NULL));
|
||||
}
|
||||
|
||||
int net_port::udp_listen_timeout(char * recv_data, int length, int sec, int usec)
|
||||
void net_port::set_receive_timeout(int sec, int usec)
|
||||
{
|
||||
fd_set fds;
|
||||
#ifdef WIN32
|
||||
DWORD tv = ( sec * 1000 ) + ( usec / 1000 );
|
||||
#else
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
|
||||
tv.tv_sec = sec;
|
||||
tv.tv_usec = usec;
|
||||
#endif
|
||||
|
||||
if(select((int)sock, &fds, NULL, NULL, &tv) <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(recvfrom(sock, recv_data, length, 0, NULL, NULL));
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
|
||||
}
|
||||
|
||||
int net_port::udp_write(char * buffer, int length)
|
||||
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
|
||||
//Function to open the port
|
||||
bool udp_client(const char* client_name, const char * port);
|
||||
bool udp_client(const char * client_name, const char * send_port, const char * recv_port);
|
||||
bool tcp_client(const char* client_name, const char * port);
|
||||
bool tcp_client_connect();
|
||||
|
||||
@@ -67,8 +68,9 @@ public:
|
||||
SOCKET * tcp_server_get_client(std::size_t client_idx);
|
||||
SOCKET * tcp_server_listen();
|
||||
|
||||
void udp_join_multicast_group(const char * group_name);
|
||||
|
||||
int udp_listen(char * recv_data, int length);
|
||||
int udp_listen_timeout(char * recv_data, int length, int sec, int usec);
|
||||
int tcp_listen(char * recv_data, int length);
|
||||
|
||||
//Function to write data to the serial port
|
||||
@@ -78,6 +80,8 @@ public:
|
||||
|
||||
void tcp_close();
|
||||
|
||||
void set_receive_timeout(int sec, int usec);
|
||||
|
||||
bool connected;
|
||||
SOCKET sock;
|
||||
|
||||
|
||||
@@ -494,6 +494,11 @@ OpenRGBDialog::OpenRGBDialog(QWidget *parent) : QMainWindow(parent), ui(new Open
|
||||
\*-----------------------------------------------------*/
|
||||
AddE131SettingsPage();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Add the Govee settings page |
|
||||
\*-----------------------------------------------------*/
|
||||
AddGoveeSettingsPage();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Add the Kasa Smart settings page |
|
||||
\*-----------------------------------------------------*/
|
||||
@@ -833,6 +838,23 @@ void OpenRGBDialog::AddE131SettingsPage()
|
||||
ui->SettingsTabBar->tabBar()->setTabButton(ui->SettingsTabBar->tabBar()->count() - 1, QTabBar::LeftSide, SettingsTabLabel);
|
||||
}
|
||||
|
||||
void OpenRGBDialog::AddGoveeSettingsPage()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Create the Settings page |
|
||||
\*-----------------------------------------------------*/
|
||||
GoveeSettingsPage = new OpenRGBGoveeSettingsPage();
|
||||
|
||||
ui->SettingsTabBar->addTab(GoveeSettingsPage, "");
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Create the tab label |
|
||||
\*-----------------------------------------------------*/
|
||||
TabLabel* SettingsTabLabel = new TabLabel(OpenRGBFont::bulb, tr("Govee Devices"), (char *)"Govee Devices", (char *)context);
|
||||
|
||||
ui->SettingsTabBar->tabBar()->setTabButton(ui->SettingsTabBar->tabBar()->count() - 1, QTabBar::LeftSide, SettingsTabLabel);
|
||||
}
|
||||
|
||||
void OpenRGBDialog::AddKasaSmartSettingsPage()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "OpenRGBE131SettingsPage/OpenRGBE131SettingsPage.h"
|
||||
#include "OpenRGBElgatoKeyLightSettingsPage/OpenRGBElgatoKeyLightSettingsPage.h"
|
||||
#include "OpenRGBElgatoLightStripSettingsPage/OpenRGBElgatoLightStripSettingsPage.h"
|
||||
#include "OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsPage.h"
|
||||
#include "OpenRGBKasaSmartSettingsPage/OpenRGBKasaSmartSettingsPage.h"
|
||||
#include "OpenRGBLIFXSettingsPage/OpenRGBLIFXSettingsPage.h"
|
||||
#include "OpenRGBPhilipsHueSettingsPage/OpenRGBPhilipsHueSettingsPage.h"
|
||||
@@ -103,6 +104,7 @@ private:
|
||||
OpenRGBE131SettingsPage *E131SettingsPage;
|
||||
OpenRGBElgatoKeyLightSettingsPage *ElgatoKeyLightSettingsPage;
|
||||
OpenRGBElgatoLightStripSettingsPage *ElgatoLightStripSettingsPage;
|
||||
OpenRGBGoveeSettingsPage *GoveeSettingsPage;
|
||||
OpenRGBKasaSmartSettingsPage *KasaSmartSettingsPage;
|
||||
OpenRGBLIFXSettingsPage *LIFXSettingsPage;
|
||||
OpenRGBPhilipsHueSettingsPage *PhilipsHueSettingsPage;
|
||||
@@ -134,6 +136,7 @@ private:
|
||||
void AddE131SettingsPage();
|
||||
void AddElgatoKeyLightSettingsPage();
|
||||
void AddElgatoLightStripSettingsPage();
|
||||
void AddGoveeSettingsPage();
|
||||
void AddKasaSmartSettingsPage();
|
||||
void AddLIFXSettingsPage();
|
||||
void AddPhilipsHueSettingsPage();
|
||||
|
||||
35
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsEntry.cpp
Normal file
35
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsEntry.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| OpenRGBGoveeSettingsEntry.cpp |
|
||||
| |
|
||||
| User interface for OpenRGB Govee settings entry |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 15 May 2025 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "OpenRGBGoveeSettingsEntry.h"
|
||||
#include "ui_OpenRGBGoveeSettingsEntry.h"
|
||||
|
||||
using namespace Ui;
|
||||
|
||||
OpenRGBGoveeSettingsEntry::OpenRGBGoveeSettingsEntry(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OpenRGBGoveeSettingsEntryUi)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
OpenRGBGoveeSettingsEntry::~OpenRGBGoveeSettingsEntry()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OpenRGBGoveeSettingsEntry::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
}
|
||||
33
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsEntry.h
Normal file
33
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| OpenRGBGoveeSettingsEntry.h |
|
||||
| |
|
||||
| User interface for OpenRGB Govee settings entry |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 15 May 2025 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include "ui_OpenRGBGoveeSettingsEntry.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class OpenRGBGoveeSettingsEntry;
|
||||
}
|
||||
|
||||
class Ui::OpenRGBGoveeSettingsEntry : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenRGBGoveeSettingsEntry(QWidget *parent = nullptr);
|
||||
~OpenRGBGoveeSettingsEntry();
|
||||
Ui::OpenRGBGoveeSettingsEntryUi *ui;
|
||||
|
||||
private slots:
|
||||
void changeEvent(QEvent *event);
|
||||
};
|
||||
46
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsEntry.ui
Normal file
46
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsEntry.ui
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OpenRGBGoveeSettingsEntryUi</class>
|
||||
<widget class="QWidget" name="OpenRGBGoveeSettingsEntryUi">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>328</width>
|
||||
<height>72</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Philips Wiz Settings Entry</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="IPLabel">
|
||||
<property name="text">
|
||||
<string>IP:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="IPEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
125
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsPage.cpp
Normal file
125
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsPage.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| OpenRGBGoveeSettingsPage.cpp |
|
||||
| |
|
||||
| User interface for OpenRGB Govee settings page |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 15 May 2025 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "OpenRGBGoveeSettingsPage.h"
|
||||
#include "ui_OpenRGBGoveeSettingsPage.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "SettingsManager.h"
|
||||
|
||||
using namespace Ui;
|
||||
|
||||
OpenRGBGoveeSettingsPage::OpenRGBGoveeSettingsPage(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OpenRGBGoveeSettingsPageUi)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
json govee_settings;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Get Govee settings |
|
||||
\*-------------------------------------------------*/
|
||||
govee_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("GoveeDevices");
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| If the Govee settings contains devices, process |
|
||||
\*-------------------------------------------------*/
|
||||
if(govee_settings.contains("devices"))
|
||||
{
|
||||
for(unsigned int device_idx = 0; device_idx < govee_settings["devices"].size(); device_idx++)
|
||||
{
|
||||
OpenRGBGoveeSettingsEntry* entry = new OpenRGBGoveeSettingsEntry;
|
||||
|
||||
if(govee_settings["devices"][device_idx].contains("ip"))
|
||||
{
|
||||
entry->ui->IPEdit->setText(QString::fromStdString(govee_settings["devices"][device_idx]["ip"]));
|
||||
}
|
||||
|
||||
entries.push_back(entry);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem;
|
||||
|
||||
item->setSizeHint(entry->sizeHint());
|
||||
|
||||
ui->GoveeDeviceList->addItem(item);
|
||||
ui->GoveeDeviceList->setItemWidget(item, entry);
|
||||
ui->GoveeDeviceList->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenRGBGoveeSettingsPage::~OpenRGBGoveeSettingsPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OpenRGBGoveeSettingsPage::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Ui::OpenRGBGoveeSettingsPage::on_AddGoveeDeviceButton_clicked()
|
||||
{
|
||||
OpenRGBGoveeSettingsEntry* entry = new OpenRGBGoveeSettingsEntry;
|
||||
entries.push_back(entry);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem;
|
||||
|
||||
item->setSizeHint(entry->sizeHint());
|
||||
|
||||
ui->GoveeDeviceList->addItem(item);
|
||||
ui->GoveeDeviceList->setItemWidget(item, entry);
|
||||
ui->GoveeDeviceList->show();
|
||||
}
|
||||
|
||||
void Ui::OpenRGBGoveeSettingsPage::on_RemoveGoveeDeviceButton_clicked()
|
||||
{
|
||||
int cur_row = ui->GoveeDeviceList->currentRow();
|
||||
|
||||
if(cur_row < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem* item = ui->GoveeDeviceList->takeItem(cur_row);
|
||||
|
||||
ui->GoveeDeviceList->removeItemWidget(item);
|
||||
delete item;
|
||||
|
||||
delete entries[cur_row];
|
||||
entries.erase(entries.begin() + cur_row);
|
||||
}
|
||||
|
||||
void Ui::OpenRGBGoveeSettingsPage::on_SaveGoveeConfigurationButton_clicked()
|
||||
{
|
||||
json govee_settings;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Get Govee settings |
|
||||
\*-------------------------------------------------*/
|
||||
govee_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("GoveeDevices");
|
||||
|
||||
govee_settings["devices"].clear();
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < entries.size(); device_idx++)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Required parameters |
|
||||
\*-------------------------------------------------*/
|
||||
govee_settings["devices"][device_idx]["ip"] = entries[device_idx]->ui->IPEdit->text().toStdString();
|
||||
}
|
||||
|
||||
ResourceManager::get()->GetSettingsManager()->SetSettings("GoveeDevices", govee_settings);
|
||||
ResourceManager::get()->GetSettingsManager()->SaveSettings();
|
||||
}
|
||||
43
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsPage.h
Normal file
43
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsPage.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| OpenRGBGoveeSettingsPage.h |
|
||||
| |
|
||||
| User interface for OpenRGB Govee settings page |
|
||||
| |
|
||||
| Adam Honse (calcprogrammer1@gmail.com) 15 May 2025 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include "ui_OpenRGBGoveeSettingsPage.h"
|
||||
#include "OpenRGBGoveeSettingsEntry.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class OpenRGBGoveeSettingsPage;
|
||||
}
|
||||
|
||||
class Ui::OpenRGBGoveeSettingsPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenRGBGoveeSettingsPage(QWidget *parent = nullptr);
|
||||
~OpenRGBGoveeSettingsPage();
|
||||
|
||||
private slots:
|
||||
void changeEvent(QEvent *event);
|
||||
void on_AddGoveeDeviceButton_clicked();
|
||||
|
||||
void on_RemoveGoveeDeviceButton_clicked();
|
||||
|
||||
void on_SaveGoveeConfigurationButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::OpenRGBGoveeSettingsPageUi *ui;
|
||||
std::vector<OpenRGBGoveeSettingsEntry*> entries;
|
||||
|
||||
};
|
||||
49
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsPage.ui
Normal file
49
qt/OpenRGBGoveeSettingsPage/OpenRGBGoveeSettingsPage.ui
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OpenRGBGoveeSettingsPageUi</class>
|
||||
<widget class="QWidget" name="OpenRGBGoveeSettingsPageUi">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Philips Wiz Settings Page</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="AddGoveeDeviceButton">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="RemoveGoveeDeviceButton">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="SaveGoveeConfigurationButton">
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QListWidget" name="GoveeDeviceList">
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user