feat: Implement WLED direct communication, config tool, and screen capture test setup.
This commit is contained in:
parent
79c5aa7f41
commit
c50aed732e
@ -9,13 +9,21 @@ set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Option to enable DebugClient
|
||||
option(USE_DEBUG_CLIENT "Use DebugClient for terminal output instead of WLEDClient" OFF)
|
||||
if(USE_DEBUG_CLIENT)
|
||||
add_compile_definitions(DEBUG_MODE)
|
||||
endif()
|
||||
|
||||
find_package(Qt6 COMPONENTS Core Gui WaylandClient Multimedia Widgets MultimediaWidgets Network OpenGL REQUIRED)
|
||||
|
||||
add_executable(Hyperion_Grabber_Wayland_QT
|
||||
hyperiongrabber.cpp
|
||||
wledclient.cpp
|
||||
debugclient.cpp # Added debugclient.cpp
|
||||
main.cpp
|
||||
WaylandGrabber.cpp
|
||||
wledconfigclient.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(Hyperion_Grabber_Wayland_QT
|
||||
@ -25,10 +33,22 @@ target_link_libraries(Hyperion_Grabber_Wayland_QT
|
||||
Qt6::Multimedia
|
||||
Qt6::Widgets
|
||||
Qt6::MultimediaWidgets
|
||||
Qt6::Network
|
||||
Qt6::OpenGL
|
||||
${PIPEWIRE_LIBRARIES}
|
||||
)
|
||||
|
||||
add_executable(wled_config_tool
|
||||
main.cpp
|
||||
wledconfigclient.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(wled_config_tool
|
||||
Qt6::Core
|
||||
Qt6::Network
|
||||
)
|
||||
target_compile_definitions(wled_config_tool PRIVATE WLED_CONFIG_TOOL_BUILD)
|
||||
|
||||
add_executable(hyperion-mock
|
||||
hyperion-mock.cpp
|
||||
)
|
||||
@ -39,6 +59,28 @@ target_link_libraries(hyperion-mock
|
||||
Qt6::Gui
|
||||
)
|
||||
|
||||
add_executable(wled_test
|
||||
wled_test.cpp
|
||||
wledclient.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(wled_test
|
||||
Qt6::Core
|
||||
Qt6::Network
|
||||
Qt6::Gui
|
||||
)
|
||||
|
||||
add_executable(screen_capture_test
|
||||
screen_capture_test.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(screen_capture_test
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
Qt6::MultimediaWidgets
|
||||
)
|
||||
|
||||
|
||||
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||
|
||||
@ -65,4 +107,4 @@ add_custom_target(generate_wayland_protocols ALL
|
||||
)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(PIPEWIRE REQUIRED libpipewire-0.3)
|
||||
pkg_check_modules(PIPEWIRE REQUIRED libpipewire-0.3)
|
||||
41
debugclient.cpp
Normal file
41
debugclient.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "debugclient.h"
|
||||
#include <QTextStream>
|
||||
|
||||
DebugClient::DebugClient(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
qDebug() << "DebugClient initialized.";
|
||||
}
|
||||
|
||||
DebugClient::~DebugClient()
|
||||
{
|
||||
qDebug() << "DebugClient destroyed.";
|
||||
}
|
||||
|
||||
QString DebugClient::rgbToAnsiColor(int r, int g, int b)
|
||||
{
|
||||
// ANSI escape codes for 256-color terminal
|
||||
// Using a simple mapping for now, can be improved for better visual representation
|
||||
return QString("\033[48;2;%1;%2;%3m \033[0m").arg(r).arg(g).arg(b);
|
||||
}
|
||||
|
||||
void DebugClient::sendImage(const QImage &image)
|
||||
{
|
||||
if (image.isNull()) {
|
||||
qWarning() << "DebugClient: Cannot display null image.";
|
||||
return;
|
||||
}
|
||||
|
||||
QTextStream out(stdout);
|
||||
out << "\033[H\033[2J"; // Clear screen
|
||||
|
||||
// Iterate through the image pixels and print ASCII representation
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
QRgb pixel = image.pixel(x, y);
|
||||
out << rgbToAnsiColor(qRed(pixel), qGreen(pixel), qBlue(pixel));
|
||||
}
|
||||
out << Qt::endl;
|
||||
}
|
||||
out << Qt::endl;
|
||||
}
|
||||
23
debugclient.h
Normal file
23
debugclient.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef DEBUGCLIENT_H
|
||||
#define DEBUGCLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QImage>
|
||||
#include <QDebug>
|
||||
|
||||
class DebugClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DebugClient(QObject *parent = nullptr);
|
||||
~DebugClient();
|
||||
|
||||
void sendImage(const QImage &image);
|
||||
|
||||
private:
|
||||
// Helper to convert RGB to an ASCII character or colored block
|
||||
QString rgbToAnsiColor(int r, int g, int b);
|
||||
};
|
||||
|
||||
#endif // DEBUGCLIENT_H
|
||||
@ -35,7 +35,11 @@ HyperionGrabber::HyperionGrabber(QHash<QString, QString> opts)
|
||||
}
|
||||
}
|
||||
|
||||
_wledClient_p = new WledClient(addr, port, this);
|
||||
#ifdef DEBUG_MODE
|
||||
_client_p = new DebugClient(this);
|
||||
#else
|
||||
_client_p = new WledClient(addr, port, this);
|
||||
#endif
|
||||
|
||||
_waylandGrabber_p = new WaylandGrabber(this);
|
||||
connect(_waylandGrabber_p, &WaylandGrabber::frameReady, this, &HyperionGrabber::_processFrame);
|
||||
@ -55,7 +59,7 @@ HyperionGrabber::~HyperionGrabber()
|
||||
_timer_p->stop();
|
||||
delete _timer_p;
|
||||
}
|
||||
delete _wledClient_p;
|
||||
delete _client_p;
|
||||
}
|
||||
|
||||
// private slots
|
||||
@ -92,5 +96,5 @@ void HyperionGrabber::_processFrame(const QVideoFrame &frame)
|
||||
scaledImage = scaledImage.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
|
||||
_wledClient_p->sendImage(scaledImage);
|
||||
_client_p->sendImage(scaledImage);
|
||||
}
|
||||
|
||||
@ -6,7 +6,12 @@
|
||||
#include <QImage>
|
||||
#include <QVideoFrame>
|
||||
|
||||
#include "wledclient.h" // Use WledClient instead of HyperionClient
|
||||
#ifdef DEBUG_MODE
|
||||
#include "debugclient.h"
|
||||
#else
|
||||
#include "wledclient.h"
|
||||
#endif
|
||||
|
||||
#include "WaylandGrabber.h"
|
||||
|
||||
class HyperionGrabber : public QObject
|
||||
@ -17,7 +22,11 @@ public:
|
||||
~HyperionGrabber();
|
||||
|
||||
private:
|
||||
WledClient *_wledClient_p; // Changed from HyperionClient
|
||||
#ifdef DEBUG_MODE
|
||||
DebugClient *_client_p; // Use DebugClient in debug mode
|
||||
#else
|
||||
WledClient *_client_p; // Use WledClient in normal mode
|
||||
#endif
|
||||
QTimer *_timer_p;
|
||||
WaylandGrabber *_waylandGrabber_p;
|
||||
|
||||
@ -25,6 +34,9 @@ private:
|
||||
unsigned short _scale_m = 8;
|
||||
unsigned short _frameskip_m = 0;
|
||||
long long _frameCounter_m = 0;
|
||||
quint64 _lastImageChecksum_m = 0; // Added for image stability check
|
||||
QImage _lastScaledImage_m; // Stores the previously sent image for comparison
|
||||
int _changeThreshold_m = 100; // Threshold for image change detection
|
||||
|
||||
private slots:
|
||||
void _processFrame(const QVideoFrame &frame);
|
||||
|
||||
170
main.cpp
170
main.cpp
@ -1,21 +1,127 @@
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <signal.h>
|
||||
#include "hyperiongrabber.h"
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QDateTime>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#ifdef WLED_CONFIG_TOOL_BUILD
|
||||
#include <QCoreApplication>
|
||||
#include "wledconfigclient.h"
|
||||
#else
|
||||
#include <QApplication>
|
||||
#include "hyperiongrabber.h"
|
||||
#endif
|
||||
|
||||
#ifndef WLED_CONFIG_TOOL_BUILD
|
||||
static HyperionGrabber *grab;
|
||||
static QApplication *qapp;
|
||||
#endif
|
||||
static QFile *logFile = nullptr;
|
||||
|
||||
void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
QString logMessage = QString("%1 %2 %3 %4 %5")
|
||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
|
||||
.arg(context.category)
|
||||
.arg(context.function)
|
||||
.arg(context.line)
|
||||
.arg(localMsg.constData());
|
||||
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
logMessage = "Debug: " + logMessage;
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
logMessage = "Info: " + logMessage;
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
logMessage = "Warning: " + logMessage;
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
logMessage = "Critical: " + logMessage;
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
logMessage = "Fatal: " + logMessage;
|
||||
break;
|
||||
}
|
||||
|
||||
if (logFile && logFile->isOpen()) {
|
||||
QTextStream stream(logFile);
|
||||
stream << logMessage << Qt::endl;
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
if (type == QtCriticalMsg || type == QtFatalMsg || !logFile || !logFile->isOpen()) {
|
||||
fprintf(stderr, "%s\n", logMessage.toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WLED_CONFIG_TOOL_BUILD
|
||||
static void quit(int)
|
||||
{
|
||||
if (grab != nullptr) {
|
||||
delete grab;
|
||||
}
|
||||
if (logFile) {
|
||||
logFile->close();
|
||||
delete logFile;
|
||||
logFile = nullptr;
|
||||
}
|
||||
qapp->exit();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WLED_CONFIG_TOOL_BUILD
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("WLED Configuration Tool");
|
||||
parser.addHelpOption();
|
||||
parser.addOption(QCommandLineOption({"a", "address"}, "IP address of the WLED device.", "address"));
|
||||
|
||||
parser.process(app);
|
||||
|
||||
if (!parser.isSet("address")) {
|
||||
qWarning() << "Error: WLED IP address not provided. Use -a or --address.";
|
||||
parser.showHelp(1);
|
||||
}
|
||||
|
||||
QString wledIp = parser.value("address");
|
||||
|
||||
WledConfigClient client(wledIp);
|
||||
|
||||
QObject::connect(&client, &WledConfigClient::infoReceived, [&](const QJsonObject &info) {
|
||||
QJsonDocument doc(info);
|
||||
qDebug() << "WLED Configuration Info:\n" << doc.toJson(QJsonDocument::Indented);
|
||||
app.quit();
|
||||
});
|
||||
|
||||
QObject::connect(&client, &WledConfigClient::error, [&](const QString &message) {
|
||||
qWarning() << "Error retrieving WLED info:" << message;
|
||||
app.quit();
|
||||
});
|
||||
|
||||
client.getInfo();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
#else
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
logFile = new QFile("output.log");
|
||||
if (!logFile->open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
|
||||
fprintf(stderr, "Warning: Could not open log file output.log for writing.\n");
|
||||
delete logFile;
|
||||
logFile = nullptr;
|
||||
}
|
||||
qInstallMessageHandler(customMessageOutput);
|
||||
|
||||
qapp = new QApplication(argc, argv);
|
||||
signal(SIGINT, quit);
|
||||
signal(SIGTERM, quit);
|
||||
@ -26,32 +132,49 @@ int main(int argc, char *argv[])
|
||||
parser.setApplicationDescription("Hyperion grabber for Wayland using Qt");
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.addOption({{"a", "address"}, "Address to the hyperion json server. (ex. 127.0.0.1)", "address"});
|
||||
parser.addOption({{"p", "port"}, "Port for the hyperion json server. (ex. 19444)", "port"});
|
||||
parser.addOption({{"c", "priority"}, "Priority to send to Hyperion, lower number means higher priority, defaults to 100. Range 0-255", "number"});
|
||||
parser.addOption({{"s", "scale"}, "Divisor used to scale your screen resolution (ex. 8 ; if your screen is 1920x1080, the result image sent to hyperion is 240x135", "scale"});
|
||||
parser.addOption({{"f", "frameskip"}, "Number of frames to skip between captures (ex. 1 ; 0 means no frames are skipped)", "frameskip"});
|
||||
parser.addOption({{"i", "inactive"}, "How many seconds after the screen is inactive to turn off the LED's. Set to 0 to disable.", "seonds"});
|
||||
parser.addOption({{"r", "redadjust"}, "Adjustment of the LED's red color (requires 3 space seperated values between 0 and 255) (ex. \"255,10,0\")", "value"});
|
||||
parser.addOption({{"g", "greenadjust"}, "Adjustment of the LED's green color (requires 3 space seperated values between 0 and 255) (ex. \"75,210,0\")", "value"});
|
||||
parser.addOption({{"b", "blueadjust"}, "Adjustment of the LED's blue color (requires 3 space seperated values between 0 and 255) (ex. \"0,10,160\")", "value"});
|
||||
parser.addOption({{"t", "temperature"}, "Adjustment of the LED's color temperature (requires 3 space seperated values between 0 and 255) (ex. \"255,255,250\")", "value"});
|
||||
parser.addOption({{"d", "threshold"}, "Set the threshold of the LED's (requires 3 space seperated values between 0.0 and 1.0) (ex. \"0.0025,0.005,0.01\")", "value"});
|
||||
parser.addOption({{"l", "transform"}, "Adjusts the luminance / saturation of the LED's values are in this order: luminanceGain, luminanceMin, saturationL (requires 3 space seperated values between 0.0 and 1.0) (ex. \"1.0,0.01,1.0\")", "value"});
|
||||
parser.addOption(QCommandLineOption({"a", "address"}, "Address to the hyperion json server. (ex. 127.0.0.1)", "address"));
|
||||
parser.addOption(QCommandLineOption({"p", "port"}, "Port for the hyperion json server. (ex. 19444)", "port"));
|
||||
parser.addOption(QCommandLineOption({"s", "scale"}, "Divisor used to scale your screen resolution (ex. 8 ; if your screen is 1920x1080, the result image sent to hyperion is 240x135", "scale"));
|
||||
parser.addOption(QCommandLineOption({"f", "frameskip"}, "Number of frames to skip between captures (ex. 1 ; 0 means no frames are skipped)", "frameskip"));
|
||||
parser.addOption(QCommandLineOption({"i", "inactive"}, "How many seconds after the screen is inactive to turn off the LED's. Set to 0 to disable.", "seonds"));
|
||||
parser.addOption(QCommandLineOption({"r", "redadjust"}, "Adjustment of the LED's red color (requires 3 space seperated values between 0 and 255) (ex. \"255,10,0\")", "value"));
|
||||
parser.addOption(QCommandLineOption({"g", "greenadjust"}, "Adjustment of the LED's green color (requires 3 space seperated values between 0 and 255) (ex. \"75,210,0\")", "value"));
|
||||
parser.addOption(QCommandLineOption({"b", "blueadjust"}, "Adjustment of the LED's blue color (requires 3 space seperated values between 0 and 255) (ex. \"0,10,160\")", "value"));
|
||||
parser.addOption(QCommandLineOption({"t", "temperature"}, "Adjustment of the LED's color temperature (requires 3 space seperated values between 0 and 255) (ex. \"255,255,250\")", "value"));
|
||||
parser.addOption(QCommandLineOption({"d", "threshold"}, "Set the threshold of the LED's (requires 3 space seperated values between 0.0 and 1.0) (ex. \"0.0025,0.005,0.01\")", "value"));
|
||||
parser.addOption(QCommandLineOption({"l", "transform"}, "Adjusts the luminance / saturation of the LED's values are in this order: luminanceGain, luminanceMin, saturationL (requires 3 space seperated values between 0.0 and 1.0) (ex. \"1.0,0.01,1.0\")", "value"));
|
||||
parser.addOption(QCommandLineOption("flash", "Flash a range of LEDs with a specific color. Usage: --flash <startIndex> <count> <R> <G> <B>", "startIndex count R G B"));
|
||||
|
||||
parser.process(*qapp);
|
||||
|
||||
if (!parser.isSet("address")) {
|
||||
qWarning() << "Hyperion address missing.";
|
||||
parser.showHelp();
|
||||
return 1;
|
||||
}
|
||||
#ifndef DEBUG_MODE
|
||||
if (parser.isSet("flash")) {
|
||||
QStringList flashArgs = parser.value("flash").split(" ", Qt::SkipEmptyParts);
|
||||
if (flashArgs.size() == 5) {
|
||||
int startIndex = flashArgs.at(0).toInt();
|
||||
int count = flashArgs.at(1).toInt();
|
||||
int r = flashArgs.at(2).toInt();
|
||||
int g = flashArgs.at(3).toInt();
|
||||
int b = flashArgs.at(4).toInt();
|
||||
|
||||
if (!parser.isSet("port")) {
|
||||
qWarning() << "Hyperion port missing.";
|
||||
parser.showHelp();
|
||||
return 1;
|
||||
// Create a temporary HyperionGrabber to access WledClient
|
||||
// This is a temporary solution for testing the flash function
|
||||
// In a real application, you'd want a more robust way to control WledClient
|
||||
QHash<QString, QString> tempOpts;
|
||||
// Pass the address and port from command line to tempGrabber
|
||||
if (parser.isSet("address")) tempOpts.insert("address", parser.value("address"));
|
||||
if (parser.isSet("port")) tempOpts.insert("port", parser.value("port"));
|
||||
|
||||
HyperionGrabber tempGrabber(tempOpts);
|
||||
tempGrabber._client_p->flashLeds(startIndex, count, QColor(r, g, b));
|
||||
qDebug() << "Flashing LEDs: startIndex=" << startIndex << ", count=" << count << ", color=" << r << "," << g << "," << b;
|
||||
return 0; // Exit after flashing
|
||||
} else {
|
||||
qWarning() << "Invalid arguments for --flash. Usage: --flash <startIndex> <count> <R> <G> <B>";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QHash<QString, QString> opts;
|
||||
for (const auto &optName : parser.optionNames()) {
|
||||
@ -63,3 +186,4 @@ int main(int argc, char *argv[])
|
||||
grab = new HyperionGrabber(opts);
|
||||
return qapp->exec();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -3,6 +3,10 @@
|
||||
#include <QScreenCapture>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QVideoSink>
|
||||
#include <QVideoFrame>
|
||||
#include <QImage>
|
||||
#include <QMediaCaptureSession> // Added
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -14,6 +18,11 @@ int main(int argc, char *argv[])
|
||||
window.show();
|
||||
|
||||
QScreenCapture screenCapture;
|
||||
QVideoSink videoSink;
|
||||
QMediaCaptureSession captureSession; // Added
|
||||
|
||||
captureSession.setScreenCapture(&screenCapture); // Modified
|
||||
captureSession.setVideoSink(&videoSink); // Modified
|
||||
|
||||
QObject::connect(&screenCapture, &QScreenCapture::activeChanged, [&](bool active) {
|
||||
qDebug() << "Screen capture active:" << active;
|
||||
@ -23,6 +32,21 @@ int main(int argc, char *argv[])
|
||||
qWarning() << "Screen capture error:" << error;
|
||||
});
|
||||
|
||||
// Connect to videoFrameChanged to save the frame
|
||||
QObject::connect(&videoSink, &QVideoSink::videoFrameChanged, [&](const QVideoFrame &frame) {
|
||||
if (frame.isValid()) {
|
||||
qDebug() << "Frame received. Saving to captured_frame.png...";
|
||||
QImage image = frame.toImage();
|
||||
if (!image.isNull()) {
|
||||
image.save("captured_frame.png");
|
||||
qDebug() << "Frame saved. Exiting.";
|
||||
QApplication::quit(); // Quit after saving the first frame
|
||||
} else {
|
||||
qWarning() << "Failed to convert QVideoFrame to QImage.";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start screen capture after a short delay to ensure window is visible
|
||||
QTimer::singleShot(1000, [&]() {
|
||||
qDebug() << "Attempting to start screen capture...";
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include "wledclient.h"
|
||||
#include <QDebug>
|
||||
#include <QColor>
|
||||
#include <QByteArray>
|
||||
#include <QtGlobal>
|
||||
|
||||
// DDP Port for WLED
|
||||
const ushort WLED_DDP_PORT = 4048;
|
||||
@ -73,4 +75,51 @@ void WledClient::sendImage(const QImage &image)
|
||||
qWarning() << "WledClient: Sent fewer bytes than expected. Expected:" << datagram.size() << "Sent:" << bytesSent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WledClient::flashLeds(int startIndex, int count, QColor color)
|
||||
{
|
||||
if (count <= 0) {
|
||||
qWarning() << "WledClient: Count must be positive for flashLeds.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Max UDP payload size (approx 508 bytes for safe transmission)
|
||||
// 4 bytes for DDP header, so 504 bytes for LED data
|
||||
const int MAX_LED_DATA_PER_PACKET = 504; // 504 bytes / 3 bytes per LED = 168 LEDs
|
||||
int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB)
|
||||
|
||||
for (int i = 0; i < count; i += ledsPerPacket) {
|
||||
QByteArray datagram;
|
||||
datagram.reserve(4 + (ledsPerPacket * 3)); // Header + max LED data
|
||||
|
||||
// Byte 0: Protocol type (DNRGB)
|
||||
datagram.append(DDP_PROTOCOL_DNRGB);
|
||||
// Byte 1: Timeout (1 second)
|
||||
datagram.append(1);
|
||||
|
||||
// Bytes 2 & 3: Starting LED index (low byte, then high byte)
|
||||
quint16 currentStartIndex = startIndex + i;
|
||||
datagram.append(currentStartIndex & 0xFF); // Low byte
|
||||
datagram.append((currentStartIndex >> 8) & 0xFF); // High byte
|
||||
|
||||
int currentLedsInPacket = qMin(ledsPerPacket, count - i);
|
||||
|
||||
for (int j = 0; j < currentLedsInPacket; ++j) {
|
||||
datagram.append(color.red());
|
||||
datagram.append(color.green());
|
||||
datagram.append(color.blue());
|
||||
}
|
||||
|
||||
qDebug() << "WledClient: Sending flashLeds datagram (hex):" << datagram.toHex();
|
||||
qDebug() << "WledClient: Datagram size:" << datagram.size();
|
||||
|
||||
qint64 bytesSent = _udpSocket->writeDatagram(datagram, _wledHost, _wledPort);
|
||||
if (bytesSent == -1) {
|
||||
qWarning() << "WledClient: Failed to send datagram for flashLeds:" << _udpSocket->errorString();
|
||||
emit error(_udpSocket->errorString());
|
||||
} else if (bytesSent != datagram.size()) {
|
||||
qWarning() << "WledClient: Sent fewer bytes than expected for flashLeds. Expected:" << datagram.size() << "Sent:" << bytesSent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ public:
|
||||
~WledClient();
|
||||
|
||||
void sendImage(const QImage &image);
|
||||
void flashLeds(int startIndex, int count, QColor color);
|
||||
|
||||
private:
|
||||
QUdpSocket *_udpSocket;
|
||||
|
||||
39
wledconfigclient.cpp
Normal file
39
wledconfigclient.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "wledconfigclient.h"
|
||||
#include <QDebug>
|
||||
|
||||
WledConfigClient::WledConfigClient(const QString &host, QObject *parent) :
|
||||
QObject(parent),
|
||||
_wledHost(host)
|
||||
{
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
connect(_networkAccessManager, &QNetworkAccessManager::finished, this, &WledConfigClient::onInfoReplyFinished);
|
||||
}
|
||||
|
||||
WledConfigClient::~WledConfigClient()
|
||||
{
|
||||
qDebug() << "WledConfigClient destroyed.";
|
||||
}
|
||||
|
||||
void WledConfigClient::getInfo()
|
||||
{
|
||||
QUrl url(QString("http://%1/json/info").arg(_wledHost));
|
||||
QNetworkRequest request(url);
|
||||
_networkAccessManager->get(request);
|
||||
qDebug() << "WledConfigClient: Requesting info from" << url.toString();
|
||||
}
|
||||
|
||||
void WledConfigClient::onInfoReplyFinished(QNetworkReply *reply)
|
||||
{
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
QByteArray responseData = reply->readAll();
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
|
||||
if (jsonDoc.isObject()) {
|
||||
emit infoReceived(jsonDoc.object());
|
||||
} else {
|
||||
emit error("Invalid JSON response from WLED /json/info");
|
||||
}
|
||||
} else {
|
||||
emit error(QString("Network error: %1").arg(reply->errorString()));
|
||||
}
|
||||
reply->deleteLater();
|
||||
}
|
||||
33
wledconfigclient.h
Normal file
33
wledconfigclient.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef WLEDCONFIGCLIENT_H
|
||||
#define WLEDCONFIGCLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
class WledConfigClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WledConfigClient(const QString &host, QObject *parent = nullptr);
|
||||
~WledConfigClient();
|
||||
|
||||
void getInfo();
|
||||
|
||||
signals:
|
||||
void infoReceived(const QJsonObject &info);
|
||||
void error(const QString &message);
|
||||
|
||||
private slots:
|
||||
void onInfoReplyFinished(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
QNetworkAccessManager *_networkAccessManager;
|
||||
QString _wledHost;
|
||||
};
|
||||
|
||||
#endif // WLEDCONFIGCLIENT_H
|
||||
Loading…
x
Reference in New Issue
Block a user