mirror of
https://github.com/mfactory-osaka/ESPTimeCast.git
synced 2026-02-19 11:54:56 -05:00
Added Lat. Long.
Latitude and Longitude support Fixed Clock and Weather duration
This commit is contained in:
@@ -53,7 +53,10 @@ int dimEndHour = 8; // 8am default
|
||||
int dimEndMinute = 0;
|
||||
int dimBrightness = 2; // Dimming level (0-15)
|
||||
|
||||
bool weatherCycleStarted = false;
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
DNSServer dnsServer;
|
||||
|
||||
@@ -284,6 +287,60 @@ void setupTime() {
|
||||
ntpSyncSuccessful = false; // Reset the flag
|
||||
}
|
||||
|
||||
String getValidLang(String lang) {
|
||||
// List of unsupported codes
|
||||
if (lang == "eo" || lang == "sw" || lang == "ja") {
|
||||
return "en"; // fallback to English
|
||||
}
|
||||
return lang; // supported language, return as is
|
||||
}
|
||||
|
||||
bool isNumber(const char* str) {
|
||||
for (int i = 0; str[i]; i++) {
|
||||
if (!isdigit(str[i]) && str[i] != '.' && str[i] != '-') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isFiveDigitZip(const char* str) {
|
||||
if (strlen(str) != 5) return false;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (!isdigit(str[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String buildWeatherURL() {
|
||||
String base = "http://api.openweathermap.org/data/2.5/weather?";
|
||||
|
||||
float lat = atof(openWeatherCity);
|
||||
float lon = atof(openWeatherCountry);
|
||||
|
||||
bool latValid = isNumber(openWeatherCity) && isNumber(openWeatherCountry) &&
|
||||
lat >= -90.0 && lat <= 90.0 &&
|
||||
lon >= -180.0 && lon <= 180.0;
|
||||
|
||||
if (latValid) {
|
||||
// Latitude/Longitude query
|
||||
base += "lat=" + String(lat, 8) + "&lon=" + String(lon, 8);
|
||||
} else if (isFiveDigitZip(openWeatherCity) &&
|
||||
String(openWeatherCountry).equalsIgnoreCase("US")) {
|
||||
// US ZIP code query
|
||||
base += "zip=" + String(openWeatherCity) + "," + String(openWeatherCountry);
|
||||
} else {
|
||||
// City name and country code
|
||||
base += "q=" + String(openWeatherCity) + "," + String(openWeatherCountry);
|
||||
}
|
||||
|
||||
base += "&appid=" + String(openWeatherApiKey);
|
||||
base += "&units=" + String(weatherUnits);
|
||||
base += "&lang=" + getValidLang(language); // Optional, safe fallback
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void printConfigToSerial() {
|
||||
Serial.println(F("========= Loaded Configuration ========="));
|
||||
Serial.print(F("WiFi SSID: "));
|
||||
@@ -304,7 +361,7 @@ void printConfigToSerial() {
|
||||
Serial.println(weatherDuration);
|
||||
Serial.print(F("TimeZone (IANA): "));
|
||||
Serial.println(timeZone);
|
||||
Serial.print(F("Days of the Week language): "));
|
||||
Serial.print(F("Days of the Week/Weather description language: "));
|
||||
Serial.println(language);
|
||||
Serial.print(F("Brightness: "));
|
||||
Serial.println(brightness);
|
||||
@@ -336,6 +393,8 @@ void printConfigToSerial() {
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This tells the compiler that handleCaptivePortal exists somewhere later in the code.
|
||||
void handleCaptivePortal(AsyncWebServerRequest *request);
|
||||
|
||||
@@ -399,6 +458,8 @@ void setupWebServer() {
|
||||
|
||||
// Specific type casting for known boolean/integer fields
|
||||
if (n == "brightness") doc[n] = v.toInt();
|
||||
else if (n == "clockDuration") doc[n] = v.toInt();
|
||||
else if (n == "weatherDuration") doc[n] = v.toInt();
|
||||
else if (n == "flipDisplay") doc[n] = (v == "true" || v == "on" || v == "1");
|
||||
else if (n == "twelveHourToggle") doc[n] = (v == "true" || v == "on" || v == "1");
|
||||
else if (n == "showDayOfWeek") doc[n] = (v == "true" || v == "on" || v == "1");
|
||||
@@ -658,7 +719,7 @@ void fetchWeather() {
|
||||
|
||||
Serial.println(F("[WEATHER] Connecting to OpenWeatherMap..."));
|
||||
const char *host = "api.openweathermap.org";
|
||||
String url = "/data/2.5/weather?q=" + String(openWeatherCity) + "," + String(openWeatherCountry) + "&appid=" + openWeatherApiKey + "&units=" + String(weatherUnits);
|
||||
String url = buildWeatherURL();
|
||||
Serial.println(F("[WEATHER] URL: ") + url);
|
||||
|
||||
IPAddress ip;
|
||||
@@ -755,8 +816,8 @@ void fetchWeather() {
|
||||
}
|
||||
|
||||
if (doc.containsKey(F("weather")) && doc[F("weather")].is<JsonArray>() && doc[F("weather")][0].containsKey(F("main"))) {
|
||||
const char *desc = doc[F("weather")][0][F("main")];
|
||||
Serial.printf("[WEATHER] Description: %s\n", weatherDescription.c_str());
|
||||
const char *desc = doc[F("weather")][0][F("description")];
|
||||
Serial.printf("[WEATHER] Description: %s\n", desc);
|
||||
} else {
|
||||
Serial.println(F("[WEATHER] Weather description not found in JSON payload"));
|
||||
}
|
||||
@@ -859,8 +920,9 @@ void loop() {
|
||||
P.displayScroll(pendingIpToShow.c_str(), PA_CENTER, PA_SCROLL_LEFT, 120);
|
||||
} else {
|
||||
// Done showing IP, resume normal display
|
||||
showingIp = false;
|
||||
showingIp = false;
|
||||
P.displayClear();
|
||||
delay(500);
|
||||
displayMode = 0; // Force clock mode
|
||||
lastSwitch = millis(); // Reset timer so clock mode gets full duration
|
||||
}
|
||||
@@ -1023,4 +1085,4 @@ void loop() {
|
||||
}
|
||||
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ textarea::placeholder {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
@media (min-width: 320px) {
|
||||
@media (min-width: 321px) {
|
||||
.form-row.two-col {
|
||||
flex-direction: row;
|
||||
gap: 1rem; }
|
||||
@@ -287,12 +287,12 @@ textarea::placeholder {
|
||||
<input type="text" id="openWeatherApiKey" name="openWeatherApiKey" />
|
||||
<div class="form-row two-col">
|
||||
<div>
|
||||
<label for="openWeatherCity">City</label>
|
||||
<input type="text" id="openWeatherCity" name="openWeatherCity" />
|
||||
<label for="openWeatherCity">City / ZIP or<br>Latitude</label>
|
||||
<input type="text" id="openWeatherCity" name="openWeatherCity"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="openWeatherCountry">Country Code</label>
|
||||
<input type="text" id="openWeatherCountry" name="openWeatherCountry" maxlength="2" />
|
||||
<label for="openWeatherCountry">Country Code or<br>Longitude</label>
|
||||
<input type="text" id="openWeatherCountry" name="openWeatherCountry"/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="weatherUnits">Temperature Units</label>
|
||||
@@ -302,6 +302,8 @@ textarea::placeholder {
|
||||
<option value="standard">Standard (K)</option>
|
||||
</select>
|
||||
<div class="small">
|
||||
Enter a city name + country code, ZIP + country code or lat. + long.<br>
|
||||
Format examples: Tokyo + JP, 94040 + US, 35.6895 + 139.6917<br><br>
|
||||
Consult the <a href="https://openweathermap.org/price" target="_blank" rel="noopener">OpenWeatherMap</a>
|
||||
documentation for info about getting your API key, city, and country code.
|
||||
</div>
|
||||
@@ -400,7 +402,7 @@ textarea::placeholder {
|
||||
<option value="Etc/UTC">Etc/UTC</option>
|
||||
</select>
|
||||
|
||||
<label for="language">Day of Week Language</label>
|
||||
<label for="language">Day of teh wekk language</label>
|
||||
<select id="language" name="language" onchange="setLanguage(this.value)">
|
||||
<option value="" disabled selected>Select language</option>
|
||||
<option value="af">Afrikaans</option>
|
||||
|
||||
Reference in New Issue
Block a user