Skip to content

Commit

Permalink
v0.97
Browse files Browse the repository at this point in the history
- Implemented a new SSL certificate for the on-device update function. closes #525
- Resolved an issue causing the integrated icon downloader to malfunction in certain countries. closes #537 #362 #644 #642
- Added support for the Polish language by converting Polish characters to their ASCII equivalents
- MQTT Placeholders in Custom Apps
- Option to set the bar backgroundcolor in the bar drawing function.  closes #577
  • Loading branch information
Blueforcer committed Dec 24, 2024
1 parent 6ec015b commit e44679d
Show file tree
Hide file tree
Showing 16 changed files with 450 additions and 302 deletions.
8 changes: 8 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ Below are the properties you can utilize in the JSON object. **All keys are opti
| `bar` | array of integers | Draws a bargraph. Without icon maximum 16 values, with icon 11 values. | N/A | X | X |
| `line` | array of integers | Draws a linechart. Without icon maximum 16 values, with icon 11 values. | N/A | X | X |
| `autoscale` | boolean | Enables or disables autoscaling for bar and linechart. | true | X | X |
| `barBC` | string or array of integers | Backgroundcolor of the bars. | 0 | X | X |
| `progress` | integer | Shows a progress bar. Value can be 0-100. | -1 | X | X |
| `progressC` | string or array of integers | The color of the progress bar. | -1 | X | X |
| `progressBC` | string or array of integers | The color of the progress bar background. | -1 | X | X |
Expand Down Expand Up @@ -232,7 +233,14 @@ Here's a sample JSON to present the text "Hello, AWTRIX 3!" in rainbow colors fo
"duration": 10
}
```

### MQTT Placeholder
This feature is particularly useful for users without a full smart home system. It eliminates the need for an external system to display data, such as from an inverter wich can send its data vie MQTT. You can simply create a [AppName].json file in the CUSTOMAPP folder with your custom app JSON keys. This JSON file will be loaded upon boot, so you don't need to send it from an external source. Or you can also use it in your HTTP or MQTT API request.
The placeholders inside the `text` value enclosed in {{}} will be replaced with the payload of the specified MQTT topic. Currently, there are no options available for formatting the payload.

```json
{"text": "Solar: {{inverter/total/P_AC}} W"}
```

### Drawing Instructions
!> Please note: Depending on the number of objects, the RAM usage can be very high. This could cause freezes or reboots.
Expand Down
8 changes: 4 additions & 4 deletions lib/webserver/esp-fs-webserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class FSWebServer
{
// String trimmed = script;
// removeWhiteSpaces(trimmed);
addOption("raw-javascript", script, true);
addOption("raw-javascript", script, true, MIN_F, MAX_F, 1.0, true);
}

void addDropdownList(const char *label, const char **array, size_t size);
Expand All @@ -165,7 +165,7 @@ class FSWebServer
// Add custom option to config webpage (type of parameter will be deduced from variable itself)
template <typename T>
inline void addOption(const char *label, T val, bool hidden = false,
double d_min = MIN_F, double d_max = MAX_F, double step = 1.0)
double d_min = MIN_F, double d_max = MAX_F, double step = 1.0, bool replace = false)
{
File file = m_filesystem->open("/DoNotTouch.json", "r");
int sz = file.size() * 1.33;
Expand Down Expand Up @@ -205,8 +205,8 @@ class FSWebServer
key += numOptions;
}

// If key is present in json, we don't need to create it.
if (doc.containsKey(key.c_str()))
// If key is present in json and replace is false, we don't need to create it.
if (doc.containsKey(key.c_str()) && !replace)
return;

// if min, max, step != from default, treat this as object in order to set other properties
Expand Down
6 changes: 3 additions & 3 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ build_flags =
-DULANZI
-DMQTT_MAX_PACKET_SIZE=8192
-D CORE_DEBUG_LEVEL=0
-D NDEBUG
-Wno-attributes
-Os
-fno-exceptions
lib_deps =
${env.lib_deps}
${env.lib_deps}


[env:awtrix2_upgrade]
platform = espressif32
Expand All @@ -58,4 +58,4 @@ build_flags =
-Os
-fno-exceptions
lib_deps =
${env.lib_deps}
${env.lib_deps}
67 changes: 44 additions & 23 deletions src/Apps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ void TimeApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x,
{
// week days on bottom line
wdPosY = 7;
timePosY = 6;
timePosY = 6 ;
}

// time
Expand Down Expand Up @@ -299,6 +299,21 @@ void BatApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, i
}
#endif

String replacePlaceholders(String text) {
int start = 0;
while ((start = text.indexOf("{{", start)) != -1) {
int end = text.indexOf("}}", start);
if (end == -1) {
break;
}
String placeholder = text.substring(start + 2, end);
String topic = placeholder;
text.replace("{{" + placeholder + "}}", MQTTManager.getValueForTopic(topic));
start = end + 2;
}
return text;
}

void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, GifPlayer *gifPlayer)
{
// Abort if notifyFlag is set
Expand Down Expand Up @@ -355,19 +370,20 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState

bool hasIcon = ca->icon || ca->jpegDataSize > 0;

// Calculate text and available width
uint16_t textWidth = 0;
if (!ca->fragments.empty())
{
for (const auto &fragment : ca->fragments)
{
textWidth += getTextWidth(fragment.c_str(), ca->textCase);
}
}
else
uint16_t textWidth = 0;
if (!ca->fragments.empty())
{
for (const auto &fragment : ca->fragments)
{
textWidth = getTextWidth(ca->text.c_str(), ca->textCase);
String replacedFragment = replacePlaceholders(fragment);
textWidth += getTextWidth(replacedFragment.c_str(), ca->textCase);
}
}
else
{
String replacedText = replacePlaceholders(ca->text);
textWidth = getTextWidth(replacedText.c_str(), ca->textCase);
}

uint16_t availableWidth = (hasIcon) ? 24 : 32;

Expand Down Expand Up @@ -436,7 +452,7 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState

if (ca->barSize > 0)
{
DisplayManager.drawBarChart(x, y, ca->barData, ca->barSize, hasIcon, ca->color);
DisplayManager.drawBarChart(x, y, ca->barData, ca->barSize, hasIcon, ca->color, ca->barBG);
}

if (ca->lineSize > 0)
Expand Down Expand Up @@ -531,6 +547,8 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState
textX = hasIcon ? 9 : 0;
}

String text =replacePlaceholders(ca->text);

if (noScrolling)
{
ca->repeat = -1; // Disable repeat if text is too short for scrolling
Expand All @@ -540,25 +558,27 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState
int16_t fragmentX = textX + ca->textOffset;
for (size_t i = 0; i < ca->fragments.size(); ++i)
{
String text =replacePlaceholders(ca->fragments[i]);
DisplayManager.setTextColor(TextEffect(ca->colors[i], ca->fade, ca->blink));
DisplayManager.printText(x + fragmentX, y + 6, ca->fragments[i].c_str(), false, ca->textCase);
fragmentX += getTextWidth(ca->fragments[i].c_str(), ca->textCase);
DisplayManager.printText(x + fragmentX, y + 6, text.c_str(), false, ca->textCase);
fragmentX += getTextWidth(text.c_str(), ca->textCase);
}
}
else
{
String text =replacePlaceholders(ca->text);
if (ca->rainbow)
{
DisplayManager.HSVtext(x + textX + ca->textOffset, 6 + y, ca->text.c_str(), false, ca->textCase);
DisplayManager.HSVtext(x + textX + ca->textOffset, 6 + y, text.c_str(), false, ca->textCase);
}
else if (ca->gradient[0] > -1 && ca->gradient[1] > -1)
{
DisplayManager.GradientText(x + textX + ca->textOffset, 6 + y, ca->text.c_str(), ca->gradient[0], ca->gradient[1], false, ca->textCase);
DisplayManager.GradientText(x + textX + ca->textOffset, 6 + y, text.c_str(), ca->gradient[0], ca->gradient[1], false, ca->textCase);
}
else
{
DisplayManager.setTextColor(TextEffect(ca->color, ca->fade, ca->blink));
DisplayManager.printText(x + textX + ca->textOffset, y + 6, ca->text.c_str(), false, ca->textCase);
DisplayManager.printText(x + textX + ca->textOffset, y + 6, text.c_str(), false, ca->textCase);
}
}
}
Expand All @@ -569,25 +589,26 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState
int16_t fragmentX = ca->scrollposition + ca->textOffset;
for (size_t i = 0; i < ca->fragments.size(); ++i)
{
String text =replacePlaceholders(ca->fragments[i]);
DisplayManager.setTextColor(TextEffect(ca->colors[i], ca->fade, ca->blink));
DisplayManager.printText(x + fragmentX, y + 6, ca->fragments[i].c_str(), false, ca->textCase);
fragmentX += getTextWidth(ca->fragments[i].c_str(), ca->textCase);
DisplayManager.printText(x + fragmentX, y + 6, text.c_str(), false, ca->textCase);
fragmentX += getTextWidth(text.c_str(), ca->textCase);
}
}
else
{
if (ca->rainbow)
{
DisplayManager.HSVtext(x + ca->scrollposition + ca->textOffset, 6 + y, ca->text.c_str(), false, ca->textCase);
DisplayManager.HSVtext(x + ca->scrollposition + ca->textOffset, 6 + y, text.c_str(), false, ca->textCase);
}
else if (ca->gradient[0] > -1 && ca->gradient[1] > -1)
{
DisplayManager.GradientText(x + ca->scrollposition + ca->textOffset, 6 + y, ca->text.c_str(), ca->gradient[0], ca->gradient[1], false, ca->textCase);
DisplayManager.GradientText(x + ca->scrollposition + ca->textOffset, 6 + y, text.c_str(), ca->gradient[0], ca->gradient[1], false, ca->textCase);
}
else
{
DisplayManager.setTextColor(TextEffect(ca->color, ca->fade, ca->blink));
DisplayManager.printText(x + ca->scrollposition + ca->textOffset, 6 + y, ca->text.c_str(), false, ca->textCase);
DisplayManager.printText(x + ca->scrollposition + ca->textOffset, 6 + y, text.c_str(), false, ca->textCase);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Apps.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct CustomApp
float iconPosition = 0;
bool iconWasPushed = false;
int barData[16] = {0};
uint32_t barBG = 0;
int lineData[16] = {0};
int gradient[2] = {0};
int barSize;
Expand Down
101 changes: 71 additions & 30 deletions src/DisplayManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,26 @@ bool DisplayManager_::parseCustomPage(const String &name, const char *json, bool
return true;
}

// Function to subscribe to MQTT topics based on placeholders in text
void subscribeToPlaceholders(String text)
{
int start = 0;
while ((start = text.indexOf("{{", start)) != -1)
{
int end = text.indexOf("}}", start);
if (end == -1)
{
break;
}
String placeholder = text.substring(start + 2, end);
String topic = placeholder;

MQTTManager.subscribe(topic.c_str());

start = end + 2;
}
}

bool DisplayManager_::generateCustomPage(const String &name, JsonObject doc, bool preventSave)
{
CustomApp customApp;
Expand Down Expand Up @@ -543,6 +563,11 @@ bool DisplayManager_::generateCustomPage(const String &name, JsonObject doc, boo

if (doc.containsKey(key))
{
if (doc.containsKey("barBC"))
{
auto color = doc["barBC"];
customApp.barBG = getColorFromJsonVariant(color, 0);
}
JsonArray data = doc[key];
int index = 0;
int maximum = 0;
Expand Down Expand Up @@ -683,6 +708,13 @@ bool DisplayManager_::generateCustomPage(const String &name, JsonObject doc, boo

customApp.colors.clear();
customApp.fragments.clear();

if (doc.containsKey("text"))
{
String text = doc["text"];
subscribeToPlaceholders(utf8ascii(text));
}

if (doc.containsKey("text") && doc["text"].is<JsonArray>())
{
JsonArray textArray = doc["text"].as<JsonArray>();
Expand Down Expand Up @@ -847,6 +879,12 @@ bool DisplayManager_::generateNotification(uint8_t source, const char *json)

if (doc.containsKey(key))
{

if (doc.containsKey("barBC"))
{
auto color = doc["barBC"];
newNotification.barBG = getColorFromJsonVariant(color, 0);
}
JsonArray data = doc[key];
int index = 0;
int maximum = 0;
Expand Down Expand Up @@ -1445,7 +1483,7 @@ void DisplayManager_::drawMenuIndicator(int cur, int total, uint32_t color)
}
}

void DisplayManager_::drawBarChart(int16_t x, int16_t y, const int newData[], byte dataSize, bool withIcon, uint32_t color)
void DisplayManager_::drawBarChart(int16_t x, int16_t y, const int data[], byte dataSize, bool withIcon, uint32_t color, uint32_t barBG)
{
int availableWidth = withIcon ? (32 - 9) : 32;
int gap = 1;
Expand All @@ -1456,27 +1494,30 @@ void DisplayManager_::drawBarChart(int16_t x, int16_t y, const int newData[], by
for (int i = 0; i < dataSize; i++)
{
int x1 = x + startX + i * (barWidth + gap);
int barHeight = newData[i];
int y1 = (barHeight > 0) ? (8 - barHeight) : 8;
int barHeight = data[i];

if (barHeight > 0)
if (barBG > 0)
{
drawFilledRect(x1, y1 + y, barWidth, barHeight, color);
// Draw background bar
drawFilledRect(x1, y, barWidth, 8, barBG);
}

int y1 = (barHeight > 0) ? (8 - barHeight) : 8;
drawFilledRect(x1, y1 + y, barWidth, barHeight, color);
}
}

void DisplayManager_::drawLineChart(int16_t x, int16_t y, const int newData[], byte dataSize, bool withIcon, uint32_t color)
void DisplayManager_::drawLineChart(int16_t x, int16_t y, const int data[], byte dataSize, bool withIcon, uint32_t color)
{
int availableWidth = withIcon ? (32 - 9) : 32;
int startX = withIcon ? 9 : 0;
float xStep = static_cast<float>(availableWidth) / static_cast<float>(dataSize - 1);
int lastX = x + startX;
int lastY = y + 8 - newData[0];
int lastY = y + 8 - data[0];
for (int i = 1; i < dataSize; i++)
{
int x1 = x + startX + static_cast<int>(xStep * i);
int y1 = y + 8 - newData[i];
int y1 = y + 8 - data[i];
drawLine(lastX, lastY, x1, y1, color);
lastX = x1;
lastY = y1;
Expand Down Expand Up @@ -1949,26 +1990,28 @@ String CRGBtoHex(CRGB color)
return String(buf);
}

String getOverlayName() {
switch(GLOBAL_OVERLAY) {
case DRIZZLE:
return "drizzle";
case RAIN:
return "rain";
case SNOW:
return "snow";
case STORM:
return "storm";
case THUNDER:
return "thunder";
case FROST:
return "frost";
case NONE:
return "clear";
default:
Serial.println(F("Invalid effect."));
return "invalid"; // Oder einen leeren String oder einen Fehlerwert zurückgeben
}
String getOverlayName()
{
switch (GLOBAL_OVERLAY)
{
case DRIZZLE:
return "drizzle";
case RAIN:
return "rain";
case SNOW:
return "snow";
case STORM:
return "storm";
case THUNDER:
return "thunder";
case FROST:
return "frost";
case NONE:
return "clear";
default:
Serial.println(F("Invalid effect."));
return "invalid"; // Oder einen leeren String oder einen Fehlerwert zurückgeben
}
}

String DisplayManager_::getSettings()
Expand Down Expand Up @@ -2018,8 +2061,6 @@ String DisplayManager_::getSettings()
return serializeJson(doc, jsonString), jsonString;
}



void DisplayManager_::setNewSettings(const char *json)
{
if (DEBUG_MODE)
Expand Down
Loading

0 comments on commit e44679d

Please sign in to comment.