diff --git a/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino b/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino
index 69d0d5c..e659a49 100644
--- a/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino
+++ b/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino
@@ -1043,10 +1043,12 @@ String normalizeWeatherDescription(String str) {
str.replace("д", "d");
str.replace("ђ", "dj");
str.replace("е", "e");
+ str.replace("ё", "e"); // Russian
str.replace("ж", "z");
str.replace("з", "z");
str.replace("и", "i");
- str.replace("ј", "j");
+ str.replace("й", "j"); // Russian
+ str.replace("ј", "j"); // Serbian
str.replace("к", "k");
str.replace("л", "l");
str.replace("љ", "lj");
@@ -1066,6 +1068,11 @@ String normalizeWeatherDescription(String str) {
str.replace("ч", "c");
str.replace("џ", "dz");
str.replace("ш", "s");
+ str.replace("щ", "sh"); // Russian
+ str.replace("ы", "y"); // Russian
+ str.replace("э", "e"); // Russian
+ str.replace("ю", "yu"); // Russian
+ str.replace("я", "ya"); // Russian
// Latin diacritics → ASCII
str.replace("å", "a");
@@ -1247,13 +1254,12 @@ void fetchWeather() {
Serial.print(F("[WEATHER] URL: ")); // Use F() with Serial.print
Serial.println(url);
- HTTPClient http; // Create an HTTPClient object
WiFiClientSecure client; // use secure client for HTTPS
- client.setInsecure(); // disable certificate validation
-
+ client.stop(); // ensure previous session closed
+ client.setInsecure(); // no cert validation
+ HTTPClient http; // Create an HTTPClient object
http.begin(client, url); // Pass the WiFiClient object and the URL
-
- http.setTimeout(10000); // Sets both connection and stream timeout to 10 seconds
+ http.setTimeout(10000); // Sets both connection and stream timeout to 10 seconds
Serial.println(F("[WEATHER] Sending GET request..."));
int httpCode = http.GET(); // Send the GET request
@@ -1394,8 +1400,7 @@ void advanceDisplayMode() {
} else if (weatherAvailable && (strlen(openWeatherApiKey) == 32) && (strlen(openWeatherCity) > 0) && (strlen(openWeatherCountry) > 0)) {
displayMode = 1;
Serial.println(F("[DISPLAY] Switching to display mode: WEATHER (from Clock)"));
- } else if (countdownEnabled && !countdownFinished && ntpSyncSuccessful &&
- countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
+ } else if (countdownEnabled && !countdownFinished && ntpSyncSuccessful && countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
displayMode = 3;
Serial.println(F("[DISPLAY] Switching to display mode: COUNTDOWN (from Clock, weather skipped)"));
} else if (nightscoutConfigured) {
@@ -1409,8 +1414,7 @@ void advanceDisplayMode() {
if (weatherAvailable && (strlen(openWeatherApiKey) == 32) && (strlen(openWeatherCity) > 0) && (strlen(openWeatherCountry) > 0)) {
displayMode = 1;
Serial.println(F("[DISPLAY] Switching to display mode: WEATHER (from Date)"));
- } else if (countdownEnabled && !countdownFinished && ntpSyncSuccessful &&
- countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
+ } else if (countdownEnabled && !countdownFinished && ntpSyncSuccessful && countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
displayMode = 3;
Serial.println(F("[DISPLAY] Switching to display mode: COUNTDOWN (from Date, weather skipped)"));
} else if (nightscoutConfigured) {
@@ -1424,8 +1428,7 @@ void advanceDisplayMode() {
if (showWeatherDescription && weatherAvailable && weatherDescription.length() > 0) {
displayMode = 2;
Serial.println(F("[DISPLAY] Switching to display mode: DESCRIPTION (from Weather)"));
- } else if (countdownEnabled && !countdownFinished && ntpSyncSuccessful &&
- countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
+ } else if (countdownEnabled && !countdownFinished && ntpSyncSuccessful && countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
displayMode = 3;
Serial.println(F("[DISPLAY] Switching to display mode: COUNTDOWN (from Weather)"));
} else if (nightscoutConfigured) {
@@ -1436,8 +1439,7 @@ void advanceDisplayMode() {
Serial.println(F("[DISPLAY] Switching to display mode: CLOCK (from Weather)"));
}
} else if (displayMode == 2) { // Weather Description
- if (countdownEnabled && !countdownFinished && ntpSyncSuccessful &&
- countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
+ if (countdownEnabled && !countdownFinished && ntpSyncSuccessful && countdownTargetTimestamp > 0 && countdownTargetTimestamp > time(nullptr)) {
displayMode = 3;
Serial.println(F("[DISPLAY] Switching to display mode: COUNTDOWN (from Description)"));
} else if (nightscoutConfigured) {
@@ -2428,6 +2430,7 @@ void loop() {
"pl", // Polish
"pt", // Portuguese
"ro", // Romanian
+ "ru", // Russian
"sk", // Slovak
"sl", // Slovenian
"sr", // Serbian
diff --git a/ESPTimeCast_ESP32/data/index.html b/ESPTimeCast_ESP32/data/index.html
index 12e1de2..0b2977b 100644
--- a/ESPTimeCast_ESP32/data/index.html
+++ b/ESPTimeCast_ESP32/data/index.html
@@ -455,6 +455,7 @@ textarea::placeholder {
+
diff --git a/ESPTimeCast_ESP32/days_lookup.h b/ESPTimeCast_ESP32/days_lookup.h
index 8c884dc..6ffcaf4 100644
--- a/ESPTimeCast_ESP32/days_lookup.h
+++ b/ESPTimeCast_ESP32/days_lookup.h
@@ -29,6 +29,7 @@ const DaysOfWeekMapping days_mappings[] = {
{ "pl", { "n&i&e", "p&o&n", "w&t&o", "s&r&o", "c&z&w", "p&i&a", "s&o&b" } },
{ "pt", { "d&o&m", "s&e&g", "t&e&r", "q&u&a", "q&u&i", "s&e&x", "s&a&b" } },
{ "ro", { "d&u&m", "l&u&n", "m&a&r", "m&i&e", "j&o&i", "v&i&n", "s&a&m" } },
+ { "ru", { "p&n", "v&t", "s&r", "c&h", "p&t", "s&b", "v&s" } },
{ "sk", { "n&e&d", "p&o&n", "u&t&o", "s&t&r", "s&t&v", "p&i&a", "s&o&b" } },
{ "sl", { "n&e&d", "p&o&n", "t&o&r", "s&r&e", "c&e&t", "p&e&t", "s&o&b" } },
{ "sr", { "n&e&d", "p&o&n", "u&t&o", "s&r&e", "c&e&t", "p&e&t", "s&u&b" } },
diff --git a/ESPTimeCast_ESP32/months_lookup.h b/ESPTimeCast_ESP32/months_lookup.h
index acd4ec5..44a0ca0 100644
--- a/ESPTimeCast_ESP32/months_lookup.h
+++ b/ESPTimeCast_ESP32/months_lookup.h
@@ -29,6 +29,7 @@ const MonthsMapping months_mappings[] = {
{ "pl", { "s&t&y", "l&u&t", "m&a&r", "k&w&i", "m&a&j", "c&z&e", "l&i&p", "s&i&e", "w&r&z", "p&a&z", "l&i&s", "g&r&u" } }, // Polish
{ "pt", { "j&a&n", "f&e&v", "m&a&r", "a&b&r", "m&a&i", "j&u&n", "j&u&l", "a&g&o", "s&e&t", "o&u&t", "n&o&v", "d&e&z" } }, // Portuguese
{ "ro", { "i&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&i", "i&u&n", "i&u&l", "a&u&g", "s&e&p", "o&c&t", "n&o&v", "d&e&c" } }, // Romanian
+ { "ru", { "i&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&i", "i&u&n", "i&u&l", "a&u&g", "s&e&p", "o&c&t", "n&o&i", "d&e&c" } }, // Russian
{ "sk", { "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&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Slovak
{ "sl", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&j", "j&u&n", "j&u&l", "a&v&g", "s&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Slovenian
{ "sr", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&j", "j&u&n", "j&u&l", "a&v&g", "s&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Serbian
diff --git a/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino b/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino
index 877c072..1bec6bb 100644
--- a/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino
+++ b/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino
@@ -1042,10 +1042,12 @@ String normalizeWeatherDescription(String str) {
str.replace("д", "d");
str.replace("ђ", "dj");
str.replace("е", "e");
+ str.replace("ё", "e"); // Russian
str.replace("ж", "z");
str.replace("з", "z");
str.replace("и", "i");
- str.replace("ј", "j");
+ str.replace("й", "j"); // Russian
+ str.replace("ј", "j"); // Serbian
str.replace("к", "k");
str.replace("л", "l");
str.replace("љ", "lj");
@@ -1065,6 +1067,11 @@ String normalizeWeatherDescription(String str) {
str.replace("ч", "c");
str.replace("џ", "dz");
str.replace("ш", "s");
+ str.replace("щ", "sh"); // Russian
+ str.replace("ы", "y"); // Russian
+ str.replace("э", "e"); // Russian
+ str.replace("ю", "yu"); // Russian
+ str.replace("я", "ya"); // Russian
// Latin diacritics → ASCII
str.replace("å", "a");
@@ -1245,12 +1252,12 @@ void fetchWeather() {
String url = buildWeatherURL();
Serial.println(F("[WEATHER] URL: ") + url);
- HTTPClient http; // Create an HTTPClient object
WiFiClientSecure client; // use secure client for HTTPS
- client.setInsecure(); // disable certificate validation
-
+ client.stop(); // ensure previous session closed
+ client.setInsecure(); // no cert validation
+ HTTPClient http; // Create an HTTPClient object
http.begin(client, url); // Pass the WiFiClient object and the URL
-
+ client.setBufferSizes(512, 512);
http.setTimeout(10000); // Sets both connection and stream timeout to 10 seconds
Serial.println(F("[WEATHER] Sending GET request..."));
@@ -1774,6 +1781,9 @@ void loop() {
weatherFetchInitiated = false;
shouldFetchWeatherNow = false;
}
+
+
+
const char *const *daysOfTheWeek = getDaysOfWeek(language);
const char *daySymbol = daysOfTheWeek[timeinfo.tm_wday];
@@ -2409,6 +2419,7 @@ void loop() {
"pl", // Polish
"pt", // Portuguese
"ro", // Romanian
+ "ru", // Russian
"sk", // Slovak
"sl", // Slovenian
"sr", // Serbian
diff --git a/ESPTimeCast_ESP8266/data/index.html b/ESPTimeCast_ESP8266/data/index.html
index 12e1de2..0b2977b 100644
--- a/ESPTimeCast_ESP8266/data/index.html
+++ b/ESPTimeCast_ESP8266/data/index.html
@@ -455,6 +455,7 @@ textarea::placeholder {
+
diff --git a/ESPTimeCast_ESP8266/days_lookup.h b/ESPTimeCast_ESP8266/days_lookup.h
index 8c884dc..6ffcaf4 100644
--- a/ESPTimeCast_ESP8266/days_lookup.h
+++ b/ESPTimeCast_ESP8266/days_lookup.h
@@ -29,6 +29,7 @@ const DaysOfWeekMapping days_mappings[] = {
{ "pl", { "n&i&e", "p&o&n", "w&t&o", "s&r&o", "c&z&w", "p&i&a", "s&o&b" } },
{ "pt", { "d&o&m", "s&e&g", "t&e&r", "q&u&a", "q&u&i", "s&e&x", "s&a&b" } },
{ "ro", { "d&u&m", "l&u&n", "m&a&r", "m&i&e", "j&o&i", "v&i&n", "s&a&m" } },
+ { "ru", { "p&n", "v&t", "s&r", "c&h", "p&t", "s&b", "v&s" } },
{ "sk", { "n&e&d", "p&o&n", "u&t&o", "s&t&r", "s&t&v", "p&i&a", "s&o&b" } },
{ "sl", { "n&e&d", "p&o&n", "t&o&r", "s&r&e", "c&e&t", "p&e&t", "s&o&b" } },
{ "sr", { "n&e&d", "p&o&n", "u&t&o", "s&r&e", "c&e&t", "p&e&t", "s&u&b" } },
diff --git a/ESPTimeCast_ESP8266/months_lookup.h b/ESPTimeCast_ESP8266/months_lookup.h
index acd4ec5..44a0ca0 100644
--- a/ESPTimeCast_ESP8266/months_lookup.h
+++ b/ESPTimeCast_ESP8266/months_lookup.h
@@ -29,6 +29,7 @@ const MonthsMapping months_mappings[] = {
{ "pl", { "s&t&y", "l&u&t", "m&a&r", "k&w&i", "m&a&j", "c&z&e", "l&i&p", "s&i&e", "w&r&z", "p&a&z", "l&i&s", "g&r&u" } }, // Polish
{ "pt", { "j&a&n", "f&e&v", "m&a&r", "a&b&r", "m&a&i", "j&u&n", "j&u&l", "a&g&o", "s&e&t", "o&u&t", "n&o&v", "d&e&z" } }, // Portuguese
{ "ro", { "i&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&i", "i&u&n", "i&u&l", "a&u&g", "s&e&p", "o&c&t", "n&o&v", "d&e&c" } }, // Romanian
+ { "ru", { "i&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&i", "i&u&n", "i&u&l", "a&u&g", "s&e&p", "o&c&t", "n&o&i", "d&e&c" } }, // Russian
{ "sk", { "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&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Slovak
{ "sl", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&j", "j&u&n", "j&u&l", "a&v&g", "s&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Slovenian
{ "sr", { "j&a&n", "f&e&b", "m&a&r", "a&p&r", "m&a&j", "j&u&n", "j&u&l", "a&v&g", "s&e&p", "o&k&t", "n&o&v", "d&e&c" } }, // Serbian