mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 08:06:46 +01:00
Json url file template (#128)
json template url + file implementation. The file contains the json template, and can contain variables that will be extracted from the json in the url. The url is fetched at regular intervals.
This commit is contained in:
@@ -31,7 +31,8 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo,
|
||||
void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginfo, imgParam &imageParams);
|
||||
uint8_t drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
|
||||
void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
|
||||
int getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo, imgParam &imageParams);
|
||||
bool getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo, imgParam &imageParams);
|
||||
extern bool getJsonTemplateFileExtractVariables(String &filename, String jsonfile, JsonDocument &variables, tagRecord *&taginfo, imgParam &imageParams);
|
||||
int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, tagRecord *&taginfo, imgParam &imageParams);
|
||||
void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgParam &imageParams);
|
||||
void drawElement(const JsonObject &element, TFT_eSprite &spr);
|
||||
|
||||
@@ -78,7 +78,7 @@ static bool httpGetJson(String &url, JsonDocument &json, const uint16_t timeout,
|
||||
const int httpCode = http.GET();
|
||||
if (httpCode != 200) {
|
||||
http.end();
|
||||
wsErr("http " + httpCode);
|
||||
wsErr(String("[httpGetJson] http code") + httpCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ static bool httpGetJson(String &url, JsonDocument &json, const uint16_t timeout,
|
||||
}
|
||||
http.end();
|
||||
if (error) {
|
||||
Serial.println(error.c_str());
|
||||
Serial.printf("[httpGetJson] JSON: %s\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -377,13 +377,31 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
||||
{
|
||||
const String configFilename = cfgobj["filename"].as<String>();
|
||||
if (!util::isEmptyOrNull(configFilename)) {
|
||||
const int result = getJsonTemplateFile(filename, configFilename, taginfo, imageParams);
|
||||
if (result) {
|
||||
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), taginfo, imageParams);
|
||||
String configUrl = cfgobj["url"].as<String>();
|
||||
if (!util::isEmptyOrNull(configUrl)) {
|
||||
StaticJsonDocument<1000> json;
|
||||
Serial.println("Get json url + file");
|
||||
if (util::httpGetJson(configUrl, json, 1000)) {
|
||||
if (getJsonTemplateFileExtractVariables(filename, configFilename, json, taginfo, imageParams)) {
|
||||
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), taginfo, imageParams);
|
||||
} else {
|
||||
wsErr("error opening file " + configFilename);
|
||||
}
|
||||
const int interval = cfgobj["interval"].as<int>();
|
||||
taginfo->nextupdate = now + 60 * (interval < 3 ? 15 : interval);
|
||||
} else {
|
||||
taginfo->nextupdate = now + 600;
|
||||
}
|
||||
|
||||
} else {
|
||||
wsErr("error opening file " + configFilename);
|
||||
const bool result = getJsonTemplateFile(filename, configFilename, taginfo, imageParams);
|
||||
if (result) {
|
||||
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), taginfo, imageParams);
|
||||
} else {
|
||||
wsErr("error opening file " + configFilename);
|
||||
}
|
||||
taginfo->nextupdate = 3216153600;
|
||||
}
|
||||
taginfo->nextupdate = 3216153600;
|
||||
} else {
|
||||
const int httpcode = getJsonTemplateUrl(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], String(hexmac), taginfo, imageParams);
|
||||
const int interval = cfgobj["interval"].as<int>();
|
||||
@@ -541,11 +559,10 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) {
|
||||
return;
|
||||
}
|
||||
|
||||
TFT_eSprite spr = TFT_eSprite(&tft);
|
||||
|
||||
StaticJsonDocument<512> loc;
|
||||
getTemplate(loc, 1, taginfo->hwType);
|
||||
|
||||
TFT_eSprite spr = TFT_eSprite(&tft);
|
||||
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
||||
|
||||
const auto &date = loc["date"];
|
||||
@@ -1099,7 +1116,7 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
|
||||
spr.deleteSprite();
|
||||
}
|
||||
|
||||
int getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo, imgParam &imageParams) {
|
||||
bool getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo, imgParam &imageParams) {
|
||||
if (jsonfile.c_str()[0] != '/') {
|
||||
jsonfile = "/" + jsonfile;
|
||||
}
|
||||
@@ -1108,9 +1125,159 @@ int getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo,
|
||||
drawJsonStream(file, filename, taginfo, imageParams);
|
||||
file.close();
|
||||
// contentFS->remove(jsonfile);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief Extract a variable with the given path from the given json
|
||||
/// @note Float and double values are rounded to 2 decimal places
|
||||
/// @param json Json document
|
||||
/// @param path Path in form of a.b.1.c
|
||||
/// @return Value as string
|
||||
String extractValueFromJson(JsonDocument &json, const String &path) {
|
||||
JsonVariant currentObj = json.as<JsonVariant>();
|
||||
char *segment = strtok(const_cast<char *>(path.c_str()), ".");
|
||||
|
||||
while (segment != NULL) {
|
||||
if (currentObj.is<JsonObject>()) {
|
||||
currentObj = currentObj.as<JsonObject>()[segment];
|
||||
} else if (currentObj.is<JsonArray>()) {
|
||||
int index = atoi(segment);
|
||||
currentObj = currentObj.as<JsonArray>()[index];
|
||||
} else {
|
||||
Serial.printf("Invalid JSON structure at path segment: %s\n", segment);
|
||||
return "";
|
||||
}
|
||||
segment = strtok(NULL, ".");
|
||||
}
|
||||
|
||||
if (!currentObj.is<int>() && currentObj.is<float>()) {
|
||||
return String(currentObj.as<float>(), 2);
|
||||
}
|
||||
|
||||
return currentObj.as<String>();
|
||||
}
|
||||
|
||||
/// @brief Replaces json placeholders ({.a.b.1.c}) with variables
|
||||
class DataInterceptor : public Stream {
|
||||
private:
|
||||
/// @brief Stream being wrapped
|
||||
Stream &_stream;
|
||||
/// @brief Json containing variables
|
||||
JsonDocument &_variables;
|
||||
/// @brief Parsing buffer
|
||||
String _buffer;
|
||||
/// @brief Buffer size
|
||||
const size_t _bufferSize = 32;
|
||||
|
||||
public:
|
||||
DataInterceptor(Stream &stream, JsonDocument &variables)
|
||||
: _stream(stream), _variables(variables) {
|
||||
}
|
||||
|
||||
int available() override {
|
||||
return _buffer.length() + _stream.available();
|
||||
}
|
||||
|
||||
int read() override {
|
||||
findAndReplace();
|
||||
|
||||
if (_buffer.length() > 0) {
|
||||
const int data = _buffer[0];
|
||||
_buffer.remove(0, 1);
|
||||
return data;
|
||||
}
|
||||
|
||||
return -1; // No more data
|
||||
}
|
||||
|
||||
int peek() override {
|
||||
findAndReplace();
|
||||
return _buffer.length() ? _buffer[0] : -1;
|
||||
}
|
||||
|
||||
size_t write(uint8_t data) override {
|
||||
return _stream.write(data);
|
||||
}
|
||||
|
||||
private:
|
||||
/// @brief Fill buffer, find and replace json variables
|
||||
void findAndReplace() {
|
||||
unsigned int len;
|
||||
while ((len = _buffer.length()) < _bufferSize) {
|
||||
const int data = _stream.read();
|
||||
if (data == -1) {
|
||||
break; // No more data to read
|
||||
}
|
||||
_buffer += (char)data;
|
||||
}
|
||||
|
||||
if (len < 4) {
|
||||
// There are no variables with less than 4 characters
|
||||
return;
|
||||
}
|
||||
|
||||
int endIndex = findVar(_buffer, 0);
|
||||
if (endIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const String varCleaned = _buffer.substring(1, endIndex - 1);
|
||||
String replacement = extractValueFromJson(_variables, varCleaned);
|
||||
|
||||
// Check for operator and second variable
|
||||
if (endIndex + 3 < len) {
|
||||
const char op = _buffer[endIndex];
|
||||
if ((op == '*' || op == '/' || op == '+' || op == '-')) {
|
||||
const int endIndex2 = findVar(_buffer, endIndex + 1);
|
||||
if (endIndex2 != -1) {
|
||||
const String var2Cleaned = _buffer.substring(endIndex + 2, endIndex2 - 1);
|
||||
const float v2 = extractValueFromJson(_variables, var2Cleaned).toFloat();
|
||||
endIndex = endIndex2;
|
||||
|
||||
if (op == '*') {
|
||||
replacement = String(replacement.toFloat() * v2, 0);
|
||||
} else if (op == '/') {
|
||||
replacement = abs(v2) > 0.0f ? String(replacement.toFloat() / v2, 0) : "0";
|
||||
} else if (op == '+') {
|
||||
replacement = String(replacement.toFloat() + v2, 0);
|
||||
} else if (op == '-') {
|
||||
replacement = String(replacement.toFloat() - v2, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_buffer = replacement + _buffer.substring(endIndex);
|
||||
}
|
||||
|
||||
/// @brief Find a var at given start index
|
||||
/// @param buffer Buffer to search in
|
||||
/// @param index Index to look at
|
||||
/// @return Endindex
|
||||
int findVar(const String &buffer, const int index) {
|
||||
if (buffer[index] != '{' || buffer[index + 1] != '.') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return buffer.indexOf("}", index + 2) + 1;
|
||||
}
|
||||
};
|
||||
|
||||
bool getJsonTemplateFileExtractVariables(String &filename, String jsonfile, JsonDocument &variables, tagRecord *&taginfo, imgParam &imageParams) {
|
||||
if (jsonfile.c_str()[0] != '/') {
|
||||
jsonfile = "/" + jsonfile;
|
||||
}
|
||||
File file = contentFS->open(jsonfile, "r");
|
||||
if (file) {
|
||||
auto interceptor = DataInterceptor(file, variables);
|
||||
drawJsonStream(interceptor, filename, taginfo, imageParams);
|
||||
file.close();
|
||||
// contentFS->remove(jsonfile);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, tagRecord *&taginfo, imgParam &imageParams) {
|
||||
|
||||
Reference in New Issue
Block a user