mirror of
https://github.com/mfactory-osaka/ESPTimeCast.git
synced 2026-04-03 03:00:24 -04:00
Added Irish language
Added Irish language. Bug fixes: weather description random characters addressed.
This commit is contained in:
@@ -68,7 +68,7 @@ int dimBrightness = 2; // Dimming level (0-15)
|
||||
bool countdownEnabled = false;
|
||||
time_t countdownTargetTimestamp = 0; // Unix timestamp
|
||||
char countdownLabel[64] = ""; // Label for the countdown
|
||||
bool isDramaticCountdown = true; // Default to the dramatic countdown mode
|
||||
bool isDramaticCountdown = true; // Default to the dramatic countdown mode
|
||||
|
||||
// State management
|
||||
bool weatherCycleStarted = false;
|
||||
@@ -267,7 +267,7 @@ void loadConfig() {
|
||||
|
||||
countdownEnabled = countdownObj["enabled"] | false;
|
||||
countdownTargetTimestamp = countdownObj["targetTimestamp"] | 0;
|
||||
isDramaticCountdown = countdownObj["isDramaticCountdown"] | true;
|
||||
isDramaticCountdown = countdownObj["isDramaticCountdown"] | true;
|
||||
|
||||
JsonVariant labelVariant = countdownObj["label"];
|
||||
if (labelVariant.isNull() || !labelVariant.is<const char *>()) {
|
||||
@@ -285,7 +285,7 @@ void loadConfig() {
|
||||
countdownEnabled = false;
|
||||
countdownTargetTimestamp = 0;
|
||||
strcpy(countdownLabel, "");
|
||||
isDramaticCountdown = true;
|
||||
isDramaticCountdown = true;
|
||||
Serial.println(F("[CONFIG] Countdown object not found, defaulting to disabled."));
|
||||
countdownFinished = false;
|
||||
}
|
||||
@@ -430,7 +430,7 @@ void setupTime() {
|
||||
}
|
||||
|
||||
if (serverOk) {
|
||||
configTime(0, 0, ntpServer1, ntpServer2); // safe to call now
|
||||
configTime(0, 0, ntpServer1, ntpServer2); // safe to call now
|
||||
setenv("TZ", ianaToPosix(timeZone), 1);
|
||||
tzset();
|
||||
ntpState = NTP_SYNCING;
|
||||
@@ -440,7 +440,7 @@ void setupTime() {
|
||||
} else {
|
||||
Serial.println(F("[TIME] NTP server lookup failed — skipping sync"));
|
||||
ntpSyncSuccessful = false;
|
||||
ntpState = NTP_IDLE; // or custom NTP_ERROR state
|
||||
ntpState = NTP_IDLE; // or custom NTP_ERROR state
|
||||
// Trigger your error display here if desired
|
||||
}
|
||||
}
|
||||
@@ -927,30 +927,30 @@ void setupWebServer() {
|
||||
});
|
||||
|
||||
|
||||
server.on("/set_dramatic_countdown", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
bool enableDramaticNow = false;
|
||||
if (request->hasParam("value", true)) {
|
||||
String v = request->getParam("value", true)->value();
|
||||
enableDramaticNow = (v == "1" || v == "true" || v == "on");
|
||||
}
|
||||
server.on("/set_dramatic_countdown", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
bool enableDramaticNow = false;
|
||||
if (request->hasParam("value", true)) {
|
||||
String v = request->getParam("value", true)->value();
|
||||
enableDramaticNow = (v == "1" || v == "true" || v == "on");
|
||||
}
|
||||
|
||||
// Check if the state has changed
|
||||
if (isDramaticCountdown == enableDramaticNow) {
|
||||
Serial.println(F("[WEBSERVER] Dramatic Countdown state unchanged, ignoring."));
|
||||
// Check if the state has changed
|
||||
if (isDramaticCountdown == enableDramaticNow) {
|
||||
Serial.println(F("[WEBSERVER] Dramatic Countdown state unchanged, ignoring."));
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the global variable
|
||||
isDramaticCountdown = enableDramaticNow;
|
||||
|
||||
// Call saveCountdownConfig with only the existing parameters.
|
||||
// It will read the updated global variable 'isDramaticCountdown'.
|
||||
saveCountdownConfig(countdownEnabled, countdownTargetTimestamp, countdownLabel);
|
||||
|
||||
Serial.printf("[WEBSERVER] Set Dramatic Countdown to %d\n", isDramaticCountdown);
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the global variable
|
||||
isDramaticCountdown = enableDramaticNow;
|
||||
|
||||
// Call saveCountdownConfig with only the existing parameters.
|
||||
// It will read the updated global variable 'isDramaticCountdown'.
|
||||
saveCountdownConfig(countdownEnabled, countdownTargetTimestamp, countdownLabel);
|
||||
|
||||
Serial.printf("[WEBSERVER] Set Dramatic Countdown to %d\n", isDramaticCountdown);
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
server.begin();
|
||||
@@ -1143,7 +1143,7 @@ String buildWeatherURL() {
|
||||
|
||||
String langForAPI = String(language);
|
||||
|
||||
if (langForAPI == "eo" || langForAPI == "sw" || langForAPI == "ja") {
|
||||
if (langForAPI == "eo" || langForAPI == "ga" || langForAPI == "sw" || langForAPI == "ja") {
|
||||
langForAPI = "en";
|
||||
}
|
||||
base += "&lang=" + langForAPI;
|
||||
@@ -1317,7 +1317,7 @@ void advanceDisplayMode() {
|
||||
String ntpField = String(ntpServer2);
|
||||
bool nightscoutConfigured = ntpField.startsWith("https://");
|
||||
|
||||
if (displayMode == 0) { // Clock
|
||||
if (displayMode == 0) { // Clock
|
||||
if (showDate) {
|
||||
displayMode = 5; // Date mode right after Clock
|
||||
Serial.println(F("[DISPLAY] Switching to display mode: DATE (from Clock)"));
|
||||
@@ -1348,7 +1348,7 @@ void advanceDisplayMode() {
|
||||
displayMode = 0;
|
||||
Serial.println(F("[DISPLAY] Switching to display mode: CLOCK (from Date)"));
|
||||
}
|
||||
} else if (displayMode == 1) { // Weather
|
||||
} else if (displayMode == 1) { // Weather
|
||||
if (showWeatherDescription && weatherAvailable && weatherDescription.length() > 0) {
|
||||
displayMode = 2;
|
||||
Serial.println(F("[DISPLAY] Switching to display mode: DESCRIPTION (from Weather)"));
|
||||
@@ -1362,7 +1362,7 @@ void advanceDisplayMode() {
|
||||
displayMode = 0;
|
||||
Serial.println(F("[DISPLAY] Switching to display mode: CLOCK (from Weather)"));
|
||||
}
|
||||
} else if (displayMode == 2) { // Weather Description
|
||||
} else if (displayMode == 2) { // Weather Description
|
||||
if (countdownEnabled && !countdownFinished && ntpSyncSuccessful) {
|
||||
displayMode = 3;
|
||||
Serial.println(F("[DISPLAY] Switching to display mode: COUNTDOWN (from Description)"));
|
||||
@@ -1748,16 +1748,20 @@ void loop() {
|
||||
|
||||
|
||||
|
||||
|
||||
// --- WEATHER DESCRIPTION Display Mode ---
|
||||
if (displayMode == 2 && showWeatherDescription && weatherAvailable && weatherDescription.length() > 0) {
|
||||
String desc = weatherDescription;
|
||||
desc.toUpperCase();
|
||||
|
||||
// prepare safe buffer
|
||||
static char descBuffer[128]; // large enough for OWM translations
|
||||
desc.toCharArray(descBuffer, sizeof(descBuffer));
|
||||
|
||||
if (desc.length() > 8) {
|
||||
if (!descScrolling) {
|
||||
P.displayClear();
|
||||
textEffect_t actualScrollDirection = getEffectiveScrollDirection(PA_SCROLL_LEFT, flipDisplay);
|
||||
P.displayScroll(desc.c_str(), PA_CENTER, actualScrollDirection, GENERAL_SCROLL_SPEED);
|
||||
P.displayScroll(descBuffer, PA_CENTER, actualScrollDirection, GENERAL_SCROLL_SPEED);
|
||||
descScrolling = true;
|
||||
descScrollEndTime = 0; // reset end time at start
|
||||
}
|
||||
@@ -1780,7 +1784,7 @@ void loop() {
|
||||
if (descStartTime == 0) {
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(1);
|
||||
P.print(desc.c_str());
|
||||
P.print(descBuffer);
|
||||
descStartTime = millis();
|
||||
}
|
||||
if (millis() - descStartTime > descriptionDuration) {
|
||||
@@ -2022,7 +2026,7 @@ void loop() {
|
||||
P.displayAnimate();
|
||||
}
|
||||
|
||||
// --- NEW: SINGLE-LINE COUNTDOWN LOGIC ---
|
||||
// --- NEW: SINGLE-LINE COUNTDOWN LOGIC ---
|
||||
else {
|
||||
long days = timeRemaining / (24 * 3600);
|
||||
long hours = (timeRemaining % (24 * 3600)) / 3600;
|
||||
@@ -2052,9 +2056,9 @@ void loop() {
|
||||
} else {
|
||||
sprintf(buf, "%s IN: %02ldH %02ldM %02ldS", label.c_str(), hours, minutes, seconds);
|
||||
}
|
||||
|
||||
|
||||
String fullString = String(buf);
|
||||
|
||||
|
||||
// Display the full string and scroll it
|
||||
P.displayClear();
|
||||
P.setTextAlignment(PA_LEFT);
|
||||
@@ -2074,14 +2078,14 @@ void loop() {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keep alignment reset just in case
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(1);
|
||||
yield();
|
||||
return;
|
||||
} // End of if (displayMode == 3 && ...)
|
||||
// Keep alignment reset just in case
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(1);
|
||||
yield();
|
||||
return;
|
||||
} // End of if (displayMode == 3 && ...)
|
||||
|
||||
|
||||
// --- NIGHTSCOUT Display Mode ---
|
||||
@@ -2197,6 +2201,7 @@ void loop() {
|
||||
"et", // Estonian
|
||||
"fi", // Finnish
|
||||
"fr", // French
|
||||
"ga", // Irish
|
||||
"hr", // Croatian
|
||||
"hu", // Hungarian
|
||||
"it", // Italian
|
||||
|
||||
@@ -447,6 +447,7 @@ textarea::placeholder {
|
||||
<option value="de">German</option>
|
||||
<option value="hu">Hungarian</option>
|
||||
<option value="it">Italian</option>
|
||||
<option value="ga">Irish</option>
|
||||
<option value="ja">Japanese</option>
|
||||
<option value="lv">Latvian</option>
|
||||
<option value="lt">Lithuanian</option>
|
||||
|
||||
@@ -17,6 +17,7 @@ const DaysOfWeekMapping days_mappings[] = {
|
||||
{ "et", { "p&a", "e&s", "t&e", "k&o", "n&e", "r&e", "l&a" } },
|
||||
{ "fi", { "s&u&n", "m&a&a", "t&i&s", "k&e&s", "t&o&r", "p&e&r", "l&a&u" } },
|
||||
{ "fr", { "d&i&m", "l&u&n", "m&a&r", "m&e&r", "j&e&u", "v&e&n", "s&a&m" } },
|
||||
{ "ga", { "d&o&m", "l&u&a", "m&a&i", "c&e&a", "d&e&a", "a&o&i", "s&a&t" } },
|
||||
{ "hr", { "n&e&d", "p&o&n", "u&t&o", "s&r&i", "c&e&t", "p&e&t", "s&u&b" } },
|
||||
{ "hu", { "v&a&s", "h&e&t", "k&e&d", "s&z&e", "c&s&u", "p&e&t", "s&z&o" } },
|
||||
{ "it", { "d&o&m", "l&u&n", "m&a&r", "m&e&r", "g&i&o", "v&e&n", "s&a&b" } },
|
||||
|
||||
@@ -43,7 +43,7 @@ MD_MAX72XX::fontType_t mFactory[] PROGMEM =
|
||||
6, 66, 37, 18, 72, 164, 66, // 37 - '%'
|
||||
1, 1, // 38 - '&'
|
||||
1, 6, // 39 - ''
|
||||
1, 0, // 40 - '('
|
||||
13, 254, 17, 17, 254, 0, 126, 129, 65, 190, 0, 129, 255, 129, // 40 - '('
|
||||
17, 130, 186, 198, 254, 134, 234, 134, 254, 250, 130, 250, 254, 134, 234, 134, 254, 124, // 41 - ')'
|
||||
20, 250, 130, 250, 254, 130, 170, 186, 254, 130, 250, 226, 250, 134, 254, 130, 234, 234, 246, 254, 124, // 42 - '*'
|
||||
1, 0, // 43 - '+'
|
||||
|
||||
@@ -20,6 +20,7 @@ const MonthsMapping months_mappings[] = {
|
||||
{ "hr", { "s&i&j", "v&e&l", "o&z&u", "t&r&a", "s&v&i", "l&i&p", "s&r&p", "k&o&l", "r&u&j", "l&i&s", "s&t&u", "p&r&o" } }, // Croatian
|
||||
{ "hu", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&j", "j&u&n", "j&u&l", "a&u&g", "s&z&e", "o&k&t", "n&o&v", "d&e&c" } }, // Hungarian
|
||||
{ "it", { "g&e&n", "f&e&b", "m&a&r", "a&p&r", "m&a&g", "g&i&u", "l&u&g", "a&g&o", "s&e&t", "o&t&t", "n&o&v", "d&i&c" } }, // Italian
|
||||
{ "ga", { "e&a&n", "f&e&a", "m&a&r", "a&i&b", "b&e&a", "m&e&i", "i&u&i", "l&u&n", "m&e&a", "d&e&i", "s&a&m", "n&o&l" } }, // Irish
|
||||
{ "ja", { "1 ²", "2 ²", "3 ²", "4 ²", "5 ²", "6 ²", "7 ²", "8 ²", "9 ²", "10 ²", "11 ²", "12 ²" } }, // Japanese
|
||||
{ "lt", { "s&a&u", "v&a&s", "k&o&v", "b&a&l", "g&e&g", "b&i&r", "l&i&e", "r&u&g", "s&w&e", "s&p&a", "l&a&p", "g&r&u" } }, // Lithuanian
|
||||
{ "lv", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&i", "j&u&n", "j&u&l", "a&u&g", "s&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Latvian
|
||||
|
||||
@@ -68,7 +68,7 @@ int dimBrightness = 2; // Dimming level (0-15)
|
||||
bool countdownEnabled = false;
|
||||
time_t countdownTargetTimestamp = 0; // Unix timestamp
|
||||
char countdownLabel[64] = ""; // Label for the countdown
|
||||
bool isDramaticCountdown = true; // Default to the dramatic countdown mode
|
||||
bool isDramaticCountdown = true; // Default to the dramatic countdown mode
|
||||
|
||||
// State management
|
||||
bool weatherCycleStarted = false;
|
||||
@@ -267,8 +267,8 @@ void loadConfig() {
|
||||
|
||||
countdownEnabled = countdownObj["enabled"] | false;
|
||||
countdownTargetTimestamp = countdownObj["targetTimestamp"] | 0;
|
||||
isDramaticCountdown = countdownObj["isDramaticCountdown"] | true;
|
||||
|
||||
isDramaticCountdown = countdownObj["isDramaticCountdown"] | true;
|
||||
|
||||
|
||||
JsonVariant labelVariant = countdownObj["label"];
|
||||
if (labelVariant.isNull() || !labelVariant.is<const char *>()) {
|
||||
@@ -286,7 +286,7 @@ void loadConfig() {
|
||||
countdownEnabled = false;
|
||||
countdownTargetTimestamp = 0;
|
||||
strcpy(countdownLabel, "");
|
||||
isDramaticCountdown = true;
|
||||
isDramaticCountdown = true;
|
||||
Serial.println(F("[CONFIG] Countdown object not found, defaulting to disabled."));
|
||||
countdownFinished = false;
|
||||
}
|
||||
@@ -928,30 +928,30 @@ void setupWebServer() {
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
});
|
||||
|
||||
server.on("/set_dramatic_countdown", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
bool enableDramaticNow = false;
|
||||
if (request->hasParam("value", true)) {
|
||||
String v = request->getParam("value", true)->value();
|
||||
enableDramaticNow = (v == "1" || v == "true" || v == "on");
|
||||
}
|
||||
server.on("/set_dramatic_countdown", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
bool enableDramaticNow = false;
|
||||
if (request->hasParam("value", true)) {
|
||||
String v = request->getParam("value", true)->value();
|
||||
enableDramaticNow = (v == "1" || v == "true" || v == "on");
|
||||
}
|
||||
|
||||
// Check if the state has changed
|
||||
if (isDramaticCountdown == enableDramaticNow) {
|
||||
Serial.println(F("[WEBSERVER] Dramatic Countdown state unchanged, ignoring."));
|
||||
// Check if the state has changed
|
||||
if (isDramaticCountdown == enableDramaticNow) {
|
||||
Serial.println(F("[WEBSERVER] Dramatic Countdown state unchanged, ignoring."));
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the global variable
|
||||
isDramaticCountdown = enableDramaticNow;
|
||||
|
||||
// Call saveCountdownConfig with only the existing parameters.
|
||||
// It will read the updated global variable 'isDramaticCountdown'.
|
||||
saveCountdownConfig(countdownEnabled, countdownTargetTimestamp, countdownLabel);
|
||||
|
||||
Serial.printf("[WEBSERVER] Set Dramatic Countdown to %d\n", isDramaticCountdown);
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the global variable
|
||||
isDramaticCountdown = enableDramaticNow;
|
||||
|
||||
// Call saveCountdownConfig with only the existing parameters.
|
||||
// It will read the updated global variable 'isDramaticCountdown'.
|
||||
saveCountdownConfig(countdownEnabled, countdownTargetTimestamp, countdownLabel);
|
||||
|
||||
Serial.printf("[WEBSERVER] Set Dramatic Countdown to %d\n", isDramaticCountdown);
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
server.begin();
|
||||
@@ -1142,7 +1142,7 @@ String buildWeatherURL() {
|
||||
|
||||
String langForAPI = String(language);
|
||||
|
||||
if (langForAPI == "eo" || langForAPI == "sw" || langForAPI == "ja") {
|
||||
if (langForAPI == "eo" || langForAPI == "ga" || langForAPI == "sw" || langForAPI == "ja") {
|
||||
langForAPI = "en";
|
||||
}
|
||||
base += "&lang=" + langForAPI;
|
||||
@@ -1743,16 +1743,19 @@ void loop() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- WEATHER DESCRIPTION Display Mode ---
|
||||
if (displayMode == 2 && showWeatherDescription && weatherAvailable && weatherDescription.length() > 0) {
|
||||
String desc = weatherDescription;
|
||||
|
||||
// prepare safe buffer
|
||||
static char descBuffer[128]; // large enough for OWM translations
|
||||
desc.toCharArray(descBuffer, sizeof(descBuffer));
|
||||
|
||||
if (desc.length() > 8) {
|
||||
if (!descScrolling) {
|
||||
P.displayClear();
|
||||
textEffect_t actualScrollDirection = getEffectiveScrollDirection(PA_SCROLL_LEFT, flipDisplay);
|
||||
P.displayScroll(desc.c_str(), PA_CENTER, actualScrollDirection, GENERAL_SCROLL_SPEED);
|
||||
P.displayScroll(descBuffer, PA_CENTER, actualScrollDirection, GENERAL_SCROLL_SPEED);
|
||||
descScrolling = true;
|
||||
descScrollEndTime = 0; // reset end time at start
|
||||
}
|
||||
@@ -1775,7 +1778,7 @@ void loop() {
|
||||
if (descStartTime == 0) {
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(1);
|
||||
P.print(desc.c_str());
|
||||
P.print(descBuffer);
|
||||
descStartTime = millis();
|
||||
}
|
||||
if (millis() - descStartTime > descriptionDuration) {
|
||||
@@ -2016,7 +2019,7 @@ void loop() {
|
||||
P.displayAnimate();
|
||||
}
|
||||
|
||||
// --- NEW: SINGLE-LINE COUNTDOWN LOGIC ---
|
||||
// --- NEW: SINGLE-LINE COUNTDOWN LOGIC ---
|
||||
else {
|
||||
long days = timeRemaining / (24 * 3600);
|
||||
long hours = (timeRemaining % (24 * 3600)) / 3600;
|
||||
@@ -2046,9 +2049,9 @@ void loop() {
|
||||
} else {
|
||||
sprintf(buf, "%s IN: %02ldH %02ldM %02ldS", label.c_str(), hours, minutes, seconds);
|
||||
}
|
||||
|
||||
|
||||
String fullString = String(buf);
|
||||
|
||||
|
||||
// Display the full string and scroll it
|
||||
P.displayClear();
|
||||
P.setTextAlignment(PA_LEFT);
|
||||
@@ -2068,177 +2071,178 @@ void loop() {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep alignment reset just in case
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(1);
|
||||
yield();
|
||||
return;
|
||||
} // End of if (displayMode == 3 && ...)
|
||||
|
||||
|
||||
// --- NIGHTSCOUT Display Mode ---
|
||||
if (displayMode == 4) {
|
||||
String ntpField = String(ntpServer2);
|
||||
|
||||
// These static variables will retain their values between calls to this block
|
||||
static unsigned long lastNightscoutFetchTime = 0;
|
||||
const unsigned long NIGHTSCOUT_FETCH_INTERVAL = 150000; // 2.5 minutes
|
||||
static int currentGlucose = -1;
|
||||
static String currentDirection = "?";
|
||||
|
||||
// Check if it's time to fetch new data or if we have no data yet
|
||||
if (currentGlucose == -1 || millis() - lastNightscoutFetchTime >= NIGHTSCOUT_FETCH_INTERVAL) {
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
HTTPClient https;
|
||||
https.begin(client, ntpField);
|
||||
|
||||
https.setTimeout(5000); // This sets both the connection and response timeout.
|
||||
|
||||
Serial.print("[HTTPS] Nightscout fetch initiated...\n");
|
||||
int httpCode = https.GET();
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
String payload = https.getString();
|
||||
StaticJsonDocument<1024> doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
|
||||
if (!error && doc.is<JsonArray>() && doc.size() > 0) {
|
||||
JsonObject firstReading = doc[0].as<JsonObject>();
|
||||
currentGlucose = firstReading["glucose"] | firstReading["sgv"] | -1;
|
||||
currentDirection = firstReading["direction"] | "?";
|
||||
|
||||
Serial.printf("Nightscout data fetched: mg/dL %d %s\n", currentGlucose, currentDirection.c_str());
|
||||
} else {
|
||||
Serial.println("Failed to parse Nightscout JSON");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
lastNightscoutFetchTime = millis(); // Update the timestamp
|
||||
}
|
||||
|
||||
// Display the data we have, which is now stored in static variables
|
||||
if (currentGlucose != -1) {
|
||||
char arrow;
|
||||
if (currentDirection == "Flat") arrow = 139;
|
||||
else if (currentDirection == "SingleUp") arrow = 134;
|
||||
else if (currentDirection == "DoubleUp") arrow = 135;
|
||||
else if (currentDirection == "SingleDown") arrow = 136;
|
||||
else if (currentDirection == "DoubleDown") arrow = 137;
|
||||
else if (currentDirection == "FortyFiveUp") arrow = 138;
|
||||
else if (currentDirection == "FortyFiveDown") arrow = 140;
|
||||
else arrow = '?';
|
||||
|
||||
String displayText = String(currentGlucose) + String(arrow);
|
||||
|
||||
// Keep alignment reset just in case
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(1);
|
||||
P.print(displayText.c_str());
|
||||
|
||||
delay(weatherDuration);
|
||||
advanceDisplayMode();
|
||||
yield();
|
||||
return;
|
||||
} else {
|
||||
// If no data is available after the first fetch attempt, show an error and advance
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(0);
|
||||
P.print(F("?)"));
|
||||
delay(2000); // Wait 2 seconds before advancing
|
||||
advanceDisplayMode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // End of if (displayMode == 3 && ...)
|
||||
|
||||
//DATE Display Mode
|
||||
else if (displayMode == 5 && showDate) {
|
||||
|
||||
// --- VALID DATE CHECK ---
|
||||
if (timeinfo.tm_year < 120 || timeinfo.tm_mday <= 0 || timeinfo.tm_mon < 0 || timeinfo.tm_mon > 11) {
|
||||
advanceDisplayMode();
|
||||
return; // skip drawing
|
||||
}
|
||||
// -------------------------
|
||||
String dateString;
|
||||
// --- NIGHTSCOUT Display Mode ---
|
||||
if (displayMode == 4) {
|
||||
String ntpField = String(ntpServer2);
|
||||
|
||||
// Get localized month names
|
||||
const char *const *months = getMonthsOfYear(language);
|
||||
String monthAbbr = String(months[timeinfo.tm_mon]).substring(0, 5);
|
||||
monthAbbr.toLowerCase();
|
||||
// These static variables will retain their values between calls to this block
|
||||
static unsigned long lastNightscoutFetchTime = 0;
|
||||
const unsigned long NIGHTSCOUT_FETCH_INTERVAL = 150000; // 2.5 minutes
|
||||
static int currentGlucose = -1;
|
||||
static String currentDirection = "?";
|
||||
|
||||
// Add spaces between day digits
|
||||
String dayString = String(timeinfo.tm_mday);
|
||||
String spacedDay = "";
|
||||
for (size_t i = 0; i < dayString.length(); i++) {
|
||||
spacedDay += dayString[i];
|
||||
if (i < dayString.length() - 1) spacedDay += " ";
|
||||
// Check if it's time to fetch new data or if we have no data yet
|
||||
if (currentGlucose == -1 || millis() - lastNightscoutFetchTime >= NIGHTSCOUT_FETCH_INTERVAL) {
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
HTTPClient https;
|
||||
https.begin(client, ntpField);
|
||||
|
||||
https.setTimeout(5000); // This sets both the connection and response timeout.
|
||||
|
||||
Serial.print("[HTTPS] Nightscout fetch initiated...\n");
|
||||
int httpCode = https.GET();
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
String payload = https.getString();
|
||||
StaticJsonDocument<1024> doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
|
||||
if (!error && doc.is<JsonArray>() && doc.size() > 0) {
|
||||
JsonObject firstReading = doc[0].as<JsonObject>();
|
||||
currentGlucose = firstReading["glucose"] | firstReading["sgv"] | -1;
|
||||
currentDirection = firstReading["direction"] | "?";
|
||||
|
||||
Serial.printf("Nightscout data fetched: mg/dL %d %s\n", currentGlucose, currentDirection.c_str());
|
||||
} else {
|
||||
Serial.println("Failed to parse Nightscout JSON");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
lastNightscoutFetchTime = millis(); // Update the timestamp
|
||||
}
|
||||
|
||||
// Display the data we have, which is now stored in static variables
|
||||
if (currentGlucose != -1) {
|
||||
char arrow;
|
||||
if (currentDirection == "Flat") arrow = 139;
|
||||
else if (currentDirection == "SingleUp") arrow = 134;
|
||||
else if (currentDirection == "DoubleUp") arrow = 135;
|
||||
else if (currentDirection == "SingleDown") arrow = 136;
|
||||
else if (currentDirection == "DoubleDown") arrow = 137;
|
||||
else if (currentDirection == "FortyFiveUp") arrow = 138;
|
||||
else if (currentDirection == "FortyFiveDown") arrow = 140;
|
||||
else arrow = '?';
|
||||
|
||||
String displayText = String(currentGlucose) + String(arrow);
|
||||
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(1);
|
||||
P.print(displayText.c_str());
|
||||
|
||||
delay(weatherDuration);
|
||||
advanceDisplayMode();
|
||||
return;
|
||||
} else {
|
||||
// If no data is available after the first fetch attempt, show an error and advance
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(0);
|
||||
P.print(F("?)"));
|
||||
delay(2000); // Wait 2 seconds before advancing
|
||||
advanceDisplayMode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check if day should come first for given language
|
||||
auto isDayFirst = [](const String &lang) {
|
||||
// Languages with DD-MM order
|
||||
const char *dayFirstLangs[] = {
|
||||
"af", // Afrikaans
|
||||
"cs", // Czech
|
||||
"da", // Danish
|
||||
"de", // German
|
||||
"eo", // Esperanto
|
||||
"es", // Spanish
|
||||
"et", // Estonian
|
||||
"fi", // Finnish
|
||||
"fr", // French
|
||||
"hr", // Croatian
|
||||
"hu", // Hungarian
|
||||
"it", // Italian
|
||||
"lt", // Lithuanian
|
||||
"lv", // Latvian
|
||||
"nl", // Dutch
|
||||
"no", // Norwegian
|
||||
"pl", // Polish
|
||||
"pt", // Portuguese
|
||||
"ro", // Romanian
|
||||
"sk", // Slovak
|
||||
"sl", // Slovenian
|
||||
"sr", // Serbian
|
||||
"sv", // Swedish
|
||||
"sw", // Swahili
|
||||
"tr" // Turkish
|
||||
//DATE Display Mode
|
||||
else if (displayMode == 5 && showDate) {
|
||||
|
||||
// --- VALID DATE CHECK ---
|
||||
if (timeinfo.tm_year < 120 || timeinfo.tm_mday <= 0 || timeinfo.tm_mon < 0 || timeinfo.tm_mon > 11) {
|
||||
advanceDisplayMode();
|
||||
return; // skip drawing
|
||||
}
|
||||
// -------------------------
|
||||
String dateString;
|
||||
|
||||
// Get localized month names
|
||||
const char *const *months = getMonthsOfYear(language);
|
||||
String monthAbbr = String(months[timeinfo.tm_mon]).substring(0, 5);
|
||||
monthAbbr.toLowerCase();
|
||||
|
||||
// Add spaces between day digits
|
||||
String dayString = String(timeinfo.tm_mday);
|
||||
String spacedDay = "";
|
||||
for (size_t i = 0; i < dayString.length(); i++) {
|
||||
spacedDay += dayString[i];
|
||||
if (i < dayString.length() - 1) spacedDay += " ";
|
||||
}
|
||||
|
||||
// Function to check if day should come first for given language
|
||||
auto isDayFirst = [](const String &lang) {
|
||||
// Languages with DD-MM order
|
||||
const char *dayFirstLangs[] = {
|
||||
"af", // Afrikaans
|
||||
"cs", // Czech
|
||||
"da", // Danish
|
||||
"de", // German
|
||||
"eo", // Esperanto
|
||||
"es", // Spanish
|
||||
"et", // Estonian
|
||||
"fi", // Finnish
|
||||
"fr", // French
|
||||
"ga", // Irish
|
||||
"hr", // Croatian
|
||||
"hu", // Hungarian
|
||||
"it", // Italian
|
||||
"lt", // Lithuanian
|
||||
"lv", // Latvian
|
||||
"nl", // Dutch
|
||||
"no", // Norwegian
|
||||
"pl", // Polish
|
||||
"pt", // Portuguese
|
||||
"ro", // Romanian
|
||||
"sk", // Slovak
|
||||
"sl", // Slovenian
|
||||
"sr", // Serbian
|
||||
"sv", // Swedish
|
||||
"sw", // Swahili
|
||||
"tr" // Turkish
|
||||
};
|
||||
for (auto lf : dayFirstLangs) {
|
||||
if (lang.equalsIgnoreCase(lf)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (auto lf : dayFirstLangs) {
|
||||
if (lang.equalsIgnoreCase(lf)) {
|
||||
return true;
|
||||
|
||||
String langForDate = String(language);
|
||||
|
||||
if (langForDate == "ja") {
|
||||
// Japanese: month number (spaced digits) + day + symbol
|
||||
String spacedMonth = "";
|
||||
String monthNum = String(timeinfo.tm_mon + 1);
|
||||
dateString = monthAbbr + " " + spacedDay + " ±";
|
||||
|
||||
} else {
|
||||
if (isDayFirst(language)) {
|
||||
dateString = spacedDay + " " + monthAbbr;
|
||||
} else {
|
||||
dateString = monthAbbr + " " + spacedDay;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
String langForDate = String(language);
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(0);
|
||||
P.print(dateString);
|
||||
|
||||
if (langForDate == "ja") {
|
||||
// Japanese: month number (spaced digits) + day + symbol
|
||||
String spacedMonth = "";
|
||||
String monthNum = String(timeinfo.tm_mon + 1);
|
||||
dateString = monthAbbr + " " + spacedDay + " ±";
|
||||
|
||||
} else {
|
||||
if (isDayFirst(language)) {
|
||||
dateString = spacedDay + " " + monthAbbr;
|
||||
} else {
|
||||
dateString = monthAbbr + " " + spacedDay;
|
||||
if (millis() - lastSwitch > weatherDuration) {
|
||||
advanceDisplayMode();
|
||||
}
|
||||
}
|
||||
|
||||
P.setTextAlignment(PA_CENTER);
|
||||
P.setCharSpacing(0);
|
||||
P.print(dateString);
|
||||
|
||||
if (millis() - lastSwitch > weatherDuration) {
|
||||
advanceDisplayMode();
|
||||
}
|
||||
}
|
||||
yield();
|
||||
yield();
|
||||
}
|
||||
@@ -447,6 +447,7 @@ textarea::placeholder {
|
||||
<option value="de">German</option>
|
||||
<option value="hu">Hungarian</option>
|
||||
<option value="it">Italian</option>
|
||||
<option value="ga">Irish</option>
|
||||
<option value="ja">Japanese</option>
|
||||
<option value="lv">Latvian</option>
|
||||
<option value="lt">Lithuanian</option>
|
||||
|
||||
@@ -17,6 +17,7 @@ const DaysOfWeekMapping days_mappings[] = {
|
||||
{ "et", { "p&a", "e&s", "t&e", "k&o", "n&e", "r&e", "l&a" } },
|
||||
{ "fi", { "s&u&n", "m&a&a", "t&i&s", "k&e&s", "t&o&r", "p&e&r", "l&a&u" } },
|
||||
{ "fr", { "d&i&m", "l&u&n", "m&a&r", "m&e&r", "j&e&u", "v&e&n", "s&a&m" } },
|
||||
{ "ga", { "d&o&m", "l&u&a", "m&a&i", "c&e&a", "d&e&a", "a&o&i", "s&a&t" } },
|
||||
{ "hr", { "n&e&d", "p&o&n", "u&t&o", "s&r&i", "c&e&t", "p&e&t", "s&u&b" } },
|
||||
{ "hu", { "v&a&s", "h&e&t", "k&e&d", "s&z&e", "c&s&u", "p&e&t", "s&z&o" } },
|
||||
{ "it", { "d&o&m", "l&u&n", "m&a&r", "m&e&r", "g&i&o", "v&e&n", "s&a&b" } },
|
||||
|
||||
@@ -43,7 +43,7 @@ MD_MAX72XX::fontType_t mFactory[] PROGMEM =
|
||||
6, 66, 37, 18, 72, 164, 66, // 37 - '%'
|
||||
1, 1, // 38 - '&'
|
||||
1, 6, // 39 - ''
|
||||
1, 0, // 40 - '('
|
||||
13, 254, 17, 17, 254, 0, 126, 129, 65, 190, 0, 129, 255, 129, // 40 - '('
|
||||
17, 130, 186, 198, 254, 134, 234, 134, 254, 250, 130, 250, 254, 134, 234, 134, 254, 124, // 41 - ')'
|
||||
20, 250, 130, 250, 254, 130, 170, 186, 254, 130, 250, 226, 250, 134, 254, 130, 234, 234, 246, 254, 124, // 42 - '*'
|
||||
1, 0, // 43 - '+'
|
||||
|
||||
@@ -20,6 +20,7 @@ const MonthsMapping months_mappings[] = {
|
||||
{ "hr", { "s&i&j", "v&e&l", "o&z&u", "t&r&a", "s&v&i", "l&i&p", "s&r&p", "k&o&l", "r&u&j", "l&i&s", "s&t&u", "p&r&o" } }, // Croatian
|
||||
{ "hu", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&j", "j&u&n", "j&u&l", "a&u&g", "s&z&e", "o&k&t", "n&o&v", "d&e&c" } }, // Hungarian
|
||||
{ "it", { "g&e&n", "f&e&b", "m&a&r", "a&p&r", "m&a&g", "g&i&u", "l&u&g", "a&g&o", "s&e&t", "o&t&t", "n&o&v", "d&i&c" } }, // Italian
|
||||
{ "ga", { "e&a&n", "f&e&a", "m&a&r", "a&i&b", "b&e&a", "m&e&i", "i&u&i", "l&u&n", "m&e&a", "d&e&i", "s&a&m", "n&o&l" } }, // Irish
|
||||
{ "ja", { "1 ²", "2 ²", "3 ²", "4 ²", "5 ²", "6 ²", "7 ²", "8 ²", "9 ²", "10 ²", "11 ²", "12 ²" } }, // Japanese
|
||||
{ "lt", { "s&a&u", "v&a&s", "k&o&v", "b&a&l", "g&e&g", "b&i&r", "l&i&e", "r&u&g", "s&w&e", "s&p&a", "l&a&p", "g&r&u" } }, // Lithuanian
|
||||
{ "lv", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&i", "j&u&n", "j&u&l", "a&u&g", "s&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Latvian
|
||||
|
||||
Reference in New Issue
Block a user