#include #include #include #include #include #include #include #include #include #include #include "wledconfigclient.h" #include "wledclient.h" // Forward declarations void runDemoMode(const QString &address, quint16 port, int totalLeds); void runFlashMode(const QString &address, quint16 port, int totalLeds, const QString &flashParams); void delay(int milliseconds); void printAsciiLayout(int ledsBottom, int ledsRight, int ledsTop, int ledsLeft, int offset, int totalLeds); int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName("wled_config_tool"); QCoreApplication::setApplicationVersion("1.0"); QCommandLineParser parser; parser.setApplicationDescription("WLED Configuration Tool"); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption addressOption(QStringList() << "a" << "address", "WLED device IP address or hostname.", "address"); parser.addOption(addressOption); QCommandLineOption portOption(QStringList() << "p" << "port", "WLED device UDP port.", "port", "21324"); parser.addOption(portOption); QCommandLineOption saveOption(QStringList() << "s" << "save", "Save configuration to a file.", "filepath"); parser.addOption(saveOption); QCommandLineOption configureLayoutOption(QStringList() << "l" << "configure-layout", "Start interactive LED layout configuration."); parser.addOption(configureLayoutOption); QCommandLineOption demoOption(QStringList() << "d" << "demo", "Run a demo sequence on the LEDs."); parser.addOption(demoOption); QCommandLineOption flashOption(QStringList() << "f" << "flash", "Flash a range of LEDs. Format: startIndex,count,color (e.g., 10,20,#FF0000).", "params"); parser.addOption(flashOption); parser.process(app); if (!parser.isSet(addressOption)) { qCritical() << "Error: WLED address not specified. Use -a or --address."; parser.showHelp(1); } QString wledAddress = parser.value(addressOption); quint16 wledPort = parser.value(portOption).toUShort(); if (parser.isSet(configureLayoutOption)) { auto configClient = new WledConfigClient(wledAddress, &app); QObject::connect(configClient, &WledConfigClient::infoReceived, [&](const QJsonObject &info) { int totalLeds = info["leds"].toObject()["count"].toInt(); qDebug() << "Your WLED device reports" << totalLeds << "LEDs."; QTextStream cout(stdout); QTextStream cin(stdin); cout << "Starting interactive LED layout configuration.\n"; cout << "Please enter the number of LEDs for each section.\n"; int ledsBottom, ledsRight, ledsTop, ledsLeft, offset; cout << "LEDs on the bottom: "; cout.flush(); cin >> ledsBottom; cout << "LEDs on the right side: "; cout.flush(); cin >> ledsRight; cout << "LEDs on the top: "; cout.flush(); cin >> ledsTop; cout << "LEDs on the left side: "; cout.flush(); cin >> ledsLeft; cout << "Enter the starting offset (0 if the strip starts at the first LED): "; cout.flush(); cin >> offset; int configuredLeds = ledsBottom + ledsRight + ledsTop + ledsLeft; cout << "Total LEDs configured: " << configuredLeds << "\n"; cout.flush(); if (configuredLeds > totalLeds) { qWarning() << "Warning: The number of configured LEDs (" << configuredLeds << ") is greater than the number of LEDs reported by the device (" << totalLeds << ")."; } auto wledClient = new WledClient(wledAddress, wledPort, &app); auto colors = new QVector(totalLeds, Qt::black); int currentIndex = offset; // Bottom for (int i = 0; i < ledsBottom; ++i) { (*colors)[(currentIndex + i) % totalLeds] = Qt::red; } currentIndex += ledsBottom; // Right for (int i = 0; i < ledsRight; ++i) { (*colors)[(currentIndex + i) % totalLeds] = Qt::green; } currentIndex += ledsRight; // Top for (int i = 0; i < ledsTop; ++i) { (*colors)[(currentIndex + i) % totalLeds] = Qt::blue; } currentIndex += ledsTop; // Left for (int i = 0; i < ledsLeft; ++i) { (*colors)[(currentIndex + i) % totalLeds] = Qt::yellow; } wledClient->setLedsColor(*colors, 60); // Set timeout to 60 seconds cout << "LEDs have been lit up according to your configuration.\n"; cout.flush(); printAsciiLayout(ledsBottom, ledsRight, ledsTop, ledsLeft, offset, totalLeds); cout << "Press Enter to turn off LEDs and exit.\n"; cout.flush(); auto notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, &app); QObject::connect(notifier, &QSocketNotifier::activated, [=, &app]() { colors->fill(Qt::black); wledClient->setLedsColor(*colors); app.quit(); }); }); QObject::connect(configClient, &WledConfigClient::error, [&](const QString &message) { qCritical() << "Error:" << message; app.quit(); }); configClient->getInfo(); return app.exec(); } WledConfigClient client(wledAddress); QObject::connect(&client, &WledConfigClient::infoReceived, [&](const QJsonObject &info) { if (parser.isSet(demoOption) || parser.isSet(flashOption)) { int totalLeds = info["leds"].toObject()["count"].toInt(); if (parser.isSet(demoOption)) { runDemoMode(wledAddress, wledPort, totalLeds); app.quit(); } else if (parser.isSet(flashOption)) { runFlashMode(wledAddress, wledPort, totalLeds, parser.value(flashOption)); app.quit(); } } else { QJsonDocument doc(info); QFile stdoutFile; stdoutFile.open(stdout, QFile::WriteOnly); QTextStream ts(&stdoutFile); ts << "WLED Configuration Info:\n" << doc.toJson(QJsonDocument::Indented) << Qt::endl; stdoutFile.close(); QString saveFilePath = parser.value(saveOption); if (!saveFilePath.isEmpty()) { QFile file(saveFilePath); if (file.open(QIODevice::WriteOnly)) { file.write(doc.toJson(QJsonDocument::Indented)); file.close(); qDebug() << "Configuration saved to" << saveFilePath; } else { qWarning() << "Failed to save configuration to" << saveFilePath << ":" << file.errorString(); } } app.quit(); } }); QObject::connect(&client, &WledConfigClient::error, [&](const QString &message) { qCritical() << "Error:" << message; app.quit(); }); client.getInfo(); return app.exec(); } // ANSI Color Codes const QString ANSI_RESET = "\033[0m"; const QString ANSI_RED_BG = "\033[41m"; const QString ANSI_GREEN_BG = "\033[42m"; const QString ANSI_BLUE_BG = "\033[44m"; const QString ANSI_YELLOW_BG = "\033[43m"; void printAsciiLayout(int ledsBottom, int ledsRight, int ledsTop, int ledsLeft, int offset, int totalLeds) { QTextStream cout(stdout); int maxHorizontal = qMax(ledsTop, ledsBottom); int maxVertical = qMax(ledsLeft, ledsRight); // Top Row cout << " "; // Indent for left side for (int i = 0; i < ledsTop; ++i) { cout << ANSI_BLUE_BG << " " << ANSI_RESET; // Two spaces for a square } cout << "\n"; // Middle Rows (Left and Right) for (int i = 0; i < maxVertical; ++i) { if (i < ledsLeft) { cout << ANSI_YELLOW_BG << " " << ANSI_RESET; } else { cout << " "; // Empty space if no left LED } // Calculate spaces between left and right int horizontalSpace = maxHorizontal * 2; // Each LED is two chars cout << QString(horizontalSpace, ' '); if (i < ledsRight) { cout << ANSI_GREEN_BG << " " << ANSI_RESET; } cout << "\n"; } // Bottom Row cout << " "; // Indent for left side for (int i = 0; i < ledsBottom; ++i) { cout << ANSI_RED_BG << " " << ANSI_RESET; } cout << "\n"; cout << "\nLayout details:\n"; cout << " Bottom LEDs (Red): " << ledsBottom << "\n"; cout << " Right LEDs (Green): " << ledsRight << "\n"; cout << " Top LEDs (Blue): " << ledsTop << "\n"; cout << " Left LEDs (Yellow): " << ledsLeft << "\n"; cout << " Offset: " << offset << "\n"; cout << " Total LEDs: " << totalLeds << "\n"; cout.flush(); } void runDemoMode(const QString &address, quint16 port, int totalLeds) { qDebug() << "Running demo mode with" << totalLeds << "LEDs on" << address << ":" << port; qDebug() << "Press Ctrl+C to exit."; WledClient client(address, port); QVector colors(totalLeds, Qt::black); // Seed the random number generator QTime time = QTime::currentTime(); srand(time.msec()); while (true) { // Turn on LEDs one by one with random colors for (int i = 0; i < totalLeds; ++i) { colors[i] = QColor::fromRgb(rand() % 256, rand() % 256, rand() % 256); client.setLedsColor(colors); delay(50); } // Turn off all LEDs colors.fill(Qt::black); client.setLedsColor(colors); delay(1000); } } void runFlashMode(const QString &address, quint16 port, int totalLeds, const QString &flashParams) { qDebug() << "Running flash mode with params:" << flashParams; QStringList params = flashParams.split(','); if (params.size() != 3) { qCritical() << "Error: Invalid flash parameters. Expected format: startIndex,count,color"; return; } bool ok1, ok2; int startIndex = params[0].toInt(&ok1); int count = params[1].toInt(&ok2); QColor color(params[2]); if (!ok1 || !ok2 || !color.isValid()) { qCritical() << "Error: Invalid flash parameters. Could not parse values."; return; } if (startIndex < 0 || count <= 0 || startIndex + count > totalLeds) { qCritical() << "Error: Invalid range. Start index must be non-negative, count must be positive, and the range must be within the total number of LEDs (" << totalLeds << ")."; return; } WledClient client(address, port); QVector colors(totalLeds, Qt::black); for (int i = 0; i < count; ++i) { colors[startIndex + i] = color; } client.setLedsColor(colors); qDebug() << "Flashed" << count << "LEDs starting from index" << startIndex << "with color" << color.name(); } void delay(int milliseconds) { QEventLoop loop; QTimer::singleShot(milliseconds, &loop, &QEventLoop::quit); loop.exec(); }