diff --git a/ESP32_AP-Flasher/data/www/content_cards.json.gz b/ESP32_AP-Flasher/data/www/content_cards.json.gz index ccac4508..868b6c54 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/include/contentmanager.h b/ESP32_AP-Flasher/include/contentmanager.h index f4948a6b..84eb144d 100644 --- a/ESP32_AP-Flasher/include/contentmanager.h +++ b/ESP32_AP-Flasher/include/contentmanager.h @@ -1,6 +1,7 @@ #include #include #include +#include #include "makeimage.h" #include "tag_db.h" @@ -21,7 +22,7 @@ bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin, void drawString(TFT_eSprite &spr, String content, int16_t posx, int16_t posy, String font, byte align = 0, uint16_t color = TFT_BLACK, uint16_t size = 30, uint16_t bgcolor = TFT_WHITE); void drawTextBox(TFT_eSprite &spr, String &content, int16_t &posx, int16_t &posy, int16_t boxwidth, int16_t boxheight, String font, uint16_t color = TFT_BLACK, uint16_t bgcolor = TFT_WHITE, float lineheight = 1, byte align = TL_DATUM); void initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams); -void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams); +void drawDate(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams); void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo, imgParam &imageParams); void drawWeather(String &filename, JsonObject &cfgobj, const tagRecord *taginfo, imgParam &imageParams); void drawForecast(String &filename, JsonObject &cfgobj, const tagRecord *taginfo, imgParam &imageParams); diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 29f860d1..a5578ed5 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -336,7 +336,7 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) { case 1: // Today - drawDate(filename, taginfo, imageParams); + drawDate(filename, cfgobj, taginfo, imageParams); taginfo->nextupdate = util::getMidnightTime(); updateTagImage(filename, mac, (taginfo->nextupdate - now) / 60 - 10, taginfo, imageParams); break; @@ -790,7 +790,41 @@ void initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams) { spr.fillSprite(TFT_WHITE); } -void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) { +String utf8FromCodepoint(uint16_t cp) { + char buf[4] = {0}; + if (cp < 0x80) { + buf[0] = cp; + } else if (cp < 0x800) { + buf[0] = 0xC0 | (cp >> 6); + buf[1] = 0x80 | (cp & 0x3F); + } else { + buf[0] = 0xE0 | ((cp >> 12) & 0x0F); + buf[1] = 0x80 | ((cp >> 6) & 0x3F); + buf[2] = 0x80 | (cp & 0x3F); + } + return String(buf); +} + +String formatUtcToLocal(const String &s) { + int h, m, ss = 0; + if (sscanf(s.c_str(), "%d:%d:%d", &h, &m, &ss) < 2 || h > 23 || m > 59 || ss > 59) + return "-"; + + time_t n = time(nullptr); + struct tm tm = *localtime(&n); + tm.tm_hour = h; + tm.tm_min = m; + tm.tm_sec = ss; + + time_t utc = mktime(&tm) - _timezone + (tm.tm_isdst ? 3600 : 0); + localtime_r(&utc, &tm); + + char buf[6]; + snprintf(buf, 6, "%02d:%02d", tm.tm_hour, tm.tm_min); + return buf; +} + +void drawDate(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) { time_t now; time(&now); struct tm timeinfo; @@ -811,19 +845,48 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) { TFT_eSprite spr = TFT_eSprite(&tft); initSprite(spr, imageParams.width, imageParams.height, imageParams); - const auto &date = loc["date"]; - const auto &weekday = loc["weekday"]; - if (date) { + auto date = loc["date"]; + auto weekday = loc["weekday"]; + const auto &month = loc["month"]; + const auto &day = loc["day"]; + + if (cfgobj["location"]) { + const String lat = cfgobj["#lat"]; + const String lon = cfgobj["#lon"]; + JsonDocument doc; + + const bool success = util::httpGetJson("https://api.farmsense.net/v1/daylengths/?d=" + String(now) + "&lat=" + lat + "&lon=" + lon + "&tz=UTC", doc, 5000); + if (success && loc["sunrise"].is()) { + String sunrise = formatUtcToLocal(doc[0]["Sunrise"]); + String sunset = formatUtcToLocal(doc[0]["Sunset"]); + const auto &sunriseicon = loc["sunrise"]; + const auto &sunseticon = loc["sunset"]; + drawString(spr, String("\uF046 "), sunriseicon[0], sunriseicon[1].as(), "/fonts/weathericons.ttf", TR_DATUM, TFT_BLACK, sunriseicon[3]); + drawString(spr, String("\uF047 "), sunseticon[0], sunseticon[1].as(), "/fonts/weathericons.ttf", TR_DATUM, TFT_BLACK, sunseticon[3]); + drawString(spr, sunrise, sunriseicon[0], sunriseicon[1], sunriseicon[2], TL_DATUM, TFT_BLACK, sunriseicon[3]); + drawString(spr, sunset, sunseticon[0], sunseticon[1], sunseticon[2], TL_DATUM, TFT_BLACK, sunseticon[3]); + + const bool success = util::httpGetJson("https://api.farmsense.net/v1/moonphases/?d=" + String(now), doc, 5000); + if (success && loc["moonicon"].is()) { + uint8_t moonage = doc[0]["Index"].as(); + const auto &moonicon = loc["moonicon"]; + uint16_t moonIconId = 0xf095 + moonage; + String moonIcon = utf8FromCodepoint(moonIconId); + drawString(spr, moonIcon, moonicon[0], moonicon[1], "/fonts/weathericons.ttf", TC_DATUM, TFT_BLACK, moonicon[2]); + } + + date = loc["altdate"]; + weekday = loc["altweekday"]; + } + } + if (date.is()) { drawString(spr, languageDays[timeinfo.tm_wday], weekday[0], weekday[1], weekday[2], TC_DATUM, imageParams.highlightColor, weekday[3]); drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[timeinfo.tm_mon], date[0], date[1], date[2], TC_DATUM, TFT_BLACK, date[3]); } else { - const auto &month = loc["month"]; - const auto &day = loc["day"]; drawString(spr, languageDays[timeinfo.tm_wday], weekday[0], weekday[1], weekday[2], TC_DATUM, TFT_BLACK, weekday[3]); drawString(spr, String(languageMonth[timeinfo.tm_mon]), month[0], month[1], month[2], TC_DATUM, TFT_BLACK, month[3]); drawString(spr, String(timeinfo.tm_mday), day[0], day[1], day[2], TC_DATUM, imageParams.highlightColor, day[3]); } - spr2buffer(spr, filename, imageParams); spr.deleteSprite(); } diff --git a/ESP32_AP-Flasher/wwwroot/content_cards.json b/ESP32_AP-Flasher/wwwroot/content_cards.json index 00af7db6..14836c02 100644 --- a/ESP32_AP-Flasher/wwwroot/content_cards.json +++ b/ESP32_AP-Flasher/wwwroot/content_cards.json @@ -44,8 +44,27 @@ { "id": 1, "name": "Current date", - "desc": "Shows the current date", - "param": [ ] + "desc": "Shows the current date. In some templates, the sunrise and sunset times are shown as well, when you fill in your location.", + "param": [ + { + "key": "location", + "name": "Location", + "desc": "Name of the city. This is used to lookup the lat/long data, and to display as the title", + "type": "geoselect" + }, + { + "key": "#lat", + "name": "Lat", + "desc": "Latitude (set automatic when generating image)", + "type": "ro" + }, + { + "key": "#lon", + "name": "Lon", + "desc": "Longitude (set automatic when generating image)", + "type": "ro" + } + ] }, { "id": 2, diff --git a/resources/tagtypes/47.json b/resources/tagtypes/47.json index 9bc101a9..a48902aa 100644 --- a/resources/tagtypes/47.json +++ b/resources/tagtypes/47.json @@ -1,5 +1,5 @@ { - "version": 3, + "version": 4, "name": "M3 2.7\"", "width": 300, "height": 200, @@ -16,8 +16,15 @@ "contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20, 21, 27 ], "template": { "1": { - "weekday": [ 150, 25, "Signika-SB.ttf", 60 ], - "date": [ 150, 100, "Signika-SB.ttf", 48 ] + "weekday": [ 150, -5, "Signika-SB.ttf", 60 ], + "day": [ 150, 37, "Signika-SB.ttf", 100 ], + "month": [ 150, 125, "Signika-SB.ttf", 60 ], + + "altweekday": [ 150, -5, "Signika-SB.ttf", 60 ], + "altdate": [ 150, 65, "Signika-SB.ttf", 54 ], + "moonicon": [ 150, 115, 62 ], + "sunrise": [ 50, 140, "Signika-SB.ttf", 28 ], + "sunset": [ 220, 140, "Signika-SB.ttf", 28 ] }, "2": { "fonts": [ "Signika-SB.ttf", 200, 200, 170, 120, 100, 80 ],