Added Lat. Long.

Latitude and Longitude support
Fixed Clock and Weather duration
This commit is contained in:
mfactory-osaka
2025-07-09 08:54:08 +09:00
parent d467ab6ed4
commit 2f4b861f29
2 changed files with 76 additions and 12 deletions

View File

@@ -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();
}
}

View File

@@ -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>