323 lines
9.0 KiB
C++
323 lines
9.0 KiB
C++
/** The MIT License (MIT)
|
|
|
|
Copyright (c) 2018 David Payne
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#include "OpenWeatherMapClient.h"
|
|
#include <WiFiClientSecure.h>
|
|
|
|
OpenWeatherMapClient::OpenWeatherMapClient(String ApiKey, int CityIDs[], int cityCount, boolean isMetric, String language) {
|
|
updateCityIdList(CityIDs, cityCount);
|
|
updateLanguage(language);
|
|
myApiKey = ApiKey;
|
|
setMetric(isMetric);
|
|
}
|
|
|
|
void OpenWeatherMapClient::updateWeatherApiKey(String ApiKey) {
|
|
ApiKey.trim();
|
|
myApiKey = ApiKey;
|
|
}
|
|
|
|
void OpenWeatherMapClient::updateLanguage(String language) {
|
|
lang = language;
|
|
if (lang == "") {
|
|
lang = "es";
|
|
}
|
|
}
|
|
|
|
void OpenWeatherMapClient::updateWeather() {
|
|
WiFiClientSecure weatherClient;
|
|
weatherClient.setTimeout(15000);
|
|
weatherClient.setInsecure();
|
|
#ifdef ESP8266
|
|
weatherClient.setBufferSizes(1024, 512);
|
|
#endif
|
|
|
|
String firstId = myCityIDs;
|
|
int comma = firstId.indexOf(',');
|
|
if (comma > 0) firstId.remove(comma);
|
|
|
|
myApiKey.trim();
|
|
|
|
String path = "/data/2.5/weather?id=" + firstId +
|
|
"&units=" + units +
|
|
"&appid=" + myApiKey +
|
|
"&lang=" + lang;
|
|
|
|
Serial.println("\n[OWM] GET " + path);
|
|
|
|
if (!weatherClient.connect(servername, 443)) {
|
|
Serial.println("[OWM] Conexión TLS fallida");
|
|
weathers[0].cached = true;
|
|
weathers[0].error = "TLS connect fail";
|
|
return;
|
|
}
|
|
|
|
weatherClient.print(String("GET ") + path + " HTTP/1.1\r\n" +
|
|
"Host: " + String(servername) + "\r\n" +
|
|
"User-Agent: ESP8266-OWM/1.0\r\n" +
|
|
"Connection: close\r\n\r\n");
|
|
|
|
String statusLine = weatherClient.readStringUntil('\n');
|
|
statusLine.trim();
|
|
Serial.println("[OWM] " + statusLine);
|
|
if (!statusLine.startsWith("HTTP/1.1 ")) {
|
|
weathers[0].cached = true;
|
|
weathers[0].error = "Invalid HTTP";
|
|
return;
|
|
}
|
|
int httpCode = statusLine.substring(9, 12).toInt();
|
|
|
|
if (!weatherClient.find("\r\n\r\n")) {
|
|
Serial.println("[OWM] No end-of-headers");
|
|
weathers[0].cached = true;
|
|
weathers[0].error = "Bad headers";
|
|
return;
|
|
}
|
|
|
|
const size_t bufferSize = 4096;
|
|
DynamicJsonBuffer jsonBuffer(bufferSize);
|
|
JsonObject& root = jsonBuffer.parseObject(weatherClient);
|
|
if (!root.success()) {
|
|
Serial.println("[OWM] JSON parse FAIL");
|
|
weathers[0].cached = true;
|
|
weathers[0].error = "JSON parse fail";
|
|
return;
|
|
}
|
|
|
|
String cod = root["cod"].is<const char*>() ? (const char*)root["cod"] : String((int)root["cod"]);
|
|
if (cod != "200") {
|
|
weathers[0].cached = true;
|
|
weathers[0].error = root["message"].is<const char*>() ? (const char*)root["message"] : "cod!=200";
|
|
Serial.println("[OWM] API error: " + weathers[0].error);
|
|
return;
|
|
}
|
|
|
|
JsonObject& main = root["main"];
|
|
JsonObject& wind = root["wind"];
|
|
JsonObject& sys = root["sys"];
|
|
JsonArray& wArr = root["weather"];
|
|
|
|
weathers[0].lat = String((double)root["coord"]["lat"]);
|
|
weathers[0].lon = String((double)root["coord"]["lon"]);
|
|
weathers[0].dt = String((long) root["dt"]);
|
|
weathers[0].city = root["name"].is<const char*>() ? (const char*)root["name"] : "";
|
|
weathers[0].country = sys["country"].is<const char*>() ? (const char*)sys["country"] : "";
|
|
weathers[0].temp = String((double)main["temp"]);
|
|
weathers[0].humidity = String((int) main["humidity"]);
|
|
weathers[0].wind = String((double)wind["speed"]);
|
|
|
|
if (wArr.size() > 0) {
|
|
JsonObject& w0 = wArr[0];
|
|
weathers[0].condition = w0["main"].is<const char*>() ? (const char*)w0["main"] : "";
|
|
weathers[0].description = w0["description"].is<const char*>() ? (const char*)w0["description"] : "";
|
|
weathers[0].weatherId = String((int)w0["id"]);
|
|
weathers[0].icon = w0["icon"].is<const char*>() ? (const char*)w0["icon"] : "";
|
|
} else {
|
|
weathers[0].condition = "";
|
|
weathers[0].description = "";
|
|
weathers[0].weatherId = "";
|
|
weathers[0].icon = "";
|
|
}
|
|
|
|
weathers[0].cached = false;
|
|
weathers[0].error = "";
|
|
|
|
Serial.println("[OWM] OK city=" + weathers[0].city +
|
|
" temp=" + weathers[0].temp +
|
|
" icon=" + weathers[0].icon);
|
|
}
|
|
|
|
|
|
|
|
String OpenWeatherMapClient::roundValue(String value) {
|
|
float f = value.toFloat();
|
|
int rounded = (int)(f+0.5f);
|
|
return String(rounded);
|
|
}
|
|
|
|
void OpenWeatherMapClient::updateCityIdList(int CityIDs[], int cityCount) {
|
|
myCityIDs = "";
|
|
for (int inx = 0; inx < cityCount; inx++) {
|
|
if (CityIDs[inx] > 0) {
|
|
if (myCityIDs != "") {
|
|
myCityIDs = myCityIDs + ",";
|
|
}
|
|
myCityIDs = myCityIDs + String(CityIDs[inx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void OpenWeatherMapClient::setMetric(boolean isMetric) {
|
|
if (isMetric) {
|
|
units = "metric";
|
|
} else {
|
|
units = "imperial";
|
|
}
|
|
}
|
|
|
|
String OpenWeatherMapClient::getWeatherResults() {
|
|
return result;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getLat(int index) {
|
|
return weathers[index].lat;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getLon(int index) {
|
|
return weathers[index].lon;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getDt(int index) {
|
|
return weathers[index].dt;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getCity(int index) {
|
|
return weathers[index].city;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getCountry(int index) {
|
|
return weathers[index].country;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getTemp(int index) {
|
|
return weathers[index].temp;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getTempRounded(int index) {
|
|
return roundValue(getTemp(index));
|
|
}
|
|
|
|
String OpenWeatherMapClient::getHumidity(int index) {
|
|
return weathers[index].humidity;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getHumidityRounded(int index) {
|
|
return roundValue(getHumidity(index));
|
|
}
|
|
|
|
String OpenWeatherMapClient::getCondition(int index) {
|
|
return weathers[index].condition;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getWind(int index) {
|
|
return weathers[index].wind;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getWindRounded(int index) {
|
|
return roundValue(getWind(index));
|
|
}
|
|
|
|
String OpenWeatherMapClient::getWeatherId(int index) {
|
|
return weathers[index].weatherId;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getDescription(int index) {
|
|
return weathers[index].description;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getIcon(int index) {
|
|
return weathers[index].icon;
|
|
}
|
|
|
|
boolean OpenWeatherMapClient::getCached() {
|
|
return weathers[0].cached;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getMyCityIDs() {
|
|
return myCityIDs;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getError() {
|
|
return weathers[0].error;
|
|
}
|
|
|
|
String OpenWeatherMapClient::getWeatherIcon(int index)
|
|
{
|
|
int id = getWeatherId(index).toInt();
|
|
String W = ")";
|
|
switch(id)
|
|
{
|
|
case 800: W = "B"; break;
|
|
case 801: W = "Y"; break;
|
|
case 802: W = "H"; break;
|
|
case 803: W = "H"; break;
|
|
case 804: W = "Y"; break;
|
|
|
|
case 200: W = "0"; break;
|
|
case 201: W = "0"; break;
|
|
case 202: W = "0"; break;
|
|
case 210: W = "0"; break;
|
|
case 211: W = "0"; break;
|
|
case 212: W = "0"; break;
|
|
case 221: W = "0"; break;
|
|
case 230: W = "0"; break;
|
|
case 231: W = "0"; break;
|
|
case 232: W = "0"; break;
|
|
|
|
case 300: W = "R"; break;
|
|
case 301: W = "R"; break;
|
|
case 302: W = "R"; break;
|
|
case 310: W = "R"; break;
|
|
case 311: W = "R"; break;
|
|
case 312: W = "R"; break;
|
|
case 313: W = "R"; break;
|
|
case 314: W = "R"; break;
|
|
case 321: W = "R"; break;
|
|
|
|
case 500: W = "R"; break;
|
|
case 501: W = "R"; break;
|
|
case 502: W = "R"; break;
|
|
case 503: W = "R"; break;
|
|
case 504: W = "R"; break;
|
|
case 511: W = "R"; break;
|
|
case 520: W = "R"; break;
|
|
case 521: W = "R"; break;
|
|
case 522: W = "R"; break;
|
|
case 531: W = "R"; break;
|
|
|
|
case 600: W = "W"; break;
|
|
case 601: W = "W"; break;
|
|
case 602: W = "W"; break;
|
|
case 611: W = "W"; break;
|
|
case 612: W = "W"; break;
|
|
case 615: W = "W"; break;
|
|
case 616: W = "W"; break;
|
|
case 620: W = "W"; break;
|
|
case 621: W = "W"; break;
|
|
case 622: W = "W"; break;
|
|
|
|
case 701: W = "M"; break;
|
|
case 711: W = "M"; break;
|
|
case 721: W = "M"; break;
|
|
case 731: W = "M"; break;
|
|
case 741: W = "M"; break;
|
|
case 751: W = "M"; break;
|
|
case 761: W = "M"; break;
|
|
case 762: W = "M"; break;
|
|
case 771: W = "M"; break;
|
|
case 781: W = "M"; break;
|
|
|
|
default:break;
|
|
}
|
|
return W;
|
|
}
|