#include "contentmanager.h" #include #include #include #include "newproto.h" #include #include #include #include "commstructs.h" #include "makeimage.h" #include "web.h" void contentRunner() { time_t now; time(&now); for (int16_t c = 0; c < tagDB.size(); c++) { tagRecord* taginfo = nullptr; taginfo = tagDB.at(c); if (now >= taginfo->nextupdate || taginfo->button) { uint8_t mac8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; memcpy(mac8 + 2, taginfo->mac, 6); uint8_t src[8]; *((uint64_t *)src) = swap64(*((uint64_t *)mac8)); drawNew(src, taginfo->button, taginfo); taginfo->button = false; } } } void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { time_t now; time(&now); char buffer[64]; uint8_t src[8]; *((uint64_t *)src) = swap64(*((uint64_t *)mac)); sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", src[2], src[3], src[4], src[5], src[6], src[7]); String dst = (String)buffer; String filename = "/" + dst + ".bmp"; struct tm time_info; getLocalTime(&time_info); time_info.tm_hour = 0; time_info.tm_min = 0; time_info.tm_sec = 0; time_info.tm_mday++; time_t midnight = mktime(&time_info); DynamicJsonDocument doc(500); deserializeJson(doc, taginfo->modeConfigJson); JsonObject cfgobj = doc.as(); wsLog("Updating " + dst + " mode " + String(taginfo->contentMode)); taginfo->nextupdate = now + 600; switch (taginfo->contentMode) { case Image: filename = cfgobj["filename"].as(); if (filename && filename !="null" && !cfgobj["#fetched"].as()) { if (prepareDataAvail(&filename, DATATYPE_IMGRAW, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; } else { wsErr("Error accessing " + filename); } taginfo->nextupdate = 3216153600; } break; case Today: drawDate(filename); // updateTagImage(filename, mac, (midnight - now) / 60 - 10); updateTagImage(filename, mac, 60); taginfo->nextupdate = midnight; break; case CountDays: if (buttonPressed) cfgobj["counter"] = 0; drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]); updateTagImage(filename, mac, (buttonPressed?0:60)); cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = midnight; break; case CountHours: if (buttonPressed) cfgobj["counter"] = 0; drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]); // updateTagImage(&filename, mac, (3600 - now % 3600) / 60); // taginfo->nextupdate = now + 3600 - (now % 3600); updateTagImage(filename, mac, (buttonPressed?0:3)); cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = now + 300; break; case Weather: // https://open-meteo.com/ break; case Firmware: filename = cfgobj["filename"].as(); if (filename && filename != "null" && !cfgobj["#fetched"].as()) { if (prepareDataAvail(&filename, DATATYPE_UPDATE, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; } else { wsErr("Error accessing " + filename); } cfgobj["filename"]=""; taginfo->nextupdate = 3216153600; taginfo->contentMode = Image; } else { taginfo->nextupdate = now + 300; } break; case Memo: break; case ImageUrl: if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"])) { updateTagImage(filename, mac, cfgobj["interval"].as()); cfgobj["#fetched"] = now; } taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as()) ; break; } taginfo->modeConfigJson = doc.as(); } bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin) { prepareDataAvail(&filename, DATATYPE_IMGRAW, dst, nextCheckin); return true; } void drawDate(String &filename) { TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); time_t now; time(&now); struct tm timeinfo; localtime_r(&now, &timeinfo); String Dag[] = {"zondag","maandag","dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"}; String Maand[] = {"januari", "februari", "maart", "april", "mei", "juni","juli", "augustus", "september", "oktober", "november", "december"}; int weekday_number = timeinfo.tm_wday; int month_number = timeinfo.tm_mon; LittleFS.begin(); long w = 296, h = 128; // mag staand of liggend spr.createSprite(w, h); if (spr.getPointer() == nullptr) { wsErr("Failed to create sprite in drawDate"); } spr.setColorDepth(8); spr.fillSprite(TFT_WHITE); spr.setTextDatum(TC_DATUM); spr.loadFont("fonts/calibrib62", LittleFS); spr.setTextColor(TFT_RED, TFT_WHITE); spr.drawString(Dag[timeinfo.tm_wday], w / 2, 10); spr.loadFont("fonts/calibrib50", LittleFS); spr.setTextColor(TFT_BLACK, TFT_WHITE); spr.drawString(String(timeinfo.tm_mday) + " " + Maand[timeinfo.tm_mon], w / 2, 73); spr.unloadFont(); spr2grays(spr, w, h, filename); spr.deleteSprite(); } void drawNumber(String &filename, int32_t count, int32_t thresholdred) { TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); LittleFS.begin(); long w = 296, h = 128; spr.createSprite(w, h); if (spr.getPointer() == nullptr) { wsErr("Failed to create sprite in drawNumber"); } spr.setColorDepth(8); spr.fillSprite(TFT_WHITE); spr.setTextDatum(MC_DATUM); if (count > thresholdred) { spr.setTextColor(TFT_RED, TFT_WHITE); } else { spr.setTextColor(TFT_BLACK, TFT_WHITE); } String font = "fonts/numbers1-2"; if (count > 999) font = "fonts/numbers2-2"; if (count > 9999) font = "fonts/numbers3-2"; spr.loadFont(font, LittleFS); spr.drawString(String(count), w/2, h/2+10); spr.unloadFont(); spr2grays(spr, w, h, filename); spr.deleteSprite(); } bool getImgURL(String &filename, String URL, time_t fetched) { // https://images.klari.net/kat-bw29.jpg LittleFS.begin(); Serial.println("get external " + URL); HTTPClient http; http.begin(URL); http.addHeader("If-Modified-Since", formatHttpDate(fetched)); http.setTimeout(5000); //timeout in ms int httpCode = http.GET(); if (httpCode == 200) { File f = LittleFS.open(filename, "w"); if (f) { http.writeToStream(&f); f.close(); jpg2grays(filename, filename); } } else { if (httpCode!=304) { wsErr("http " + URL + " " + String(httpCode)); } else { wsLog("http " + URL + " " + String(httpCode)); } } http.end(); return (httpCode == 200); } char *formatHttpDate(time_t t) { static char buf[40]; struct tm *timeinfo; timeinfo = gmtime(&t); strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", timeinfo); return buf; }