Need help modifying JSON Parser

I am very new, and there is much I don't know...

I have a ESP8266 with a ILI9341 TFT display. I am using it to pull AccuWeather data and then displaying it.

I am using this AccuWeather JSON parser - GitHub - jackmax/AccuWeatherLibrary: AccuWeather API library for ESP8266 Arduino
Use this link to see unmodified files, and also an example ino file that uses the library.

Everything is working so far, for example I get Temps for the five next days, TotalLiquids(rain), Icons, Headline Text, and others. But I am trying to get the AirAndPollen AirQuality value, which should give a number around 76, but I only get zero. I do not get any errors with the library mods that are attached. To find my changes, do a search for "air".

So I must not have modified the AccuWeatherLibrary.cpp and/or AccuWeatherLibrary.h files correctly. Ironically, the library author said, "It does not retrieve all data that AccuWeather has to offer (e.g. past data or air quality info). It should be relatively easy to add this functionality if someone wants it."

I have spent a week on this, trying different things, but I can't get it.

Attached are the two library files and the AccuWeather JSON file. As mentioned, best to search for "air" to find my attempted changes, and in the JSON search for "AirAndPollen".

Thanks for any help, as I feel like giving up, as everything else is working fine.

PS I hope I don't need to provide my main code, as it is very long, so it would need to be parred down and personal information would need to be removed. As I said, all other data is working fine, just not my mods to the library.

AccuWeatherLibrary.h:

#pragma once

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include "JsonStreamingParser.h"
#include "JsonListener.h"
#include <limits.h>
#include <vector>
#include <map>

//Structures for holding the received data
typedef struct {
    String LocalObservationDateTime;
    String UVIndexText;
    String WeatherText;
    float CloudCover;
    float Pressure;
    float RealFeelTemperature;
    float RealFeelTemperatureShade;
    float RelativeHumidity;
    float Temperature;
    float Visibility;
    float WindGustSpeed;
    float WindSpeed;
    int EpochTime;
    int16_t WindDirection;
    uint8_t IsDayTime;
    uint8_t UVIndex;
    uint8_t WeatherIcon;
}
AccuweatherCurrentData;

typedef struct {
    String DateTime;
    String IconPhrase;
    String UVIndexText;
    float Ice;
    float Rain;
    float RealFeelTemperature;
    float RelativeHumidity;
    float Snow;
    float Temperature;
    float TotalLiquid;
    float Visibility;
    float WindGustSpeed;
    float WindSpeed;
    int EpochDateTime;
    int16_t WindDirection;
    uint8_t CloudCover;
    uint8_t IceProbability;
    uint8_t IsDaylight;
    uint8_t PrecipitationProbability;
    uint8_t RainProbability;
    uint8_t SnowProbability;
    uint8_t UVIndex;
    uint8_t WeatherIcon;
}
AccuweatherHourlyData;

typedef struct {
    String IconPhrase;
    String LongPhrase;
    float Ice;
    float Rain;
    float RelativeHumidity;
    float Snow;
    float TotalLiquid;
    float Visibility;
    float WindGustSpeed;
    float WindSpeed;
    int16_t WindDirection;
    uint8_t AirAndPollen;
    uint8_t CloudCover;
    uint8_t IceProbability;
    uint8_t PrecipitationProbability;
    uint8_t RainProbability;
    uint8_t SnowProbability;
    uint8_t ThunderstormProbability;
    uint8_t WeatherIcon;
    String Text;  //Headline Text
}
AccuweatherForecastData;

typedef struct {
    String Date;
    float TempMin;
    float TempMax;
    float RealFeelTempMin;
    float RealFeelTempMax;
    float HoursOfSun;
    int EpochDate;
    int SunRise;
    int SunSet; 
    uint8_t AirAndPollen;   
    String Text;  //Headline Text
    AccuweatherForecastData Day;
    AccuweatherForecastData Night;
}
AccuweatherDailyData;

//Tokens for different keys that can be found in the JSON responses
//To extend the functionality, first a new token should be added to the enum below,
//then a mapping from String to that token in the map.
enum AccuParserTokens_ {
    ACCUPARSER0,
    ACCUPARSERUnknown,
    //ACCUPARSERAirQuality,
    ACCUPARSERAirAndPollen,
    ACCUPARSERBase,
    ACCUPARSERList,
    ACCUPARSERObject,
    ACCUPARSERUnitPlaceholder,
    ACCUPARSERCloudCover,
    ACCUPARSERDateTime,
    ACCUPARSERDay,
    ACCUPARSERDegrees,
    ACCUPARSERDirection,
    ACCUPARSEREnglish,
    ACCUPARSEREpochDateTime,
    ACCUPARSEREpochRise,
    ACCUPARSEREpochSet,
    ACCUPARSEREpochTime,
    ACCUPARSERHoursOfSun,
    ACCUPARSERIce,
    ACCUPARSERIceProbability,
    ACCUPARSERIconPhrase,
    ACCUPARSERImperial,
    ACCUPARSERIsDaylight,
    ACCUPARSERIsDayTime,
    ACCUPARSERLocalObservationDateTime,
    ACCUPARSERLongPhrase,
    ACCUPARSERMaximum,
    ACCUPARSERMetric,
    ACCUPARSERMinimum,
    ACCUPARSERMoon,
    ACCUPARSERNight,
    ACCUPARSERPrecip1hr,
    ACCUPARSERPrecipitationProbability,
    ACCUPARSERPressure,
    ACCUPARSERRain,
    ACCUPARSERRainProbability,
    ACCUPARSERRealFeelTemperature,
    ACCUPARSERRealFeelTemperatureShade,
    ACCUPARSERRelativeHumidity,
    ACCUPARSERShortPhrase,
    ACCUPARSERSnow,
    ACCUPARSERSnowProbability,
    ACCUPARSERSpeed,
    ACCUPARSERSun,
    ACCUPARSERTemperature,
    ACCUPARSERText,  // Headline text
    ACCUPARSERThunderstormProbability,
    ACCUPARSERTotalLiquid,
    ACCUPARSERUnit,
    ACCUPARSERUVIndex,
    ACCUPARSERUVIndexText,
    ACCUPARSERValue,
    ACCUPARSERVisibility,
    ACCUPARSERWeatherIcon,
    ACCUPARSERWeatherText,
    ACCUPARSERWind,
    ACCUPARSERWindGust,
    ACCUPARSERDailyForecasts,
    ACCUPARSERDate,
    ACCUPARSEREpochDate,
    ACCUPARSERIcon,
    ACCUPARSERHeadline,
};

//I do this to save memory - by default enums are ints, so to save a bit of memory I use chars instead.
//WARNING: change this if you will need more than 256 tokens
typedef uint8_t AccuParserTokens;

//The parser creates a stack while parsing the JSON response. This allows a lot of flexibility when creating a parser (e.g. we can infer all previous keys and objects before a given value)
//The stack contains tokens instead of strings. This greatly reduces memory usage and allows us to compare stack contents much quicker.
class AccuweatherParser: public JsonListener {
    public:
        AccuweatherParser(int maxListLength_, bool isMetric_ = true);
        virtual void whitespace(char c);
        virtual void key(String key);
        virtual void value(String value);
        virtual void startDocument();
        virtual void endDocument();
        virtual void startArray();
        virtual void endArray();
        virtual void startObject();
        virtual void endObject();
        bool isMetric = true;

    protected:
        std::vector<AccuParserTokens> tokenStack;
        void DEBUG_printStack();
        bool stackSuffix(const AccuParserTokens suffix[], int suffix_len);
        bool stackContains(const AccuParserTokens token);
        void popAllKeys();
        int baseListIdx = -1;
        int maxListLength = INT_MAX;
        bool listFull = false;
};

//Descendant classes for parsing particular types of responses
class AccuweatherCurrentParser: public AccuweatherParser {
    public:
        AccuweatherCurrentParser(AccuweatherCurrentData* data_ptr_, bool isMetric_);
        virtual void value(String value);
    protected:
        AccuweatherCurrentData* data_ptr;
};

class AccuweatherHourlyParser: public AccuweatherParser {
    public:
        AccuweatherHourlyParser(AccuweatherHourlyData* data_ptr_, int maxListLength_);
        virtual void value(String value);
    protected:
        AccuweatherHourlyData* data_ptr;
};

class AccuweatherDailyParser: public AccuweatherParser {
    public:
        AccuweatherDailyParser(AccuweatherDailyData* data_ptr_, int maxListLength_);
        virtual void startObject();
        virtual void value(String value);
    protected:
        AccuweatherDailyData* data_ptr;
};

//Main class for sending requests and parsing responses
class Accuweather {
public:
    HTTPClient http;
    JsonStreamingParser parser;
    AccuweatherParser* listener = NULL;
    const int locationID;
    const char* languageID;
    const char* apiKey;
    const bool isMetric;
    int length;

    Accuweather(const char* apiKey_, const int locationID_, const char* languageID_, const bool isMetric_):
        locationID(locationID_),
        languageID(languageID_),
        apiKey(apiKey_),
        isMetric(isMetric_){

    }

    int getCurrent(AccuweatherCurrentData* data_ptr);
    int getHourly(AccuweatherHourlyData* data_ptr, int hours);
    int getDaily(AccuweatherDailyData* data_ptr, int days);
    int continueDownload();
    void freeConnection();
};

AccuWeatherLibrary.cpp:

#include "AccuWeatherLibrary.h"
#include "JsonListener.h"

#define SIZE_OF_CONST_TABLE(x) sizeof(x)/sizeof(x[0])
#define STACK_HAS_SUFFIX(x) stackSuffix(x, SIZE_OF_CONST_TABLE(x))
/*
 *  Parser constants
 */
//A map to transform strings into tokens
static const std::map<const String, const AccuParserTokens> stringToTokenMap = {
    { "0", ACCUPARSER0 },
    //{ "AirQuality", ACCUPARSERAirQuality },
    { "AirAndPollen", ACCUPARSERAirAndPollen },
    { "CloudCover", ACCUPARSERCloudCover },
    { "DailyForecasts", ACCUPARSERDailyForecasts },
    { "Date", ACCUPARSERDate },
    { "DateTime", ACCUPARSERDateTime },
    { "Day", ACCUPARSERDay },
    { "Degrees", ACCUPARSERDegrees },
    { "Direction", ACCUPARSERDirection },
    { "English", ACCUPARSEREnglish },
    { "EpochDate", ACCUPARSEREpochDate },
    { "EpochDateTime", ACCUPARSEREpochDateTime },
    { "EpochRise", ACCUPARSEREpochRise },
    { "EpochSet", ACCUPARSEREpochSet },
    { "EpochTime", ACCUPARSEREpochTime },
    { "Headline", ACCUPARSERHeadline },
    { "HoursOfSun", ACCUPARSERHoursOfSun },
    { "Ice", ACCUPARSERIce },
    { "IceProbability", ACCUPARSERIceProbability },
    { "Icon", ACCUPARSERIcon },
    { "IconPhrase", ACCUPARSERIconPhrase },
    { "Imperial", ACCUPARSERImperial },
    { "IsDaylight", ACCUPARSERIsDaylight },
    { "IsDayTime", ACCUPARSERIsDayTime },
    { "LocalObservationDateTime", ACCUPARSERLocalObservationDateTime },
    { "LongPhrase", ACCUPARSERLongPhrase },
    { "Maximum", ACCUPARSERMaximum },
    { "Metric", ACCUPARSERMetric },
    { "Minimum", ACCUPARSERMinimum },
    { "Moon", ACCUPARSERMoon },
    { "Night", ACCUPARSERNight },
    { "Precip1hr", ACCUPARSERPrecip1hr },
    { "PrecipitationProbability", ACCUPARSERPrecipitationProbability },
    { "Pressure", ACCUPARSERPressure },
    { "Rain", ACCUPARSERRain },
    { "RainProbability", ACCUPARSERRainProbability },
    { "RealFeelTemperature", ACCUPARSERRealFeelTemperature },
    { "RealFeelTemperatureShade", ACCUPARSERRealFeelTemperatureShade },
    { "RelativeHumidity", ACCUPARSERRelativeHumidity },
    { "ShortPhrase", ACCUPARSERShortPhrase },
    { "Snow", ACCUPARSERSnow },
    { "SnowProbability", ACCUPARSERSnowProbability },
    { "Speed", ACCUPARSERSpeed },
    { "Sun", ACCUPARSERSun },
    { "Temperature", ACCUPARSERTemperature },
    { "Text", ACCUPARSERText },  //Headline Text
    { "ThunderstormProbability", ACCUPARSERThunderstormProbability },
    { "TotalLiquid", ACCUPARSERTotalLiquid },
    { "Unit", ACCUPARSERUnit },
    { "UVIndex", ACCUPARSERUVIndex },
    { "UVIndexText", ACCUPARSERUVIndexText },
    { "Value", ACCUPARSERValue },
    { "Visibility", ACCUPARSERVisibility },
    { "WeatherIcon", ACCUPARSERWeatherIcon },
    { "WeatherText", ACCUPARSERWeatherText },
    { "Wind", ACCUPARSERWind },
    { "WindGust", ACCUPARSERWindGust },
};

//Arrays of stack suffixes - if the stack has this suffix, this means that this piece of data can be obtained
static const AccuParserTokens LocalObservationDateTime_suffix[] = {ACCUPARSERLocalObservationDateTime};
static const AccuParserTokens DateTime_suffix[] = {ACCUPARSERDateTime};
static const AccuParserTokens EpochTime_suffix[] = {ACCUPARSEREpochTime};
static const AccuParserTokens EpochDateTime_suffix[] = {ACCUPARSEREpochDateTime};
static const AccuParserTokens Date_suffix[] = {ACCUPARSERDate};
static const AccuParserTokens EpochDate_suffix[] = {ACCUPARSEREpochDate};
static const AccuParserTokens HoursOfSun_suffix[] = {ACCUPARSERHoursOfSun};
static const AccuParserTokens WeatherText_suffix[] = {ACCUPARSERWeatherText};
static const AccuParserTokens WeatherIcon_suffix[] = {ACCUPARSERWeatherIcon};
static const AccuParserTokens Icon_suffix[] = {ACCUPARSERIcon};
static const AccuParserTokens IconPhrase_suffix[] = {ACCUPARSERIconPhrase};
static const AccuParserTokens LongPhrase_suffix[] = {ACCUPARSERLongPhrase};
//static const AccuParserTokens IsDayTime_suffix[] = {ACCUPARSEREpochTime};
static const AccuParserTokens IsDayTime_suffix[] = {ACCUPARSERIsDayTime};
static const AccuParserTokens IsDaylight_suffix[] = {ACCUPARSERIsDaylight};
static const AccuParserTokens RelativeHumidity_suffix[] = {ACCUPARSERRelativeHumidity};
static const AccuParserTokens WindDirection_suffix[] = {ACCUPARSERWind, ACCUPARSERObject, ACCUPARSERDirection, ACCUPARSERObject, ACCUPARSERDegrees};
static const AccuParserTokens UVIndex_suffix[] = {ACCUPARSERUVIndex};
static const AccuParserTokens UVIndexText_suffix[] = {ACCUPARSERUVIndexText};
static const AccuParserTokens CloudCover_suffix[] = {ACCUPARSERCloudCover};

static const AccuParserTokens UnitTemperature_suffix[] = {ACCUPARSERTemperature, ACCUPARSERObject, ACCUPARSERUnitPlaceholder, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens RealFeelUnitTemperature_suffix[] = {ACCUPARSERRealFeelTemperature, ACCUPARSERObject, ACCUPARSERUnitPlaceholder, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens RealFeelUnitTemperatureShade_suffix[] = {ACCUPARSERRealFeelTemperatureShade, ACCUPARSERObject, ACCUPARSERUnitPlaceholder, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens WindUnitSpeed_suffix[] = {ACCUPARSERWind, ACCUPARSERObject, ACCUPARSERSpeed, ACCUPARSERObject, ACCUPARSERUnitPlaceholder, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens WindGustUnitSpeed_suffix[] = {ACCUPARSERWindGust, ACCUPARSERObject, ACCUPARSERSpeed, ACCUPARSERObject, ACCUPARSERUnitPlaceholder, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens UnitPressure_suffix[] = {ACCUPARSERPressure, ACCUPARSERObject, ACCUPARSERUnitPlaceholder, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens UnitVisibility_suffix[] = {ACCUPARSERVisibility, ACCUPARSERObject, ACCUPARSERUnitPlaceholder, ACCUPARSERObject, ACCUPARSERValue};

static const AccuParserTokens Temperature_suffix[] = {ACCUPARSERTemperature, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens RealFeelTemperature_suffix[] = {ACCUPARSERRealFeelTemperature, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens RealFeelTemperatureShade_suffix[] = {ACCUPARSERRealFeelTemperatureShade, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens WindSpeed_suffix[] = {ACCUPARSERWind, ACCUPARSERObject, ACCUPARSERSpeed, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens WindGustSpeed_suffix[] = {ACCUPARSERWindGust, ACCUPARSERObject, ACCUPARSERSpeed, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens Pressure_suffix[] = {ACCUPARSERPressure, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens Visibility_suffix[] = {ACCUPARSERVisibility, ACCUPARSERObject, ACCUPARSERValue};

static const AccuParserTokens TempMin_suffix[] = {ACCUPARSERTemperature, ACCUPARSERObject, ACCUPARSERMinimum, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens RealFeelTempMin_suffix[] = {ACCUPARSERRealFeelTemperature, ACCUPARSERObject, ACCUPARSERMinimum, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens TempMax_suffix[] = {ACCUPARSERTemperature, ACCUPARSERObject, ACCUPARSERMaximum, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens RealFeelTempMax_suffix[] = {ACCUPARSERRealFeelTemperature, ACCUPARSERObject, ACCUPARSERMaximum, ACCUPARSERObject, ACCUPARSERValue};

static const AccuParserTokens SunRise_suffix[] = {ACCUPARSERSun, ACCUPARSERObject, ACCUPARSEREpochRise};
static const AccuParserTokens SunSet_suffix[] = {ACCUPARSERSun, ACCUPARSERObject, ACCUPARSEREpochSet};

static const AccuParserTokens PrecipitationProbability_suffix[] = {ACCUPARSERPrecipitationProbability};
static const AccuParserTokens ThunderstormProbability_suffix[] = {ACCUPARSERThunderstormProbability};
static const AccuParserTokens RainProbability_suffix[] = {ACCUPARSERRainProbability};
static const AccuParserTokens SnowProbability_suffix[] = {ACCUPARSERSnowProbability};
static const AccuParserTokens IceProbability_suffix[] = {ACCUPARSERIceProbability};
static const AccuParserTokens TotalLiquid_suffix[] = {ACCUPARSERTotalLiquid, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens Rain_suffix[] = {ACCUPARSERRain, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens Snow_suffix[] = {ACCUPARSERSnow, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens Ice_suffix[] = {ACCUPARSERIce, ACCUPARSERObject, ACCUPARSERValue};

static const AccuParserTokens baseList_suffix[] = {ACCUPARSERBase, ACCUPARSERList, ACCUPARSERObject};
static const AccuParserTokens dailyForecastsList_suffix[] = {ACCUPARSERBase, ACCUPARSERObject, ACCUPARSERDailyForecasts, ACCUPARSERList, ACCUPARSERObject};

//static const AccuParserTokens Text_suffix[] = {ACCUPARSERText, ACCUPARSERObject, ACCUPARSERValue};
//static const AccuParserTokens Headline_suffix[] = {ACCUPARSERBase, ACCUPARSERObject, ACCUPARSERHeadline, ACCUPARSERList, ACCUPARSERObject};
static const AccuParserTokens Text_suffix[]= {ACCUPARSERText};  //Headline Text
static const AccuParserTokens AirAndPollen_suffix[] = {ACCUPARSERAirAndPollen	, ACCUPARSERObject, ACCUPARSER0, ACCUPARSERObject, ACCUPARSERValue};
static const AccuParserTokens zero_suffix[]= {ACCUPARSER0, ACCUPARSERObject, ACCUPARSERValue};
/*
 *  Parser base class
 */
AccuweatherParser::AccuweatherParser(int maxListLength_, bool isMetric_){
    maxListLength = maxListLength_;
    isMetric = isMetric_;
}

void AccuweatherParser::startDocument() {
    tokenStack.push_back(ACCUPARSERBase);
}

void AccuweatherParser::endDocument() {
  if (tokenStack.back() != ACCUPARSERBase){
        Serial.println(F("ERROR: Document token not found"));
    }
    popAllKeys();
}

void AccuweatherParser::key(String key) {
    if (baseListIdx >= maxListLength){
        return;
    }
    auto it = stringToTokenMap.find(key);
    if (it != stringToTokenMap.end()){
        tokenStack.push_back(it->second);
    }
    else {
        tokenStack.push_back(ACCUPARSERUnknown);
    }
}

void AccuweatherParser::value(String value) {
    if (baseListIdx >= maxListLength){
        return;
    }
    Serial.println("value: " + value);
    popAllKeys();
}

void AccuweatherParser::startArray() {
    tokenStack.push_back(ACCUPARSERList);
}

void AccuweatherParser::endArray() {
    if (tokenStack.back() != ACCUPARSERList){
        Serial.println(F("ERROR: List token not found"));
    }
    tokenStack.pop_back();

    popAllKeys();
}

void AccuweatherParser::startObject() {
    tokenStack.push_back(ACCUPARSERObject);
    if (STACK_HAS_SUFFIX(baseList_suffix)){
        baseListIdx++;
    }
}

void AccuweatherParser::endObject() {
    if (tokenStack.back() != ACCUPARSERObject){
        Serial.println(F("ERROR: Object token not found"));
    }
    tokenStack.pop_back();

    popAllKeys();
}

void AccuweatherParser::whitespace(char c) {
    //ignore
}

//Some housekeeping functions
void AccuweatherParser::DEBUG_printStack(){
    for (auto it = tokenStack.begin() ; it != tokenStack.end(); ++it){
        Serial.print(">");
        Serial.print(*it);
    }
    Serial.println("*");
}

bool AccuweatherParser::stackSuffix(const AccuParserTokens suffix[], int suffix_len){
    int stackSize = tokenStack.size();
    if (stackSize < suffix_len){
        return false;
    }
    if (suffix_len == 0){
        return true;
    }
    do {
        stackSize--;
        suffix_len--;
        if (tokenStack[stackSize] != suffix[suffix_len]){
            if (suffix[suffix_len] == ACCUPARSERUnitPlaceholder){
                if (isMetric){
                    if (tokenStack[stackSize] != ACCUPARSERMetric){
                        return false;
                    }
                }
                else {
                    if (tokenStack[stackSize] != ACCUPARSERImperial){
                        return false;
                    }
                }
            }
            else {
                return false;
            }
        }
    }
    while (stackSize > 0 && suffix_len > 0);
    return true;
}

bool AccuweatherParser::stackContains(const AccuParserTokens token){
    for (auto it = tokenStack.rbegin(); it != tokenStack.rend(); it++)
        if (*it == token)
            return true;
    return false;
}

void AccuweatherParser::popAllKeys(){
    while (!(tokenStack.back() == ACCUPARSERObject || tokenStack.back() == ACCUPARSERList || tokenStack.back() == ACCUPARSERBase)){
        tokenStack.pop_back();
    }
}

/*
 *  Parsers for specific endpoints
 */
AccuweatherCurrentParser::AccuweatherCurrentParser(AccuweatherCurrentData* data_ptr_, bool isMetric_) : AccuweatherParser(1, isMetric_){
    data_ptr = data_ptr_;
}

void AccuweatherCurrentParser::value(String value){
    if (baseListIdx >= maxListLength){
        return;
    }

    if (STACK_HAS_SUFFIX(LocalObservationDateTime_suffix)) {
        data_ptr->LocalObservationDateTime = value;
    }
    else if (STACK_HAS_SUFFIX(EpochTime_suffix)){
        data_ptr->EpochTime = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(WeatherText_suffix)){
        data_ptr->WeatherText = value;
    }
    else if (STACK_HAS_SUFFIX(WeatherIcon_suffix)){
        data_ptr->WeatherIcon = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(IsDayTime_suffix)){
        data_ptr->IsDayTime = value == "true";
    }
    else if (STACK_HAS_SUFFIX(UnitTemperature_suffix)){
        data_ptr->Temperature = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(RealFeelUnitTemperature_suffix)){
        data_ptr->RealFeelTemperature = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(RealFeelUnitTemperatureShade_suffix)){
        data_ptr->RealFeelTemperatureShade = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(RelativeHumidity_suffix)){
        data_ptr->RelativeHumidity = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(WindDirection_suffix)){
        data_ptr->WindDirection = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(WindUnitSpeed_suffix)){
        data_ptr->WindSpeed = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(WindGustUnitSpeed_suffix)){
        data_ptr->WindGustSpeed = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(UVIndex_suffix)){
        data_ptr->UVIndex = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(UVIndexText_suffix)){
        data_ptr->UVIndexText = value;
    }
    else if (STACK_HAS_SUFFIX(UnitVisibility_suffix)){
        data_ptr->Visibility = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(CloudCover_suffix)){
        data_ptr->CloudCover = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(UnitPressure_suffix)){
        data_ptr->Pressure = value.toFloat();
    }
    
    popAllKeys();
}

AccuweatherHourlyParser::AccuweatherHourlyParser(AccuweatherHourlyData* data_ptr_, int maxListLength_) : AccuweatherParser(maxListLength_){
    data_ptr = data_ptr_;
}

void AccuweatherHourlyParser::value(String value){
    if (baseListIdx >= maxListLength){
        return;
    }

    if (STACK_HAS_SUFFIX(DateTime_suffix)) {
        data_ptr[baseListIdx].DateTime = value;
    }
    else if (STACK_HAS_SUFFIX(EpochDateTime_suffix)) {
        data_ptr[baseListIdx].EpochDateTime = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(WeatherIcon_suffix)) {
        data_ptr[baseListIdx].WeatherIcon = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(IconPhrase_suffix)) {
        data_ptr[baseListIdx].IconPhrase = value;
    }
    else if (STACK_HAS_SUFFIX(IsDaylight_suffix)) {
        data_ptr[baseListIdx].IsDaylight = value == "true";
    }
    else if (STACK_HAS_SUFFIX(Temperature_suffix)) {
        data_ptr[baseListIdx].Temperature = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(RealFeelTemperature_suffix)) {
        data_ptr[baseListIdx].RealFeelTemperature = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(WindSpeed_suffix)) {
        data_ptr[baseListIdx].WindSpeed = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(WindDirection_suffix)) {
        data_ptr[baseListIdx].WindDirection = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(WindGustSpeed_suffix)) {
        data_ptr[baseListIdx].WindGustSpeed = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(RelativeHumidity_suffix)) {
        data_ptr[baseListIdx].RelativeHumidity = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(Visibility_suffix)) {
        data_ptr[baseListIdx].Visibility = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(UVIndex_suffix)) {
        data_ptr[baseListIdx].UVIndex = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(UVIndexText_suffix)) {
        data_ptr[baseListIdx].UVIndexText = value;
    }
    else if (STACK_HAS_SUFFIX(PrecipitationProbability_suffix)) {
        data_ptr[baseListIdx].PrecipitationProbability = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(RainProbability_suffix)) {
        data_ptr[baseListIdx].RainProbability = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(SnowProbability_suffix)) {
        data_ptr[baseListIdx].SnowProbability = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(IceProbability_suffix)) {
        data_ptr[baseListIdx].IceProbability = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(TotalLiquid_suffix)) {
        data_ptr[baseListIdx].TotalLiquid = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(Rain_suffix)) {
        data_ptr[baseListIdx].Rain = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(Snow_suffix)) {
        data_ptr[baseListIdx].Snow = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(Ice_suffix)) {
        data_ptr[baseListIdx].Ice = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(CloudCover_suffix)) {
        data_ptr[baseListIdx].CloudCover = value.toInt();
    }

    popAllKeys();
}

AccuweatherDailyParser::AccuweatherDailyParser(AccuweatherDailyData* data_ptr_, int maxListLength_) : AccuweatherParser(maxListLength_){
    data_ptr = data_ptr_;
}

void AccuweatherDailyParser::startObject() {
    tokenStack.push_back(ACCUPARSERObject);
    if (STACK_HAS_SUFFIX(dailyForecastsList_suffix)){
        baseListIdx++;
    }
}

void AccuweatherDailyParser::value(String value){
    if (baseListIdx >= maxListLength){
        return;
    }

    if (STACK_HAS_SUFFIX(Date_suffix)) {
        data_ptr[baseListIdx].Date = value;
    }
    else if (STACK_HAS_SUFFIX(TempMin_suffix)) {
        data_ptr[baseListIdx].TempMin = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(TempMax_suffix)) {
        data_ptr[baseListIdx].TempMax = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(RealFeelTempMin_suffix)) {
        data_ptr[baseListIdx].RealFeelTempMin = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(RealFeelTempMax_suffix)) {
        data_ptr[baseListIdx].RealFeelTempMax = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(HoursOfSun_suffix)) {
        data_ptr[baseListIdx].HoursOfSun = value.toFloat();
    }
    else if (STACK_HAS_SUFFIX(EpochDate_suffix)) {
        data_ptr[baseListIdx].EpochDate = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(SunRise_suffix)) {
        data_ptr[baseListIdx].SunRise = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(SunSet_suffix)) {
        data_ptr[baseListIdx].SunSet = value.toInt();
    }
    else if (STACK_HAS_SUFFIX(IconPhrase_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.IconPhrase = value;
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.IconPhrase = value;

    }
    else if (STACK_HAS_SUFFIX(LongPhrase_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.LongPhrase = value;
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.LongPhrase = value;

    }
    else if (STACK_HAS_SUFFIX(Ice_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.Ice = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.Ice = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(Rain_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.Rain = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.Rain = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(RelativeHumidity_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.RelativeHumidity = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.RelativeHumidity = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(Snow_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.Snow = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.Snow = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(TotalLiquid_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.TotalLiquid = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.TotalLiquid = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(Visibility_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.Visibility = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.Visibility = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(WindGustSpeed_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.WindGustSpeed = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.WindGustSpeed = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(WindSpeed_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.WindSpeed = value.toFloat();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.WindSpeed = value.toFloat();

    }
    else if (STACK_HAS_SUFFIX(WindDirection_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.WindDirection = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.WindDirection = value.toInt();

    }
    else if (STACK_HAS_SUFFIX(CloudCover_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.CloudCover = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.CloudCover = value.toInt();

    }
    else if (STACK_HAS_SUFFIX(IceProbability_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.IceProbability = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.IceProbability = value.toInt();

    }
    else if (STACK_HAS_SUFFIX(PrecipitationProbability_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.PrecipitationProbability = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.PrecipitationProbability = value.toInt();

    }
    else if (STACK_HAS_SUFFIX(RainProbability_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.RainProbability = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.RainProbability = value.toInt();

    }
    else if (STACK_HAS_SUFFIX(SnowProbability_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.SnowProbability = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.SnowProbability = value.toInt();

    }
    else if (STACK_HAS_SUFFIX(ThunderstormProbability_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.ThunderstormProbability = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.ThunderstormProbability = value.toInt();

    }
    else if (STACK_HAS_SUFFIX(Icon_suffix)) {
        if (stackContains(ACCUPARSERDay))
            data_ptr[baseListIdx].Day.WeatherIcon = value.toInt();
        else if (stackContains(ACCUPARSERNight))
            data_ptr[baseListIdx].Night.WeatherIcon = value.toInt();

    }

    //Headline Text
      else if (STACK_HAS_SUFFIX(Text_suffix)) {
        data_ptr[0].Text = value;
    }
    // AirQuality
    //  else if (STACK_HAS_SUFFIX(AirAndPollen_suffix)) {
    //    if (stackContains(ACCUPARSER0))
    //      data_ptr[0].AirAndPollen = value.toInt();
    //}
    else if (STACK_HAS_SUFFIX(zero_suffix)) {
        if (stackContains(ACCUPARSERAirAndPollen))
          data_ptr[0].AirAndPollen = value.toInt();
    }
    

    popAllKeys();
}

/*
 *  Data retrieval over HTTP
 */
static const char currentURLTemplate[] PROGMEM = "http://dataservice.accuweather.com/currentconditions/v1/%d?apikey=%s&details=true&language=%s";
int Accuweather::getCurrent(AccuweatherCurrentData* data_ptr) {
    char url[256];
    snprintf_P(url, 256, currentURLTemplate, locationID, apiKey, languageID);
    WiFiClient client;
    http.begin(client, url);
    int httpCode = http.GET();
    if (httpCode > 0) {
      if (httpCode == HTTP_CODE_OK) {
            length = http.getSize();
            parser.reset();
            listener = new AccuweatherCurrentParser(data_ptr, isMetric);
            parser.setListener(listener);
            return 0;
        }
    }
    //If we got here, we failed to get data
    http.end();
    return httpCode;
}

static const char hourlyURLTemplate[] PROGMEM = "http://dataservice.accuweather.com/forecasts/v1/hourly/%dhour/%d?apikey=%s&details=true&metric=%s&language=%s";
int Accuweather::getHourly(AccuweatherHourlyData* data_ptr, int hours){
    static const int possibleHours[] = {120, 72, 24, 12, 1};
    int hoursToGet = possibleHours[0];
    for (int i = 0; i < SIZE_OF_CONST_TABLE(possibleHours); i++){
        if (hours <= possibleHours[i]){
            hoursToGet = possibleHours[i];
        }
        else {
            break;
        }
    }

    char url[256];
    snprintf_P(url, 256, hourlyURLTemplate, hoursToGet, locationID, apiKey, (isMetric ? "true" : "false"), languageID);
    WiFiClient client;
    http.begin(client, url);

    int httpCode = http.GET();
    if (httpCode > 0) {
      if (httpCode == HTTP_CODE_OK) {
            length = http.getSize();
            parser.reset();
            listener = new AccuweatherHourlyParser(data_ptr, hours);
            parser.setListener(listener);
            return 0;
        }
    }
    //If we got here, we failed to get data
    http.end();
    return httpCode;
}

static const char dailyURLTemplate[] PROGMEM = "http://dataservice.accuweather.com/forecasts/v1/daily/%dday/%d?apikey=%s&details=true&metric=%s&language=%s";
int Accuweather::getDaily(AccuweatherDailyData* data_ptr, int days){
    static const int possibleDays[] = {15, 10, 5, 1};
    int daysToGet = possibleDays[0];
    for (int i = 0; i < SIZE_OF_CONST_TABLE(possibleDays); i++){
        if (days <= possibleDays[i]){
            daysToGet = possibleDays[i];
        }
        else {
            break;
        }
    }

    char url[256];
    snprintf_P(url, 256, dailyURLTemplate, daysToGet, locationID, apiKey, (isMetric ? "true" : "false"), languageID);
    WiFiClient client;
    http.begin(client, url);

    int httpCode = http.GET();
    if (httpCode > 0) {
      if (httpCode == HTTP_CODE_OK) {
            length = http.getSize();
            parser.reset();
            listener = new AccuweatherDailyParser(data_ptr, days);
            parser.setListener(listener);
            return 0;
        }
    }
    //If we got here, we failed to get data
    http.end();
    return httpCode;
}

void Accuweather::freeConnection(){
    http.end();
    delete listener;
}

/*
 *  continueDownload() function downloads data in 128 byte chunks.
 *  Returns <0 on download error, 0 on download finished, >0 if the download can be continued.
 *  This function should be called repeatedly until the returned value is 0.
 *  The function will close the connection and free resources automatically.
 */
int Accuweather::continueDownload(){
    // read all data from server
    if (http.connected() && (length > 0 || length == -1)){
        // create buffer for read
        uint8_t buff[128] = { 0 };

        // get tcp stream
        WiFiClient * stream = http.getStreamPtr();

        // get available data size
        size_t size = stream->available();
        int c = size;
        if (size) {
            // read up to 128 byte
            c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
            
            for (int i = 0; i < c; i++){
                parser.parse(buff[i]);
            }
            if (length > 0) {
                length -= c;
            }
        }
        return c+1;
    }
    else if (length == 0){
        freeConnection();
        return 0;
    }
    else {
        return -1;
    }
}

AccuWeather JSON

{"Headline":{"EffectiveDate":"2024-10-25T08:00:00-07:00","EffectiveEpochDate":1729868400,"Severity":5,"Text":"Fog will affect the area Friday morning","Category":"fog","EndDate":"2024-10-25T14:00:00-07:00","EndEpochDate":1729890000,"MobileLink":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?lang=en-us","Link":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?lang=en-us"},"DailyForecasts":[{"Date":"2024-10-22T07:00:00-07:00","EpochDate":1729605600,"Sun":{"Rise":"2024-10-22T07:02:00-07:00","EpochRise":1729605720,"Set":"2024-10-22T18:08:00-07:00","EpochSet":1729645680},"Moon":{"Rise":"2024-10-22T22:40:00-07:00","EpochRise":1729662000,"Set":"2024-10-23T13:48:00-07:00","EpochSet":1729716480,"Phase":"WaningGibbous","Age":20},"Temperature":{"Minimum":{"Value":58.0,"Unit":"F","UnitType":18},"Maximum":{"Value":83.0,"Unit":"F","UnitType":18}},"RealFeelTemperature":{"Minimum":{"Value":57.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":87.0,"Unit":"F","UnitType":18,"Phrase":"Very Warm"}},"RealFeelTemperatureShade":{"Minimum":{"Value":57.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":80.0,"Unit":"F","UnitType":18,"Phrase":"Pleasant"}},"HoursOfSun":10.5,"DegreeDaySummary":{"Heating":{"Value":0.0,"Unit":"F","UnitType":18},"Cooling":{"Value":5.0,"Unit":"F","UnitType":18}},"AirAndPollen":[{"Name":"AirQuality","Value":76,"Category":"Moderate","CategoryValue":2,"Type":"Particle Pollution"},{"Name":"Grass","Value":2,"Category":"Low","CategoryValue":1},{"Name":"Mold","Value":0,"Category":"Low","CategoryValue":1},{"Name":"Ragweed","Value":5,"Category":"Low","CategoryValue":1},{"Name":"Tree","Value":7,"Category":"Low","CategoryValue":1},{"Name":"UVIndex","Value":4,"Category":"Moderate","CategoryValue":2}],"Day":{"Icon":1,"IconPhrase":"Sunny","HasPrecipitation":false,"ShortPhrase":"Sunshine","LongPhrase":"Sunshine","PrecipitationProbability":0,"ThunderstormProbability":0,"RainProbability":0,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":5.8,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":317,"Localized":"NW","English":"NW"}},"WindGust":{"Speed":{"Value":13.8,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":279,"Localized":"W","English":"W"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":5,"Evapotranspiration":{"Value":0.15,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":5367.1,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":27,"Maximum":50,"Average":37},"WetBulbTemperature":{"Minimum":{"Value":54.0,"Unit":"F","UnitType":18},"Maximum":{"Value":63.0,"Unit":"F","UnitType":18},"Average":{"Value":60.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":61.0,"Unit":"F","UnitType":18},"Maximum":{"Value":73.0,"Unit":"F","UnitType":18},"Average":{"Value":69.0,"Unit":"F","UnitType":18}}},"Night":{"Icon":33,"IconPhrase":"Clear","HasPrecipitation":false,"ShortPhrase":"Clear","LongPhrase":"Clear","PrecipitationProbability":0,"ThunderstormProbability":0,"RainProbability":0,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":4.6,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":53,"Localized":"NE","English":"NE"}},"WindGust":{"Speed":{"Value":5.8,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":318,"Localized":"NW","English":"NW"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":3,"Evapotranspiration":{"Value":0.01,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":0.0,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":56,"Maximum":70,"Average":64},"WetBulbTemperature":{"Minimum":{"Value":53.0,"Unit":"F","UnitType":18},"Maximum":{"Value":59.0,"Unit":"F","UnitType":18},"Average":{"Value":56.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":56.0,"Unit":"F","UnitType":18},"Maximum":{"Value":65.0,"Unit":"F","UnitType":18},"Average":{"Value":60.0,"Unit":"F","UnitType":18}}},"Sources":["AccuWeather"],"MobileLink":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=1&lang=en-us","Link":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=1&lang=en-us"},{"Date":"2024-10-23T07:00:00-07:00","EpochDate":1729692000,"Sun":{"Rise":"2024-10-23T07:03:00-07:00","EpochRise":1729692180,"Set":"2024-10-23T18:07:00-07:00","EpochSet":1729732020},"Moon":{"Rise":"2024-10-23T23:44:00-07:00","EpochRise":1729752240,"Set":"2024-10-24T14:27:00-07:00","EpochSet":1729805220,"Phase":"WaningGibbous","Age":21},"Temperature":{"Minimum":{"Value":59.0,"Unit":"F","UnitType":18},"Maximum":{"Value":84.0,"Unit":"F","UnitType":18}},"RealFeelTemperature":{"Minimum":{"Value":59.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":86.0,"Unit":"F","UnitType":18,"Phrase":"Very Warm"}},"RealFeelTemperatureShade":{"Minimum":{"Value":59.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":81.0,"Unit":"F","UnitType":18,"Phrase":"Pleasant"}},"HoursOfSun":10.4,"DegreeDaySummary":{"Heating":{"Value":0.0,"Unit":"F","UnitType":18},"Cooling":{"Value":6.0,"Unit":"F","UnitType":18}},"AirAndPollen":[{"Name":"AirQuality","Value":75,"Category":"Moderate","CategoryValue":2,"Type":"Particle Pollution"},{"Name":"Grass","Value":2,"Category":"Low","CategoryValue":1},{"Name":"Mold","Value":300,"Category":"Low","CategoryValue":1},{"Name":"Ragweed","Value":6,"Category":"Low","CategoryValue":1},{"Name":"Tree","Value":8,"Category":"Low","CategoryValue":1},{"Name":"UVIndex","Value":4,"Category":"Moderate","CategoryValue":2}],"Day":{"Icon":1,"IconPhrase":"Sunny","HasPrecipitation":false,"ShortPhrase":"Plenty of sunshine","LongPhrase":"Plenty of sunshine","PrecipitationProbability":0,"ThunderstormProbability":0,"RainProbability":0,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":5.8,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":229,"Localized":"SW","English":"SW"}},"WindGust":{"Speed":{"Value":11.5,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":274,"Localized":"W","English":"W"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":7,"Evapotranspiration":{"Value":0.14,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":5320.9,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":33,"Maximum":59,"Average":43},"WetBulbTemperature":{"Minimum":{"Value":54.0,"Unit":"F","UnitType":18},"Maximum":{"Value":64.0,"Unit":"F","UnitType":18},"Average":{"Value":61.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":59.0,"Unit":"F","UnitType":18},"Maximum":{"Value":74.0,"Unit":"F","UnitType":18},"Average":{"Value":69.0,"Unit":"F","UnitType":18}}},"Night":{"Icon":34,"IconPhrase":"Mostly clear","HasPrecipitation":false,"ShortPhrase":"Mainly clear","LongPhrase":"Mainly clear","PrecipitationProbability":1,"ThunderstormProbability":0,"RainProbability":1,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":4.6,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":129,"Localized":"SE","English":"SE"}},"WindGust":{"Speed":{"Value":5.8,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":131,"Localized":"SE","English":"SE"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":15,"Evapotranspiration":{"Value":0.01,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":0.0,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":61,"Maximum":75,"Average":71},"WetBulbTemperature":{"Minimum":{"Value":55.0,"Unit":"F","UnitType":18},"Maximum":{"Value":61.0,"Unit":"F","UnitType":18},"Average":{"Value":57.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":58.0,"Unit":"F","UnitType":18},"Maximum":{"Value":66.0,"Unit":"F","UnitType":18},"Average":{"Value":61.0,"Unit":"F","UnitType":18}}},"Sources":["AccuWeather"],"MobileLink":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=2&lang=en-us","Link":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=2&lang=en-us"},{"Date":"2024-10-24T07:00:00-07:00","EpochDate":1729778400,"Sun":{"Rise":"2024-10-24T07:04:00-07:00","EpochRise":1729778640,"Set":"2024-10-24T18:06:00-07:00","EpochSet":1729818360},"Moon":{"Rise":null,"EpochRise":null,"Set":"2024-10-24T14:27:00-07:00","EpochSet":1729805220,"Phase":"Last","Age":22},"Temperature":{"Minimum":{"Value":56.0,"Unit":"F","UnitType":18},"Maximum":{"Value":78.0,"Unit":"F","UnitType":18}},"RealFeelTemperature":{"Minimum":{"Value":58.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":81.0,"Unit":"F","UnitType":18,"Phrase":"Pleasant"}},"RealFeelTemperatureShade":{"Minimum":{"Value":58.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":75.0,"Unit":"F","UnitType":18,"Phrase":"Pleasant"}},"HoursOfSun":8.4,"DegreeDaySummary":{"Heating":{"Value":0.0,"Unit":"F","UnitType":18},"Cooling":{"Value":2.0,"Unit":"F","UnitType":18}},"AirAndPollen":[{"Name":"AirQuality","Value":77,"Category":"Moderate","CategoryValue":2,"Type":"Particle Pollution"},{"Name":"Grass","Value":2,"Category":"Low","CategoryValue":1},{"Name":"Mold","Value":300,"Category":"Low","CategoryValue":1},{"Name":"Ragweed","Value":6,"Category":"Low","CategoryValue":1},{"Name":"Tree","Value":8,"Category":"Low","CategoryValue":1},{"Name":"UVIndex","Value":4,"Category":"Moderate","CategoryValue":2}],"Day":{"Icon":3,"IconPhrase":"Partly sunny","HasPrecipitation":false,"ShortPhrase":"Partly sunny and nice","LongPhrase":"Partly sunny and nice","PrecipitationProbability":1,"ThunderstormProbability":0,"RainProbability":1,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":5.8,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":214,"Localized":"SW","English":"SW"}},"WindGust":{"Speed":{"Value":11.5,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":242,"Localized":"WSW","English":"WSW"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":26,"Evapotranspiration":{"Value":0.12,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":5223.1,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":41,"Maximum":64,"Average":50},"WetBulbTemperature":{"Minimum":{"Value":56.0,"Unit":"F","UnitType":18},"Maximum":{"Value":62.0,"Unit":"F","UnitType":18},"Average":{"Value":61.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":60.0,"Unit":"F","UnitType":18},"Maximum":{"Value":70.0,"Unit":"F","UnitType":18},"Average":{"Value":67.0,"Unit":"F","UnitType":18}}},"Night":{"Icon":34,"IconPhrase":"Mostly clear","HasPrecipitation":false,"ShortPhrase":"Mostly clear","LongPhrase":"Mostly clear","PrecipitationProbability":2,"ThunderstormProbability":0,"RainProbability":2,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":3.5,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":124,"Localized":"SE","English":"SE"}},"WindGust":{"Speed":{"Value":5.8,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":187,"Localized":"S","English":"S"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":25,"Evapotranspiration":{"Value":0.0,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":0.0,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":70,"Maximum":84,"Average":76},"WetBulbTemperature":{"Minimum":{"Value":52.0,"Unit":"F","UnitType":18},"Maximum":{"Value":60.0,"Unit":"F","UnitType":18},"Average":{"Value":56.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":55.0,"Unit":"F","UnitType":18},"Maximum":{"Value":64.0,"Unit":"F","UnitType":18},"Average":{"Value":59.0,"Unit":"F","UnitType":18}}},"Sources":["AccuWeather"],"MobileLink":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=3&lang=en-us","Link":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=3&lang=en-us"},{"Date":"2024-10-25T07:00:00-07:00","EpochDate":1729864800,"Sun":{"Rise":"2024-10-25T07:05:00-07:00","EpochRise":1729865100,"Set":"2024-10-25T18:05:00-07:00","EpochSet":1729904700},"Moon":{"Rise":"2024-10-25T00:47:00-07:00","EpochRise":1729842420,"Set":"2024-10-25T15:00:00-07:00","EpochSet":1729893600,"Phase":"WaningCrescent","Age":23},"Temperature":{"Minimum":{"Value":60.0,"Unit":"F","UnitType":18},"Maximum":{"Value":78.0,"Unit":"F","UnitType":18}},"RealFeelTemperature":{"Minimum":{"Value":61.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":82.0,"Unit":"F","UnitType":18,"Phrase":"Very Warm"}},"RealFeelTemperatureShade":{"Minimum":{"Value":61.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":76.0,"Unit":"F","UnitType":18,"Phrase":"Pleasant"}},"HoursOfSun":5.6,"DegreeDaySummary":{"Heating":{"Value":0.0,"Unit":"F","UnitType":18},"Cooling":{"Value":4.0,"Unit":"F","UnitType":18}},"AirAndPollen":[{"Name":"AirQuality","Value":74,"Category":"Moderate","CategoryValue":2,"Type":"Particle Pollution"},{"Name":"Grass","Value":2,"Category":"Low","CategoryValue":1},{"Name":"Mold","Value":300,"Category":"Low","CategoryValue":1},{"Name":"Ragweed","Value":6,"Category":"Low","CategoryValue":1},{"Name":"Tree","Value":6,"Category":"Low","CategoryValue":1},{"Name":"UVIndex","Value":4,"Category":"Moderate","CategoryValue":2}],"Day":{"Icon":4,"IconPhrase":"Intermittent clouds","HasPrecipitation":false,"ShortPhrase":"Areas of fog, then sun","LongPhrase":"Areas of low clouds and fog giving way to sun","PrecipitationProbability":4,"ThunderstormProbability":0,"RainProbability":4,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":3.5,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":263,"Localized":"W","English":"W"}},"WindGust":{"Speed":{"Value":10.4,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":234,"Localized":"SW","English":"SW"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":47,"Evapotranspiration":{"Value":0.09,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":4101.1,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":43,"Maximum":68,"Average":52},"WetBulbTemperature":{"Minimum":{"Value":55.0,"Unit":"F","UnitType":18},"Maximum":{"Value":63.0,"Unit":"F","UnitType":18},"Average":{"Value":61.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":58.0,"Unit":"F","UnitType":18},"Maximum":{"Value":71.0,"Unit":"F","UnitType":18},"Average":{"Value":67.0,"Unit":"F","UnitType":18}}},"Night":{"Icon":35,"IconPhrase":"Partly cloudy","HasPrecipitation":false,"ShortPhrase":"Partly cloudy","LongPhrase":"Partly cloudy","PrecipitationProbability":5,"ThunderstormProbability":0,"RainProbability":5,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":2.3,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":4,"Localized":"N","English":"N"}},"WindGust":{"Speed":{"Value":4.6,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":342,"Localized":"NNW","English":"NNW"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":49,"Evapotranspiration":{"Value":0.0,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":0.0,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":67,"Maximum":80,"Average":74},"WetBulbTemperature":{"Minimum":{"Value":54.0,"Unit":"F","UnitType":18},"Maximum":{"Value":61.0,"Unit":"F","UnitType":18},"Average":{"Value":57.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":57.0,"Unit":"F","UnitType":18},"Maximum":{"Value":64.0,"Unit":"F","UnitType":18},"Average":{"Value":60.0,"Unit":"F","UnitType":18}}},"Sources":["AccuWeather"],"MobileLink":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=4&lang=en-us","Link":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=4&lang=en-us"},{"Date":"2024-10-26T07:00:00-07:00","EpochDate":1729951200,"Sun":{"Rise":"2024-10-26T07:05:00-07:00","EpochRise":1729951500,"Set":"2024-10-26T18:04:00-07:00","EpochSet":1729991040},"Moon":{"Rise":"2024-10-26T01:47:00-07:00","EpochRise":1729932420,"Set":"2024-10-26T15:28:00-07:00","EpochSet":1729981680,"Phase":"WaningCrescent","Age":24},"Temperature":{"Minimum":{"Value":59.0,"Unit":"F","UnitType":18},"Maximum":{"Value":79.0,"Unit":"F","UnitType":18}},"RealFeelTemperature":{"Minimum":{"Value":59.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":83.0,"Unit":"F","UnitType":18,"Phrase":"Very Warm"}},"RealFeelTemperatureShade":{"Minimum":{"Value":59.0,"Unit":"F","UnitType":18,"Phrase":"Cool"},"Maximum":{"Value":77.0,"Unit":"F","UnitType":18,"Phrase":"Pleasant"}},"HoursOfSun":4.6,"DegreeDaySummary":{"Heating":{"Value":0.0,"Unit":"F","UnitType":18},"Cooling":{"Value":4.0,"Unit":"F","UnitType":18}},"AirAndPollen":[{"Name":"AirQuality","Value":78,"Category":"Moderate","CategoryValue":2,"Type":"Particle Pollution"},{"Name":"Grass","Value":2,"Category":"Low","CategoryValue":1},{"Name":"Mold","Value":300,"Category":"Low","CategoryValue":1},{"Name":"Ragweed","Value":6,"Category":"Low","CategoryValue":1},{"Name":"Tree","Value":3,"Category":"Low","CategoryValue":1},{"Name":"UVIndex","Value":4,"Category":"Moderate","CategoryValue":2}],"Day":{"Icon":4,"IconPhrase":"Intermittent clouds","HasPrecipitation":false,"ShortPhrase":"Fog, then some sun","LongPhrase":"Areas of low clouds and fog early; otherwise, partly sunny","PrecipitationProbability":4,"ThunderstormProbability":0,"RainProbability":4,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":4.6,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":285,"Localized":"WNW","English":"WNW"}},"WindGust":{"Speed":{"Value":10.4,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":282,"Localized":"WNW","English":"WNW"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":60,"Evapotranspiration":{"Value":0.09,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":3934.1,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":35,"Maximum":62,"Average":45},"WetBulbTemperature":{"Minimum":{"Value":54.0,"Unit":"F","UnitType":18},"Maximum":{"Value":61.0,"Unit":"F","UnitType":18},"Average":{"Value":59.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":57.0,"Unit":"F","UnitType":18},"Maximum":{"Value":70.0,"Unit":"F","UnitType":18},"Average":{"Value":66.0,"Unit":"F","UnitType":18}}},"Night":{"Icon":34,"IconPhrase":"Mostly clear","HasPrecipitation":false,"ShortPhrase":"Clear to partly cloudy","LongPhrase":"Clear to partly cloudy","PrecipitationProbability":2,"ThunderstormProbability":0,"RainProbability":2,"SnowProbability":0,"IceProbability":0,"Wind":{"Speed":{"Value":3.5,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":124,"Localized":"SE","English":"SE"}},"WindGust":{"Speed":{"Value":4.6,"Unit":"mi/h","UnitType":9},"Direction":{"Degrees":125,"Localized":"SE","English":"SE"}},"TotalLiquid":{"Value":0.0,"Unit":"in","UnitType":1},"Rain":{"Value":0.0,"Unit":"in","UnitType":1},"Snow":{"Value":0.0,"Unit":"in","UnitType":1},"Ice":{"Value":0.0,"Unit":"in","UnitType":1},"HoursOfPrecipitation":0.0,"HoursOfRain":0.0,"HoursOfSnow":0.0,"HoursOfIce":0.0,"CloudCover":52,"Evapotranspiration":{"Value":0.0,"Unit":"in","UnitType":1},"SolarIrradiance":{"Value":0.0,"Unit":"W/m²","UnitType":33},"RelativeHumidity":{"Minimum":65,"Maximum":74,"Average":69},"WetBulbTemperature":{"Minimum":{"Value":55.0,"Unit":"F","UnitType":18},"Maximum":{"Value":59.0,"Unit":"F","UnitType":18},"Average":{"Value":57.0,"Unit":"F","UnitType":18}},"WetBulbGlobeTemperature":{"Minimum":{"Value":57.0,"Unit":"F","UnitType":18},"Maximum":{"Value":63.0,"Unit":"F","UnitType":18},"Average":{"Value":60.0,"Unit":"F","UnitType":18}}},"Sources":["AccuWeather"],"MobileLink":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=5&lang=en-us","Link":"http://www.accuweather.com/en/us/laguna-hills-ca/92653/daily-weather-forecast/2178888?day=5&lang=en-us"}]}