v0.45
- 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
This commit is contained in:
@@ -245,9 +245,9 @@ 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);
|
||||
@@ -255,7 +255,7 @@ void DisplayManager_::generateCustomPage(String name, String payload)
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(1024);
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
DeserializationError error = deserializeJson(doc, json);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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>());
|
||||
@@ -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"))
|
||||
{
|
||||
@@ -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>();
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -45,18 +45,19 @@ public:
|
||||
void setTextColor(uint16_t color);
|
||||
void setFPS(uint8_t);
|
||||
void MatrixState(bool);
|
||||
void generateNotification(String);
|
||||
void generateCustomPage(String, String);
|
||||
void generateNotification(const char *json);
|
||||
void generateCustomPage(String, const char *json);
|
||||
void printText(int16_t x, int16_t y, const char *text, bool centered, bool ignoreUppercase);
|
||||
bool setAutoTransition(bool active);
|
||||
void switchToApp(String Payload);
|
||||
void setNewSettings(String Payload);
|
||||
void switchToApp(const char *json);
|
||||
void setNewSettings(const char *json);
|
||||
void drawGIF(uint16_t x, uint16_t y, fs::File gifFile);
|
||||
void drawJPG(uint16_t x, uint16_t y, fs::File jpgFile);
|
||||
void drawProgressBar(int cur, int total);
|
||||
void drawMenuIndicator(int cur, int total, uint16_t color);
|
||||
void drawBMP(int16_t x, int16_t y, const uint16_t bitmap[], int16_t w, int16_t h);
|
||||
void drawBarChart(int16_t x, int16_t y,const int data[], byte dataSize, bool withIcon, uint16_t color);
|
||||
void drawBarChart(int16_t x, int16_t y, const int data[], byte dataSize, bool withIcon, uint16_t color);
|
||||
void updateAppVector(const char *json);
|
||||
};
|
||||
|
||||
extern DisplayManager_ &DisplayManager;
|
||||
|
||||
@@ -65,7 +65,7 @@ IPAddress gateway;
|
||||
IPAddress subnet;
|
||||
IPAddress primaryDNS;
|
||||
IPAddress secondaryDNS;
|
||||
const char *VERSION = "0.44";
|
||||
const char *VERSION = "0.45";
|
||||
String MQTT_HOST = "";
|
||||
uint16_t MQTT_PORT = 1883;
|
||||
String MQTT_USER;
|
||||
|
||||
@@ -128,21 +128,22 @@ void onBrightnessCommand(uint8_t brightness, HALight *sender)
|
||||
void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
|
||||
{
|
||||
String strTopic = String(topic);
|
||||
String strPayload = String((const char *)payload).substring(0, length);
|
||||
|
||||
char *payloadCopy = new char[length + 1];
|
||||
memcpy(payloadCopy, payload, length);
|
||||
payloadCopy[length] = '\0';
|
||||
if (strTopic == MQTT_PREFIX + "/notify")
|
||||
{
|
||||
if (payload[0] != '{' || payload[length - 1] != '}')
|
||||
{
|
||||
return;
|
||||
}
|
||||
DisplayManager.generateNotification(strPayload);
|
||||
DisplayManager.generateNotification(payloadCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strTopic == MQTT_PREFIX + "/timer")
|
||||
{
|
||||
DisplayManager.gererateTimer(strPayload);
|
||||
DisplayManager.gererateTimer(payloadCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,15 +153,21 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
|
||||
return;
|
||||
}
|
||||
|
||||
if (strTopic == MQTT_PREFIX + "/apps")
|
||||
{
|
||||
DisplayManager.updateAppVector(payloadCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strTopic == MQTT_PREFIX + "/switch")
|
||||
{
|
||||
DisplayManager.switchToApp(strPayload);
|
||||
DisplayManager.switchToApp(payloadCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strTopic == MQTT_PREFIX + "/settings")
|
||||
{
|
||||
DisplayManager.setNewSettings(strPayload);
|
||||
DisplayManager.setNewSettings(payloadCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -185,26 +192,34 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
|
||||
topic_str = topic_str.substring(prefix.length());
|
||||
}
|
||||
|
||||
DisplayManager.generateCustomPage(topic_str, strPayload);
|
||||
DisplayManager.generateCustomPage(topic_str, payloadCopy);
|
||||
return;
|
||||
}
|
||||
delete[] payloadCopy;
|
||||
}
|
||||
|
||||
void onMqttConnected()
|
||||
{
|
||||
String prefix = MQTT_PREFIX;
|
||||
mqtt.subscribe((prefix + String("/brightness")).c_str());
|
||||
mqtt.subscribe((prefix + String("/notify/dismiss")).c_str());
|
||||
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());
|
||||
mqtt.subscribe((prefix + String("/settings")).c_str());
|
||||
mqtt.subscribe((prefix + String("/previousapp")).c_str());
|
||||
mqtt.subscribe((prefix + String("/nextapp")).c_str());
|
||||
Serial.println("MQTT Connected");
|
||||
const char* topics[] PROGMEM = {
|
||||
"/brightness",
|
||||
"/notify/dismiss",
|
||||
"/notify",
|
||||
"/timer",
|
||||
"/custom/#",
|
||||
"/switch",
|
||||
"/settings",
|
||||
"/previousapp",
|
||||
"/nextapp",
|
||||
"/nextapp",
|
||||
"/apps"
|
||||
};
|
||||
for (const char* topic : topics) {
|
||||
String fullTopic = prefix + topic;
|
||||
mqtt.subscribe(fullTopic.c_str());
|
||||
}
|
||||
Serial.println(F("MQTT Connected"));
|
||||
}
|
||||
|
||||
void connect()
|
||||
{
|
||||
mqtt.onMessage(onMqttMessage);
|
||||
@@ -212,12 +227,12 @@ void connect()
|
||||
|
||||
if (MQTT_USER == "" || MQTT_PASS == "")
|
||||
{
|
||||
Serial.println("Connecting to MQTT w/o login");
|
||||
Serial.println(F("Connecting to MQTT w/o login"));
|
||||
mqtt.begin(MQTT_HOST.c_str(), MQTT_PORT, nullptr, nullptr, MQTT_PREFIX.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Connecting to MQTT with login");
|
||||
Serial.println(F("Connecting to MQTT with login"));
|
||||
mqtt.begin(MQTT_HOST.c_str(), MQTT_PORT, MQTT_USER.c_str(), MQTT_PASS.c_str(), MQTT_PREFIX.c_str());
|
||||
}
|
||||
}
|
||||
@@ -365,7 +380,7 @@ void MQTTManager_::setup()
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Homeassistant discovery disabled");
|
||||
Serial.println(F("Homeassistant discovery disabled"));
|
||||
mqtt.disableHA();
|
||||
}
|
||||
connect();
|
||||
|
||||
Reference in New Issue
Block a user