Skip to content

Commit

Permalink
v0.45
Browse files Browse the repository at this point in the history
- Introduces an function that allows users to add, remove, and rearrange multiple apps on the device at once via MQTT. This provides greater flexibility and customization options. Please note that this function is experimental and should be used with caution.

https://blueforcer.github.io/awtrix-light/#/mqtt?id=addremove-and-rearange-apps
  • Loading branch information
Blueforcer committed Mar 30, 2023
1 parent 783180a commit 39e1b0b
Show file tree
Hide file tree
Showing 10 changed files with 295 additions and 49 deletions.
2 changes: 1 addition & 1 deletion docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

- Features
- [Apps](apps.md)
- [Custom Pages & Notifications](custom.md)
- [Alarm clock](alarm.md)
- [Timer](timer.md)
- [Icons](icons.md)
- [Sounds](sounds.md)

- MQTT
- [Custom Pages & Notifications](custom.md)
- [Commands](mqtt.md)
2 changes: 1 addition & 1 deletion docs/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The JSON object has the following properties:

| Key | Type | Description | Default |
| --- | ---- | ----------- | ------- |
| `pos` | number | defines the position of your custompage in the loop, starting at 1 for the first position. This will only apply with your first push. You cant change the position afterwards. For that you need to delete it and add it again. | At the end of the loop |
| `pos` | number | defines the position of your custompage in the loop, starting at 0 for the first position. This will only apply with your first push. You cant change the position afterwards with [this function](mqtt?id=addremove-and-rearange-apps) |
| `text` | string | The text to display on the page. | |
| `icon` | string | The icon ID or filename (without extension) to display on the page. | |
| `repeat` | number | Sets how many times the text should be scrolled through the matrix before the display ends. | 1 |
Expand Down
2 changes: 2 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="https://unpkg.com/docsify-copy-code@2"></script>
<script src="//unpkg.com/docsify-share/build/index.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1.22/components/prism-json.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-json.min.js"></script>

</body>

Expand Down
70 changes: 70 additions & 0 deletions docs/mqtt.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,76 @@ Built-in app names are:

For custom apps, use the name you set in the topic. For example, if `[PREFIX]/custom/test` is your topic, then `test` is the name.

## Add/remove and rearange apps


| Topic |
| --- |
| `[PREFIX]/apps` |

!> This function provides users with the ability to manage the apps on their device by adding, removing, and rearranging them. However, as it is an experimental feature, caution should be exercised, particularly when attempting to rearrange multiple apps at once, as this can lead to unintended consequences due to the resulting shifts in position of other apps.

By using this function, users can add or remove native apps, as well as custom apps, from the device.
However, it is important to note that custom apps are only temporarily loaded into memory and cannot be added again using this function.
To add a custom app again, you must send it to awtrix via mqtt again.

Additionally, you can rearrange the position of all apps on the device by specifying a new position in the JSON array.
This provides flexibility in organizing apps according to personal preference.

The JSON payload is an array of objects, where each object represents an app to be displayed on awtrix. Each app object contains the following fields:

`"name"`: The name of the app ("time", "date", "temp", "hum", "bat") are the native apps.
For custom apps, use the name you set in the topic. For example, if `[PREFIX]/custom/test` is your topic, then `test` is the name.
`"show"`: A boolean indicating whether the app should be shown on the screen or not. If not present, the app is considered active by default.
`"pos"`: An integer indicating the position of the app in the list. If not present, the app will be added to the end of the list.

> You can also just send the information for one app.
```json
[
{
"name":"time",
"show":true,
"pos":3
},
{
"name":"date",
"pos":0
},
{
"name":"temp",
"pos":2
},
{
"name":"hum",
"show":true,
"pos":0
},
{
"name":"bat",
"show":false
},
{
"name":"github",
"show":true,
"pos":4
}
]
```



In this example,
- The "time" app is active and should be displayed in position 3.
- The "date" app should be displayed in position 0.
- The "temp" app should be displayed in position 2.
- The "hum" app should be displayed at first position.
- The "bat" app is inactive and will be removed,
- and the "github" app is active and should be displayed in position 4.




## Change Settings
Change various settings related to the app display.

Expand Down
3 changes: 0 additions & 3 deletions lib/MatrixUI/MatrixDisplayUi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,11 @@ void MatrixDisplayUi::setApps(const std::vector<std::pair<String, AppCallback>>
{
delete[] AppFunctions;
AppCount = appPairs.size();
Serial.println(AppCount);
AppFunctions = new AppCallback[AppCount];

for (size_t i = 0; i < AppCount; ++i)
{
AppFunctions[i] = appPairs[i].second;
}

this->resetState();
}

Expand Down
4 changes: 2 additions & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ board = esp32dev
board_build.partitions = awtrix_partition.csv
upload_speed = 921600
framework = arduino
build_flags = -DULANZI
build_flags = -DULANZI -D MQTT_MAX_PACKET_SIZE=1024
lib_deps =
adafruit/Adafruit SHT31 Library@^2.2.0
bblanchon/ArduinoJson@^6.20.0
Expand All @@ -29,7 +29,7 @@ board = wemos_d1_mini32
board_build.partitions = awtrix_partition.csv
upload_speed = 921600
framework = arduino
build_flags = -DAWTRIX_UPGRADE
build_flags = -DAWTRIX_UPGRADE -D MQTT_MAX_PACKET_SIZE=1024
lib_deps =
adafruit/Adafruit SHT31 Library@^2.2.0
bblanchon/ArduinoJson@^6.20.0
Expand Down
191 changes: 176 additions & 15 deletions src/DisplayManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,17 +245,17 @@ void removeCustomApp(const String &name)
}
}

void DisplayManager_::generateCustomPage(String name, String payload)
void DisplayManager_::generateCustomPage(String name, const char *json)
{
if (payload == "" && customApps.count(name))
if (json == "" && customApps.count(name))
{
customApps.erase(customApps.find(name));
removeCustomApp(name);
return;
}

DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, payload);
DeserializationError error = deserializeJson(doc, json);
if (error)
return;

Expand Down Expand Up @@ -300,9 +300,30 @@ void DisplayManager_::generateCustomPage(String name, String payload)
customApp.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0;
customApp.name = name;
customApp.text = utf8ascii(doc["text"].as<String>());
customApp.color = doc.containsKey("color") ? doc["color"].is<String>() ? hexToRgb565(doc["color"]) : doc["color"].is<JsonArray>() ? hexToRgb565(doc["color"].as<String>())
: TEXTCOLOR_565
: TEXTCOLOR_565;

if (doc.containsKey("color"))
{
auto color = doc["color"];
if (color.is<String>())
{
customApp.color = hexToRgb565(color.as<String>());
}
else if (color.is<JsonArray>() && color.size() == 3)
{
uint8_t r = color[0];
uint8_t g = color[1];
uint8_t b = color[2];
customApp.color = (r << 11) | (g << 5) | b;
}
else
{
customApp.color = TEXTCOLOR_565;
}
}
else
{
customApp.color = TEXTCOLOR_565;
}

if (currentCustomApp != name)
{
Expand Down Expand Up @@ -346,10 +367,10 @@ void DisplayManager_::generateCustomPage(String name, String payload)
customApps[name] = customApp;
}

void DisplayManager_::generateNotification(String payload)
void DisplayManager_::generateNotification(const char *json)
{
StaticJsonDocument<1024> doc;
deserializeJson(doc, payload);
deserializeJson(doc, json);

notify.duration = doc.containsKey("duration") ? doc["duration"].as<int>() * 1000 : TIME_PER_APP;
notify.text = utf8ascii(doc["text"].as<String>());
Expand Down Expand Up @@ -388,9 +409,29 @@ void DisplayManager_::generateNotification(String payload)
notify.barSize = 0;
}

notify.color = doc.containsKey("color") ? doc["color"].is<String>() ? hexToRgb565(doc["color"]) : doc["color"].is<JsonArray>() ? hexToRgb565(doc["color"].as<String>())
: TEXTCOLOR_565
: TEXTCOLOR_565;
if (doc.containsKey("color"))
{
auto color = doc["color"];
if (color.is<String>())
{
notify.color = hexToRgb565(color.as<String>());
}
else if (color.is<JsonArray>() && color.size() == 3)
{
uint8_t r = color[0];
uint8_t g = color[1];
uint8_t b = color[2];
notify.color = (r << 11) | (g << 5) | b;
}
else
{
notify.color = TEXTCOLOR_565;
}
}
else
{
notify.color = TEXTCOLOR_565;
}

if (doc.containsKey("icon"))
{
Expand Down Expand Up @@ -607,10 +648,10 @@ void DisplayManager_::gererateTimer(String Payload)
TimerTicker.attach_ms(interval, timerCallback);
}

void DisplayManager_::switchToApp(String Payload)
void DisplayManager_::switchToApp(const char *json)
{
DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, Payload);
DeserializationError error = deserializeJson(doc, json);
if (error)
return;
String name = doc["name"].as<String>();
Expand All @@ -620,10 +661,10 @@ void DisplayManager_::switchToApp(String Payload)
ui.transitionToApp(index);
}

void DisplayManager_::setNewSettings(String Payload)
void DisplayManager_::setNewSettings(const char *json)
{
DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, Payload);
DeserializationError error = deserializeJson(doc, json);
if (error)
return;
TIME_PER_APP = doc.containsKey("apptime") ? doc["apptime"] : TIME_PER_APP;
Expand Down Expand Up @@ -714,4 +755,124 @@ void DisplayManager_::drawBarChart(int16_t x, int16_t y, const int data[], byte
int y1 = min(8 - barHeight, 7);
matrix.fillRect(x1, y1 + y, barWidth, barHeight, color);
}
}

void DisplayManager_::updateAppVector(const char *json)
{
// Parse the JSON input
DynamicJsonDocument doc(1024);
auto error = deserializeJson(doc, json);
if (error)
{
// If parsing fails, print an error message and return
Serial.print("Failed to parse JSON: ");
Serial.println(error.c_str());
return;
}

// Create new vectors to store updated apps
std::vector<std::pair<String, AppCallback>> newApps;
std::vector<String> activeApps;

// Loop through all apps in the JSON input
for (const auto &app : doc.as<JsonArray>())
{
// Get the app name, active status, and position (if specified)
String name = app["name"].as<String>();
bool show = true;
int position = -1;

if (app.containsKey("show"))
{
show = app["show"].as<bool>();
}
if (app.containsKey("pos"))
{
position = app["pos"].as<int>();
}

// Find the corresponding AppCallback function based on the app name
AppCallback callback;
if (name == "time")
{
callback = TimeApp;
SHOW_TIME = show;
}
else if (name == "date")
{
callback = DateApp;
SHOW_DATE = show;
}
else if (name == "temp")
{
callback = TempApp;
SHOW_TEMP = show;
}
else if (name == "hum")
{
callback = HumApp;
SHOW_HUM = show;
}
else if (name == "bat")
{
callback = BatApp;
SHOW_BAT = show;
}
else
{
// If the app is not one of the built-in apps, check if it's already in the vector
int appIndex = findAppIndexByName(name);
if (appIndex >= 0)
{
// The app is in the vector, so we can move it to a new position or remove it
auto it = Apps.begin() + appIndex;
if (show)
{
if (position >= 0 && static_cast<size_t>(position) < newApps.size())
{
Apps.erase(it);
newApps.insert(newApps.begin() + position, std::make_pair(name, it->second));
}
}
else
{
// If the app is being removed, also remove it from the customApps map
if (customApps.count(name))
{
customApps.erase(customApps.find(name));
removeCustomApp(name);
}
}
}
continue;
}
if (show)
{
// Add the app to the new vector
if (position >= 0 && static_cast<size_t>(position) < newApps.size())
{
newApps.insert(newApps.begin() + position, std::make_pair(name, callback));
}
else
{
newApps.emplace_back(name, callback);
}
}

activeApps.push_back(name);
}

// Loop through all apps currently in the vector
for (const auto &app : Apps)
{
// If the app is not in the updated activeApps vector, add it to the newApps vector
if (std::find(activeApps.begin(), activeApps.end(), app.first) == activeApps.end())
{
newApps.push_back(app);
}
}

// Update the apps vector, set it in the UI, and save settings
Apps = std::move(newApps);
ui.setApps(Apps);
}
Loading

0 comments on commit 39e1b0b

Please sign in to comment.