diff --git a/README.md b/README.md
index 370e0a8..f15c738 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-# OpenWeatherOneCall v3.3.3 ![IMAGE OF LIGHTNING](https://github.com/JHershey69/OpenWeatherOneCall/blob/master/images/lightning.jpg)
+# OpenWeatherOneCall v4.0.0 ![IMAGE OF LIGHTNING](https://github.com/JHershey69/OpenWeatherOneCall/blob/master/images/lightning.jpg)
## This is for ESP32 only
##This is an upgrade for OpenWeatherMap API 3.0 and Air Pollution API 2.5
-**Please be advised** OpenWeather has changed ONE CALL to a subscription service. Signing up is
+**Please be advised** OpenWeather has changed ONE CALL to a subscription PAY BY CALL service. Signing up is
required to get your KEY, but you get 1000 calls a day for free (this is one call every 90 seconds and that is A LOT for free)
and you will continue to get all of the features of this library.
@@ -57,4 +57,6 @@ v3.0.0 has a **Legacy Mode** to maintain ease of use for previous versions
--Minor fix to update API change for Air Quality.
**v3.3.3**
--Minor fix to update API change for Air Quality and Example scripts.
+
**v4.0.0**
+
--MAJOR REWRITE with addition options and variable SEE DOCUMENTATION. Some features deprecated no longer supported.
diff --git a/docs/OPEN WEATHER ONE CALL ADDENDUM.pdf b/docs/OPEN WEATHER ONE CALL ADDENDUM.pdf
deleted file mode 100644
index 137e9a3..0000000
Binary files a/docs/OPEN WEATHER ONE CALL ADDENDUM.pdf and /dev/null differ
diff --git a/docs/OpenWeatherOneCall Manual v3.0.4.pdf b/docs/OpenWeatherOneCall Manual v3.0.4.pdf
deleted file mode 100644
index d31f7c1..0000000
Binary files a/docs/OpenWeatherOneCall Manual v3.0.4.pdf and /dev/null differ
diff --git a/docs/OpenWeatherOneCall v3.0.4 Variables - Weather.pdf b/docs/OpenWeatherOneCall v3.0.4 Variables - Weather.pdf
deleted file mode 100644
index ed68cd6..0000000
Binary files a/docs/OpenWeatherOneCall v3.0.4 Variables - Weather.pdf and /dev/null differ
diff --git a/docs/OpenWeatherOneCall v4.0.0 Variables - Weather.pdf b/docs/OpenWeatherOneCall v4.0.0 Variables - Weather.pdf
new file mode 100644
index 0000000..7c005f8
Binary files /dev/null and b/docs/OpenWeatherOneCall v4.0.0 Variables - Weather.pdf differ
diff --git a/docs/OpenWeatherOneCall_Manual_4.pdf b/docs/OpenWeatherOneCall_Manual_4.pdf
new file mode 100644
index 0000000..778ecc0
Binary files /dev/null and b/docs/OpenWeatherOneCall_Manual_4.pdf differ
diff --git a/docs/README.md b/docs/README.md
index 02d99c1..dfd797a 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1 +1 @@
-**Documentation for v3.0.4 and above**
+**Documentation for v4.0.0 and above**
diff --git a/examples/Simple Latitude Longitude Weather/Simple_Latitude_Longitude_Weather_Example.ino b/examples/Simple Latitude Longitude Weather/Simple_Latitude_Longitude_Weather_Example.ino
index e24eeb2..070ac43 100644
--- a/examples/Simple Latitude Longitude Weather/Simple_Latitude_Longitude_Weather_Example.ino
+++ b/examples/Simple Latitude Longitude Weather/Simple_Latitude_Longitude_Weather_Example.ino
@@ -1,7 +1,7 @@
/*
Open Weather One Call Library
- v3.0.4
- Copyright 2020 - Jessica Hershey
+ v4.0.0
+ Copyright 2020=2024 - Jessica Hershey
www.github.com/JHershey69
One Call API key at www.openweathermap.org
@@ -17,50 +17,86 @@
*/
// ===== Required libraries (Other required libraries are installed in header file)
+
#include
-#include //<-- Might be native to ESP32
-// ===============================================================================
+
+// ======= END REQUIRED LIBRARIES =======================================
+
// ========= Required only for TFT Display ==============
#include
-#include // Hardware-specific library
+#include // Hardware-specific library
-TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
+TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
//========= End TFT Library =============================
#define HOMESSID "YOUR SSID HERE"
#define HOMEPW "YOUR SSID PW HERE"
-#define ONECALLKEY "YOUR ONE CALL API KEY HERE"
+char ONECALLKEY[] = "YOUR ONE CALL KEY HERE";
// Only needed if WiFiTri installed for Triangulation may have fee from Google
-#define GOOGLEKEY "YOUR GOOGLE DEVELOPER API KEY""
+// #define GOOGLEKEY "YOUR GOOGLE DEVELOPER API KEY""
+
+
+// OpenWeatherOneCall API Options and Settings
+//*************** LOCATION ************************
-// OpenWeatherOneCall variables
+int myTimeZone = -4; //<--------- GMT OFFSET
-//For Latitude and Longitude Location setting if used
-float myLATITUDE = 39.9537; //<-----This is Toms River, NJ
+//Location MODES are used to tell the program how you are delivering your coordinates:
+// 1 LAT/LON <----- Can be from GPS, manual as below, or whatever method gets you a latitude and longitude except IP location
+// 2 CITYID <----- From OpenWeatherMap City ID list. Not very accurate.
+// 3 IP ADDRESS [NOT REALLY SUGGESTED CAN BE WILDLY OFFSET LOCATION IF HOTSPOT]
+
+int locationMode = 1;
+
+//For Latitude and Longitude Location MODE 1 setting if used
+float myLATITUDE = 39.9537; //<-----This is Toms River, NJ
float myLONGITUDE = -74.1979;
-//For City ID Location setting if used
-int myCITYID = 4504476; //<-----Toms River, NJ USA
+//For City ID Location MODE setting if used
+int myCITYID = 4504476; //<-----Toms River, NJ USA
+
+//*************** END LOCATION **********************
+
+//*************** FLAGS *****************************
+int myCURRENT = 1; //<-----0 is CURRENT off, 1 is on
+int myAIRQUALITY = 1; //<-----0 is Air Quality off, 1 is on
+int myTIMESTAMP = 1; //<-----0 is TIMESTAMP off, 1 is TIMESTAMP on
+char timestampDate[11] = "03/03/2023"; //<-----MM/DD/YYYY right here limit is 4 days in future, past is 01/01/1979
+int myOVERVIEW = 1; //<-----0 is OVERVIEW off, 1 is OVERVIEW on
+char overviewDate[9] = "TOMORROW"; //<-----"TODAY" or "TOMORROW"
+//***************************************************
-int myUNITS = IMPERIAL; //<-----METRIC, IMPERIAL, KELVIN (IMPERIAL is default)
+//************** EXCLUDES ***************************
+// Excludes are EXCL_D, EXCL_H, EXCL_M, EXCL_A
+// Those are DAILY, HOURLY, MINUTELY, and ALERTS
+// You set them like this: myEXCLUDES = EXCL_D+EXCL_H+EXCL_M+EXCL_A
+// If you leave them set to 0 as below you get a JSON file with ALL CURRENT WEATHER measurements which is a huge file
-//Can't get current and historical at the same time
-int myHISTORY = NULL; //<-----Only required for historical data up to 5 days
+int myEXCLUDES = 0; //<-----0 Excludes is default
+//***************************************************
-//See manual for excludes, only CURRENT Data allows 1,000,000 calls a month
-int myEXCLUDES = EXCL_D+EXCL_H+EXCL_M; //<-----0 Excludes is default
+//*************** UNITS OF MEASURMENT and DATE TIME FORMAT ***************
+int myUNITS = IMPERIAL; //<-----METRIC, IMPERIAL, KELVIN (IMPERIAL is default)
+
+//Date Time Format
+int myDTF = 1; //1 M/D/Y 24H
+ // 2 D/M/Y 24H
+ // 3 M/D/Y 12H
+ // 4 D/M/Y 12H
+
+//************************************************************************
//for debugging loop counting
int nextCall = 0;
//int callAttempt = 1;
-OpenWeatherOneCall OWOC; // <------ Invoke Library like this
+OpenWeatherOneCall OWOC; // <------ Invoke Library like this
// WiFi Connect function **********************************
void connectWifi() {
@@ -73,11 +109,12 @@ void connectWifi() {
TryNum++;
if (TryNum > 20) {
printf("\nUnable to connect to WiFi. Please check your parameters.\n");
- for (;;);
+ for (;;)
+ ;
}
}
printf("Connected to: % s\n\n", HOMESSID);
-} //================== END WIFI CONNECT =======================
+} //================== END WIFI CONNECT =======================
@@ -92,95 +129,102 @@ void setup() {
// WiFi Connection required *********************
while (WiFi.status() != WL_CONNECTED) {
connectWifi();
- } //<----------------End WiFi Connection Check
-
-
- /*
- Getting the Weather
- If you desire WiFi Triangulation to find your
- Latitude and Longitude, please install the
- WiFiTriangulation Library
- Otherwise Latitude and Longitude can be from any
- source such as a GPS or entered manually.
- If you DO NOT set the Latitude and Longitude
- the program will attempt to find your location
- by IP Address (which could be grossly innacurate
- if you are using a HOTSPOT on your phone)
- */
-
- // First set your Key value
- OWOC.setOpenWeatherKey(ONECALLKEY);
-
- // Second set your position (can be CITY ID, IP ADDRESS, GPS, or Manual LATITUDE/LONGITUDE)
- // Choose one of the following options
-
- // Here we use the Lat and Lon for Pensacola, Florida (Using the predefined values)
- OWOC.setLatLon(myLATITUDE, myLONGITUDE);
-
- // If we are using CITY ID
- //OWOC.setLatLon(myCITYID);
-
- // If we want to use IP Address Geolocation use no arguments
- //OWOC.setLatLon();
+ } //<----------------End WiFi Connection Check
- // Third set any EXCLUDES if required (Here we are not using any
- OWOC.setExcl(myEXCLUDES);
-
- // Set History if you want historical weather other wise NULL
- OWOC.setHistory(myHISTORY);
-
- // Set UNITS of MEASURE otherwise default is IMPERIAL
- OWOC.setUnits(myUNITS);
//Now call the weather. Please notice no arguments are required in this call
OWOC.parseWeather();
- //Now display some information, note the pointer requirement for current and alert, this is NEW for v3.0.0
+ //Now display some information, note the pointer requirement for current and alert
- // Location info is available for ALL modes (History/Current)
+ // Location info is available for ALL modes
printf("\nLocation: % s, % s % s\n", OWOC.location.CITY, OWOC.location.STATE, OWOC.location.COUNTRY);
+
//Verify all other values exist before using
- if (myHISTORY) //Remember you can't get historical and current weather at the same time so we are checking here for NULL
- {
- if (OWOC.history)
- {
- printf("Mean Temp for % s : % .0f\n", OWOC.history[0].weekDayName, OWOC.history[0].temperature);
- }
- } else //If History was NULL we go to Current Weather. Air Quality is returned with Current Weather (Historical Air Quality COMING SOON!)
+
+ if (OWOC.current) //Check if data is in the struct for all variables before using them
{
- if (OWOC.current) //Always check to see if data is in the struct for all variables before using them
- {
- printf("Current Temp : % .0f\n", OWOC.current->temperature);
- printf("Current Humidity : % .0f\n", OWOC.current->humidity);
+ printf("\nCURRENT\n");
+ printf("Temp : % .0f\n", OWOC.current->temperature);
+ printf("Humidity : % .0f\n", OWOC.current->humidity);
+ } else printf("\nCURRENT IS OFF or EMPTY\n");
+
+ //Forecast is part of CURRENT and has info for the 8 days future weather. See variables PDF
+ // The JSON holds this info in the DAILY section and EXCL_D turns this off
+ if (OWOC.forecast) { //Check if data is in the struct for all variables before using them
+ printf("\nFORECAST - Up to 8 days future forecast\n");
+ for (int x = 0; x < 8; x++) {
+ printf("Date: %s High Temperature: % .0f\n", OWOC.forecast[x].readableDateTime, OWOC.forecast[x].temperatureHigh);
}
+ } else printf("\nFORECAST IS OFF or EMPTY\n");
- if(OWOC.quality) //See Addendum for complete list of Air Quality Variables
- {
- printf("\nAir Quality : % d\n", OWOC.quality->aqi);
- printf("Carbon : % .2f\n", OWOC.quality->co);
- printf("Nitrogen : % .2f\n", OWOC.quality->no);
- printf("Ozone : % .2f\n", OWOC.quality->o3);
- }
+ //Hour is part of CURRENT and has info for 48 hours future weather. See variables PDF
+ // The JSON holds this info in the HOURLY section and EXCL_H turns this off
+ if (OWOC.hour) { //Check if data is in the struct for all variables before using them
+ printf("\nHOURLY - Up to 48 hours forecast\n");
- if (OWOC.forecast)
- {
- printf("\nForecast Temp Tomorrow : % .0f\n", OWOC.forecast[1].temperatureHigh);
+ for (int x = 0; x < 6; x++) {
+ printf("%s Actual Temp: % .0f\n", OWOC.hour[x].readableTime, OWOC.hour[x].temperature);
+ printf("%s Feels Like Temp: % .0f\n", OWOC.hour[x].readableTime, OWOC.hour[x].apparentTemperature);
}
+ } else printf("\nHOURLY IS OFF or EMPTY\n");
- if (OWOC.alert) //Only if ALERTS aren't excluded
- {
- printf("\nALERT *** ALERT *** ALERT\n");
- printf("Sender : % s\n", OWOC.alert->senderName);
- printf("Event : % s\n", OWOC.alert->event);
- printf("ALERT : % s\n", OWOC.alert->summary);
- } else {printf("\nNo Alerts For Area\n");
+ //MINUTELY is part of CURRENT and has info for 60 minutes future weather. See variables PDF
+ // The JSON holds this info in the MINUTELY section and EXCL_M turns this off
+ if (OWOC.minute) { //Check if data is in the struct for all variables before using them
+ printf("\nMINUTELY - Up to 60 minutes precipitation forecast\n");
+
+ for (int x = 0; x < 30; x++) {
+ printf("%s Precipitation: % .2f\n", OWOC.minute[x].readableTime, OWOC.minute[x].precipitation);
}
+ } else printf("\nHOURLY IS OFF or EMPTY\n");
+ //ALERTS is part of CURRENT and has info for alerts in your area. See variables PDF
+ // The JSON holds this info in the ALERTS section and EXCL_A turns this off
+ if (OWOC.alert) //Check if data is in the struct for all variables before using them
+ {
+ printf("\nALERT *** ALERT *** ALERT\n");
+ printf("Sender : % s\n", OWOC.alert->senderName);
+ printf("Event : % s\n", OWOC.alert->event);
+ printf("ALERT : % s\n", OWOC.alert->summary);
+ } else {
+ printf("\nNo Alerts For Area, Alerts are EXCLUDED, or Alerts struct is empty\n");
}
+ //The following sections are separate from CURRENT and are turned off and on with their own flags
+
+ if (OWOC.timestamp) { //Check if data is in the struct for all variables before using them
+ printf("\nTIMESTAMP - Single day weather for any date from 01-01-1979 to 4 days in future\n");
+ printf("% s - % s\nTemperature: % .0f\nFeels like: % .0f\nHumidity: % .0F%%\n", OWOC.timestamp[0].weekDayName, OWOC.timestamp[0].readableDateTime, OWOC.timestamp[0].temperature,OWOC.timestamp[0].apparentTemperature,OWOC.timestamp[0].humidity);
+ } else printf("\nTIMESTAMP IS OFF or EMPTY\n");
+
+ if (OWOC.overView) //Check if data is in the struct for all variables before using them
+ {
+ printf("\nOVERVIEW - Summary for TODAY or TOMORROW generated by AI\n");
+
+ // Print the struct values to verify
+ printf("Latitude: %.6f\n", OWOC.overView->lat); // %.6f for printing double with 6 decimal places
+ printf("Longitude: %.6f\n", OWOC.overView->lon);
+ printf("Timezone: %s\n", OWOC.overView->tz); // %s for const char* (string)
+ printf("Date: %s\n", OWOC.overView->date);
+ printf("Units: %s\n", OWOC.overView->units);
+ printf("Weather Overview: %s\n", OWOC.overView->weather_overview);
+
+ } else printf("\nOVERVIEW IS OFF or EMPTY\n");
+
+ if (OWOC.quality) //Check if data is in the struct for all variables before using them
+ {
+ printf("\nAir Quality and Pollution Numbers for today");
+ printf("\nAir Quality : % d\n", OWOC.quality->aqi);
+ printf("Carbon : % .2f\n", OWOC.quality->co);
+ printf("Nitrogen : % .2f\n", OWOC.quality->no);
+ printf("Ozone : % .2f\n", OWOC.quality->o3);
+ } else printf("\nAIR QUALITY IS OFF or EMPTY\n");
+
+
+
}
void loop() {
-
}
diff --git a/examples/platformio.ini b/examples/platformio.ini
index a6ff8dd..a835ebf 100644
--- a/examples/platformio.ini
+++ b/examples/platformio.ini
@@ -17,7 +17,7 @@ framework = arduino
upload_speed = 1500000
monitor_speed = 115200
lib_deps =
- ArduinoJson @ 6.17.2
+ ArduinoJson @ 7
[env:esp32dev]
platform = espressif32
diff --git a/library.json b/library.json
index 259a1e4..91fba6b 100644
--- a/library.json
+++ b/library.json
@@ -1,6 +1,6 @@
{
"name": "OpenWeatherOneCall",
- "version": "3.3.3",
+ "version": "4.0.0",
"keywords": "openweather, darksky, weather, esp32, arduino",
"description": "OpenWeatherMap Library to use One Call on the ESP32. This library allows the ESP32 to receive and parse the json from OpenWeatherMap",
"dependencies":
@@ -8,7 +8,7 @@
{
"owner": "bblanchon",
"name": "ArduinoJson",
- "version": "^6.17.2"
+ "version": "^7.0.0"
}
],
"repository":
diff --git a/library.properties b/library.properties
index 02596c4..65d9908 100644
--- a/library.properties
+++ b/library.properties
@@ -1,9 +1,9 @@
name=OpenWeatherOneCall
-version=3.3.3
+version=4.0.0
author=JHershey69
maintainer=JHershey69
-sentence=Current and Seven Day Weather Forecast Library for ESP32.
-paragraph=This library depends on ArduinoJson v6+
+sentence=Weather Forecast Library for ESP32.
+paragraph=This library depends on ArduinoJson v7+
category=other
url=https://github.com/JHershey69/OpenWeatherOneCall
architectures=esp32
diff --git a/src/DateTimeConversion.cpp b/src/DateTimeConversion.cpp
index 78927ee..5380dc7 100644
--- a/src/DateTimeConversion.cpp
+++ b/src/DateTimeConversion.cpp
@@ -1,9 +1,9 @@
-// OpenWeatherOneCall v3.1.8
+// OpenWeatherOneCall v4.0.0
// www.github.com/jhershey69
+// DO NOT EDIT
+
+#include "OpenWeatherOneCall.h"
-#include
-#include
-#include
// Takes any EPOCH time converts to Human Readable
void dateTimeConversion(long _epoch, char *_buffer, int _format)
@@ -17,11 +17,30 @@ void dateTimeConversion(long _epoch, char *_buffer, int _format)
5/6 TIME ONLY 24H
7/8 TIME ONLY 12H
9 DAY SHORTNAME
+ 10 M/D/Y ONLY
+ 11 D/M/Y ONLY
*/
+ // NTP Server
+ const char* ntpServer = "pool.ntp.org";
+ const long gmtOffset_sec = myTimeZone * 3600;
+ const int daylightOffset_sec = 0;
+
+// Initialize NTP
+ configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
+
+
time_t rawtime = _epoch;
struct tm *ptm = localtime(&rawtime);
+
+ // Add 1 to the day
+ ptm->tm_mday += 1;
+
+ // Normalize the time structure (this handles overflow of days, months, etc.)
+ mktime(ptm);
+
+
switch (_format)
{
@@ -55,10 +74,22 @@ void dateTimeConversion(long _epoch, char *_buffer, int _format)
// DAY SHORTNAME
strftime(_buffer,20,"%a",ptm);
break;
+ case 10:
+ // M/D/Y 24H
+ strftime(_buffer,20,"%m/%d/%Y",ptm);
+ break;
+ case 11:
+ // D/M/Y 24H
+ strftime(_buffer,20,"%d/%m/%Y",ptm);
+ break;
default:
// M/D/Y 24H
- strftime(_buffer,20,"%m/%d/%Y %R",ptm);
+ strftime(_buffer,20,"%m/%d/%Y",ptm);
}
}
+
+
+
+
diff --git a/src/OpenWeatherOneCall.cpp b/src/OpenWeatherOneCall.cpp
index e5717d8..2ab3d6d 100644
--- a/src/OpenWeatherOneCall.cpp
+++ b/src/OpenWeatherOneCall.cpp
@@ -1,6 +1,7 @@
/*
- OpenWeatherOneCall.cpp v3.3.3
- copyright 2020 - Jessica Hershey
+ OpenWeatherOneCall.cpp v4.0.0
+ Updated for ArduinoJSON v7 on Aug 1, 2024
+ copyright 2020/2024 - Jessica Hershey
www.github.com/JHershey69
Open Weather Map - Weather Conditions
@@ -9,9 +10,16 @@
REVISION HISTORY
See User Manual
+
+ **************************************************************
+ ************NO USER EDITABLE LINES IN THIS FILE***************
+ **************************************************************
*/
#include "OpenWeatherOneCall.h"
+
+
+
void dateTimeConversion(long _epoch, char *_buffer, int _format);
OpenWeatherOneCall::OpenWeatherOneCall()
@@ -29,30 +37,57 @@ char DS_URL2[100];
#define AQ_URL2 "&lon="
#define AQ_URL3 "&appid="
-
-
-
-// For Historical Weather Calls **********
+// For TIMESTAMP Weather Calls **********
#define TS_URL1 "https://api.openweathermap.org/data/3.0/onecall/timemachine"
#define TS_URL2 "&dt="
+// For OVERVIEW weather calls ***********
+#define OV_URL1 "https://api.openweathermap.org/data/3.0/onecall/overview"
+#define OV_URL2 "&date="
+
// For CITY Id calls
#define CI_URL1 "api.openweathermap.org/data/3.0/weather?id="
#define CI_URL2 "&appid="
#define SIZEOF(a) sizeof(a)/sizeof(*a)
+
// Main Method for Weather API Call and Parsing
int OpenWeatherOneCall::parseWeather(void)
{
+ OpenWeatherOneCall::setOpenWeatherKey(ONECALLKEY);
+ OpenWeatherOneCall::setExcl(myEXCLUDES);
+ OpenWeatherOneCall::setUnits(myUNITS);
+ OpenWeatherOneCall::setDateTimeFormat(myDTF);
+ OpenWeatherOneCall::setCurrent(myCURRENT);
+ OpenWeatherOneCall::setAirQuality(myAIRQUALITY);
+ OpenWeatherOneCall::setTimestamp(myTIMESTAMP, timestampDate);
+ OpenWeatherOneCall::setOverview(myOVERVIEW, overviewDate);
+
+
int error_code = 0;
- // WiFi.status not declared in this scope error
if (WiFi.status() != WL_CONNECTED)
{
return 25;
}
- // End WiFi error
+
+
+ // LOCATION MODE SET
+
+ if(locationMode == 1)
+ {
+ OpenWeatherOneCall::setLatLon(myLATITUDE, myLONGITUDE);
+ }
+ else if(locationMode == 2)
+ {
+ OpenWeatherOneCall::setLatLon(myCITYID);
+ }
+ else if(locationMode == 3)
+ {
+ OpenWeatherOneCall::setLatLon();
+ }
+
unsigned int SIZE_CAPACITY = 32768;
@@ -63,29 +98,41 @@ int OpenWeatherOneCall::parseWeather(void)
{
OpenWeatherOneCall::getLocationInfo();
- if(USER_PARAM.OPEN_WEATHER_HISTORY) //If Historical Weather is requested, no CURRENT weather returned
+ if(USER_PARAM.OPEN_WEATHER_TIMESTAMP)
{
- OpenWeatherOneCall::freeCurrentMem();
- OpenWeatherOneCall::freeForecastMem();
- OpenWeatherOneCall::freeAlertMem();
- OpenWeatherOneCall::freeHourMem();
- OpenWeatherOneCall::freeMinuteMem();
-
- int error_code = OpenWeatherOneCall::createHistory();
+ int error_code = OpenWeatherOneCall::createTimestamp();
}
else
{
- OpenWeatherOneCall::freeHistoryMem();
+ OpenWeatherOneCall::freeTimestampMem();
+ }
- // TEST AIR QUALITY CALL *********************
- OpenWeatherOneCall::createAQ(384);
+ if(USER_PARAM.OPEN_WEATHER_OVERVIEW)
+ {
+ int error_code = OpenWeatherOneCall::createOverview();
+ }
+ else
+ {
+ OpenWeatherOneCall::freeOverviewMem();
+ }
+ if(USER_PARAM.OPEN_WEATHER_AIRQUALITY)
+ {
+ int error_code = OpenWeatherOneCall::createAQ(384);
+ }
- //********************************************
+ if(USER_PARAM.OPEN_WEATHER_CURRENT)
+ {
int error_code = OpenWeatherOneCall::createCurrent(SIZE_CAPACITY);
-
}
+ else
+ {
+ OpenWeatherOneCall::freeCurrentMem();
+ }
+
+
+
if(error_code)
{
return error_code;
@@ -98,335 +145,161 @@ int OpenWeatherOneCall::parseWeather(void)
return 0;
}
+/*
+REMOVED LEGACY CALLING METHOD AS OF 3.3.4
+*/
-int OpenWeatherOneCall::parseWeather(char* DKEY, char* GKEY, float SEEK_LATITUDE, float SEEK_LONGITUDE, bool SET_UNITS, int CITY_ID, int API_EXCLUDES, int GET_HISTORY)
-{
- //Legacy calling Method for versions prior to v3.0.0
-
- OpenWeatherOneCall::setOpenWeatherKey(DKEY);
- if(SET_UNITS)
- {
- OpenWeatherOneCall::setUnits(METRIC);
- }
- OpenWeatherOneCall::setExcl(API_EXCLUDES);
- OpenWeatherOneCall::setHistory(GET_HISTORY);
- if(SEEK_LATITUDE && SEEK_LONGITUDE)
- {
- OpenWeatherOneCall::setLatLon(SEEK_LATITUDE,SEEK_LONGITUDE);
- }
- else if (CITY_ID)
- {
- OpenWeatherOneCall::setLatLon(CITY_ID);
- }
- else
- OpenWeatherOneCall::setLatLon();
- OpenWeatherOneCall::parseWeather();
-}
-
-int OpenWeatherOneCall::setLatLon(float _LAT, float _LON)
-{
- int error_code = 0;
-
- if(abs(_LAT) <= 90)
- {
- USER_PARAM.OPEN_WEATHER_LATITUDE = _LAT;
- location.LATITUDE = _LAT; //User copy
- }
- else
- error_code += 1;
-
- if(abs(_LON) <= 180)
- {
- USER_PARAM.OPEN_WEATHER_LONGITUDE = _LON;
- location.LONGITUDE = _LON; //User copy
- }
- else
- error_code += 2;
-
- if(error_code)
- return error_code;
-
- return (EXIT_SUCCESS);
-}
-
-int OpenWeatherOneCall::setLatLon(int _CITY_ID)
-{
- int error_code = 0;
-
- char cityURL[110];
- char* URL1 = "http://api.openweathermap.org/data/3.0/weather?id=";
- char* URL2 = "&appid=";
-
- sprintf(cityURL,"%s%d%s%s",URL1,_CITY_ID,URL2,USER_PARAM.OPEN_WEATHER_DKEY);
- error_code = OpenWeatherOneCall::parseCityCoordinates(cityURL);
- if(error_code)
- return error_code;
-
- return (EXIT_SUCCESS);
-
-}
-
-int OpenWeatherOneCall::setLatLon(void)
+int OpenWeatherOneCall::createTimestamp()
{
- // IP address of NON-CELLULAR WiFi. Hotspots won't work properly.
-
int error_code = 0;
- error_code = OpenWeatherOneCall::getIPLocation();
- if(error_code)
- {
- return error_code;
- }
- error_code = OpenWeatherOneCall::getIPAPILocation(_ipapiURL);
- if(error_code)
- {
- return error_code;
- }
+ char timestampURL[200] = {0};
- return 0;
-}
+ long tempEPOCH = extractDate(USER_PARAM.OPEN_WEATHER_TIMESTAMP_DATE);
+ //Setup API URL for TIMESTAMP Weather Call [Single Timestamp only]
-int OpenWeatherOneCall::parseCityCoordinates(char* CTY_URL)
-{
- int error_code = 0;
+ sprintf(timestampURL,"%s?lat=%.6f&lon=%.6f%s%ld&units=%s%s%s",TS_URL1,USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE,TS_URL2,tempEPOCH,units,DS_URL3,USER_PARAM.OPEN_WEATHER_DKEY);
HTTPClient http;
- http.begin(CTY_URL);
+ http.begin(timestampURL);
int httpCode = http.GET();
- if(httpCode > 399)
+
+ if (httpCode > 399)
{
- if(httpCode == 404)
+ if(httpCode == 401)
{
http.end();
- error_code += 4;
- return error_code;
+ return 22;
}
else
{
http.end();
- error_code += 5;
- return error_code;
+ return 21;
}
-
}
- const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(13) + 270;
- DynamicJsonDocument doc(capacity);
+
+ JsonDocument doc;
deserializeJson(doc, http.getString());
- if(doc["coord"]["lon"])
- {
- USER_PARAM.OPEN_WEATHER_LONGITUDE = doc["coord"]["lon"]; // -74.2
- }
- else
- {
- error_code += 6;
- }
+ strncpy(location.timezone,doc["timezone"],50);
+ location.timezoneOffset = doc["timezone_offset"];
- if(doc["coord"]["lat"])
+ if(!timestamp)
{
- USER_PARAM.OPEN_WEATHER_LATITUDE = doc["coord"]["lat"]; // 39.95
+ timestamp = (struct TIMESTAMP *)calloc(25,sizeof(struct TIMESTAMP));
}
- else
+
+ //Current in historical is the time of the request on that day
+ JsonObject current = doc["data"][0];
+ timestamp[0].dayTime = current["dt"]; // 1607292481
+
+ if(current["dt"])
{
- error_code += 7;
+ // Create human readable date and time
+
+ long tempTime = current["dt"];
+ tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,timestamp[0].readableDateTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT+9);
}
- http.end();
+ timestamp[0].sunrise = current["sunrise"]; // 1607256309
- if(error_code)
+ if(current["sunrise"])
{
- http.end();
- return error_code;
+ // Create human readable date and time
+
+ long tempTime = current["sunrise"];
+ tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,timestamp[0].readableSunrise,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
}
- return (EXIT_SUCCESS);
-}
+ timestamp[0].sunset = current["sunset"]; // 1607290280
-int OpenWeatherOneCall::getIPLocation()
-{
- int error_code = 0;
+ if(current["sunset"])
+ {
+ // Create human readable date and time
- HTTPClient http;
- http.begin("https://api64.ipify.org/ HTTP/1.1\r\nHost: api.ipify.org\r\n\r\n");
+ long tempTime = current["sunset"];
+ tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,timestamp[0].readableSunset,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
+ }
- int httpCode = http.GET();
+ timestamp[0].temperature = current["temp"]; // 35.82
+ timestamp[0].apparentTemperature = current["feels_like"]; // 21.7
+ timestamp[0].pressure = current["pressure"]; // 1010
+ timestamp[0].humidity = current["humidity"]; // 51
+ timestamp[0].dewPoint = current["dew_point"]; // 20.88
+ timestamp[0].uvIndex = current["uvi"]; // 1.54
+ timestamp[0].cloudCover = current["clouds"]; // 1
+ timestamp[0].visibility = current["visibility"]; // 16093
+ timestamp[0].windSpeed = current["wind_speed"]; // 16.11
+ timestamp[0].windBearing = current["wind_deg"]; // 300
+ timestamp[0].windGust = current["wind_gust"]; // 24.16
- if(httpCode > 399)
+ // New rain and snow =======================
+ if(current["rain"])
{
- if(httpCode == 404)
+ if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
{
- http.end();
- error_code += 8;
- return error_code;
+ float temp = current["rain"]["1h"];
+ timestamp[0].rainVolume = (temp/25.4); // 95
}
else
- {
- http.end();
- error_code += 9;
- return error_code;
- }
+ timestamp[0].rainVolume = current["rain"]["1h"]; // 95
}
-
- int streamSize = http.getSize();
- char stringVarout[streamSize+1];
-
- String stringVarin = http.getString();
- strcpy(stringVarout,stringVarin.c_str());
-
- http.end();
-
- strncpy(_ipapiURL,"https://ipapi.co/",38);
- strcat(_ipapiURL,stringVarout);
- strcat(_ipapiURL,"/json/");
-
- return 0;
-}
+ else
+ timestamp[0].rainVolume = 0;
-int OpenWeatherOneCall::getIPAPILocation(char* URL)
-{
- int error_code = 0;
- HTTPClient http;
- http.begin(URL);
- int ipapi_httpCode = http.GET();
- if(ipapi_httpCode > 399)
+ if(current["snow"])
{
- if(ipapi_httpCode == 404)
+ if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
{
- error_code += 10;
+ float temp = current["snow"]["1h"];
+ timestamp[0].snowVolume = (temp/25.4); // 95
}
else
- {
- error_code += 11;
- }
- http.end();
- return error_code;
+ timestamp[0].snowVolume = current["snow"]["1h"]; // 95
+
}
+ else
+ timestamp[0].snowVolume = 0;
- const size_t capacity = JSON_OBJECT_SIZE(26) + 490;
- DynamicJsonDocument doc(capacity);
- deserializeJson(doc, http.getString());
+ JsonObject current_weather_0 = current["weather"][0];
+ timestamp[0].id = current_weather_0["id"]; // 800
- strncpy(location.CITY,doc["city"],60); // "Lakewood"
- strncpy(location.STATE,doc["region_code"],2); // "NJ"
- strncpy(location.COUNTRY,doc["country_code"],3); // "US"
- USER_PARAM.OPEN_WEATHER_LATITUDE = doc["latitude"]; // 40.0881
- location.LATITUDE = doc["latitude"];
- USER_PARAM.OPEN_WEATHER_LONGITUDE = doc["longitude"]; // -74.1963
- location.LONGITUDE = doc["longitude"];
+ timestamp[0].main = (char *)realloc(timestamp[0].main,sizeof(char) * strlen(current_weather_0["main"])+1);
+ strncpy(timestamp[0].main,current_weather_0["main"],strlen(current_weather_0["main"])+1);
- http.end();
+ timestamp[0].summary = (char *)realloc(timestamp[0].summary,sizeof(char) * strlen(current_weather_0["description"])+1);
+ strncpy(timestamp[0].summary,current_weather_0["description"],strlen(current_weather_0["description"])+1);
- return 0;
-}
+ strncpy(timestamp[0].icon,current_weather_0["icon"],strlen(current_weather_0["icon"])+1);
+ dateTimeConversion(timestamp[0].dayTime,timestamp[0].weekDayName,9);
-void OpenWeatherOneCall::initAPI(void)
-{
- memset(USER_PARAM.OPEN_WEATHER_DKEY,NULL,1);
- USER_PARAM.OPEN_WEATHER_LATITUDE = 0.0;
- USER_PARAM.OPEN_WEATHER_LONGITUDE = 0.0;
- USER_PARAM.OPEN_WEATHER_UNITS = 2;
- USER_PARAM.OPEN_WEATHER_EXCLUDES = 0;
- USER_PARAM.OPEN_WEATHER_HISTORY = 0;
+ http.end();
+ return 0;
}
-
-int OpenWeatherOneCall::getLocationInfo()
+int OpenWeatherOneCall::createCurrent(int sizeCap)
{
- int error_code = 0;
- char locationURL[200];
+ char getURL[200] = {0};
+ int alertz = 0;
- sprintf(locationURL,"https://api.bigdatacloud.net/data/reverse-geocode-client/?latitude=%f&longitude=%f",USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE);
+ sprintf(getURL,"%s?lat=%.6f&lon=%.6f&lang=%s%s&units=%s%s%s",DS_URL1,USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE,USER_PARAM.OPEN_WEATHER_LANGUAGE,DS_URL2,units,DS_URL3,USER_PARAM.OPEN_WEATHER_DKEY);
+ // printf("\n%s\n",getURL);
HTTPClient http;
- http.begin(locationURL); //<------------ Connect to OpenWeatherMap
-
- int httpCode = http.GET();
- if (httpCode > 399) //<- Check for connect errors
- {
- if(httpCode == 401)
- {
- error_code = 17;
- }
- else if(httpCode == 404)
- {
- error_code = 18;
- }
- else
- error_code = 19;
-
- http.end();
- return error_code;
- }
-
-
- //const size_t capacity = JSON_ARRAY_SIZE(4) + JSON_ARRAY_SIZE(5) + JSON_OBJECT_SIZE(2) + 2*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 3*JSON_OBJECT_SIZE(6) + 2*JSON_OBJECT_SIZE(8) + JSON_OBJECT_SIZE(14) + 1050;
- DynamicJsonDocument doc(3072);
-
- deserializeJson(doc, http.getString());
-
- if(doc["locality"])
- {
- strncpy(location.CITY,doc["locality"],60);
- }
- else
- {
- http.end();
- return 20;
- }
-
- if(doc["principalSubdivisionCode"])
- {
- strncpy(location.STATE,doc["principalSubdivisionCode"],10);
- }
- else
- {
- http.end();
- return 20;
- }
-
- if(doc["countryCode"])
- {
- strncpy(location.COUNTRY,doc["countryCode"],10);
- }
- else
- {
- http.end();
- return 20;
- }
-
-
- http.end();
-
- return 0;
-}
-
-
-int OpenWeatherOneCall::createHistory()
-{
- int error_code = 0;
- char historyURL[200] = {0};
-
- if(USER_PARAM.OPEN_WEATHER_HISTORY > 5)
- {
- return 16;
- }
-
- //Gets Timestamp for EPOCH calculation below
- char tempURL[200];
- sprintf(tempURL,"https://api.openweathermap.org/data/3.0/onecall?lat=%.6f&lon=%.6f&exclude=minutely,hourly,daily,alerts&units=IMPERIAL&appid=%s",USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE,USER_PARAM.OPEN_WEATHER_DKEY);
-
- HTTPClient http;
- http.begin(tempURL);
+ http.begin(getURL);
int httpCode = http.GET();
if (httpCode > 399)
@@ -436,451 +309,167 @@ int OpenWeatherOneCall::createHistory()
http.end();
return 22;
}
- else
- {
- http.end();
- return 21;
- }
- }
-
- DynamicJsonDocument toc(768);
- deserializeJson(toc, http.getString());
-
- JsonObject toc_current = toc["current"];
- long tempEPOCH = toc_current["dt"]; // 1608323864
-
- tempEPOCH = tempEPOCH - (86400 * USER_PARAM.OPEN_WEATHER_HISTORY);
-
- http.end();
-
- //Timemachine request to OWM
- sprintf(historyURL,"%s?lat=%.6f&lon=%.6f%s%ld&units=%s%s%s",TS_URL1,USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE,TS_URL2,tempEPOCH,units,DS_URL3,USER_PARAM.OPEN_WEATHER_DKEY);
-
- http.begin(historyURL);
- httpCode = http.GET();
+ http.end();
+ return 21;
- if (httpCode > 399)
- {
- if(httpCode == 401)
- {
- http.end();
- return 22;
- }
- else
- {
- http.end();
- return 21;
- }
}
-
- DynamicJsonDocument doc(9000);
-
+ const size_t capacity = sizeCap;
+ JsonDocument doc;
deserializeJson(doc, http.getString());
+
strncpy(location.timezone,doc["timezone"],50);
location.timezoneOffset = doc["timezone_offset"];
- if(!history)
- {
- history = (struct HISTORICAL *)calloc(25,sizeof(struct HISTORICAL));
- }
-
- //Current in historical is the time of the request on that day
- JsonObject current = doc["current"];
- history[0].dayTime = current["dt"]; // 1607292481
-
- if(current["dt"])
- {
- long tempTime = current["dt"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,history[0].readableDateTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
- }
-
- history[0].sunrise = current["sunrise"]; // 1607256309
-
- if(current["sunrise"])
- {
- long tempTime = current["sunrise"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,history[0].readableSunrise,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
- }
-
- history[0].sunset = current["sunset"]; // 1607290280
-
- if(current["sunset"])
+ if(exclude.current)
{
- long tempTime = current["sunset"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,history[0].readableSunset,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
+ OpenWeatherOneCall::freeCurrentMem();
}
-
- history[0].temperature = current["temp"]; // 35.82
- history[0].apparentTemperature = current["feels_like"]; // 21.7
- history[0].pressure = current["pressure"]; // 1010
- history[0].humidity = current["humidity"]; // 51
- history[0].dewPoint = current["dew_point"]; // 20.88
- history[0].uvIndex = current["uvi"]; // 1.54
- history[0].cloudCover = current["clouds"]; // 1
- history[0].visibility = current["visibility"]; // 16093
- history[0].windSpeed = current["wind_speed"]; // 16.11
- history[0].windBearing = current["wind_deg"]; // 300
- history[0].windGust = current["wind_gust"]; // 24.16
-
- // New rain and snow =======================
- if(current["rain"])
+ else
{
- if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
+ if(!current)
{
- float temp = current["rain"]["1h"];
- history[0].rainVolume = (temp/25.4); // 95
+ current = (struct nowData *)calloc(1,sizeof(struct nowData));
+ if(current == NULL)
+ {
+ return 23;
+ }
}
- else
- history[0].rainVolume = current["rain"]["1h"]; // 95
-
- }else history[0].rainVolume = 0;
-
+ JsonObject currently = doc["current"];
+ current->dayTime = currently["dt"]; // 1586781931
- if(current["snow"])
- {
- if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
+ if(currently["dt"])
{
- float temp = current["snow"]["1h"];
- history[0].snowVolume = (temp/25.4); // 95
- }
- else
- history[0].snowVolume = current["snow"]["1h"]; // 95
-
- }else history[0].snowVolume = 0;
-
-
-
- JsonObject current_weather_0 = current["weather"][0];
- history[0].id = current_weather_0["id"]; // 800
-
- history[0].main = (char *)realloc(history[0].main,sizeof(char) * strlen(current_weather_0["main"])+1);
- strncpy(history[0].main,current_weather_0["main"],strlen(current_weather_0["main"])+1);
-
- history[0].summary = (char *)realloc(history[0].summary,sizeof(char) * strlen(current_weather_0["description"])+1);
- strncpy(history[0].summary,current_weather_0["description"],strlen(current_weather_0["description"])+1);
-
- strncpy(history[0].icon,current_weather_0["icon"],strlen(current_weather_0["icon"])+1);
-
- dateTimeConversion(history[0].dayTime,history[0].weekDayName,9);
+ // Create human readable date and time
+ long tempTime = currently["dt"];
+ tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,current->readableDateTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
+ dateTimeConversion(tempTime,current->readableWeekdayName,9);
+ }
- //Hourly report for the day requested begins at 00:00GMT
- JsonArray hourly = doc["hourly"];
- for(int x = 1; x < 24; x++)
- {
- JsonObject hourly_0 = hourly[x-1];
- history[x].dayTime = hourly_0["dt"]; // 1607212800
+ current->sunriseTime = currently["sunrise"]; // 1612267442
- if(hourly_0["dt"])
+ if(currently["sunrise"])
{
- long tempTime = hourly_0["dt"];
+ // Create human readable date and time
+
+ long tempTime = currently["sunrise"];
tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,history[x].readableDateTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
+ dateTimeConversion(tempTime,current->readableSunrise,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
}
- history[x].sunrise = current["sunrise"] | 1607256309; // 1607256309
+ current->sunsetTime = currently["sunset"]; // 1612304218
- if(current["sunrise"])
+ if(currently["sunset"])
{
- long tempTime = current["sunrise"];
+ // Create human readable date and time
+
+ long tempTime = currently["sunset"];
tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,history[x].readableSunrise,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
+ dateTimeConversion(tempTime,current->readableSunset,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
}
- history[x].sunset = current["sunset"] | 1607290280; // 1607290280
- if(current["sunset"])
+
+ current->temperature = currently["temp"]; // 287.59
+ current->apparentTemperature = currently["feels_like"]; // 281.42
+ current->pressure = currently["pressure"]; // 1011
+ current->humidity = currently["humidity"]; // 93
+ current->dewPoint = currently["dew_point"]; // 286.47
+ current->uvIndex = currently["uvi"]; // 6.31
+ current->cloudCover = currently["clouds"]; // 90
+ current->visibility = currently["visibility"]; // 8047
+ current->windSpeed = currently["wind_speed"]; // 10.3
+ current->windBearing = currently["wind_deg"]; // 170
+
+ if(currently["wind_gust"])
{
- long tempTime = current["sunset"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,history[x].readableSunset,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
+ current->windGust = currently["wind_gust"];
}
- history[x].temperature = hourly_0["temp"]; // 40.21
- history[x].apparentTemperature = hourly_0["feels_like"]; // 29.39
- history[x].pressure = hourly_0["pressure"]; // 1007
- history[x].humidity = hourly_0["humidity"]; // 56
- history[x].dewPoint = hourly_0["dew_point"]; // 26.51
- history[x].cloudCover = hourly_0["clouds"]; // 1
- history[x].visibility = hourly_0["visibility"]; // 16093
- history[x].windSpeed = hourly_0["wind_speed"]; // 11.41
- history[x].windBearing = hourly_0["wind_deg"]; // 290
-
- // New rain and snow =======================
- if(hourly_0["rain"])
+ if(currently["snow"]["1h"])
{
if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
{
- float temp = hourly_0["rain"]["1h"];
- history[x].rainVolume = (temp/25.4); // 95
+ float temp = currently["snow"]["1h"];
+ current->snowVolume = (temp/25.4); // 95
}
else
- history[x].rainVolume = hourly_0["rain"]["1h"]; // 95
-
- }else history[x].rainVolume = 0;
+ current->snowVolume = currently["snow"]["1h"]; // 95
+ }
+ else
+ current->snowVolume = 0;
- if(hourly_0["snow"])
+ if(currently["rain"]["1h"])
{
if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
{
- float temp = hourly_0["snow"]["1h"];
- history[x].snowVolume = (temp/25.4); // 95
+ float temp = currently["rain"]["1h"];
+ current->rainVolume = (temp/25.4); // 95
}
else
- history[x].snowVolume = hourly_0["snow"]["1h"]; // 95
-
- }else history[x].snowVolume = 0;
-
- JsonObject hourly_0_weather_0 = hourly_0["weather"][0];
- history[x].id = hourly_0_weather_0["id"]; // 800
+ current->rainVolume = currently["rain"]["1h"]; // 95
- history[x].main = (char *)realloc(history[x].main,sizeof(char) * strlen(hourly_0_weather_0["main"])+1);
- strncpy(history[x].main,hourly_0_weather_0["main"],strlen(hourly_0_weather_0["main"])+1);
+ }
+ else
+ current->rainVolume = 0;
- history[x].summary = (char *)realloc(history[x].summary,sizeof(char) * strlen(hourly_0_weather_0["description"])+1);
- strncpy(history[x].summary,hourly_0_weather_0["description"],strlen(hourly_0_weather_0["description"])+1);
- strncpy(history[x].icon,hourly_0_weather_0["icon"],strlen(hourly_0_weather_0["icon"])+1);
- dateTimeConversion(history[0].dayTime,history[x].weekDayName,9);
- }
- http.end();
- return 0;
-}
+ current->id = currently["weather"][0]["id"];
-int OpenWeatherOneCall::createAQ(int sizeCap)
-{
- char getURL[200] = {0};
+ current->main = (char *)realloc(current->main,sizeof(char) * strlen(currently["weather"][0]["main"])+1);
+ if(current->main == NULL)
+ {
+ return 23;
+ }
+ strncpy(current->main,currently["weather"][0]["main"],strlen(currently["weather"][0]["main"])+1);
- sprintf(getURL,"%s%.6f%s%.6f%s%s",AQ_URL1,USER_PARAM.OPEN_WEATHER_LATITUDE,AQ_URL2,USER_PARAM.OPEN_WEATHER_LONGITUDE,AQ_URL3,USER_PARAM.OPEN_WEATHER_DKEY);
- // printf("%s\n",getURL);
+ current->summary = (char *)realloc(current->summary,sizeof(char) * strlen(currently["weather"][0]["description"])+1);
+ if(current->summary == NULL)
+ {
+ return 23;
+ }
+ strncpy(current->summary,currently["weather"][0]["description"],strlen(currently["weather"][0]["description"])+1);
- HTTPClient http;
- http.begin(getURL);
- int httpCode = http.GET();
+ strncpy(current->icon,currently["weather"][0]["icon"],strlen(currently["weather"][0]["icon"])+1);
+ }
- if (httpCode > 399)
+ if(exclude.daily)
{
- if(httpCode == 401)
+ OpenWeatherOneCall::freeForecastMem();
+ }
+ else
+ {
+ if(!forecast)
{
- http.end();
- return 22;
+ forecast = (struct futureData *)calloc(8,sizeof(struct futureData));
+ if(forecast == NULL)
+ {
+ return 23;
+ }
}
- http.end();
- return 21;
-
- }
+ JsonArray daily = doc["daily"];
+ for (int x = 0; x < 8; x++)
+ {
+ forecast[x].dayTime = daily[x]["dt"]; // 1586793600
- const size_t capacity = sizeCap;
- DynamicJsonDocument doc(capacity);
- deserializeJson(doc, http.getString());
- doc.shrinkToFit();
+ if(daily[x]["dt"])
+ {
+ long tempTime = daily[x]["dt"];
+ tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,forecast[x].readableDateTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT+9);
+ }
- quality = (struct airQuality *)calloc(1,sizeof(struct airQuality));
-
- float coord_lon = doc["coord"]["lon"]; // -74.1975
- float coord_lat = doc["coord"]["lat"]; // 39.9533
-
- JsonObject list_0 = doc["list"][0];
-
- quality -> aqi = list_0["main"]["aqi"]; // 2
-
- JsonObject list_0_components = list_0["components"];
- quality -> co = list_0_components["co"]; // 230.31
- quality -> no = list_0_components["no"]; // 0.43
- quality -> no2 = list_0_components["no2"]; // 1.69
- quality -> o3 = list_0_components["o3"]; // 100.14
- quality -> so2 = list_0_components["so2"]; // 0.88
- quality -> pm2_5 = list_0_components["pm2_5"]; // 0.76
- quality -> pm10 = list_0_components["pm10"]; // 1.04
- quality -> nh3 = list_0_components["nh3"]; // 0.43
-
- quality -> dayTime = list_0["dt"]; // 1615838400
-
- http.end();
- return 0;
-
-
-}
-
-int OpenWeatherOneCall::createCurrent(int sizeCap)
-{
-
- char getURL[200] = {0};
- int alertz = 0;
-
- sprintf(getURL,"%s?lat=%.6f&lon=%.6f&lang=%s%s&units=%s%s%s",DS_URL1,USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE,USER_PARAM.OPEN_WEATHER_LANGUAGE,DS_URL2,units,DS_URL3,USER_PARAM.OPEN_WEATHER_DKEY);
- // printf("\n%s\n",getURL);
-
- HTTPClient http;
- http.begin(getURL);
- int httpCode = http.GET();
-
- if (httpCode > 399)
- {
- if(httpCode == 401)
- {
- http.end();
- return 22;
- }
-
- http.end();
- return 21;
-
- }
-
- const size_t capacity = sizeCap;
- DynamicJsonDocument doc(capacity);
- deserializeJson(doc, http.getString());
- doc.shrinkToFit();
-
- strncpy(location.timezone,doc["timezone"],50);
- location.timezoneOffset = doc["timezone_offset"];
-
- if(exclude.current)
- {
- OpenWeatherOneCall::freeCurrentMem();
- }
- else
- {
- if(!current)
- {
- current = (struct nowData *)calloc(1,sizeof(struct nowData));
- if(current == NULL)
- {
- return 23;
- }
- }
-
- JsonObject currently = doc["current"];
- current->dayTime = currently["dt"]; // 1586781931
-
- if(currently["dt"])
- {
- long tempTime = currently["dt"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,current->readableDateTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
- dateTimeConversion(tempTime,current->readableWeekdayName,9);
- }
-
-
- current->sunriseTime = currently["sunrise"]; // 1612267442
-
- if(currently["sunrise"])
- {
- long tempTime = currently["sunrise"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,current->readableSunrise,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
- }
-
- current->sunsetTime = currently["sunset"]; // 1612304218
-
- if(currently["sunset"])
- {
- long tempTime = currently["sunset"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,current->readableSunset,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
- }
-
-
-
- current->temperature = currently["temp"]; // 287.59
- current->apparentTemperature = currently["feels_like"]; // 281.42
- current->pressure = currently["pressure"]; // 1011
- current->humidity = currently["humidity"]; // 93
- current->dewPoint = currently["dew_point"]; // 286.47
- current->uvIndex = currently["uvi"]; // 6.31
- current->cloudCover = currently["clouds"]; // 90
- current->visibility = currently["visibility"]; // 8047
- current->windSpeed = currently["wind_speed"]; // 10.3
- current->windBearing = currently["wind_deg"]; // 170
-
- if(currently["wind_gust"])
- {
- current->windGust = currently["wind_gust"];
- }
-
- if(currently["snow"]["1h"])
- {
- if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
- {
- float temp = currently["snow"]["1h"];
- current->snowVolume = (temp/25.4); // 95
- } else current->snowVolume = currently["snow"]["1h"]; // 95
-
- } else current->snowVolume = 0;
-
- if(currently["rain"]["1h"])
- {
- if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
- {
- float temp = currently["rain"]["1h"];
- current->rainVolume = (temp/25.4); // 95
- } else current->rainVolume = currently["rain"]["1h"]; // 95
-
- } else current->rainVolume = 0;
-
-
-
-
- current->id = currently["weather"][0]["id"];
-
- current->main = (char *)realloc(current->main,sizeof(char) * strlen(currently["weather"][0]["main"])+1);
- if(current->main == NULL)
- {
- return 23;
- }
- strncpy(current->main,currently["weather"][0]["main"],strlen(currently["weather"][0]["main"])+1);
-
- current->summary = (char *)realloc(current->summary,sizeof(char) * strlen(currently["weather"][0]["description"])+1);
- if(current->summary == NULL)
- {
- return 23;
- }
- strncpy(current->summary,currently["weather"][0]["description"],strlen(currently["weather"][0]["description"])+1);
-
- strncpy(current->icon,currently["weather"][0]["icon"],strlen(currently["weather"][0]["icon"])+1);
- }
-
- if(exclude.daily)
- {
- OpenWeatherOneCall::freeForecastMem();
- }
- else
- {
- if(!forecast)
- {
- forecast = (struct futureData *)calloc(8,sizeof(struct futureData));
- if(forecast == NULL)
- {
- return 23;
- }
- }
-
- JsonArray daily = doc["daily"];
- for (int x = 0; x < 8; x++)
- {
- forecast[x].dayTime = daily[x]["dt"]; // 1586793600
-
- if(daily[x]["dt"])
- {
- long tempTime = daily[x]["dt"];
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,forecast[x].readableDateTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
- }
-
- forecast[x].sunriseTime = daily[x]["sunrise"]; // 1586773262
+ forecast[x].sunriseTime = daily[x]["sunrise"]; // 1586773262
if(daily[x]["sunrise"])
{
@@ -956,9 +545,13 @@ int OpenWeatherOneCall::createCurrent(int sizeCap)
{
float temp = daily[x]["rain"];
forecast[x].rainVolume = (temp/25.4); // 95
- } else forecast[x].rainVolume = daily[x]["rain"]; // 95
+ }
+ else
+ forecast[x].rainVolume = daily[x]["rain"]; // 95
- } else forecast[x].rainVolume = 0; // 95
+ }
+ else
+ forecast[x].rainVolume = 0; // 95
if(daily[x]["snow"])
@@ -967,9 +560,13 @@ int OpenWeatherOneCall::createCurrent(int sizeCap)
{
float temp = daily[x]["snow"];
forecast[x].snowVolume = (temp/25.4); // 95
- } else forecast[x].snowVolume = daily[x]["snow"]; // 95
+ }
+ else
+ forecast[x].snowVolume = daily[x]["snow"]; // 95
- } else forecast[x].snowVolume = 0;
+ }
+ else
+ forecast[x].snowVolume = 0;
forecast[x].uvIndex = daily[x]["uvi"]; // 6.31
@@ -995,7 +592,6 @@ int OpenWeatherOneCall::createCurrent(int sizeCap)
}
}
- printf("Number of ALERTS: %d\n\n",MAX_NUM_ALERTS);
if(!alert);
{
@@ -1031,163 +627,654 @@ int OpenWeatherOneCall::createCurrent(int sizeCap)
}
strncpy(alert[x].event,ALERTS_0["event"],strlen(ALERTS_0["event"])+1);
- }
+ }
+
+ if(ALERTS_0["start"])
+ {
+ long tempTime = ALERTS_0["start"];
+ alert[x].alertStart = tempTime;
+ tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,alert[x].startInfo,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
+ }
+
+ if(ALERTS_0["end"])
+ {
+ long tempTime = ALERTS_0["end"];
+ alert[x].alertEnd = tempTime;
+ tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,alert[x].endInfo,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
+ }
+
+ if(ALERTS_0["description"])
+ {
+ alert[x].summary = (char *)realloc(alert[x].summary,sizeof(char) * strlen(ALERTS_0["description"])+1);
+ if(alert[x].summary == NULL)
+ {
+ return 23;
+ }
+ strncpy(alert[x].summary,ALERTS_0["description"],strlen(ALERTS_0["description"])+1);
+ }
+ } //end for
+
+ }
+ else
+ {
+ // If alerts are not excluded but there are none, reset NUM_MAX_ALERTS and MEMORY
+ MAX_NUM_ALERTS = 0;
+ OpenWeatherOneCall::freeAlertMem();
+ }
+ }
+
+
+ if(exclude.hourly)
+ {
+ OpenWeatherOneCall::freeHourMem();
+ }
+ else
+ {
+ if(doc["hourly"])
+ {
+ if(!hour)
+ {
+ hour = (struct HOURLY *)calloc(48, sizeof(struct HOURLY));
+ if(hour == NULL)
+ {
+ return 23;
+ }
+ }
+
+ JsonArray hourly = doc["hourly"];
+ for(int h = 0; h < 48; h++)
+ {
+
+ JsonObject hourly_0 = hourly[h];
+ //hour[h].dayTime = hourly_0["dt"]; // 1604336400
+
+ if(hourly_0["dt"])
+ {
+ long tempTime = hourly_0["dt"];
+ hour[h].dayTime = tempTime;
+ //tempTime += location.timezoneOffset;
+ dateTimeConversion(tempTime,hour[h].readableTime,6);
+ }
+
+ hour[h].temperature = hourly_0["temp"]; // 46.58
+ hour[h].apparentTemperature = hourly_0["feels_like"]; // 28.54
+ hour[h].pressure = hourly_0["pressure"]; // 1015
+ hour[h].humidity = hourly_0["humidity"]; // 31
+ hour[h].dewPoint = hourly_0["dew_point"]; // 19.2
+ hour[h].cloudCover = hourly_0["clouds"]; // 20
+ hour[h].visibility = hourly_0["visibility"]; // 10000
+ hour[h].windSpeed = hourly_0["wind_speed"]; // 22.77
+ hour[h].windBearing = hourly_0["wind_deg"]; // 300
+
+
+ if(hourly_0["snow"])
+ {
+ if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
+ {
+ float temp = hourly_0["snow"];
+ hour[h].snowVolume = (temp/25.4); // 95
+ }
+ else
+ hour[h].snowVolume = hourly_0["snow"]; // 95
+
+ }
+ else
+ hour[h].snowVolume = 0;
+
+
+ if(hourly_0["rain"])
+ {
+ if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
+ {
+ float temp = hourly_0["rain"];
+ hour[h].rainVolume = (temp/25.4); // 95
+ }
+ else
+ hour[h].rainVolume = hourly_0["rain"]; // 95
+
+ }
+ else
+ hour[h].rainVolume = 0;
+
+
+ JsonObject hourly_0_weather_0 = hourly_0["weather"][0];
+ hour[h].id = hourly_0_weather_0["id"]; // 801
+
+ if(hourly_0_weather_0["main"])
+ {
+ hour[h].main = (char *)realloc(hour[h].main,sizeof(char) * strlen(hourly_0_weather_0["main"])+1);
+ if(hour[h].main == NULL)
+ {
+ return 23;
+ }
+ strncpy(hour[h].main,hourly_0_weather_0["main"],strlen(hourly_0_weather_0["main"])+1);
+ }
+
+ if(hourly_0_weather_0["description"])
+ {
+ hour[h].summary = (char *)realloc(hour[h].summary,sizeof(char) * strlen(hourly_0_weather_0["description"])+1);
+ if(hour[h].summary == NULL)
+ {
+ return 23;
+ }
+ strncpy(hour[h].summary,hourly_0_weather_0["description"],strlen(hourly_0_weather_0["description"])+1);
+ }
+
+
+ strncpy(hour[h].icon,hourly_0_weather_0["icon"],strlen(hourly_0_weather_0["icon"])+1);
+
+ hour[h].pop = hourly_0["pop"]; // 0
+ }
+ }
+ }
+
+
+ if(exclude.minutely)
+ {
+ OpenWeatherOneCall::freeMinuteMem();
+ }
+ else
+ {
+ if(doc["minutely"])
+ {
+ if(!minute)
+ {
+ minute = (struct MINUTELY *)calloc(61, sizeof(struct MINUTELY));
+ if(minute == NULL)
+ {
+ return 23;
+ }
+ }
+
+
+ JsonArray minutely = doc["minutely"];
+
+ for(int x = 0; x<61; x++)
+ {
+ minute[x].dayTime = minutely[x]["dt"];
+ long tempTime = minutely[x]["dt"];
+ //tempTime += -3600;
+ dateTimeConversion(tempTime,minute[x].readableTime,USER_PARAM.OPEN_WEATHER_DATEFORMAT+4);
+
+ minute[x].precipitation = minutely[x]["precipitation"]; // 0
+ }
+ }
+ }
+
+ http.end();
+ return 0;
+}
+
+int OpenWeatherOneCall::createOverview()
+{
+
+ // NTP Server
+ const char* ntpServer = "pool.ntp.org";
+ const long gmtOffset_sec = myTimeZone * 3600;
+ const int daylightOffset_sec = 3600;
+ char timeString[11];
+ int maxRetries = 10;
+
+ // Initialize NTP
+ configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
+
+ // Get current time
+ struct tm timeinfo;
+
+ int retries = 0;
+ while (!getLocalTime(&timeinfo) && retries < maxRetries)
+ {
+ Serial.println("Failed to obtain time, retrying...");
+ delay(2000); // Wait 2 seconds before retrying
+ retries++;
+ }
+ if (retries == maxRetries)
+ {
+ Serial.println("Failed to obtain time after maximum retries");
+ return false;
+ }
+ Serial.println("Time obtained successfully");
+
+ if(strcmp(overviewDate,"TODAY")==0)
+ {
+ strftime(timeString, sizeof(timeString), "%Y-%m-%d", &timeinfo);
+ }
+ else if(strcmp(overviewDate,"TOMORROW")==0)
+ {
+ // Add 1 to the day
+ timeinfo.tm_mday += 1;
+
+ // Normalize the time structure (this handles overflow of days, months, etc.)
+ mktime(&timeinfo);
+ strftime(timeString, sizeof(timeString), "%Y-%m-%d", &timeinfo);
+ }
+
+ char overviewURL[200] = {0};
+
+ sprintf(overviewURL,"%s?lat=%.6f&lon=%.6f%s%s&units=%s%s%s",OV_URL1,USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE,OV_URL2,timeString,OV_units,DS_URL3,USER_PARAM.OPEN_WEATHER_DKEY);
+
+ //printf("\n%s\n",overviewURL);
+
+ HTTPClient http;
+ http.begin(overviewURL);
+ int httpCode = http.GET();
+
+ if (httpCode > 399)
+ {
+ if(httpCode == 401)
+ {
+ http.end();
+ return 22;
+ }
+ else
+ {
+ http.end();
+ return 21;
+ }
+ }
+
+ if(!overView)
+ {
+ overView = (struct OVERVIEW *)calloc(1,sizeof(struct OVERVIEW));
+ if(overView == NULL)
+ {
+ return 23;
+ }
+ }
+ // String input;
+
+ JsonDocument doc;
+
+ DeserializationError error = deserializeJson(doc, http.getString());
+
+ if (error)
+ {
+ Serial.print("deserializeJson() failed: ");
+ Serial.println(error.c_str());
+ return 22;
+ }
+
+
+ double lat = doc["lat"]; // 39.953701
+ double lon = doc["lon"]; // -74.197899
+ const char* tz = doc["tz"]; // "-04:00"
+ const char* date = doc["date"]; // "2024-08-06"
+ const char* units = doc["units"]; // "imperial"
+ const char* weather_overview = doc["weather_overview"]; // "The current weather in our area is overcast ..
+
+
+ overView->lat = doc["lat"]; // 39.953701
+ overView->lon = doc["lon"]; // -74.197899
+ overView->tz = strdup(doc["tz"]); // "-04:00"
+ overView->date = strdup(doc["date"]); // "2024-08-06"
+ overView->units = strdup(doc["units"]); // "imperial"
+ overView->weather_overview = strdup(doc["weather_overview"]);
+
+
+ http.end();
+ return 0;
+}
+
+void OpenWeatherOneCall::allocateAndCopy(char** destination, const char* source)
+{
+ *destination = (char*)malloc(strlen(source) + 1); // Allocate memory
+ if (*destination != NULL)
+ {
+ strcpy(*destination, source); // Copy the string
+ }
+}
+
+int OpenWeatherOneCall::setLatLon(float _LAT, float _LON)
+{
+ int error_code = 0;
+
+ if(abs(_LAT) <= 90)
+ {
+ USER_PARAM.OPEN_WEATHER_LATITUDE = _LAT;
+ location.LATITUDE = _LAT; //User copy
+ }
+ else
+ error_code += 1;
+
+ if(abs(_LON) <= 180)
+ {
+ USER_PARAM.OPEN_WEATHER_LONGITUDE = _LON;
+ location.LONGITUDE = _LON; //User copy
+ }
+ else
+ error_code += 2;
+
+ if(error_code)
+ return error_code;
+
+ return (EXIT_SUCCESS);
+}
+
+int OpenWeatherOneCall::setLatLon(int _CITY_ID)
+{
+ int error_code = 0;
+
+ char cityURL[110];
+ char* URL1 = "http://api.openweathermap.org/data/3.0/weather?id=";
+ char* URL2 = "&appid=";
+
+ sprintf(cityURL,"%s%d%s%s",URL1,_CITY_ID,URL2,USER_PARAM.OPEN_WEATHER_DKEY);
+ error_code = OpenWeatherOneCall::parseCityCoordinates(cityURL);
+ if(error_code)
+ return error_code;
+
+ return (EXIT_SUCCESS);
+
+}
+
+int OpenWeatherOneCall::setLatLon(void)
+{
+ // IP address of NON-CELLULAR WiFi. Hotspots won't work properly.
+
+ int error_code = 0;
+ error_code = OpenWeatherOneCall::getIPLocation();
+ if(error_code)
+ {
+ return error_code;
+ }
+
+ error_code = OpenWeatherOneCall::getIPAPILocation(_ipapiURL);
+ if(error_code)
+ {
+ return error_code;
+ }
+
+ return 0;
+}
+
+
+int OpenWeatherOneCall::parseCityCoordinates(char* CTY_URL)
+{
+ int error_code = 0;
+
+ HTTPClient http;
+ http.begin(CTY_URL);
+ int httpCode = http.GET();
+ if(httpCode > 399)
+ {
+ if(httpCode == 404)
+ {
+ http.end();
+ error_code += 4;
+ return error_code;
+ }
+ else
+ {
+ http.end();
+ error_code += 5;
+ return error_code;
+ }
+
+ }
+
+ const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(13) + 270;
+ JsonDocument doc;
+
+ deserializeJson(doc, http.getString());
+
+ if(doc["coord"]["lon"])
+ {
+ USER_PARAM.OPEN_WEATHER_LONGITUDE = doc["coord"]["lon"]; // -74.2
+ }
+ else
+ {
+ error_code += 6;
+ }
+
+ if(doc["coord"]["lat"])
+ {
+ USER_PARAM.OPEN_WEATHER_LATITUDE = doc["coord"]["lat"]; // 39.95
+ }
+ else
+ {
+ error_code += 7;
+ }
+
+ http.end();
+
+ if(error_code)
+ {
+ http.end();
+ return error_code;
+ }
+
+ return (EXIT_SUCCESS);
+}
+
+int OpenWeatherOneCall::getIPLocation()
+{
+ int error_code = 0;
+
+ HTTPClient http;
+ http.begin("https://api64.ipify.org/ HTTP/1.1\r\nHost: api.ipify.org\r\n\r\n");
+
+ int httpCode = http.GET();
+
+ if(httpCode > 399)
+ {
+ if(httpCode == 404)
+ {
+ http.end();
+ error_code += 8;
+ return error_code;
+ }
+ else
+ {
+ http.end();
+ error_code += 9;
+ return error_code;
+ }
+
+ }
+
+ int streamSize = http.getSize();
+ char stringVarout[streamSize+1];
+
+ String stringVarin = http.getString();
+ strcpy(stringVarout,stringVarin.c_str());
+
+ http.end();
+
+ strncpy(_ipapiURL,"https://ipapi.co/",38);
+ strcat(_ipapiURL,stringVarout);
+ strcat(_ipapiURL,"/json/");
+
+ return 0;
+}
+
+
+int OpenWeatherOneCall::getIPAPILocation(char* URL)
+{
+ int error_code = 0;
+ HTTPClient http;
+ http.begin(URL);
+ int ipapi_httpCode = http.GET();
+
+ if(ipapi_httpCode > 399)
+ {
+ if(ipapi_httpCode == 404)
+ {
+ error_code += 10;
+ }
+ else
+ {
+ error_code += 11;
+ }
+ http.end();
+ return error_code;
+ }
+
+
+ const size_t capacity = JSON_OBJECT_SIZE(26) + 490;
+ JsonDocument doc;
+
+ deserializeJson(doc, http.getString());
+
+ strncpy(location.CITY,doc["city"],60); // "Lakewood"
+ strncpy(location.STATE,doc["region_code"],2); // "NJ"
+ strncpy(location.COUNTRY,doc["country_code"],3); // "US"
+ USER_PARAM.OPEN_WEATHER_LATITUDE = doc["latitude"]; // 40.0881
+ location.LATITUDE = doc["latitude"];
+ USER_PARAM.OPEN_WEATHER_LONGITUDE = doc["longitude"]; // -74.1963
+ location.LONGITUDE = doc["longitude"];
+
+ http.end();
+
+ return 0;
+}
+
+
+void OpenWeatherOneCall::initAPI(void)
+{
+ memset(USER_PARAM.OPEN_WEATHER_DKEY,NULL,1);
+ USER_PARAM.OPEN_WEATHER_LATITUDE = 0.0;
+ USER_PARAM.OPEN_WEATHER_LONGITUDE = 0.0;
+ USER_PARAM.OPEN_WEATHER_UNITS = 2;
+ USER_PARAM.OPEN_WEATHER_EXCLUDES = 0;
+ USER_PARAM.OPEN_WEATHER_TIMESTAMP = 0;
+ strcpy(USER_PARAM.OPEN_WEATHER_TIMESTAMP_DATE,"01/01/1979");
+ USER_PARAM.OPEN_WEATHER_OVERVIEW = 0;
+ strcpy(USER_PARAM.OPEN_WEATHER_OVERVIEW_DATE,"0000-00-00");
+
+
+}
+
+
+int OpenWeatherOneCall::getLocationInfo()
+{
+ int error_code = 0;
- if(ALERTS_0["start"])
- {
- long tempTime = ALERTS_0["start"];
- alert[x].alertStart = tempTime;
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,alert[x].startInfo,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
- }
+ char locationURL[200];
- if(ALERTS_0["end"])
- {
- long tempTime = ALERTS_0["end"];
- alert[x].alertEnd = tempTime;
- tempTime += location.timezoneOffset;
- dateTimeConversion(tempTime,alert[x].endInfo,USER_PARAM.OPEN_WEATHER_DATEFORMAT);
- }
+ sprintf(locationURL,"https://api.bigdatacloud.net/data/reverse-geocode-client/?latitude=%f&longitude=%f",USER_PARAM.OPEN_WEATHER_LATITUDE,USER_PARAM.OPEN_WEATHER_LONGITUDE);
- if(ALERTS_0["description"])
- {
- alert[x].summary = (char *)realloc(alert[x].summary,sizeof(char) * strlen(ALERTS_0["description"])+1);
- if(alert[x].summary == NULL)
- {
- return 23;
- }
- strncpy(alert[x].summary,ALERTS_0["description"],strlen(ALERTS_0["description"])+1);
- }
- } //end for
+ HTTPClient http;
+ http.begin(locationURL);
- }else{
- // If alerts are not excluded but there are none, reset NUM_MAX_ALERTS and MEMORY
- MAX_NUM_ALERTS = 0;
- OpenWeatherOneCall::freeAlertMem();
+ int httpCode = http.GET();
+ if (httpCode > 399)
+ {
+ if(httpCode == 401)
+ {
+ error_code = 17;
+ }
+ else if(httpCode == 404)
+ {
+ error_code = 18;
}
+ else
+ error_code = 19;
+
+ http.end();
+ return error_code;
}
- if(exclude.hourly)
+ JsonDocument doc;
+
+ deserializeJson(doc, http.getString());
+
+ if(doc["locality"])
{
- OpenWeatherOneCall::freeHourMem();
+ strncpy(location.CITY,doc["locality"],60);
}
else
{
- if(doc["hourly"])
- {
- if(!hour)
- {
- hour = (struct HOURLY *)calloc(48, sizeof(struct HOURLY));
- if(hour == NULL)
- {
- return 23;
- }
- }
+ http.end();
+ return 20;
+ }
- JsonArray hourly = doc["hourly"];
- for(int h = 0; h < 48; h++)
- {
+ if(doc["principalSubdivisionCode"])
+ {
+ strncpy(location.STATE,doc["principalSubdivisionCode"],10);
+ }
+ else
+ {
+ http.end();
+ return 20;
+ }
- JsonObject hourly_0 = hourly[h];
- hour[h].dayTime = hourly_0["dt"]; // 1604336400
- hour[h].temperature = hourly_0["temp"]; // 46.58
- hour[h].apparentTemperature = hourly_0["feels_like"]; // 28.54
- hour[h].pressure = hourly_0["pressure"]; // 1015
- hour[h].humidity = hourly_0["humidity"]; // 31
- hour[h].dewPoint = hourly_0["dew_point"]; // 19.2
- hour[h].cloudCover = hourly_0["clouds"]; // 20
- hour[h].visibility = hourly_0["visibility"]; // 10000
- hour[h].windSpeed = hourly_0["wind_speed"]; // 22.77
- hour[h].windBearing = hourly_0["wind_deg"]; // 300
+ if(doc["countryCode"])
+ {
+ strncpy(location.COUNTRY,doc["countryCode"],10);
+ }
+ else
+ {
+ http.end();
+ return 20;
+ }
- if(hourly_0["snow"])
- {
- if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
- {
- float temp = hourly_0["snow"];
- hour[h].snowVolume = (temp/25.4); // 95
- } else hour[h].snowVolume = hourly_0["snow"]; // 95
+ http.end();
- } else hour[h].snowVolume = 0;
+ return 0;
+}
- if(hourly_0["rain"])
- {
- if(USER_PARAM.OPEN_WEATHER_UNITS == 2)
- {
- float temp = hourly_0["rain"];
- hour[h].rainVolume = (temp/25.4); // 95
- } else hour[h].rainVolume = hourly_0["rain"]; // 95
- } else hour[h].rainVolume = 0;
+int OpenWeatherOneCall::createAQ(int sizeCap)
+{
+ char getURL[200] = {0};
+ sprintf(getURL,"%s%.6f%s%.6f%s%s",AQ_URL1,USER_PARAM.OPEN_WEATHER_LATITUDE,AQ_URL2,USER_PARAM.OPEN_WEATHER_LONGITUDE,AQ_URL3,USER_PARAM.OPEN_WEATHER_DKEY);
- JsonObject hourly_0_weather_0 = hourly_0["weather"][0];
- hour[h].id = hourly_0_weather_0["id"]; // 801
+ HTTPClient http;
+ http.begin(getURL);
+ int httpCode = http.GET();
- if(hourly_0_weather_0["main"])
- {
- hour[h].main = (char *)realloc(hour[h].main,sizeof(char) * strlen(hourly_0_weather_0["main"])+1);
- if(hour[h].main == NULL)
- {
- return 23;
- }
- strncpy(hour[h].main,hourly_0_weather_0["main"],strlen(hourly_0_weather_0["main"])+1);
- }
+ if (httpCode > 399)
+ {
+ if(httpCode == 401)
+ {
+ http.end();
+ return 22;
+ }
- if(hourly_0_weather_0["description"])
- {
- hour[h].summary = (char *)realloc(hour[h].summary,sizeof(char) * strlen(hourly_0_weather_0["description"])+1);
- if(hour[h].summary == NULL)
- {
- return 23;
- }
- strncpy(hour[h].summary,hourly_0_weather_0["description"],strlen(hourly_0_weather_0["description"])+1);
- }
+ http.end();
+ return 21;
+ }
- strncpy(hour[h].icon,hourly_0_weather_0["icon"],strlen(hourly_0_weather_0["icon"])+1);
+ const size_t capacity = sizeCap;
+ JsonDocument doc;
+ deserializeJson(doc, http.getString());
- hour[h].pop = hourly_0["pop"]; // 0
- }
- }
- }
+ quality = (struct airQuality *)calloc(1,sizeof(struct airQuality));
- if(exclude.minutely)
- {
- OpenWeatherOneCall::freeMinuteMem();
- }
- else
- {
- if(doc["minutely"])
- {
- if(!minute)
- {
- minute = (struct MINUTELY *)calloc(61, sizeof(struct MINUTELY));
- if(minute == NULL)
- {
- return 23;
- }
- }
+ float coord_lon = doc["coord"]["lon"]; // -74.1975
+ float coord_lat = doc["coord"]["lat"]; // 39.9533
+ JsonObject list_0 = doc["list"][0];
- JsonArray minutely = doc["minutely"];
+ quality -> aqi = list_0["main"]["aqi"]; // 2
- for(int x = 0; x<61; x++)
- {
- minute[x].dayTime = minutely[x]["dt"];
- minute[x].precipitation = minutely[x]["precipitation"]; // 0
- }
- }
- }
+ JsonObject list_0_components = list_0["components"];
+ quality -> co = list_0_components["co"]; // 230.31
+ quality -> no = list_0_components["no"]; // 0.43
+ quality -> no2 = list_0_components["no2"]; // 1.69
+ quality -> o3 = list_0_components["o3"]; // 100.14
+ quality -> so2 = list_0_components["so2"]; // 0.88
+ quality -> pm2_5 = list_0_components["pm2_5"]; // 0.76
+ quality -> pm10 = list_0_components["pm10"]; // 1.04
+ quality -> nh3 = list_0_components["nh3"]; // 0.43
+
+ quality -> dayTime = list_0["dt"]; // 1615838400
http.end();
return 0;
+
+
}
+
int OpenWeatherOneCall::setExcludes(int EXCL)
{
unsigned int excludeMemSize = 32768;
@@ -1286,27 +1373,54 @@ int OpenWeatherOneCall::setUnits(int _UNIT)
{
case 3:
strcpy(units,"STANDARD");
+ strcpy(OV_units,"standard");
break;
case 2:
strcpy(units,"IMPERIAL");
+ strcpy(OV_units,"imperial");
break;
case 1:
strcpy(units,"METRIC");
+ strcpy(OV_units,"metric");
break;
default :
strcpy(units,"IMPERIAL");
+ strcpy(OV_units,"imperial");
}
return 0;
}
-int OpenWeatherOneCall::setHistory(int _HIS)
+int OpenWeatherOneCall::setCurrent(int _CUR)
{
- if(_HIS > 5)
- {
- return 16;
- }
- USER_PARAM.OPEN_WEATHER_HISTORY = _HIS;
+
+ USER_PARAM.OPEN_WEATHER_CURRENT = _CUR;
+
+ return 0;
+}
+
+int OpenWeatherOneCall::setAirQuality(int _AQ)
+{
+
+ USER_PARAM.OPEN_WEATHER_AIRQUALITY = _AQ;
+
+ return 0;
+}
+
+int OpenWeatherOneCall::setTimestamp(int _HIS, const char* _DATE)
+{
+
+ USER_PARAM.OPEN_WEATHER_TIMESTAMP = _HIS;
+ strcpy(USER_PARAM.OPEN_WEATHER_TIMESTAMP_DATE,_DATE);
+
+ return 0;
+}
+
+int OpenWeatherOneCall::setOverview(int _OVR, const char* _DATE)
+{
+
+ USER_PARAM.OPEN_WEATHER_OVERVIEW = _OVR;
+ strcpy(USER_PARAM.OPEN_WEATHER_OVERVIEW_DATE,_DATE);
return 0;
}
@@ -1319,6 +1433,66 @@ int OpenWeatherOneCall::setDateTimeFormat(int _DTF)
return 0;
}
+// Takes any Human Readable date converts to epoch
+long OpenWeatherOneCall::extractDate(char str[11])
+{
+
+ long epochH;
+
+ struct tm t;
+ time_t t_of_day;
+
+ char m[3];
+ char d[3];
+ char yr[5];
+
+
+ for(int x=0; x<10; x++)
+ {
+ if(isDigit(str[x]))
+ {
+ if(x<2)
+ {
+ m[x] = str[x];
+ }
+ else
+ {
+ if(x<5)
+ {
+ d[x-3] = str[x];
+ }
+ else
+ {
+ if(x<10)
+ {
+ yr[x-6] = str[x];
+ }
+ }
+ }
+ }
+ }
+
+ String month = String(m);
+ String day = String(d);
+ String year = String(yr);
+
+
+ t.tm_year = year.toInt()-1900; // Year - 1900
+ t.tm_mon = month.toInt() - 1; // Month, where 0 = jan
+ t.tm_mday = day.toInt(); // Day of the month
+ t.tm_hour = 00;
+ t.tm_min = 00;
+ t.tm_sec = 00;
+ t.tm_isdst = -1; // Is DST on? 1 = yes, 0 = no, -1 = unknown
+ t_of_day = mktime(&t);
+
+
+ return (long) t_of_day;
+
+}
+
+
+
// free routines
void OpenWeatherOneCall::freeCurrentMem(void)
@@ -1352,19 +1526,19 @@ void OpenWeatherOneCall::freeForecastMem(void)
}
-void OpenWeatherOneCall::freeHistoryMem(void)
+void OpenWeatherOneCall::freeTimestampMem(void)
{
- if(history)
+ if(timestamp)
{
for( int x = 24; x > 0; x--)
{
- free(history[x-1].summary);
- free(history[x-1].main);
+ free(timestamp[x-1].summary);
+ free(timestamp[x-1].main);
}
- free(history);
- history[0].summary = NULL;
- history[0].main = NULL;
- history = NULL;
+ free(timestamp);
+ timestamp[0].summary = NULL;
+ timestamp[0].main = NULL;
+ timestamp = NULL;
}
}
@@ -1375,9 +1549,9 @@ void OpenWeatherOneCall::freeAlertMem(void)
{
for( int x = MAX_NUM_ALERTS; x > 0; x--)
{
- free(alert[x].senderName);
- free(alert[x].event);
- free(alert[x].summary);
+ free(alert[x].senderName);
+ free(alert[x].event);
+ free(alert[x].summary);
}
free(alert);
alert[0].senderName = NULL;
@@ -1421,6 +1595,15 @@ void OpenWeatherOneCall::freeQualityMem(void)
}
}
+void OpenWeatherOneCall::freeOverviewMem(void)
+{
+ if (overView)
+ {
+ free(overView);
+ overView = NULL;
+ }
+}
+
char* OpenWeatherOneCall::getErrorMsgs(int _errMsg)
{
if((_errMsg > SIZEOF(errorMsgs)) || (_errMsg < 1))
@@ -1438,8 +1621,9 @@ OpenWeatherOneCall::~OpenWeatherOneCall()
OpenWeatherOneCall::freeAlertMem();
OpenWeatherOneCall::freeHourMem();
OpenWeatherOneCall::freeMinuteMem();
- OpenWeatherOneCall::freeHistoryMem();
+ OpenWeatherOneCall::freeTimestampMem();
OpenWeatherOneCall::freeQualityMem();
+ OpenWeatherOneCall::freeOverviewMem();
}
diff --git a/src/OpenWeatherOneCall.h b/src/OpenWeatherOneCall.h
index 3e9decf..678dc94 100644
--- a/src/OpenWeatherOneCall.h
+++ b/src/OpenWeatherOneCall.h
@@ -1,10 +1,10 @@
/*
OpenWeatherOnecall.h
- Upgrade v3.3.3
+ Upgrade v4.0
copyright 2020 - Jessica Hershey
www.github.com/jHershey69
- WEATHER: Current, hourly, minutely, 8 day future, 5 day history
+ WEATHER: Current, hourly, minutely, 8 day future, 45 year history, Air quality, Summary for today and tomorrow
REQUIRES: OpenWeatherMap.com API key
INQUIRE BY: Latitude/Longitude, IP Address, CITY ID
@@ -17,11 +17,18 @@
#include // Required but installed in the ESP32
-#include // Version 6 Required
+#include
+#include "ESP32Time.h"
+
+#include "globals.h"
+
+#include // Version 7 Required
#include
#include
#include
+#include
#include "errMsgs.h"
+#include "globals.h"
@@ -58,6 +65,11 @@ class OpenWeatherOneCall
//Methods
int parseWeather(void);
+ long extractDate(char str[11]);
+ void allocateAndCopy(char** destination, const char* source);
+
+
+
void initAPI(void);
int setOpenWeatherKey(char* owKey);
@@ -66,7 +78,8 @@ class OpenWeatherOneCall
int setLatLon(void);
int setExcl(int _EXCL);
int setUnits(int _UNIT);
- int setHistory(int _HIS);
+ int setTimestamp(int _HIS, const char* _DATE);
+ int setOverview(int _OVR, const char* _DATE);
int setDateTimeFormat(int _DTF);
char* getErrorMsgs(int errorMsg);
char* nextLanguage(char * shrtPtr, char* lngPtr, int _langNum);
@@ -75,9 +88,6 @@ class OpenWeatherOneCall
- //Legacy Method
- int parseWeather(char* DKEY, char* GKEY, float SEEK_LATITUDE, float SEEK_LONGITUDE, bool SET_UNITS, int CITY_ID, int API_EXCLUDES, int GET_HISTORY);
-
//Destructor
~OpenWeatherOneCall();
@@ -186,6 +196,7 @@ class OpenWeatherOneCall
struct HOURLY
{
long dayTime; // 1604336400
+ char readableTime[5];
float temperature; // 46.58
float apparentTemperature; // 28.54
float pressure; // 1015
@@ -210,41 +221,53 @@ class OpenWeatherOneCall
struct MINUTELY
{
long dayTime; // 1604341320
+ char readableTime[5];
float precipitation; // 0
} *minute = NULL; //[61]
struct ALERTS
{
- char* senderName; //[30] = "No Alert"; // "NWS Philadelphia - Mount Holly (New Jersey, Delaware, Southeastern Pennsylvania)"
- char* event; //[50] = "No Event"; // "Gale Watch"
- long alertStart; // 1604271600
+ char* senderName; //[30] = "No Alert"; // "NWS Philadelphia - Mount Holly (New Jersey, Delaware, Southeastern Pennsylvania)"
+ char* event; //[50] = "No Event"; // "Gale Watch"
+ long alertStart; // 1604271600
char startInfo[20];
long alertEnd;
char endInfo[20];
char *summary;
} *alert = NULL;
+ struct OVERVIEW
+ {
+ double lat; // 39.953701
+ double lon; // -74.197899
+ const char* tz; // "-04:00"
+ const char* date; // "2024-08-06"
+ const char* units; // "imperial"
+ const char* weather_overview;
+ } *overView = NULL;
+
- struct HISTORICAL
+ struct TIMESTAMP
{
+ long hepoch = NULL;
char weekDayName[4];
- long dayTime; // 1604242490
+ long dayTime; // 1604242490
char readableDateTime[20];
- long sunrise; // 1604230151
+ long sunrise; // 1604230151
char readableSunrise[5];
- long sunset; // 1604267932
+ long sunset; // 1604267932
char readableSunset[5];
- float temperature; // 285.9
+ float temperature; // 285.9
float apparentTemperature; // 283.42
- float pressure; // 1016
- float humidity; // 76
- float dewPoint; // 281.78
- float uvIndex; // 3.1
- float cloudCover; // 90
- float visibility; // 16093
- float windSpeed; // 3.1
- float windBearing; // 160
+ float pressure; // 1016
+ float humidity; // 76
+ float dewPoint; // 281.78
+ float uvIndex; // 3.1
+ float cloudCover; // 90
+ float visibility; // 16093
+ float windSpeed; // 3.1
+ float windBearing; // 160
float windGust;
float rainVolume;
float snowVolume;
@@ -253,7 +276,7 @@ class OpenWeatherOneCall
char* summary; // "overcast clouds"
char icon[4]; // "04d"
- } *history = NULL; //[25]
+ } *timestamp = NULL; //[25]
const char* short_names[7] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
char buffer[40];
@@ -267,19 +290,25 @@ class OpenWeatherOneCall
int parseCityCoordinates(char* CTY_URL);
int getIPLocation();
int getIPAPILocation(char* URL);
- int createHistory(void);
+ int createTimestamp(void);
+ int createOverview(void);
int createCurrent(int);
int setExcludes(int EXCL);
int getLocationInfo();
int createAQ(int);
+ int setCurrent(int _CUR);
+ int setAirQuality(int _AQ);
void freeCurrentMem(void);
void freeForecastMem(void);
void freeAlertMem(void);
void freeHourMem(void);
void freeMinuteMem(void);
- void freeHistoryMem(void);
+ void freeTimestampMem(void);
void freeQualityMem(void);
+ void freeOverviewMem(void);
+
+
//Variables
// For eventual struct calls
@@ -292,11 +321,18 @@ class OpenWeatherOneCall
float OPEN_WEATHER_LONGITUDE = NULL;
int OPEN_WEATHER_UNITS = 2;
int OPEN_WEATHER_EXCLUDES = NULL;
- int OPEN_WEATHER_HISTORY = NULL;
+ int OPEN_WEATHER_TIMESTAMP = NULL;
+ char OPEN_WEATHER_TIMESTAMP_DATE[11] = "01/01/2024";
+ int OPEN_WEATHER_OVERVIEW = NULL;
+ char OPEN_WEATHER_OVERVIEW_DATE[11];
+ int OPEN_WEATHER_CURRENT = NULL;
+ int OPEN_WEATHER_AIRQUALITY = NULL;
} USER_PARAM;
char units[10] = "IMPERIAL";
+ char OV_units[10] = "imperial";
+
char _ipapiURL[38];
int summary_len = 0;
diff --git a/src/globals.h b/src/globals.h
new file mode 100644
index 0000000..d93fce5
--- /dev/null
+++ b/src/globals.h
@@ -0,0 +1,25 @@
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+//DO NOT EDIT THIS FILE ******************************
+// OpenWeatherOneCall v4.0.0
+
+
+extern float myLATITUDE;
+extern float myLONGITUDE;
+extern int myCITYID;
+extern int myUNITS;
+extern int myDTF;
+extern int myTimeZone;
+extern int myTIMESTAMP;
+extern int myCURRENT;
+extern int myAIRQUALITY;
+extern char timestampDate[11];
+extern int myOVERVIEW;
+extern char overviewDate[9];
+extern int myEXCLUDES;
+extern char ONECALLKEY[];
+extern int locationMode;
+
+
+#endif // GLOBALS_H