/* Weather Access API Information provided by the Hong Kong Observatory open data API */ //Scheduler entry point, update today weather information (executed per hr) void todayWeatherUpdateCallback() { updateCurrentWeatherInfo(); renderHomepageWeatherInfo(); } //Scheduler entry point, updated every 6 hr void weatherForcastCallback() { updateAndRenderForcastInfo(); } //Current weather info const char* rhurl = "https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType=rhrread&lang=en"; //Weather forcast info const char* wfurl = "https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType=fnd&lang=en"; /* Weather Forcast Loaders */ bool updateAndRenderForcastInfo() { Serial.println("Updating weather forcast info..."); WiFiClientSecure httpsClient; // Use WiFiClientSecure for HTTPS httpsClient.setInsecure(); // Ignore SSL certificate verification (for simplicity) if (httpsClient.connect("data.weather.gov.hk", 443)) { Serial.println("Downloading forcast info"); httpsClient.print(String("GET ") + wfurl + " HTTP/1.1\r\n" + "Host: data.weather.gov.hk\r\n" + "Connection: close\r\n\r\n"); String response = ""; //Skip through the headers while (httpsClient.connected()) { String line = httpsClient.readStringUntil('\n'); if (line == "\r") { break; } } //Read the JSON body String line = ""; while (httpsClient.available()) { line = httpsClient.readStringUntil('\n'); //Read Line by Line response += line; } //Trim the first unwannted byte and the last int startTrimPos = response.indexOf("{"); int lastTrimPos = response.lastIndexOf("}"); response = response.substring(startTrimPos, lastTrimPos + 1); //Serial.println(response); if (response.length() < 10){ changePage("error"); sendCommand("t1.txt=\"Invalid resp: " + response + "\""); delay(10000); changePage("home"); return false; } // Build the filter StaticJsonDocument<1024> filter; for (int i = 0; i < 7; i++) { filter["weatherForecast"][i]["week"] = true; filter["weatherForecast"][i]["forecastMintemp"]["value"] = true; filter["weatherForecast"][i]["forecastMaxtemp"]["value"] = true; filter["weatherForecast"][i]["forecastMaxrh"]["value"] = true; filter["weatherForecast"][i]["ForecastIcon"] = true; } // Parse JSON data StaticJsonDocument<5120> jsonDoc; auto error = deserializeJson(jsonDoc, response, DeserializationOption::Filter(filter)); if (error) { Serial.print(F("deserializeJson() failed with code ")); Serial.println(error.c_str()); return false; } //serializeJsonPretty(jsonDoc, Serial); //Render the weather forcast onto the HMI for (int i = 0; i < 7; i++) { //Get day of week String dow = jsonDoc["weatherForecast"][i]["week"]; dow = dow.substring(0, 3); //Get min-max temp int minTemp = jsonDoc["weatherForecast"][i]["forecastMintemp"]["value"]; int maxTemp = jsonDoc["weatherForecast"][i]["forecastMaxtemp"]["value"]; int maxRh = jsonDoc["weatherForecast"][i]["forecastMaxrh"]["value"]; //Get forcast icon int forcastIcon = jsonDoc["weatherForecast"][i]["ForecastIcon"]; renderForcastField(String(i), dow, minTemp, maxTemp, maxRh, forcastIcon); } } return true; } /* Current Weather Loaders */ //Render the homepage weather info from the buffered information void renderHomepageWeatherInfo() { if (currentPage == 0) { printIntCommand("home.temp.txt=", currentTemp); printIntCommand("home.humd.txt=", currentHumd); printIntCommand("home.rain.txt=", currentRain); //Change the main weather icon if (isNight()) { if (currentIsRaining) { sendCommand("home.wicon.pic=11"); } else { sendCommand("home.wicon.pic=7"); } } else { if (currentIsRaining) { sendCommand("home.wicon.pic=11"); } else { sendCommand("home.wicon.pic=6"); } } } } void updateCurrentWeatherInfo() { WiFiClientSecure httpsClient; // Use WiFiClientSecure for HTTPS httpsClient.setInsecure(); // Ignore SSL certificate verification (for simplicity) if (httpsClient.connect("data.weather.gov.hk", 443)) { Serial.println("Downloading current weather info"); httpsClient.print(String("GET ") + rhurl + " HTTP/1.1\r\n" + "Host: data.weather.gov.hk\r\n" + "Connection: close\r\n\r\n"); String response = ""; //Skip through the headers while (httpsClient.connected()) { String line = httpsClient.readStringUntil('\n'); if (line == "\r") { break; } } //Read the JSON body String line; while (httpsClient.available()) { line = httpsClient.readStringUntil('\n'); //Read Line by Line response += line; } //Trim the first unwannted byte and the last int startTrimPos = response.indexOf("{"); int lastTrimPos = response.lastIndexOf("}"); response = response.substring(startTrimPos, lastTrimPos + 1); //Serial.println(response); // Build the filter StaticJsonDocument<1024> filter; filter["temperature"]["data"][0]["value"] = true; filter["humidity"]["data"][0]["value"] = true; filter["rainfall"]["data"][0]["max"] = true; filter["rainfall"]["data"][0]["main"] = true; // Parse JSON data /* DynamicJsonDocument jsonDoc(8192); // Change the size according to your JSON data auto error = deserializeJson(jsonDoc, response); if (error) { Serial.print(F("deserializeJson() failed with code ")); Serial.println(error.c_str()); return; } */ // Parse JSON data StaticJsonDocument<4096> jsonDoc; auto error = deserializeJson(jsonDoc, response, DeserializationOption::Filter(filter)); if (error) { Serial.print(F("deserializeJson() failed with code ")); Serial.println(error.c_str()); return; } // Access and return the temperature value currentTemp = jsonDoc["temperature"]["data"][19]["value"]; currentHumd = jsonDoc["humidity"]["data"][0]["value"]; currentRain = jsonDoc["rainfall"]["data"][16]["max"]; currentIsRaining = String(jsonDoc["rainfall"]["data"][16]["main"]) == "TRUE"; // Serial debug prints Serial.println(int(jsonDoc["temperature"]["data"][19]["value"])); Serial.println(int(jsonDoc["humidity"]["data"][0]["value"])); Serial.println(int(jsonDoc["rainfall"]["data"][16]["max"])); Serial.println(String(jsonDoc["rainfall"]["data"][16]["main"])); } else { Serial.println("Failed to connect to server"); } }