diff --git a/docs/api.md b/docs/api.md index ab0c0ac6..8a6507d1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -20,6 +20,24 @@ Plays a RTTTL sound from the MELODIES folder. | --- | --- | --- | --- | | `[PREFIX]/sound` | `http://[IP]/api/sound` | {"sound":"alarm"} | POST | +## Moodlight +Allows to set the whole matrix to a custom color. + +| Topic | URL | Payload/Body | HTTP method | +| --- | --- | --- | --- | +| `[PREFIX]/moodlight` | `http://[IP]/api/moodlight` | see below | POST | + +Possible moodlight options: +```json +{"brightness":200,"kelvin":2300} +``` +```json +{"brightness":200,"color":[155,38,182]} +``` +```json +{"brightness":200,"color":"#FF00FF"} +``` + ## Colored indicators @@ -93,58 +111,37 @@ Here's an example JSON object to display the text "Hello, AWTRIX Light!" with th } ``` -### Drawing Instructions -!> Pease note: Depending on the amount of objects, the RAM usage can be very high. This could cause freezes or reboots. - It's important to be mindful of the number of objects and the complexity of the drawing instructions to avoid performance issues. +### Drawing Instructions +!> Please note: Depending on the number of objects, the RAM usage can be very high. This could cause freezes or reboots. +It's important to be mindful of the number of objects and the complexity of the drawing instructions to avoid performance issues. -Each drawing instruction is an object with a required command key `c` and additional keys depending on the command: +Each drawing instruction is an object with a required command key and an array of values depending on the command: -| Command | Additional Keys | Description | -| ------- | --------------- | ----------- | -| `dp` | `x`, `y`, `cl` | Draw a pixel at position (`x`, `y`) with color `cl` | -| `dl` | `x0`, `y0`, `x1`, `y1`, `cl` | Draw a line from (`x0`, `y0`) to (`x1`, `y1`) with color `cl` | -| `dr` | `x`, `y`, `w`, `h`, `cl` | Draw a rectangle with top-left corner at (`x`, `y`), width `w`, height `h`, and color `cl` | -| `df` | `x`, `y`, `w`, `h`, `cl` | Draw a filled rectangle with top-left corner at (`x`, `y`), width `w`, height `h`, and color `cl` | -| `dc` | `x`, `y`, `r`, `cl` | Draw a circle with center at (`x`, `y`), radius `r`, and color `cl` | -| `dfc` | `x`, `y`, `r`, `cl` | Draw a filled circle with center at (`x`, `y`), radius `r`, and color `cl` | -| `dt` | `x`, `y`, `t`, `cl` | Draw text `t` with top-left corner at (`x`, `y`) and color `cl` | - +| Command | Array Values | Description | +| ------- | -------------------- | ----------- | +| `dp` | `[x, y, cl]` | Draw a pixel at position (`x`, `y`) with color `cl` | +| `dl` | `[x0, y0, x1, y1, cl]` | Draw a line from (`x0`, `y0`) to (`x1`, `y1`) with color `cl` | +| `dr` | `[x, y, w, h, cl]` | Draw a rectangle with top-left corner at (`x`, `y`), width `w`, height `h`, and color `cl` | +| `df` | `[x, y, w, h, cl]` | Draw a filled rectangle with top-left corner at (`x`, `y`), width `w`, height `h`, and color `cl` | +| `dc` | `[x, y, r, cl]` | Draw a circle with center at (`x`, `y`), radius `r`, and color `cl` | +| `dfc` | `[x, y, r, cl]` | Draw a filled circle with center at (`x`, `y`), radius `r`, and color `cl` | +| `dt` | `[x, y, t, cl]` | Draw text `t` with top-left corner at (`x`, `y`) and color `cl` | -Color values can be a hex string or an array of R, G, B values: -`"#FFFFFF" or [255, 255, 0]` +Color values can be a hex string or an array of R, G, B values: +`"#FFFFFF" or [255, 255, 0]` -### Example +### Example Here's an example JSON object to draw a red circle, a blue rectangle, and the text "Hello" in green: -```json -{ "draw": [ - { - "c": "dc", - "x": 28, - "y": 4, - "r": 3, - "cl": "#FF0000" - }, - { - "c": "dr", - "x": 20, - "y": 4, - "w": 4, - "h": 4, - "cl": "#0000FF" - }, - { - "c": "dt", - "x": 0, - "y": 0, - "t": "Hello", - "cl": "#00FF00" - } - ] -} -``` - +```json +{"draw":[ + {"dc": [28, 4, 3, "#FF0000"]}, + {"dr": [20, 4, 4, 4, "#0000FF"]}, + {"dt": [0, 0, "Hello", "#00FF00"]} +]} +``` + ### Display a text in colored fragments You can display a text where you allowed to colorize fragments of the text. Simply send an array of your fragments, containing `"t"` as your textfragment and `"c"` for the color hex value`. diff --git a/lib/webserver/src/esp-fs-webserver.cpp b/lib/webserver/src/esp-fs-webserver.cpp index 9cb87a16..212b111a 100644 --- a/lib/webserver/src/esp-fs-webserver.cpp +++ b/lib/webserver/src/esp-fs-webserver.cpp @@ -114,7 +114,7 @@ bool FSWebServer::begin(const char *path) // OTA update via webbrowser m_httpUpdater.setup(webserver); - webserver->enableCrossOrigin(true); + webserver->enableCORS(true); webserver->setContentLength(1024); @@ -605,7 +605,7 @@ void FSWebServer::replyOK() void FSWebServer::replyToCLient(int msg_type = 0, const char *msg = "") { - webserver->sendHeader("Access-Control-Allow-Origin", "*"); + //webserver->sendHeader("Access-Control-Allow-Origin", "*"); switch (msg_type) { case OK: diff --git a/src/Apps.h b/src/Apps.h index d9fb8851..a5287c45 100644 --- a/src/Apps.h +++ b/src/Apps.h @@ -28,7 +28,7 @@ String WEATHER_HUM; struct CustomApp { - std::vector drawInstructions; + String drawInstructions; int16_t scrollposition = 0; int16_t scrollDelay = 0; String text; @@ -66,7 +66,7 @@ std::map customApps; struct Notification { - std::vector drawInstructions; + String drawInstructions; int16_t scrollposition = 34; int16_t scrollDelay = 0; String text; @@ -300,7 +300,7 @@ void MenuApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer * if (!MenuManager.inMenu) return; matrix->fillScreen(0); - DisplayManager.printText(0, 6, utf8ascii(MenuManager.menutext()).c_str(), true, true); + DisplayManager.printText(0, 6, utf8ascii(MenuManager.menutext()).c_str(), true, 2); } void AlarmApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer *gifPlayer) @@ -571,7 +571,7 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState } } - if (ca->drawInstructions.size() > 0) + if (ca->drawInstructions.length() > 0) { DisplayManager.processDrawInstructions(x, y, ca->drawInstructions); } @@ -811,7 +811,7 @@ void NotifyApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer DisplayManager.drawProgressBar((hasIcon ? 9 : 0), 7, notify.progress, notify.pColor, notify.pbColor); } - if (notify.drawInstructions.size() > 0) + if (notify.drawInstructions.length() > 0) { DisplayManager.processDrawInstructions(0, 0, notify.drawInstructions); } diff --git a/src/DisplayManager.cpp b/src/DisplayManager.cpp index 6f9bdfc4..72a9e8eb 100644 --- a/src/DisplayManager.cpp +++ b/src/DisplayManager.cpp @@ -49,8 +49,6 @@ CRGB leds[MATRIX_WIDTH * MATRIX_HEIGHT]; FastLED_NeoMatrix *matrix = new FastLED_NeoMatrix(leds, 8, 8, 4, 1, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE); MatrixDisplayUi *ui = new MatrixDisplayUi(matrix); -uint8_t lastBrightness; - DisplayManager_ &DisplayManager_::getInstance() { static DisplayManager_ instance; @@ -263,11 +261,34 @@ void pushCustomApp(String name, int position) void removeCustomAppFromApps(const String &name, bool setApps) { - auto it = std::find_if(Apps.begin(), Apps.end(), [&name](const std::pair &appPair) - { return appPair.first == name; }); + // Remove apps from Apps list + auto it = Apps.begin(); + while (it != Apps.end()) + { + if (it->first.startsWith(name)) + { + it = Apps.erase(it); + } + else + { + ++it; + } + } + + // Remove apps from customApps map + auto mapIt = customApps.begin(); + while (mapIt != customApps.end()) + { + if (mapIt->first.startsWith(name)) + { + mapIt = customApps.erase(mapIt); + } + else + { + ++mapIt; + } + } - Apps.erase(it); - customApps.erase(customApps.find(name)); if (setApps) ui->setApps(Apps); DisplayManager.getInstance().setAutoTransition(true); @@ -308,15 +329,47 @@ bool parseFragmentsText(const String &jsonText, std::vector &colors, s return true; } -void DisplayManager_::generateCustomPage(const String &name, const char *json) +void DisplayManager_::parseCustomPage(const String &name, const char *json) { - if (strcmp(json, "") == 0 && customApps.count(name)) + + if (strcmp(json, "") == 0) { removeCustomAppFromApps(name, true); return; } + DynamicJsonDocument doc(4096); + DeserializationError error = deserializeJson(doc, json); + if (error) + { + doc.clear(); + return; + } - StaticJsonDocument<2048> doc; + if (doc.is()) + { + generateCustomPage(name, json); + } + else if (doc.is()) + { + JsonArray customPagesArray = doc.as(); + int cpIndex = 0; + for (JsonVariant customPage : customPagesArray) + { + JsonObject customPageObject = customPage.as(); + String customPageJson; + serializeJson(customPageObject, customPageJson); + generateCustomPage(name + String(cpIndex), customPageJson.c_str()); + ++cpIndex; + } + } + + doc.clear(); +} + +void DisplayManager_::generateCustomPage(const String &name, const char *json) +{ + + DynamicJsonDocument doc(2048); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -447,16 +500,13 @@ void DisplayManager_::generateCustomPage(const String &name, const char *json) customApp.lineSize = 0; } - customApp.drawInstructions.clear(); if (doc.containsKey("draw")) { - JsonArray instructions = doc["draw"]; - for (JsonObject instruction : instructions) - { - String instructionStr; - serializeJson(instruction, instructionStr); - customApp.drawInstructions.push_back(instructionStr); - } + customApp.drawInstructions = doc["draw"].as(); + } + else + { + customApp.drawInstructions = ""; } customApp.duration = doc.containsKey("duration") ? doc["duration"].as() * 1000 : 0; @@ -571,16 +621,14 @@ void DisplayManager_::generateNotification(const char *json) notify.background = getColorFromJsonVariant(background, 0); } notify.flag = true; - notify.drawInstructions.clear(); + if (doc.containsKey("draw")) { - JsonArray instructions = doc["draw"]; - for (JsonObject instruction : instructions) - { - String instructionStr; - serializeJson(instruction, instructionStr); - notify.drawInstructions.push_back(instructionStr); - } + notify.drawInstructions = doc["draw"].as(); + } + else + { + notify.drawInstructions = ""; } notify.duration = doc.containsKey("duration") ? doc["duration"].as() * 1000 : TIME_PER_APP; @@ -859,6 +907,9 @@ void DisplayManager_::tick() { // handled by the DMXFrame callback } + else if (MOODLIGHT_MODE) + { + } else { ui->update(); @@ -1193,7 +1244,7 @@ void DisplayManager_::updateAppVector(const char *json) { if (appIt != Apps.end()) { - + Apps.erase(appIt); } } @@ -1304,7 +1355,7 @@ void DisplayManager_::powerStateParse(const char *json) DeserializationError error = deserializeJson(doc, json); if (error) { - DEBUG_PRINTLN(F("Failed to parse json")); + setPower((strcmp(json, "true") == 0 || strcmp(json, "1") == 0) ? true : false); return; } @@ -1508,7 +1559,15 @@ void DisplayManager_::setNewSettings(const char *json) if (error) return; - TIME_PER_APP = doc.containsKey("ATIME") ? doc["ATIME"] : TIME_PER_APP; + if (doc.containsKey("ATIME")) + { + int atime = doc["ATIME"].as(); + TIME_PER_APP = atime * 1000; + } + else + { + TIME_PER_APP = TIME_PER_APP; + } TIME_PER_TRANSITION = doc.containsKey("TSPEED") ? doc["TSPEED"] : TIME_PER_TRANSITION; MATRIX_FPS = doc.containsKey("FPS") ? doc["FPS"] : MATRIX_FPS; BRIGHTNESS = doc.containsKey("BRI") ? doc["BRI"] : BRIGHTNESS; @@ -1688,70 +1747,141 @@ void DisplayManager_::reorderApps(const String &jsonString) ui->forceResetState(); } -void DisplayManager_::processDrawInstructions(int16_t xOffset, int16_t yOffset, const std::vector &drawInstructions) +void DisplayManager_::processDrawInstructions(int16_t xOffset, int16_t yOffset, String &drawInstructions) { - for (const String &instructionStr : drawInstructions) + StaticJsonDocument<512> doc; + DeserializationError error = deserializeJson(doc, drawInstructions); + + if (error) { - StaticJsonDocument<128> instructionDoc; - deserializeJson(instructionDoc, instructionStr); - JsonObject instruction = instructionDoc.as(); + Serial.println("Error parsing JSON draw instructions"); + return; + } - String command = instruction["c"].as(); - Serial.println(command); - auto cl = instruction["cl"]; - uint16_t color = getColorFromJsonVariant(cl, TEXTCOLOR_565); - if (command == "dp") - { - int x = instruction["x"].as(); - int y = instruction["y"].as(); - matrix->drawPixel(x + xOffset, y + yOffset, color); - } - else if (command == "dl") - { - int x0 = instruction["x0"].as(); - int y0 = instruction["y0"].as(); - int x1 = instruction["x1"].as(); - int y1 = instruction["y1"].as(); - matrix->drawLine(x0 + xOffset, y0 + yOffset, x1 + xOffset, y1 + yOffset, color); - } - else if (command == "dr") - { - int x = instruction["x"].as(); - int y = instruction["y"].as(); - int w = instruction["w"].as(); - int h = instruction["h"].as(); - matrix->drawRect(x + xOffset, y + yOffset, w, h, color); - } - else if (command == "df") - { - int x = instruction["x"].as(); - int y = instruction["y"].as(); - int w = instruction["w"].as(); - int h = instruction["h"].as(); - matrix->fillRect(x + xOffset, y + yOffset, w, h, color); - } - else if (command == "dc") - { - int x = instruction["x"].as(); - int y = instruction["y"].as(); - int r = instruction["r"].as(); - matrix->drawCircle(x + xOffset, y + yOffset, r, color); - } - else if (command == "dfc") + if (!doc.is()) + { + Serial.println("Invalid JSON draw instructions format"); + return; + } + + JsonArray instructions = doc.as(); + for (JsonObject instruction : instructions) + { + for (auto kvp : instruction) { - int x = instruction["x"].as(); - int y = instruction["y"].as(); - int r = instruction["r"].as(); - matrix->fillCircle(x + xOffset, y + yOffset, r, color); + String command = kvp.key().c_str(); + + JsonArray params = kvp.value().as(); + if (command == "dp") + { + int x = params[0].as(); + int y = params[1].as(); + auto color1 = params[2]; + uint16_t color = getColorFromJsonVariant(color1, TEXTCOLOR_565); + matrix->drawPixel(x + xOffset, y + yOffset, color); + } + else if (command == "dl") + { + int x0 = params[0].as(); + int y0 = params[1].as(); + int x1 = params[2].as(); + int y1 = params[3].as(); + auto color2 = params[4]; + uint16_t color = getColorFromJsonVariant(color2, TEXTCOLOR_565); + matrix->drawLine(x0 + xOffset, y0 + yOffset, x1 + xOffset, y1 + yOffset, color); + } + else if (command == "dr") + { + int x = params[0].as(); + int y = params[1].as(); + int w = params[2].as(); + int h = params[3].as(); + auto color3 = params[4]; + uint16_t color = getColorFromJsonVariant(color3, TEXTCOLOR_565); + matrix->drawRect(x + xOffset, y + yOffset, w, h, color); + } + else if (command == "df") + { + int x = params[0].as(); + int y = params[1].as(); + int w = params[2].as(); + int h = params[3].as(); + auto color4 = params[4]; + uint16_t color = getColorFromJsonVariant(color4, TEXTCOLOR_565); + matrix->fillRect(x + xOffset, y + yOffset, w, h, color); + } + else if (command == "dc") + { + int x = params[0].as(); + int y = params[1].as(); + int r = params[2].as(); + auto color5 = params[3]; + uint16_t color = getColorFromJsonVariant(color5, TEXTCOLOR_565); + matrix->drawCircle(x + xOffset, y + yOffset, r, color); + } + else if (command == "dfc") + { + int x = params[0].as(); + int y = params[1].as(); + int r = params[2].as(); + auto color6 = params[3]; + uint16_t color = getColorFromJsonVariant(color6, TEXTCOLOR_565); + matrix->fillCircle(x + xOffset, y + yOffset, r, color); + } + else if (command == "dt") + { + int x = params[0].as(); + int y = params[1].as(); + String text = params[2].as(); + auto color7 = params[3]; + uint16_t color = getColorFromJsonVariant(color7, TEXTCOLOR_565); + matrix->setCursor(x + xOffset, y + yOffset + 5); + matrix->setTextColor(color); + matrix->print(text); + } } - else if (command == "dt") + } +} + +void DisplayManager_::moodlight(const char *json) +{ + if (strcmp(json, "") == 0) + { + MOODLIGHT_MODE = false; + return; + } + + DynamicJsonDocument doc(512); + DeserializationError error = deserializeJson(doc, json); + if (error) + return; + + int brightness = doc["brightness"] | BRIGHTNESS; + matrix->setBrightness(brightness); + + if (doc.containsKey("kelvin")) + { + int kelvin = doc["kelvin"]; + CRGB color; + color = kelvinToRGB(kelvin); + color.nscale8(brightness); + for (int i = 0; i < 256; i++) { - int x = instruction["x"].as(); - int y = instruction["y"].as(); - String text = instruction["t"].as(); - matrix->setCursor(x + xOffset, y + yOffset + 5); - matrix->setTextColor(color); - matrix->print(text); + leds[i] = color; } } -} + else if (doc.containsKey("color")) + { + auto c = doc["color"]; + uint16_t color565 = getColorFromJsonVariant(c, TEXTCOLOR_565); + matrix->fillScreen(color565); + } + else + { + return; + } + + MOODLIGHT_MODE = true; + + matrix->show(); +} \ No newline at end of file diff --git a/src/DisplayManager.h b/src/DisplayManager.h index a0e98827..7b1c27a9 100644 --- a/src/DisplayManager.h +++ b/src/DisplayManager.h @@ -82,10 +82,12 @@ class DisplayManager_ void showSleepAnimation(); void showCurtainEffect(); void sendAppLoop(); - void processDrawInstructions(int16_t x, int16_t y, const std::vector &drawInstructions); + void processDrawInstructions(int16_t x, int16_t y, String &drawInstructions); String ledsAsJson(); String getAppsWithIcon(); void startE131(); + void parseCustomPage(const String &name, const char *json); + void moodlight(const char *json); }; extern DisplayManager_ &DisplayManager; diff --git a/src/Functions.h b/src/Functions.h index 15daed63..884192ae 100644 --- a/src/Functions.h +++ b/src/Functions.h @@ -31,6 +31,37 @@ const uint8_t PROGMEM gamma8[] = { 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; +CRGB kelvinToRGB(int kelvin) { + float temperature = kelvin / 100.0; + float red, green, blue; + + if (temperature <= 66) { + red = 255; + green = temperature; + green = 99.4708025861 * log(green) - 161.1195681661; + } else { + red = temperature - 60; + red = 329.698727446 * pow(red, -0.1332047592); + green = temperature - 60; + green = 288.1221695283 * pow(green, -0.0755148492); + } + + if (temperature >= 66) { + blue = 255; + } else if (temperature <= 19) { + blue = 0; + } else { + blue = temperature - 10; + blue = 138.5177312231 * log(blue) - 305.0447927307; + } + + red = constrain(red, 0, 255); + green = constrain(green, 0, 255); + blue = constrain(blue, 0, 255); + + return CRGB((uint8_t)red, (uint8_t)green, (uint8_t)blue); +} + CRGB applyGammaCorrection(const CRGB& color) { CRGB correctedColor; correctedColor.r = pgm_read_byte(&gamma8[color.r]); @@ -68,21 +99,33 @@ uint16_t getColorFromJsonVariant(JsonVariant colorVariant, uint16_t defaultColor { if (colorVariant.is()) { - return hexToRgb565(colorVariant.as(),defaultColor); - } - else if (colorVariant.is() && colorVariant.size() == 3) - { - uint8_t r = colorVariant[0]; - uint8_t g = colorVariant[1]; - uint8_t b = colorVariant[2]; - return (r << 11) | (g << 5) | b; + return hexToRgb565(colorVariant.as(), defaultColor); } - else + else if (colorVariant.is()) { - return defaultColor; + JsonArray colorArray = colorVariant.as(); + if (colorArray.size() == 3) // RGB + { + uint8_t r = colorArray[0]; + uint8_t g = colorArray[1]; + uint8_t b = colorArray[2]; + return (r << 11) | (g << 5) | b; + } + else if (colorArray.size() == 4 && colorArray[0] == "HSV") // HSV + { + uint8_t h = colorArray[1]; + uint8_t s = colorArray[2]; + uint8_t v = colorArray[3]; + CRGB rgb; + hsv2rgb_spectrum(CHSV(h, s, v), rgb); + return (rgb.red << 11) | (rgb.green << 5) | rgb.blue; + } } + + return defaultColor; } + uint16_t getTextWidth(const char *text, byte textCase) { uint16_t width = 0; diff --git a/src/Globals.cpp b/src/Globals.cpp index 136850a2..e91d23dd 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -214,7 +214,7 @@ IPAddress gateway; IPAddress subnet; IPAddress primaryDNS; IPAddress secondaryDNS; -const char *VERSION = "0.60"; +const char *VERSION = "0.61"; String MQTT_HOST = ""; uint16_t MQTT_PORT = 1883; @@ -238,7 +238,7 @@ String NET_GW = "192.168.178.1"; String NET_SN = "255.255.255.0"; String NET_PDNS = "8.8.8.8"; String NET_SDNS = "1.1.1.1"; -int TIME_PER_APP = 7000; +long TIME_PER_APP = 7000; uint8_t MATRIX_FPS = 23; int TIME_PER_TRANSITION = 400; String NTP_SERVER = "de.pool.ntp.org"; @@ -302,3 +302,4 @@ uint16_t BAT_COLOR = 0; uint16_t TEMP_COLOR = 0; uint16_t HUM_COLOR = 0; bool ARTNET_MODE; +bool MOODLIGHT_MODE; \ No newline at end of file diff --git a/src/Globals.h b/src/Globals.h index 16d05afb..0aca7792 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -55,7 +55,7 @@ extern String NET_GW; extern String NET_SN; extern String NET_PDNS; extern String NET_SDNS; -extern int TIME_PER_APP; +extern long TIME_PER_APP; extern uint8_t MATRIX_FPS; extern int TIME_PER_TRANSITION; extern String NTP_SERVER; @@ -117,4 +117,5 @@ extern uint16_t BAT_COLOR; extern uint16_t TEMP_COLOR; extern uint16_t HUM_COLOR; extern bool ARTNET_MODE; +extern bool MOODLIGHT_MODE; #endif // Globals_H \ No newline at end of file diff --git a/src/MQTTManager.cpp b/src/MQTTManager.cpp index 4bad3af5..7abb0c77 100644 --- a/src/MQTTManager.cpp +++ b/src/MQTTManager.cpp @@ -235,6 +235,12 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) DisplayManager.indicatorParser(2, payloadCopy); delete[] payloadCopy; return; + } + if (strTopic.equals(MQTT_PREFIX + "/moodlight")) + { + DisplayManager.moodlight(payloadCopy); + delete[] payloadCopy; + return; } if (strTopic.equals(MQTT_PREFIX + "/reboot")) { @@ -257,7 +263,7 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) if (topic_str.startsWith(prefix)) { topic_str = topic_str.substring(prefix.length()); - DisplayManager.generateCustomPage(topic_str, payloadCopy); + DisplayManager.parseCustomPage(topic_str, payloadCopy); } delete[] payloadCopy; @@ -289,12 +295,14 @@ void onMqttConnected() "/timeformat", "/dateformat", "/reboot", + "/moodlight", "/sound"}; for (const char *topic : topics) { DEBUG_PRINTF("Subscribe to topic %s", topic); String fullTopic = prefix + topic; mqtt.subscribe(fullTopic.c_str()); + delay(30); } } diff --git a/src/MatrixDisplayUi.cpp b/src/MatrixDisplayUi.cpp index b6c4a235..bd1c756a 100644 --- a/src/MatrixDisplayUi.cpp +++ b/src/MatrixDisplayUi.cpp @@ -79,9 +79,9 @@ void MatrixDisplayUi::setsetAutoTransitionBackwards() this->state.appTransitionDirection = -1; this->lastTransitionDirection = -1; } -void MatrixDisplayUi::setTimePerApp(uint16_t time) +void MatrixDisplayUi::setTimePerApp(long time) { - this->ticksPerApp = (int)((float)time / (float)updateInterval); + this->ticksPerApp = time / (long)updateInterval; } void MatrixDisplayUi::setTimePerTransition(uint16_t time) { diff --git a/src/MatrixDisplayUi.h b/src/MatrixDisplayUi.h index 2007dc47..db60523b 100644 --- a/src/MatrixDisplayUi.h +++ b/src/MatrixDisplayUi.h @@ -82,7 +82,7 @@ class MatrixDisplayUi AnimationDirection appAnimationDirection = SLIDE_DOWN; int8_t lastTransitionDirection = 1; - uint16_t ticksPerApp = 151; // ~ 5000ms at 30 FPS + long ticksPerApp = 151; // ~ 5000ms at 30 FPS uint16_t ticksPerTransition = 15; // ~ 500ms at 30 FPS bool setAutoTransition = true; @@ -142,7 +142,7 @@ class MatrixDisplayUi /** * Set the approx. time a app is displayed */ - void setTimePerApp(uint16_t time); + void setTimePerApp(long time); /** * Set the approx. time a transition will take diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp index e78ee19a..535e0793 100644 --- a/src/MenuManager.cpp +++ b/src/MenuManager.cpp @@ -246,7 +246,7 @@ void MenuManager_::rightButton() TIME_PER_TRANSITION = min(1200, TIME_PER_TRANSITION + 100); break; case AppTimeMenu: - TIME_PER_APP = min(30000, TIME_PER_APP + 1000); + TIME_PER_APP = min(static_cast(30000), TIME_PER_APP + 1000); break; case TimeFormatMenu: timeFormatIndex = (timeFormatIndex + 1) % timeFormatCount; @@ -313,7 +313,7 @@ void MenuManager_::leftButton() TIME_PER_TRANSITION = max(200, TIME_PER_TRANSITION - 100); break; case AppTimeMenu: - TIME_PER_APP = max(1000, TIME_PER_APP - 1000); + TIME_PER_APP = max(static_cast(1000), TIME_PER_APP - 1000); break; case TimeFormatMenu: timeFormatIndex = (timeFormatIndex == 0) ? timeFormatCount - 1 : timeFormatIndex - 1; diff --git a/src/PeripheryManager.cpp b/src/PeripheryManager.cpp index 21b56773..7b38fb9b 100644 --- a/src/PeripheryManager.cpp +++ b/src/PeripheryManager.cpp @@ -81,11 +81,12 @@ const unsigned long interval_LDR = 100; int total = 0; unsigned long startTime; -const int LDRReadings = 10; +const int LDRReadings = 1000; int TotalLDRReadings[LDRReadings]; float sampleSum = 0.0; float sampleAverage = 0.0; float brightnessPercent = 0.0; +int lastBrightness = 0; // The getter for the instantiated singleton instance PeripheryManager_ &PeripheryManager_::getInstance() @@ -228,7 +229,7 @@ void PeripheryManager_::parseSound(const char *json) DeserializationError error = deserializeJson(doc, json); if (error) { - DEBUG_PRINTLN(F("Failed to parse json")); + playFromFile(String(json)); return; } if (doc.containsKey("sound")) diff --git a/src/ServerManager.cpp b/src/ServerManager.cpp index c8d4906c..ae2255e1 100644 --- a/src/ServerManager.cpp +++ b/src/ServerManager.cpp @@ -75,48 +75,50 @@ void ServerManager_::setup() mws.addJavascript(custom_script); mws.addHandler("/save", HTTP_POST, saveHandler); mws.addHandler("/api/sound", HTTP_POST, []() - { PeripheryManager.parseSound(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { PeripheryManager.parseSound(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); + mws.addHandler("/api/moodlight", HTTP_POST, []() + { DisplayManager.moodlight(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/notify", HTTP_POST, []() - { DisplayManager.generateNotification(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.generateNotification(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/nextapp", HTTP_POST, []() - { DisplayManager.nextApp(); mws.webserver->send(200,"OK"); }); + { DisplayManager.nextApp(); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/previousapp", HTTP_POST, []() - { DisplayManager.previousApp(); mws.webserver->send(200,"OK"); }); + { DisplayManager.previousApp(); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/timer", HTTP_POST, []() - { DisplayManager.gererateTimer(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.gererateTimer(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/notify/dismiss", HTTP_POST, []() - { DisplayManager.dismissNotify(); mws.webserver->send(200,"OK"); }); + { DisplayManager.dismissNotify(); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/apps", HTTP_POST, []() - { DisplayManager.updateAppVector(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.updateAppVector(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/switch", HTTP_POST, []() - { DisplayManager.switchToApp(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.switchToApp(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/apps", HTTP_GET, []() { mws.webserver->send_P(200, "application/json", DisplayManager.getAppsWithIcon().c_str()); }); mws.addHandler("/api/settings", HTTP_POST, []() - { DisplayManager.setNewSettings(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.setNewSettings(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/reorder", HTTP_POST, []() - { DisplayManager.reorderApps(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.reorderApps(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/settings", HTTP_GET, []() { mws.webserver->send_P(200, "application/json", DisplayManager.getSettings().c_str()); }); mws.addHandler("/api/custom", HTTP_POST, []() - { DisplayManager.generateCustomPage(mws.webserver->arg("name"),mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.generateCustomPage(mws.webserver->arg("name"),mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/stats", HTTP_GET, []() { mws.webserver->send_P(200, "application/json", DisplayManager.getStats().c_str()); }); mws.addHandler("/api/screen", HTTP_GET, []() { mws.webserver->send_P(200, "application/json", DisplayManager.ledsAsJson().c_str()); }); mws.addHandler("/api/indicator1", HTTP_POST, []() - { DisplayManager.indicatorParser(1,mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.indicatorParser(1,mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/indicator2", HTTP_POST, []() - { DisplayManager.indicatorParser(2,mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.indicatorParser(2,mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/doupdate", HTTP_POST, []() - { mws.webserver->send(200,"OK"); if (UpdateManager.checkUpdate(true)) + { mws.webserver->send(200,"text/plain","OK"); if (UpdateManager.checkUpdate(true)) { UpdateManager.updateFirmware(); } }); mws.addHandler("/api/power", HTTP_POST, []() - { DisplayManager.powerStateParse(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); + { DisplayManager.powerStateParse(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"text/plain","OK"); }); mws.addHandler("/api/reboot", HTTP_POST, []() - { mws.webserver->send(200,"OK"); delay(200); ESP.restart(); }); + { mws.webserver->send(200,"text/plain","OK"); delay(200); ESP.restart(); }); DEBUG_PRINTLN(F("Webserver loaded")); } mws.addHandler("/version", HTTP_GET, versionHandler);