diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..d9bba67f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,52 @@ +{ + "files.associations": { + "array": "cpp", + "atomic": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "map": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/docs/flasher/firmware/firmware.bin b/docs/flasher/firmware/firmware.bin index dbfec18e..c466ca1a 100644 Binary files a/docs/flasher/firmware/firmware.bin and b/docs/flasher/firmware/firmware.bin differ diff --git a/docs/flasher/firmware/manifest.json b/docs/flasher/firmware/manifest.json index 6ee9cbeb..a917cd0e 100644 --- a/docs/flasher/firmware/manifest.json +++ b/docs/flasher/firmware/manifest.json @@ -1,6 +1,6 @@ { "name": "AWTRIX Light", - "version": "0.33", + "version": "0.35", "home_assistant_domain": "AwtrixLight", "funding_url": "https://blueforcer.de", "new_install_prompt_erase": true, diff --git a/lib/MatrixUI/MatrixDisplayUi.cpp b/lib/MatrixUI/MatrixDisplayUi.cpp index 02f99b3f..51c86785 100644 --- a/lib/MatrixUI/MatrixDisplayUi.cpp +++ b/lib/MatrixUI/MatrixDisplayUi.cpp @@ -4,8 +4,8 @@ * Copyright (c) 2016 by Daniel Eichhorn * Copyright (c) 2016 by Fabrice Weinberg * Copyright (c) 2023 by Stephan Muehl (Blueforcer) - * Note: This old lib for SSD1306 displays has been extremly - * modified for AWTRIX Light and has nothing to do with the original purposes. + * Note: This old lib for SSD1306 displays has been extremly + * modified for AWTRIX Light and has nothing to do with the original purposes. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -89,7 +89,7 @@ void MatrixDisplayUi::setAppAnimation(AnimationDirection dir) this->frameAnimationDirection = dir; } -void MatrixDisplayUi::setApps(const std::vector> &appPairs) +void MatrixDisplayUi::setApps(const std::vector> &appPairs) { delete[] AppFunctions; AppCount = appPairs.size(); @@ -262,8 +262,9 @@ void MatrixDisplayUi::drawApp() y *= dir; x1 *= dir; y1 *= dir; - bool FirstFrame = progress < 0.1; - bool LastFrame = progress > 0.9; + Serial.println(progress); + bool FirstFrame = progress < 0.2; + bool LastFrame = progress > 0.8; this->matrix->drawRect(x, y, x1, y1, matrix->Color(0, 0, 0)); (this->AppFunctions[this->state.currentFrame])(this->matrix, &this->state, x, y, FirstFrame, LastFrame); (this->AppFunctions[this->getnextAppNumber()])(this->matrix, &this->state, x1, y1, FirstFrame, LastFrame); diff --git a/lib/MatrixUI/MatrixDisplayUi.h b/lib/MatrixUI/MatrixDisplayUi.h index 240b2c11..552cdd6d 100644 --- a/lib/MatrixUI/MatrixDisplayUi.h +++ b/lib/MatrixUI/MatrixDisplayUi.h @@ -164,7 +164,7 @@ class MatrixDisplayUi /** * Add frame drawing functions */ - void setApps(const std::vector> &appPairs); + void setApps(const std::vector> &appPairs); // Overlay diff --git a/src/DisplayManager.cpp b/src/DisplayManager.cpp index ecddd366..c45eb28c 100644 --- a/src/DisplayManager.cpp +++ b/src/DisplayManager.cpp @@ -95,7 +95,7 @@ void DisplayManager_::drawJPG(uint16_t x, uint16_t y, fs::File jpgFile) void DisplayManager_::setSettings() { ui.setTargetFPS(MATRIX_FPS); - ui.setTimePerApp(TIME_PER_FRAME); + ui.setTimePerApp(TIME_PER_APP); ui.setTimePerTransition(TIME_PER_TRANSITION); } @@ -128,11 +128,11 @@ bool jpg_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) return 0; } -void DisplayManager_::printText(int16_t x, int16_t y, const char *text, bool centered) +void DisplayManager_::printText(int16_t x, int16_t y, const char *text, bool centered, bool ignoreUppercase) { if (centered) { - uint16_t textWidth = getTextWidth(text); + uint16_t textWidth = getTextWidth(text, ignoreUppercase); int16_t textX = ((32 - textWidth) / 2); matrix.setCursor(textX, y); } @@ -141,7 +141,7 @@ void DisplayManager_::printText(int16_t x, int16_t y, const char *text, bool cen matrix.setCursor(x, y); } - if (UPPERCASE_LETTERS) + if (UPPERCASE_LETTERS && !ignoreUppercase) { size_t length = strlen(text); char upperText[length + 1]; // +1 for the null terminator @@ -184,64 +184,37 @@ void DisplayManager_::HSVtext(int16_t x, int16_t y, const char *text, bool clear } char temp_str[2] = {'\0', '\0'}; temp_str[0] = text[i]; - xpos += getTextWidth(temp_str); + xpos += getTextWidth(temp_str, false); } hueOffset++; if (clear) matrix.show(); } -void pushCustomFrame(uint16_t id) +void pushCustomFrame(String name) { - if (customFrames.count(id) == 0) + + if (customFrames.count(name) == 0) { - uint16_t newID = nativeAppsCount + id; - switch (id) + ++customPagesCount; + void (*customFrames[10])(FastLED_NeoMatrix *, MatrixDisplayUiState *, int16_t, int16_t, bool, bool) = {CFrame1, CFrame2, CFrame3, CFrame4, CFrame5, CFrame6, CFrame7, CFrame8, CFrame9, CFrame10}; + if (customFrames[customPagesCount] != NULL) + { + Apps.push_back(std::make_pair(name, customFrames[customPagesCount])); + ui.setApps(Apps); // Add frames + } + else { - case 1: - Apps.push_back(std::make_pair(newID, CFrame1)); - break; - case 2: - Apps.push_back(std::make_pair(newID, CFrame2)); - break; - case 3: - Apps.push_back(std::make_pair(newID, CFrame3)); - break; - case 4: - Apps.push_back(std::make_pair(newID, CFrame4)); - break; - case 5: - Apps.push_back(std::make_pair(newID, CFrame5)); - break; - case 6: - Apps.push_back(std::make_pair(newID, CFrame6)); - break; - case 7: - Apps.push_back(std::make_pair(newID, CFrame7)); - break; - case 8: - Apps.push_back(std::make_pair(newID, CFrame8)); - break; - case 9: - Apps.push_back(std::make_pair(newID, CFrame9)); - break; - case 10: - Apps.push_back(std::make_pair(newID, CFrame10)); - break; - default: - return; - break; + ++customPagesCount; } - ui.setApps(Apps); // Add frames } } -void removeCustomFrame(uint16_t id) +void removeCustomFrame(const String &name) { - id += nativeAppsCount; - // Suchen Sie nach dem Element, das der ID entspricht - auto it = std::find_if(Apps.begin(), Apps.end(), [id](const std::pair &appPair) - { return appPair.first == id; }); + // Suchen Sie nach dem Element, das dem Namen entspricht + auto it = std::find_if(Apps.begin(), Apps.end(), [&name](const std::pair &appPair) + { return appPair.first == name; }); // Wenn das Element gefunden wurde, entfernen Sie es aus dem Vektor if (it != Apps.end()) @@ -251,13 +224,13 @@ void removeCustomFrame(uint16_t id) } } -void DisplayManager_::generateCustomPage(uint16_t id, String payload) +void DisplayManager_::generateCustomPage(String name, String payload) { - if (payload == "" && customFrames.count(id)) + if (payload == "" && customFrames.count(name)) { - customFrames.erase(customFrames.find(id)); - removeCustomFrame(id); + customFrames.erase(customFrames.find(name)); + removeCustomFrame(name); return; } @@ -268,21 +241,6 @@ void DisplayManager_::generateCustomPage(uint16_t id, String payload) CustomFrame customFrame; - if (id == 0) - { - if (doc.containsKey("id")) - { - customFrame.id = doc["id"].as(); - } - else - { - return; - } - } - - if (id > 10) - return; - if (doc.containsKey("sound")) { customFrame.sound = ("/" + doc["sound"].as() + ".txt"); @@ -292,27 +250,18 @@ void DisplayManager_::generateCustomPage(uint16_t id, String payload) customFrame.sound = ""; } - if (doc.containsKey("name")) - { - customFrame.name = doc["name"].as(); - } - else - { - customFrame.name = "Custom " + String(id); - } - customFrame.rainbow = doc.containsKey("rainbow") ? doc["rainbow"] : false; customFrame.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0; - customFrame.id = id; + customFrame.name = name; customFrame.text = utf8ascii(doc["text"].as()); customFrame.color = doc.containsKey("color") ? doc["color"].is() ? hexToRgb565(doc["color"]) : doc["color"].is() ? hexToRgb565(doc["color"].as()) : TEXTCOLOR_565 : TEXTCOLOR_565; - if (currentCustomFrame != id) + if (currentCustomFrame != name) { - customFrame.scrollposition = 34; + customFrame.scrollposition = 9; } customFrame.repeat = doc.containsKey("repeat") ? doc["repeat"].as() : -1; @@ -333,8 +282,8 @@ void DisplayManager_::generateCustomPage(uint16_t id, String payload) } } - pushCustomFrame(id); - customFrames[id] = customFrame; + pushCustomFrame(name); + customFrames[name] = customFrame; } void DisplayManager_::generateNotification(String payload) @@ -342,7 +291,7 @@ void DisplayManager_::generateNotification(String payload) StaticJsonDocument<1024> doc; deserializeJson(doc, payload); - notify.duration = doc.containsKey("duration") ? doc["duration"].as() * 1000 : TIME_PER_FRAME; + notify.duration = doc.containsKey("duration") ? doc["duration"].as() * 1000 : TIME_PER_APP; notify.text = utf8ascii(doc["text"].as()); notify.repeat = doc.containsKey("repeat") ? doc["repeat"].as() : -1; notify.rainbow = doc.containsKey("rainbow") ? doc["rainbow"].as() : false; @@ -350,7 +299,7 @@ void DisplayManager_::generateNotification(String payload) notify.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0; notify.flag = true; notify.startime = millis(); - notify.scrollposition = 34; + notify.scrollposition = 9; notify.iconWasPushed = false; notify.iconPosition = 0; @@ -387,15 +336,15 @@ void DisplayManager_::generateNotification(String payload) void DisplayManager_::loadApps() { Apps.clear(); - Apps.push_back(std::make_pair(0, TimeFrame)); + Apps.push_back(std::make_pair("time", TimeFrame)); if (SHOW_DATE) - Apps.push_back(std::make_pair(1, DateFrame)); + Apps.push_back(std::make_pair("date", DateFrame)); if (SHOW_TEMP) - Apps.push_back(std::make_pair(2, TempFrame)); + Apps.push_back(std::make_pair("temp", TempFrame)); if (SHOW_HUM) - Apps.push_back(std::make_pair(3, HumFrame)); + Apps.push_back(std::make_pair("hum", HumFrame)); if (SHOW_BATTERY) - Apps.push_back(std::make_pair(4, BatFrame)); + Apps.push_back(std::make_pair("bat", BatFrame)); // if (SHOW_WEATHER) // Apps.push_back(std::make_pair(5, WeatherFrame)); nativeAppsCount = Apps.size(); @@ -531,3 +480,16 @@ void DisplayManager_::gererateTimer(String Payload) int interval = difftime(futureTime, now) * 1000; TimerTicker.attach_ms(interval, timerCallback); } + +void DisplayManager_::switchToApp(String Payload) +{ + DynamicJsonDocument doc(512); + DeserializationError error = deserializeJson(doc, Payload); + if (error) + return; + String name = doc["name"].as(); + bool fast = doc["fast"] | false; + int index = findAppIndexByName(name); + if (index > -1) + ui.transitionToApp(index); +} \ No newline at end of file diff --git a/src/DisplayManager.h b/src/DisplayManager.h index 1f99853a..979ccbc3 100644 --- a/src/DisplayManager.h +++ b/src/DisplayManager.h @@ -44,9 +44,10 @@ class DisplayManager_ void setFPS(uint8_t); void MatrixState(bool); void generateNotification(String); - void generateCustomPage(uint16_t, String); - void printText(int16_t x, int16_t y, const char *text, bool centered); + void generateCustomPage(String, String); + void printText(int16_t x, int16_t y, const char *text, bool centered, bool ignoreUppercase); bool setAutoTransition(bool active); + void switchToApp(String Payload); void drawGIF(uint16_t x, uint16_t y, fs::File gifFile); void drawJPG(uint16_t x, uint16_t y, fs::File jpgFile); }; diff --git a/src/Frames.h b/src/Frames.h index a7d59780..5d97fffb 100644 --- a/src/Frames.h +++ b/src/Frames.h @@ -20,6 +20,7 @@ Ticker downloader; tm timeInfo; uint16_t nativeAppsCount; +uint16_t customPagesCount; int WEATHER_CODE; String WEATHER_TEMP; @@ -27,8 +28,8 @@ String WEATHER_HUM; struct CustomFrame { - uint8_t id; - int16_t scrollposition = 34; + int16_t scrollposition = 0; + int16_t scrollDelay = 0; String text; uint16_t color; File icon; @@ -44,13 +45,13 @@ struct CustomFrame bool iconWasPushed = false; }; -uint8_t currentCustomFrame; -std::map customFrames; +String currentCustomFrame; +std::map customFrames; struct Notification { - uint8_t id; int16_t scrollposition = 34; + int16_t scrollDelay = 0; String text; uint16_t color; File icon; @@ -68,9 +69,35 @@ struct Notification Notification notify; -CustomFrame *getCustomFrameById(uint8_t id) +std::vector> Apps; + +CustomFrame *getCustomFrameById(String name) +{ + return customFrames.count(name) ? &customFrames[name] : nullptr; +} + +String getFrameNameByFunction(AppCallback frameFunction) +{ + for (const auto &appPair : Apps) + { + if (appPair.second == frameFunction) + { + return appPair.first; + } + } + + return ""; // Gibt einen leeren String zurück, wenn die Frame-Funktion nicht gefunden wurde +} + +int findAppIndexByName(const String &name) { - return customFrames.count(id) ? &customFrames[id] : nullptr; + auto it = std::find_if(Apps.begin(), Apps.end(), [&name](const std::pair &appPair) + { return appPair.first == name; }); + if (it != Apps.end()) + { + return std::distance(Apps.begin(), it); + } + return -1; } void TimeFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) @@ -181,7 +208,7 @@ void MenuFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) if (!MenuManager.inMenu) return; matrix->fillScreen(0); - DisplayManager.printText(0, 6, MenuManager.menutext().c_str(), true); + DisplayManager.printText(0, 6, MenuManager.menutext().c_str(), true, true); } void AlarmFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) @@ -191,7 +218,7 @@ void AlarmFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) { matrix->fillScreen(matrix->Color(255, 0, 0)); CURRENT_APP = "Alarm"; - uint16_t textWidth = getTextWidth("ALARM"); + uint16_t textWidth = getTextWidth("ALARM",false); int16_t textX = ((32 - textWidth) / 2); matrix->setTextColor(0); matrix->setCursor(textX, 6); @@ -211,7 +238,7 @@ void TimerFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) matrix->fillScreen(matrix->Color(0, 255, 0)); CURRENT_APP = "Timer"; String menuText = "TIMER"; - uint16_t textWidth = getTextWidth(menuText.c_str()); + uint16_t textWidth = getTextWidth(menuText.c_str(),false); int16_t textX = ((32 - textWidth) / 2); matrix->setTextColor(0); matrix->setCursor(textX, 6); @@ -224,7 +251,7 @@ void TimerFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) } } -void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void ShowCustomFrame(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { // Abort if notify.flag is set if (notify.flag) @@ -233,7 +260,7 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState } // Get custom frame by ID - CustomFrame *cf = getCustomFrameById(id); + CustomFrame *cf = getCustomFrameById(name); // Abort if custom frame not found if (cf == nullptr) @@ -241,26 +268,24 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState return; } - // Initialize custom frame properties if first frame + // reset custom frame properties if last frame if (lastFrame) { cf->iconWasPushed = false; - cf->scrollposition = 34; + cf->scrollposition = 9; cf->iconPosition = 0; + cf->scrollDelay = 0; } - // Update current app and custom frame IDs CURRENT_APP = cf->name; - currentCustomFrame = id; + currentCustomFrame = name; - // Check if there is an icon bool hasIcon = cf->icon; - - // Calculate available display width based on icon uint16_t availableWidth = (hasIcon) ? 24 : 32; - // Disable auto transition if text is repeating and too wide - if ((cf->repeat > 0) && (getTextWidth(cf->text.c_str()) > availableWidth) && (state->frameState == FIXED)) + bool noScrolling = getTextWidth(cf->text.c_str(),false) <= availableWidth; + + if ((cf->repeat > 0) && (getTextWidth(cf->text.c_str(),false) > availableWidth) && (state->frameState == FIXED)) { DisplayManager.setAutoTransition(false); } @@ -269,21 +294,16 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState DisplayManager.setAutoTransition(true); } - // Check if text is wider than available display width and frame is not in transition - if (getTextWidth(cf->text.c_str()) > availableWidth && !(state->frameState == IN_TRANSITION)) + if (getTextWidth(cf->text.c_str(),false) > availableWidth && !(state->frameState == IN_TRANSITION)) { - // Reset scroll position when text has finished scrolling all the way to the left - if (cf->scrollposition <= -getTextWidth(cf->text.c_str())) + if (cf->scrollposition <= -getTextWidth(cf->text.c_str(),false)) { - cf->scrollposition = 38; - - // Set iconWasPushed to false if icon has been pushed + cf->scrollDelay = 0; + cf->scrollposition = 9; if (cf->iconWasPushed && cf->pushIcon == 2) { cf->iconWasPushed = false; } - - // Transition to next app if frame is repeating and repeat limit has been reached if ((cf->currentRepeat + 1 >= cf->repeat) && (cf->repeat > 0)) { DisplayManager.setAutoTransition(true); @@ -296,24 +316,39 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState ++cf->currentRepeat; } } - - // Update scroll position - --cf->scrollposition; } - // Calculate horizontal text alignment - int16_t textX = (hasIcon) ? ((24 - getTextWidth(cf->text.c_str())) / 2) + 9 : ((32 - getTextWidth(cf->text.c_str())) / 2); - - // Set text color + if (!noScrolling) + { + if ((cf->scrollDelay > MATRIX_FPS * 1.2)) + { + --cf->scrollposition; + } + else + { + ++cf->scrollDelay; + if (hasIcon) + { + if (cf->iconWasPushed && cf->pushIcon == 1) + { + cf->scrollposition = 0; + } + else + { + cf->scrollposition = 9; + } + } + else + { + cf->scrollposition = 0; + } + } + } + int16_t textX = (hasIcon) ? ((24 - getTextWidth(cf->text.c_str(),false)) / 2) + 9 : ((32 - getTextWidth(cf->text.c_str(),false)) / 2); matrix->setTextColor(cf->color); - - // Check if text is scrolling or not - bool noScrolling = getTextWidth(cf->text.c_str()) <= availableWidth; - if (noScrolling) { cf->repeat = -1; // Disable repeat if text is too short for scrolling - // Display text with rainbow effect if enabled if (cf->rainbow) { @@ -322,8 +357,7 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState else { // Display text - - DisplayManager.printText(x + textX, y + 6, cf->text.c_str(), false); + DisplayManager.printText(x + textX, y + 6, cf->text.c_str(), false, false); } } else @@ -335,7 +369,7 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState } else { - DisplayManager.printText(x + cf->scrollposition, 6 + y, cf->text.c_str(), false); + DisplayManager.printText(x + cf->scrollposition, 6 + y, cf->text.c_str(), false, false); } } // Display icon if present and not pushed if (hasIcon) @@ -343,14 +377,14 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState // Push icon if enabled and text is scrolling if (cf->pushIcon > 0 && !noScrolling) { - if (cf->iconPosition < 0 && cf->iconWasPushed == false && cf->scrollposition > 9) + if (cf->iconPosition < 0 && cf->iconWasPushed == false && cf->scrollposition > 8) { ++cf->iconPosition; } - if (cf->scrollposition < 9 && !cf->iconWasPushed) + if (cf->scrollposition < 8 && !cf->iconWasPushed) { - cf->iconPosition = cf->scrollposition - 9; + cf->iconPosition = cf->scrollposition - 8; if (cf->iconPosition <= -9) { @@ -376,10 +410,9 @@ void ShowCustomFrame(uint8_t id, FastLED_NeoMatrix *matrix, MatrixDisplayUiState // Draw vertical line if text is scrolling if (!noScrolling) { - matrix->drawLine(8 + x + notify.iconPosition, 0 + y, 8 + x + notify.iconPosition, 7 + y, 0); + // matrix->drawLine(8 + x + cf->iconPosition, 0 + y, 8 + x + cf->iconPosition, 7 + y, 0); } } - // Reset text color DisplayManager.getInstance().resetTextColor(); } @@ -407,6 +440,7 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) notify.scrollposition = 34; notify.iconWasPushed = false; notify.iconPosition = 0; + notify.scrollDelay = 0; return; } @@ -417,30 +451,57 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) matrix->fillRect(0, 0, 32, 8, 0); // Calculate text and available width - uint16_t textWidth = getTextWidth(notify.text.c_str()); + uint16_t textWidth = getTextWidth(notify.text.c_str(),false); uint16_t availableWidth = hasIcon ? 24 : 32; + // Check if text is scrolling + bool noScrolling = textWidth <= availableWidth; + // Check if text needs to be scrolled if (textWidth > availableWidth && notify.scrollposition <= -textWidth) { // Reset scroll position and icon position if needed - notify.scrollposition = 38; + notify.scrollDelay = 0; + notify.scrollposition = 9; if (notify.pushIcon == 2) { notify.iconWasPushed = false; - // notify.iconPosition = 0; } if (notify.repeat > 0) { --notify.repeat; + if (notify.repeat == 0) + return; } } - else if (textWidth > availableWidth) + + if (!noScrolling) { - // Update scroll position - --notify.scrollposition; + if ((notify.scrollDelay > MATRIX_FPS * 1.2)) + { + --notify.scrollposition; + } + else + { + ++notify.scrollDelay; + if (hasIcon) + { + if (notify.iconWasPushed && notify.pushIcon == 1) + { + notify.scrollposition = 0; + } + else + { + notify.scrollposition = 9; + } + } + else + { + notify.scrollposition = 0; + } + } } // Calculate text X position based on icon presence @@ -449,9 +510,6 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) // Set text color matrix->setTextColor(notify.color); - // Check if text is scrolling - bool noScrolling = textWidth <= availableWidth; - if (noScrolling) { // Disable repeat if text is not scrolling @@ -465,7 +523,7 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) else { // Display text in solid color - DisplayManager.printText(textX, 6, notify.text.c_str(), false); + DisplayManager.printText(textX, 6, notify.text.c_str(), false, false); } } else @@ -478,7 +536,7 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) else { // Display scrolling text in solid color - DisplayManager.printText(notify.scrollposition, 6, notify.text.c_str(), false); + DisplayManager.printText(notify.scrollposition, 6, notify.text.c_str(), false, false); } } @@ -488,12 +546,12 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) // Push icon if enabled and text is scrolling if (notify.pushIcon > 0 && !noScrolling) { - if (notify.iconPosition < 0 && notify.iconWasPushed == false && notify.scrollposition > 9) + if (notify.iconPosition < 0 && notify.iconWasPushed == false && notify.scrollposition > 8) { ++notify.iconPosition; } - if (notify.scrollposition < 9 && !notify.iconWasPushed) + if (notify.scrollposition < 8 && !notify.iconWasPushed) { notify.iconPosition = notify.scrollposition - 9; @@ -519,7 +577,7 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) // Display icon divider line if text is scrolling if (!noScrolling) { - matrix->drawLine(8 + notify.iconPosition, 0, 8 + notify.iconPosition, 7, 0); + // matrix->drawLine(8 + notify.iconPosition, 0, 8 + notify.iconPosition, 7, 0); } } @@ -529,52 +587,62 @@ void NotifyFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) void CFrame1(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(1, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame1); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame2(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(2, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame2); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame3(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(3, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame3); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame4(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(4, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame4); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame5(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(5, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame5); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame6(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(6, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame6); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame7(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(7, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame7); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame8(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(8, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame8); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame9(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(9, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame9); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } void CFrame10(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) { - ShowCustomFrame(10, matrix, state, x, y, firstFrame, lastFrame); + String name = getFrameNameByFunction(CFrame10); + ShowCustomFrame(name, matrix, state, x, y, firstFrame, lastFrame); } const uint16_t *getWeatherIcon(int code) @@ -599,7 +667,7 @@ void WeatherFrame(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_ DisplayManager.getInstance().resetTextColor(); matrix->drawRGBBitmap(x, y, getWeatherIcon(WEATHER_CODE), 8, 8); String text = WEATHER_TEMP + "°" + WEATHER_HUM + "%"; - uint16_t textWidth = getTextWidth(text.c_str()); + uint16_t textWidth = getTextWidth(text.c_str(), false); int16_t textX = ((23 - textWidth) / 2); matrix->setCursor(textX + 11, 6 + y); matrix->print(utf8ascii(text)); @@ -647,6 +715,5 @@ void StartAppUpdater() // getWeatherData(); } -std::vector> Apps; OverlayCallback overlays[] = {MenuFrame, NotifyFrame, AlarmFrame, TimerFrame}; #endif \ No newline at end of file diff --git a/src/Functions.h b/src/Functions.h index f8123b46..35e4c2c8 100644 --- a/src/Functions.h +++ b/src/Functions.h @@ -28,13 +28,13 @@ uint16_t hexToRgb565(String hexValue) return color; } -uint16_t getTextWidth(const char *text) +uint16_t getTextWidth(const char *text, bool ignoreUpperCase) { uint16_t width = 0; for (const char *c = text; *c != '\0'; ++c) { char current_char = *c; - if (UPPERCASE_LETTERS) + if (UPPERCASE_LETTERS && !ignoreUpperCase) { current_char = toupper(current_char); } diff --git a/src/Globals.cpp b/src/Globals.cpp index c51df5b2..fa6687f5 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -11,6 +11,8 @@ void loadSettings() AUTO_BRIGHTNESS = Settings.getBool("ABRI", true); TEXTCOLOR_565 = Settings.getUInt("COL", 0xFFFF); AUTO_TRANSITION = Settings.getBool("TRANS", true); + TIME_PER_TRANSITION = Settings.getUInt("TSPEED", 500); + TIME_PER_APP = Settings.getUInt("ADUR", 5000); Settings.end(); } @@ -22,6 +24,8 @@ void saveSettings() Settings.putBool("ABRI", AUTO_BRIGHTNESS); Settings.putBool("TRANS", AUTO_TRANSITION); Settings.putUInt("COL", TEXTCOLOR_565); + Settings.putUInt("TSPEED", TIME_PER_TRANSITION); + Settings.putUInt("ADUR", TIME_PER_APP); Settings.end(); } @@ -30,7 +34,7 @@ IPAddress gateway; IPAddress subnet; IPAddress primaryDNS; IPAddress secondaryDNS; -const char *VERSION = "0.33"; +const char *VERSION = "0.35"; String MQTT_HOST = ""; uint16_t MQTT_PORT = 1883; String MQTT_USER; @@ -51,7 +55,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_FRAME = 7000; +int TIME_PER_APP = 7000; uint8_t MATRIX_FPS = 23; int TIME_PER_TRANSITION = 500; String NTP_SERVER = "de.pool.ntp.org"; diff --git a/src/Globals.h b/src/Globals.h index 44e32383..52f3de7e 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -28,7 +28,7 @@ extern String NET_GW; extern String NET_SN; extern String NET_PDNS; extern String NET_SDNS; -extern int TIME_PER_FRAME; +extern int TIME_PER_APP; extern uint8_t MATRIX_FPS; extern int TIME_PER_TRANSITION; extern String NTP_SERVER; diff --git a/src/MQTTManager.cpp b/src/MQTTManager.cpp index 56336707..b355bd2e 100644 --- a/src/MQTTManager.cpp +++ b/src/MQTTManager.cpp @@ -132,6 +132,12 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) return; } + if (strTopic == MQTT_PREFIX + "/switch") + { + DisplayManager.switchToApp(strPayload); + return; + } + else if (strTopic.startsWith(MQTT_PREFIX + "/custom")) { String topic_str = topic; @@ -140,8 +146,8 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) { topic_str = topic_str.substring(prefix.length()); } - uint16_t id = topic_str.toInt(); - DisplayManager.generateCustomPage(id, strPayload); + + DisplayManager.generateCustomPage(topic_str, strPayload); return; } } @@ -154,6 +160,7 @@ void onMqttConnected() mqtt.subscribe((prefix + String("/notify")).c_str()); mqtt.subscribe((prefix + String("/timer")).c_str()); mqtt.subscribe((prefix + String("/custom/#")).c_str()); + mqtt.subscribe((prefix + String("/switch")).c_str()); Serial.println("MQTT Connected"); } diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp index 89516f4b..caf36fe9 100644 --- a/src/MenuManager.cpp +++ b/src/MenuManager.cpp @@ -18,12 +18,13 @@ enum MenuState RadioMenu, StationSelection, PlayingStation, - Reset, VolumeMenu, BrightnessMenu, FPSMenu, ColorMenu, - SwitchMenu + SwitchMenu, + TspeedMenu, + AppTimeMenu }; const char *menuItems[] = { @@ -31,9 +32,10 @@ const char *menuItems[] = { "FPS", "COLOR", "SWITCH", - "RESET"}; + "T-SPEED", + "APPTIME"}; -byte menuItemCount = 5; +byte menuItemCount = 6; MenuState currentState = MainMenu; @@ -98,6 +100,18 @@ String MenuManager_::menutext() { return AUTO_TRANSITION ? "ON" : "OFF"; } + else if (currentState == TspeedMenu) + { + float seconds = (float)TIME_PER_TRANSITION / 1000.0; + return String(seconds, 1) + "s"; + ; + } + else if (currentState == AppTimeMenu) + { + float seconds = (float)TIME_PER_APP / 1000.0; + return String(seconds, 0) + "s"; + ; + } return ""; } @@ -142,6 +156,14 @@ void MenuManager_::rightButton() { AUTO_TRANSITION = !AUTO_TRANSITION; } + else if (currentState == TspeedMenu) + { + TIME_PER_TRANSITION = min(1200, TIME_PER_TRANSITION + 100); + } + else if (currentState == AppTimeMenu) + { + TIME_PER_APP = min(30000, TIME_PER_APP + 1000); + } } void MenuManager_::leftButton() @@ -185,6 +207,14 @@ void MenuManager_::leftButton() { AUTO_TRANSITION = !AUTO_TRANSITION; } + else if (currentState == TspeedMenu) + { + TIME_PER_TRANSITION = max(200, TIME_PER_TRANSITION - 100); + } + else if (currentState == AppTimeMenu) + { + TIME_PER_APP = max(1000, TIME_PER_APP - 1000); + } } void MenuManager_::selectButton() @@ -210,9 +240,13 @@ void MenuManager_::selectButton() { currentState = SwitchMenu; } - else if (menuIndex == 4) // FPS + else if (menuIndex == 4) // TSPEED { - ESP.restart(); + currentState = TspeedMenu; + } + else if (menuIndex == 5) // AppTIme + { + currentState = AppTimeMenu; } } else if (currentState == StationSelection) @@ -253,6 +287,16 @@ void MenuManager_::selectButtonLong() DisplayManager.setAutoTransition(AUTO_TRANSITION); saveSettings(); } + else if (currentState == TspeedMenu) + { + DisplayManager.setSettings(); + saveSettings(); + } + else if (currentState == AppTimeMenu) + { + DisplayManager.setSettings(); + saveSettings(); + } currentState = MainMenu; } else diff --git a/src/ServerManager.cpp b/src/ServerManager.cpp index 28a83ef5..427737d0 100644 --- a/src/ServerManager.cpp +++ b/src/ServerManager.cpp @@ -94,8 +94,6 @@ void ServerManager_::setup() mws.addCSS(custom_css); mws.addJavascript(custom_script); mws.addOptionBox("General"); - mws.addOption("Duration per Page", TIME_PER_FRAME); - mws.addOption("Transistion duration", TIME_PER_TRANSITION); mws.addOption("Uppercase letters", UPPERCASE_LETTERS); mws.addOption("Show date", SHOW_DATE); mws.addOption("Show temperature", SHOW_TEMP); @@ -193,8 +191,6 @@ void ServerManager_::loadSettings() MQTT_USER = doc["Username"].as(); MQTT_PASS = doc["Password"].as(); MQTT_PREFIX = doc["Prefix"].as(); - TIME_PER_FRAME = doc["Duration per Page"].as(); - TIME_PER_TRANSITION = doc["Transistion duration"].as(); NET_STATIC = doc["Static IP"]; HA_DISCOVERY = doc["Homeassistant Discovery"]; NET_IP = doc["Local IP"].as();