mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2026-04-08 07:57:35 -04:00
272 lines
11 KiB
C++
272 lines
11 KiB
C++
/*---------------------------------------------------------*\
|
|
| GoveeController.cpp |
|
|
| |
|
|
| Driver for Govee controller |
|
|
| |
|
|
| Adam Honse (calcprogrammer1@gmail.com), 12/1/2023 |
|
|
\*---------------------------------------------------------*/
|
|
|
|
#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;
|
|
|
|
/*-----------------------------------------------------------------*\
|
|
| Open a UDP client sending to and receiving from the Govee |
|
|
| Multicast IP, send port 4001 and receive port 4002 |
|
|
\*-----------------------------------------------------------------*/
|
|
broadcast_port.udp_client("239.255.255.250", "4001", "4002");
|
|
broadcast_port.udp_join_multicast_group("239.255.255.250");
|
|
|
|
/*-----------------------------------------------------------------*\
|
|
| Start a thread to handle responses received from the Wiz device |
|
|
\*-----------------------------------------------------------------*/
|
|
ReceiveThreadRun = true;
|
|
ReceiveThread = new std::thread(&GoveeController::ReceiveThreadFunction, this);
|
|
|
|
/*-----------------------------------------------------------------*\
|
|
| 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(ReceiveThreadRun.load() == false)
|
|
{
|
|
ReceiveThread->join();
|
|
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()
|
|
{
|
|
ReceiveThreadRun = 0;
|
|
ReceiveThread->join();
|
|
delete ReceiveThread;
|
|
}
|
|
|
|
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::ReceiveThreadFunction()
|
|
{
|
|
char recv_buf[1024];
|
|
|
|
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(size > 0)
|
|
{
|
|
/*-----------------------------------------------------------------*\
|
|
| Responses are not null-terminated, so add termination |
|
|
\*-----------------------------------------------------------------*/
|
|
recv_buf[size] = '\0';
|
|
|
|
printf( "response %s \r\n", recv_buf);
|
|
|
|
/*-----------------------------------------------------------------*\
|
|
| 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"];
|
|
}
|
|
|
|
ReceiveThreadRun = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|