diff --git a/ESP32_AP-Flasher/data/tagtypes/31.json b/ESP32_AP-Flasher/data/tagtypes/31.json index e4f78bab..69169e71 100644 --- a/ESP32_AP-Flasher/data/tagtypes/31.json +++ b/ESP32_AP-Flasher/data/tagtypes/31.json @@ -33,8 +33,8 @@ "4": { "location": [ 5, 5, "fonts/bahnschrift30" ], "wind": [ 280, 5, "fonts/bahnschrift30" ], - "temp": [ 5, 65, "fonts/bahnschrift70" ], - "icon": [ 285, 20, 70, 2 ], + "temp": [ 5, 65, "fonts/calibrib80" ], + "icon": [ 275, 20, 90, 2 ], "dir": [ 245, -12, 40 ], "umbrella": [ 190, -50, 25 ] }, diff --git a/ESP32_AP-Flasher/data/www/content_cards.json.gz b/ESP32_AP-Flasher/data/www/content_cards.json.gz index a06edadc..ba51825e 100644 Binary files a/ESP32_AP-Flasher/data/www/content_cards.json.gz and b/ESP32_AP-Flasher/data/www/content_cards.json.gz differ diff --git a/ESP32_AP-Flasher/data/www/main.js.gz b/ESP32_AP-Flasher/data/www/main.js.gz index 0a17864b..a067d579 100644 Binary files a/ESP32_AP-Flasher/data/www/main.js.gz and b/ESP32_AP-Flasher/data/www/main.js.gz differ diff --git a/ESP32_AP-Flasher/data/www/setup.html.gz b/ESP32_AP-Flasher/data/www/setup.html.gz index e6ed5f76..fa2f5f6a 100644 Binary files a/ESP32_AP-Flasher/data/www/setup.html.gz and b/ESP32_AP-Flasher/data/www/setup.html.gz differ diff --git a/ESP32_AP-Flasher/data/www/setup.js.gz b/ESP32_AP-Flasher/data/www/setup.js.gz index eaac7eb4..8700eb3c 100644 Binary files a/ESP32_AP-Flasher/data/www/setup.js.gz and b/ESP32_AP-Flasher/data/www/setup.js.gz differ diff --git a/ESP32_AP-Flasher/include/tag_db.h b/ESP32_AP-Flasher/include/tag_db.h index 540304f7..3abae83b 100644 --- a/ESP32_AP-Flasher/include/tag_db.h +++ b/ESP32_AP-Flasher/include/tag_db.h @@ -15,7 +15,7 @@ #define NO_SUBGHZ_CHANNEL 255 class tagRecord { public: - tagRecord() : mac{0}, version(0), alias(""), lastseen(0), nextupdate(0), contentMode(0), pendingCount(0), md5{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0), updateCount(0) {} + tagRecord() : mac{0}, version(0), alias(""), lastseen(0), nextupdate(0), contentMode(0), pendingCount(0), md5{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0), updateCount(0), updateLast(0) {} uint8_t mac[8]; uint8_t version; @@ -45,6 +45,7 @@ class tagRecord { uint8_t currentChannel; uint8_t invert; uint32_t updateCount; + uint32_t updateLast; uint8_t dataType; String filename; diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 391eb376..1d83e0c1 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -235,9 +235,10 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) { imageParams.lut = EPD_LUT_OTA; } - int32_t interval = 60 * 60; - interval = cfgobj["interval"].as() * 60; - if (interval < 0) { + int32_t interval = cfgobj["interval"].as() * 60; + if (interval == -1440 * 60) { + interval = util::getMidnightTime() - now; + } else if (interval < 0) { interval = -interval; unsigned int secondsUntilNext = (interval - (now % interval)) % interval; interval = secondsUntilNext; @@ -341,8 +342,8 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) { case 8: // Forecast drawForecast(filename, cfgobj, taginfo, imageParams); - taginfo->nextupdate = now + 3600; - updateTagImage(filename, mac, 15, taginfo, imageParams); + taginfo->nextupdate = now + interval; + updateTagImage(filename, mac, interval / 60, taginfo, imageParams); break; case 5: // Firmware @@ -1513,15 +1514,17 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, int n = doc.size(); + int units = cfgobj["units"].as(); + if (units == 0) units = 1; double tarifkwh = cfgobj["tariffkwh"].as(); double tariftax = cfgobj["tarifftax"].as(); - double minPrice = (doc[0]["price"].as() / 10 + tarifkwh) * (1 + tariftax / 100); + double minPrice = (doc[0]["price"].as() / 10 + tarifkwh) * (1 + tariftax / 100) / units; double maxPrice = minPrice; double prices[n]; for (int i = 0; i < n; i++) { const JsonObject &obj = doc[i]; - const double price = (obj["price"].as() / 10 + tarifkwh) * (1 + tariftax / 100); + const double price = (obj["price"].as() / 10 + tarifkwh) * (1 + tariftax / 100) / units; minPrice = min(minPrice, price); maxPrice = max(maxPrice, price); prices[i] = price; @@ -1537,7 +1540,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, for (double i = minPrice; i <= maxPrice; i += yAxisScale.step) { int y = mapDouble(i, minPrice, maxPrice, spr.height() - barBottom, spr.height() - barBottom - loc["bars"][2].as()); spr.drawLine(0, y, spr.width(), y, TFT_BLACK); - drawString(spr, String(int(i)), yAxisX, y - 8, loc["yaxis"][0], TL_DATUM, TFT_BLACK); + drawString(spr, String(int(i * units)), yAxisX, y - 8, loc["yaxis"][0], TL_DATUM, TFT_BLACK); } uint16_t barwidth = loc["bars"][1].as() / n; @@ -1552,7 +1555,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, struct tm item_timeinfo; localtime_r(&item_time, &item_timeinfo); - const double price = (obj["price"].as() / 10 + tarifkwh) * (1 + tariftax / 100); + const double price = (obj["price"].as() / 10 + tarifkwh) * (1 + tariftax / 100) / units; uint16_t barcolor = getPercentileColor(prices, n, price); uint16_t thisbarh = mapDouble(price, minPrice, maxPrice, 0, loc["bars"][2].as()); diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index fa6abf02..c236dc66 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -461,6 +461,7 @@ void processXferComplete(struct espXferComplete* xfc, bool local) { clearPending(taginfo); memcpy(taginfo->md5, md5bytes, sizeof(md5bytes)); taginfo->updateCount++; + taginfo->updateLast = now; taginfo->pendingCount = countQueueItem(xfc->src); taginfo->wakeupReason = 0; if (taginfo->contentMode == 12 && local == false) { @@ -568,7 +569,10 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP if (eadr->adr.lastPacketRSSI != 0) { if (eadr->adr.wakeupReason >= 0xE0) { - if (taginfo->pendingCount == 0) taginfo->nextupdate = 0; + if (taginfo->pendingCount == 0) { + taginfo->nextupdate = 0; + memset(taginfo->md5, 0, sizeof(taginfo->md5)); + } if (local) { const char* reason = ""; @@ -643,6 +647,7 @@ void updateContent(const uint8_t* dst) { tagRecord* taginfo = tagRecord::findByMAC(dst); if (taginfo != nullptr) { clearPending(taginfo); + memset(taginfo->md5, 0, sizeof(taginfo->md5)); taginfo->nextupdate = 0; wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS); } diff --git a/ESP32_AP-Flasher/src/ota.cpp b/ESP32_AP-Flasher/src/ota.cpp index 921958b4..52821fed 100644 --- a/ESP32_AP-Flasher/src/ota.cpp +++ b/ESP32_AP-Flasher/src/ota.cpp @@ -93,34 +93,80 @@ void handleCheckFile(AsyncWebServerRequest* request) { request->send(200, "application/json", jsonResponse); } +#define UPLOAD_BUFFER_SIZE 32768 + +struct UploadInfo { + String filename; + uint8_t buffer[UPLOAD_BUFFER_SIZE]; + size_t bufferSize; +}; + void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) { + String uploadfilename; bool error = false; if (!index) { String path; if (!request->hasParam("path", true)) { - path = "/temp/null.bin"; final = true; error = true; } else { - path = request->getParam("path", true)->value(); - Serial.println("update " + path); - xSemaphoreTake(fsMutex, portMAX_DELAY); - request->_tempFile = contentFS->open(path, "w", true); + uploadfilename = request->getParam("path", true)->value(); + Serial.println("update " + uploadfilename); + File file = contentFS->open(uploadfilename, "w"); + file.close(); + UploadInfo* uploadInfo = new UploadInfo{uploadfilename, {}, 0}; + request->_tempObject = (void*)uploadInfo; } } - if (len) { - if (!request->_tempFile.write(data, len)) { - error = true; - final = true; + + UploadInfo* uploadInfo = static_cast(request->_tempObject); + + if (uploadInfo != nullptr) { + uploadfilename = uploadInfo->filename; + + if (len) { + if (uploadInfo->bufferSize + len <= UPLOAD_BUFFER_SIZE) { + memcpy(&uploadInfo->buffer[uploadInfo->bufferSize], data, len); + uploadInfo->bufferSize += len; + } else { + xSemaphoreTake(fsMutex, portMAX_DELAY); + File file = contentFS->open(uploadfilename, "a"); + if (file) { + file.write(uploadInfo->buffer, uploadInfo->bufferSize); + file.close(); + uploadInfo->bufferSize = 0; + } else { + logLine("Failed to open file for appending: " + uploadfilename); + final = true; + error = true; + } + xSemaphoreGive(fsMutex); + + memcpy(uploadInfo->buffer, data, len); + uploadInfo->bufferSize = len; + } } - } - if (final) { - request->_tempFile.close(); - xSemaphoreGive(fsMutex); - if (error) { - request->send(507, "text/plain", "Error. Disk full?"); - } else { - request->send(200, "text/plain", "Ok, file written"); + if (final) { + if (uploadInfo->bufferSize > 0) { + xSemaphoreTake(fsMutex, portMAX_DELAY); + File file = contentFS->open(uploadfilename, "a"); + if (file) { + file.write(uploadInfo->buffer, uploadInfo->bufferSize); + file.close(); + } else { + logLine("Failed to open file for appending: " + uploadfilename); + error = true; + } + xSemaphoreGive(fsMutex); + request->_tempObject = nullptr; + delete uploadInfo; + } + + if (error) { + request->send(507, "text/plain", "Error. Disk full?"); + } else { + request->send(200, "text/plain", "Ok, file written"); + } } } } diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp index fc785106..0b909e1b 100644 --- a/ESP32_AP-Flasher/src/tag_db.cpp +++ b/ESP32_AP-Flasher/src/tag_db.cpp @@ -120,6 +120,7 @@ void fillNode(JsonObject& tag, const tagRecord* taginfo) { tag["lut"] = taginfo->lut; tag["invert"] = taginfo->invert; tag["updatecount"] = taginfo->updateCount; + tag["updatelast"] = taginfo->updateLast; tag["ch"] = taginfo->currentChannel; tag["ver"] = taginfo->tagSoftwareVersion; } @@ -228,6 +229,7 @@ bool loadDB(const String& filename) { taginfo->lut = tag["lut"] | 0; taginfo->invert = tag["invert"] | 0; taginfo->updateCount = tag["updatecount"] | 0; + taginfo->updateLast = tag["updatelast"] | 0; taginfo->currentChannel = tag["ch"] | 0; taginfo->tagSoftwareVersion = tag["ver"] | 0; } diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp index 809319dc..09dbde34 100644 --- a/ESP32_AP-Flasher/src/web.cpp +++ b/ESP32_AP-Flasher/src/web.cpp @@ -651,6 +651,7 @@ void init_web() { for (size_t i = 0; i < numKeys; i++) { doc[keys[i]] = preferences.getString(keys[i], ""); } + doc["mac"] = WiFi.macAddress(); serializeJson(doc, *response); request->send(response); }); diff --git a/ESP32_AP-Flasher/wwwroot/content_cards.json b/ESP32_AP-Flasher/wwwroot/content_cards.json index 2fd95154..51d80263 100644 --- a/ESP32_AP-Flasher/wwwroot/content_cards.json +++ b/ESP32_AP-Flasher/wwwroot/content_cards.json @@ -146,6 +146,17 @@ "1": "Fahrenheit / mph / millimeters" } }, + { + "key": "interval", + "name": "Interval", + "desc": "How often the forecast is being refreshed.", + "type": "select", + "options": { + "60": "Every hour", + "180": "-Every three hours", + "-1440": "At midnight" + } + }, { "key": "#lat", "name": "Lat", @@ -380,7 +391,7 @@ { "key": "tariffkwh", "name": "Fixed surcharge", - "desc": "Fixed surcharge per kWh, in 1/100 units (cents)", + "desc": "Fixed surcharge per kWh, in 1/100 units (cents/öre/øre)", "type": "text" }, { @@ -388,7 +399,17 @@ "name": "Tax percentage", "desc": "Percentage to add to the total (for example, 21, for 21% VAT)", "type": "text" - } + }, + { + "key": "units", + "name": "Price units", + "desc": "Display whole units (EUR/NOK/DKK) or cents", + "type": "select", + "options": { + "100": "1/1 units (EUR/NOK/DKK)", + "1": "1/100 units (cents/öre/øre)" + } + } ] }, { diff --git a/ESP32_AP-Flasher/wwwroot/setup.html b/ESP32_AP-Flasher/wwwroot/setup.html index 1e0f6eff..15d4becb 100644 --- a/ESP32_AP-Flasher/wwwroot/setup.html +++ b/ESP32_AP-Flasher/wwwroot/setup.html @@ -9,6 +9,12 @@