From 1ad97b9b71669af184a898f7a5d8489571e881fb Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 18 Nov 2023 14:03:47 +0100 Subject: [PATCH] Added individual LED control to API and WebUI --- data | 2 +- src/lib/config.cpp | 2 +- src/lib/led_handler.cpp | 40 ++++++++++++--- src/lib/led_handler.hpp | 5 ++ src/lib/webserver.cpp | 105 +++++++++++++++++++++++++++++++++++++--- src/lib/webserver.hpp | 1 + 6 files changed, 139 insertions(+), 16 deletions(-) diff --git a/data b/data index 58fa1e7..d3c6e52 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 58fa1e7ecb49388bc4b59444ab3f5af7b3219699 +Subproject commit d3c6e52f3f5bae6054f8fc05b241a1e9b0a6ca1c diff --git a/src/lib/config.cpp b/src/lib/config.cpp index 7d457e8..82b8b50 100644 --- a/src/lib/config.cpp +++ b/src/lib/config.cpp @@ -209,7 +209,7 @@ void finishSetup() if (preferences.getBool("ledStatus", false)) { - setLights(preferences.getUInt("ledColor", 0xFFCC00)); + restoreLedState(); } else { diff --git a/src/lib/led_handler.cpp b/src/lib/led_handler.cpp index 4a71f94..af6c4e0 100644 --- a/src/lib/led_handler.cpp +++ b/src/lib/led_handler.cpp @@ -250,18 +250,44 @@ void setLights(uint32_t color) { bool ledStatus = true; - if (color == pixels.Color(0, 0, 0)) - { - ledStatus = false; - } else { - preferences.putUInt("ledColor", color); - } - preferences.putBool("ledStatus", ledStatus); + for (int i = 0; i < NEOPIXEL_COUNT; i++) { pixels.setPixelColor(i, color); } + pixels.show(); + + if (color == pixels.Color(0, 0, 0)) + { + ledStatus = false; + } else { + saveLedState(); + } + preferences.putBool("ledStatus", ledStatus); + +} + +void saveLedState() { + for (int i = 0; i < pixels.numPixels(); i++) + { + int pixelColor = pixels.getPixelColor(i); + char key[12]; + snprintf(key, 12, "%s%d", "ledColor_", i); + preferences.putUInt(key, pixelColor); + } + + xTaskNotifyGive(eventSourceTaskHandle); +} + +void restoreLedState() { + for (int i = 0; i < pixels.numPixels(); i++) + { + char key[12]; + snprintf(key, 12, "%s%d", "ledColor_", i); + uint pixelColor = preferences.getUInt(key, pixels.Color(0,0,0)); + pixels.setPixelColor(i, pixelColor); + } pixels.show(); } diff --git a/src/lib/led_handler.hpp b/src/lib/led_handler.hpp index c28938f..38490dd 100644 --- a/src/lib/led_handler.hpp +++ b/src/lib/led_handler.hpp @@ -2,6 +2,9 @@ #include #include +#include +#include +#include "webserver.hpp" #include "shared.hpp" #ifndef NEOPIXEL_PIN @@ -38,6 +41,8 @@ void blinkDelay(int d, int times); void blinkDelayColor(int d, int times, uint r, uint g, uint b); void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2); void clearLeds(); +void saveLedState(); +void restoreLedState(); QueueHandle_t getLedTaskQueue(); bool queueLedEffect(uint effect); void setLights(int r, int g, int b); diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index 99f387e..9064547 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -6,7 +6,6 @@ TaskHandle_t eventSourceTaskHandle; void setupWebserver() { - events.onConnect([](AsyncEventSourceClient *client) { client->send("welcome", NULL, millis(), 1000); }); @@ -39,6 +38,9 @@ void setupWebserver() AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/api/show/custom", onApiShowTextAdvanced); server.addHandler(handler); + AsyncCallbackJsonWebHandler *lightsJsonHandler = new AsyncCallbackJsonWebHandler("/api/lights", onApiLightsSetJson); + server.addHandler(lightsJsonHandler); + server.on("/api/lights/off", HTTP_GET, onApiLightsOff); server.on("/api/lights/color", HTTP_GET, onApiLightsSetColor); server.on("/api/lights", HTTP_GET, onApiLightsStatus); @@ -109,15 +111,16 @@ StaticJsonDocument<512> getLedStatusObject() { StaticJsonDocument<512> root; JsonArray colors = root.createNestedArray("data"); -// Adafruit_NeoPixel pix = getPixels(); + // Adafruit_NeoPixel pix = getPixels(); - for (uint i = 0; i < pixels.numPixels(); i++) { - uint32_t pixColor = pixels.getPixelColor(i); + for (uint i = 0; i < pixels.numPixels(); i++) + { + uint32_t pixColor = pixels.getPixelColor(pixels.numPixels() - i - 1); uint alpha = (pixColor >> 24) & 0xFF; uint red = (pixColor >> 16) & 0xFF; uint green = (pixColor >> 8) & 0xFF; uint blue = pixColor & 0xFF; - char hexColor[8]; + char hexColor[8]; sprintf(hexColor, "#%02X%02X%02X", red, green, blue); JsonObject object = colors.createNestedObject(); @@ -677,7 +680,7 @@ void onApiLightsStatus(AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream("application/json"); - serializeJson(getLedStatusObject(), *response); + serializeJson(getLedStatusObject()["data"], *response); request->send(response); } @@ -708,19 +711,107 @@ void onApiLightsSetColor(AsyncWebServerRequest *request) } } +void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json) +{ + if (request->method() == HTTP_PATCH) { + + JsonArray lights = json.as(); + + if (lights.size() != pixels.numPixels()) + { + Serial.printf("Invalid values for LED set %d\n", lights.size()); + request->send(400); + return; + } + + for (uint i = 0; i < pixels.numPixels(); i++) + { + unsigned int red, green, blue; + + if (lights[i].containsKey("red") && lights[i].containsKey("green") && lights[i].containsKey("blue")) + { + + red = lights[i]["red"].as(); + green = lights[i]["green"].as(); + blue = lights[i]["blue"].as(); + } + else if (lights[i].containsKey("hex")) + { + if (!sscanf(lights[i]["hex"].as().c_str(), "#%02X%02X%02X", &red, &green, &blue) == 3) + { + Serial.printf("Invalid hex for LED %d\n", i); + request->send(400); + return; + } + } + else + { + Serial.printf("No valid color for LED %d\n", i); + request->send(400); + return; + } + + pixels.setPixelColor((pixels.numPixels() - i - 1), pixels.Color( + red, + green, + blue)); + } + + pixels.show(); + saveLedState(); + + request->send(200); + } else { + request->send(404); + } +} + void onIndex(AsyncWebServerRequest *request) { request->send(LittleFS, "/index.html", String(), false); } void onNotFound(AsyncWebServerRequest *request) { + // Serial.printf("NotFound, URL[%s]\n", request->url()); + + // Serial.printf("NotFound, METHOD[%s]\n", request->methodToString()); + + // int headers = request->headers(); + // int i; + // for (i = 0; i < headers; i++) + // { + // AsyncWebHeader *h = request->getHeader(i); + // Serial.printf("NotFound HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); + // } + + // int params = request->params(); + // for (int i = 0; i < params; i++) + // { + // AsyncWebParameter *p = request->getParam(i); + // if (p->isFile()) + // { // p->isPost() is also true + // Serial.printf("NotFound FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); + // } + // else if (p->isPost()) + // { + // Serial.printf("NotFound POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); + // } + // else + // { + // Serial.printf("NotFound GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); + // } + // } + // Access-Control-Request-Method == POST might be better if (request->method() == HTTP_OPTIONS || request->hasHeader("Sec-Fetch-Mode")) { + // Serial.printf("NotFound, Return[%d]\n", 200); + request->send(200); } else { - request->send(200); + // Serial.printf("NotFound, Return[%d]\n", 404); + request->send(404); } }; diff --git a/src/lib/webserver.hpp b/src/lib/webserver.hpp index b929b69..82e89cc 100644 --- a/src/lib/webserver.hpp +++ b/src/lib/webserver.hpp @@ -36,6 +36,7 @@ void onApiFullRefresh(AsyncWebServerRequest *request); void onApiLightsStatus(AsyncWebServerRequest *request); void onApiLightsOff(AsyncWebServerRequest *request); void onApiLightsSetColor(AsyncWebServerRequest *request); +void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json); void onApiRestart(AsyncWebServerRequest *request);