From 55cd63dff2d8e9acb920eb01209d7b9cde1f2841 Mon Sep 17 00:00:00 2001 From: Tyler Perkins Date: Wed, 22 Dec 2021 18:27:55 -0500 Subject: [PATCH] use http json api for weather --- src/config.cpp | 20 ++++- src/panel/weather.cpp | 166 ++++++++++++++++++++++++++++++++--- src/panel/weather.hpp | 25 +++++- src/panel/weather_config.hpp | 74 +++++++++++++++- 4 files changed, 266 insertions(+), 19 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 7b28067..6cd8ba1 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -47,8 +47,26 @@ size_t FONT_LOCATIONS_LENGTH = sizeof(FONT_LOCATIONS)/sizeof(FONT_LOCATIONS[0]); // const FONT_SIZE _font_size; //}; const FONT_SIZE_STRING CONST_STRINGS[] = { - //Weather strings + //Overlay strings { "Weather", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, { "Wireless", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + + //Weather strings + { "Clear Skies", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Slightly cloudy", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Moderately cloudy", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Very cloudy", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Very humid", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Light rain", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Rain", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Overcast with showers", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Moderate showers", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Light snow", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Moderate snow", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Rain and snow", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Currently,", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Tommorow,", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + { "Day after,", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 } }, + }; size_t CONST_STRINGS_LENGTH = sizeof(CONST_STRINGS)/sizeof(CONST_STRINGS[0]); diff --git a/src/panel/weather.cpp b/src/panel/weather.cpp index 1fa76a7..8aaffde 100644 --- a/src/panel/weather.cpp +++ b/src/panel/weather.cpp @@ -19,8 +19,32 @@ weather::weather(){ _update_interval = std::chrono::milliseconds{WEATHER_UPDATE_INTERVAL}; _texture = nullptr; _title = WEATHER_TITLE; - //let set to default, will make it so it updates the texture ASAP - //_last_update; + api_curl = nullptr; + + weather_string["clearday"] = WEATHER_CLEAR_DAY; + weather_string["clearnight"] = WEATHER_CLEAR_NIGHT; + weather_string["pcloudyday"] = WEATHER_PCLOUDY_DAY; + weather_string["pcloudynight"] = WEATHER_PCLOUDY_NIGHT; + weather_string["mcloudyday"] = WEATHER_MCLOUDY_DAY; + weather_string["mcloudynight"] = WEATHER_MCLOUDY_NIGHT; + weather_string["cloudyday"] = WEATHER_CLOUDY_DAY; + weather_string["cloudynight"] = WEATHER_CLOUDY_NIGHT; + weather_string["humidday"] = WEATHER_HUMID_DAY; + weather_string["humidnight"] = WEATHER_HUMID_NIGHT; + weather_string["lightrainday"] = WEATHER_LRAIN_DAY; + weather_string["lightrainnight"] = WEATHER_LRAIN_NIGHT; + weather_string["rainday"] = WEATHER_RAIN_DAY; + weather_string["rainnight"] = WEATHER_RAIN_NIGHT; + weather_string["oshowerday"] = WEATHER_OSHOWER_DAY; + weather_string["oshowernight"] = WEATHER_OSHOWER_NIGHT; + weather_string["ishowerday"] = WEATHER_ISHOWER_DAY; + weather_string["ishowernight"] = WEATHER_ISHOWER_NIGHT; + weather_string["lightsnowday"] = WEATHER_LSNOW_DAY; + weather_string["lightsnownight"] = WEATHER_LSNOW_NIGHT; + weather_string["snowday"] = WEATHER_SNOW_DAY; + weather_string["snownight"] = WEATHER_SNOW_NIGHT; + weather_string["rainsnowday"] = WEATHER_RAINSNOW_DAY; + weather_string["rainsnownight"] = WEATHER_RAINSNOW_NIGHT; } weather::~weather(){ @@ -65,19 +89,49 @@ void weather::update() { _last_update = std::chrono::high_resolution_clock::now(); //fetch updates + //do curl setup and cleanup + //We cleanup after each operation to save on memory. + //This is because by default we only fetch every hour + if(api_curl == nullptr){ + std::cerr << "GRABING WEATHER DATA\n"; + api_curl = curl_easy_init(); + curl_easy_setopt(api_curl, CURLOPT_URL, WEATHER_URL_SOURCE); + curl_easy_setopt(api_curl, CURLOPT_WRITEFUNCTION, + dashboard::panel::weather::curl_callback); + curl_easy_setopt(api_curl, CURLOPT_WRITEDATA, &json_string); + CURLcode res = curl_easy_perform(api_curl); + curl_easy_cleanup(api_curl); + api_curl = nullptr; + } + + //prase the response + json_doc.Parse(json_string.c_str()); //update internal state - current_desc = "CURRENT DESC PLACEHOLDER"; - tommorow_desc = "TOMMOROW DESC PLACEHOLDER"; + const rapidjson::Value& curr_entry = json_doc["dataseries"]; + //get all entries + weather_today = &weather_string.at(curr_entry[0]["weather"].GetString()); + weather_tommorow = &weather_string.at(curr_entry[7]["weather"].GetString()); + weather_day_after = &weather_string.at(curr_entry[15]["weather"].GetString()); + temp_today = curr_entry[0]["temp2m"].GetInt(); + temp_tommorow = curr_entry[7]["temp2m"].GetInt(); + temp_day_after = curr_entry[15]["temp2m"].GetInt(); + + + if(!WEATHER_METRIC){ + temp_today = (temp_today * 1.8) + 32; + temp_tommorow = (temp_tommorow * 1.8) + 32; + temp_day_after = (temp_day_after * 1.8) + 32; + } } /////////////////////////////////////// // Update the texture that is being -// displayed, based on data in -// _rss +// displayed, based on data in the json feed void weather::update_texture(){ std::cerr << "WEATHER::UPDATE_TEXTURE\n"; SDL_Rect tgt; + std::string temp; SDL_SetRenderTarget(board::getRenderer(), _texture); SDL_RenderClear(board::getRenderer()); @@ -87,24 +141,100 @@ void weather::update_texture(){ board::getImage("sky.png"), NULL, NULL); //current weather + tgt.y = 50; TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), - current_desc.c_str(), + "Currently,", &tgt.w, &tgt.h); - tgt.x = SCREEN_WIDTH / 2 - (tgt.w / 2); - tgt.y = SCREEN_HEIGHT / 2 - (tgt.h / 2); + tgt.x = (SCREEN_WIDTH / 2) - (tgt.w / 2); SDL_RenderCopy(board::getRenderer(), - board::getString(current_desc.c_str(), + board::getString("Currently,", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + tgt.y += tgt.h; + TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), + weather_today->first.c_str(), + &tgt.w, &tgt.h); + tgt.x = (SCREEN_WIDTH / 2) - (tgt.w / 2); + SDL_RenderCopy(board::getRenderer(), + board::getString(weather_today->first.c_str(), + { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + tgt.y += tgt.h; + temp = std::to_string(temp_today); + if(WEATHER_METRIC) + temp += "*C"; + else + temp += "*F"; + TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), + temp.c_str(), + &tgt.w, &tgt.h); + tgt.x = (SCREEN_WIDTH / 2) - (tgt.w / 2); + SDL_RenderCopy(board::getRenderer(), + board::getString(temp.c_str(), + { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + //tommorow's weather TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), - tommorow_desc.c_str(), + "Tommorow,", &tgt.w, &tgt.h); - tgt.x = SCREEN_WIDTH / 2 - (tgt.w / 2); - tgt.y = SCREEN_HEIGHT / 2 - (tgt.h / 2) + 50; + tgt.y = (3*SCREEN_HEIGHT / 4) - (tgt.h / 2); + tgt.x = (SCREEN_WIDTH / 6) - (tgt.w / 2); SDL_RenderCopy(board::getRenderer(), - board::getString(tommorow_desc.c_str(), + board::getString("Tommorow,", { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + tgt.y += tgt.h; + TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), + weather_tommorow->first.c_str(), + &tgt.w, &tgt.h); + tgt.x = (SCREEN_WIDTH / 6) - (tgt.w / 2); + SDL_RenderCopy(board::getRenderer(), + board::getString(weather_tommorow->first.c_str(), + { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + tgt.y += tgt.h; + temp = std::to_string(temp_tommorow); + if(WEATHER_METRIC) + temp += "*C"; + else + temp += "*F"; + TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), + temp.c_str(), + &tgt.w, &tgt.h); + tgt.x = (SCREEN_WIDTH / 6) - (tgt.w / 2); + SDL_RenderCopy(board::getRenderer(), + board::getString(temp.c_str(), + { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + + //day after's weather + TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), + "Day after,", + &tgt.w, &tgt.h); + tgt.y = (3*SCREEN_HEIGHT / 4) - (tgt.h / 2); + tgt.x = (5*SCREEN_WIDTH / 6) - (tgt.w / 2); + SDL_RenderCopy(board::getRenderer(), + board::getString("Day after,", + { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + tgt.y += tgt.h; + TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), + weather_day_after->first.c_str(), + &tgt.w, &tgt.h); + tgt.x = (5*SCREEN_WIDTH / 6) - (tgt.w / 2); + SDL_RenderCopy(board::getRenderer(), + board::getString(weather_day_after->first.c_str(), + { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + tgt.y += tgt.h; + temp = std::to_string(temp_day_after); + if(WEATHER_METRIC) + temp += "*C"; + else + temp += "*F"; + TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), + temp.c_str(), + &tgt.w, &tgt.h); + tgt.x = (5*SCREEN_WIDTH / 6) - (tgt.w / 2); + SDL_RenderCopy(board::getRenderer(), + board::getString(temp.c_str(), + { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt); + + SDL_SetRenderTarget(board::getRenderer(), NULL); } @@ -123,3 +253,11 @@ void weather::initTexture(){ SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_BLEND); } } + +/////////////////////////////////////// +// Curl callback function +size_t dashboard::panel::weather::curl_callback(void* contents, size_t size, + size_t nmemb, void* userp){ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} diff --git a/src/panel/weather.hpp b/src/panel/weather.hpp index 4d85cbb..17f382c 100644 --- a/src/panel/weather.hpp +++ b/src/panel/weather.hpp @@ -14,8 +14,13 @@ #include #include +#include +#include "../util/rapidjson/document.h" + #include #include +#include +#include namespace dashboard::panel { class weather : public panel { @@ -23,16 +28,30 @@ namespace dashboard::panel { weather(); ~weather(); - void draw(); + static size_t curl_callback(void*, size_t, size_t, void*); + void draw(); private: void update(); void update_texture(); void initTexture(); - std::string current_desc; - std::string tommorow_desc; + static constexpr bool TODAY = true; + static constexpr bool TOMMOROW = false; + std::unordered_map> weather_string; + + std::pair* weather_today; + std::pair* weather_tommorow; + std::pair* weather_day_after; + short temp_today; + short temp_tommorow; + short temp_day_after; + + CURL* api_curl; + std::string json_string; + rapidjson::Document json_doc; std::chrono::time_point _last_update; std::chrono::milliseconds _update_interval; diff --git a/src/panel/weather_config.hpp b/src/panel/weather_config.hpp index c28088c..94f9b2e 100644 --- a/src/panel/weather_config.hpp +++ b/src/panel/weather_config.hpp @@ -6,13 +6,16 @@ #pragma once +#include +#include + namespace dashboard::panel { //This will be displayed at the top left on the status bar. Set to a blank //string to not show anything constexpr char WEATHER_TITLE[] = "Weather"; //New York RSS feed - static const char* WEATHER_URL_SOURCE = "http://rss.accuweather.com/rss/liveweather_rss.asp?locCode=10007"; + static const char* WEATHER_URL_SOURCE = "https://www.7timer.info/bin/civil.php?lon=41.13&lat=-81.48&unit=british&output=json&tzshift=0"; //Default time the slide is shown on screen, in ms //Default 5s @@ -22,4 +25,73 @@ namespace dashboard::panel { //Default 1 hour constexpr size_t WEATHER_UPDATE_INTERVAL = 3600000; + //Display temperatures in metric? + //Default false + constexpr bool WEATHER_METRIC = false; + + //The following are all of the strings that will be shown for each state of + //weather, along with the image that will be shown + //The struct is of form {WEATHER_STRING, WEATHER_IMAGE} + //with weather string being the string shown on screen, and weather image + //being the name of the preloaded image file to be shown. It is best that + //these images be square. + const std::pair WEATHER_CLEAR_DAY = + {"Clear skies", "clearday.png"}; + const std::pair WEATHER_CLEAR_NIGHT = + {"Clear skies", "clearnight.png"}; + + const std::pair WEATHER_PCLOUDY_DAY = + {"Slightly cloudy", "pcloudyday.png"}; + const std::pair WEATHER_PCLOUDY_NIGHT = + {"Slightly cloudy", "pcloudynight.png"}; + + const std::pair WEATHER_MCLOUDY_DAY = + {"Moderately cloudy", "mcloudyday.png"}; + const std::pair WEATHER_MCLOUDY_NIGHT = + {"Moderately cloudy", "mcloudynight.png"}; + + const std::pair WEATHER_CLOUDY_DAY = + {"Very cloudy", "cloudyday.png"}; + const std::pair WEATHER_CLOUDY_NIGHT = + {"Very cloudy", "cloudynight.png"}; + + const std::pair WEATHER_HUMID_DAY = + {"Very humid", "humidday.png"}; + const std::pair WEATHER_HUMID_NIGHT = + {"Very humid", "humidnight.png"}; + + const std::pair WEATHER_LRAIN_DAY = + {"Light rain", "lrainday.png"}; + const std::pair WEATHER_LRAIN_NIGHT = + {"Light rain", "lrainnight.png"}; + + const std::pair WEATHER_RAIN_DAY = + {"Rain", "rainday.png"}; + const std::pair WEATHER_RAIN_NIGHT = + {"Rain", "rainnight.png"}; + + const std::pair WEATHER_OSHOWER_DAY = + {"Overcast with showers", "oshowerday.png"}; + const std::pair WEATHER_OSHOWER_NIGHT = + {"Overcast with showers", "oshowernight.png"}; + + const std::pair WEATHER_ISHOWER_DAY = + {"Moderate showers", "ishowerday.png"}; + const std::pair WEATHER_ISHOWER_NIGHT = + {"Moderate showers", "ishowernight.png"}; + + const std::pair WEATHER_LSNOW_DAY = + {"Light snow", "lsnowday.png"}; + const std::pair WEATHER_LSNOW_NIGHT = + {"Light snow", "lsnownight.png"}; + + const std::pair WEATHER_SNOW_DAY = + {"Moderate snow", "snowday.png"}; + const std::pair WEATHER_SNOW_NIGHT = + {"Moderate snow", "snownight.png"}; + + const std::pair WEATHER_RAINSNOW_DAY = + {"Rain and snow", "rainsnowday.png"}; + const std::pair WEATHER_RAINSNOW_NIGHT = + {"Rain and snow", "rainsnownight.png"}; }