113 lines
3.7 KiB
C++
113 lines
3.7 KiB
C++
#include "widgets/weather.hpp"
|
|
|
|
#include <curl/curl.h>
|
|
#include <glibmm/main.h>
|
|
#include <ios>
|
|
#include <nlohmann/json.hpp>
|
|
#include <spdlog/spdlog.h>
|
|
#include <sstream>
|
|
#include <thread>
|
|
|
|
namespace {
|
|
constexpr const char *kWeatherUrl =
|
|
"https://api.open-meteo.com/v1/forecast?latitude=49.0094&longitude=8.4044&daily=temperature_2m_max,temperature_2m_min,weather_code&hourly=temperature_2m,rain¤t=temperature_2m&timezone=Europe%2FBerlin";
|
|
|
|
size_t write_to_buffer(void *contents, size_t size, size_t nmemb, void *userp) {
|
|
size_t total = size * nmemb;
|
|
auto *buffer = static_cast<std::string *>(userp);
|
|
buffer->append(static_cast<char *>(contents), total);
|
|
return total;
|
|
}
|
|
|
|
std::string formatTemp(double value) {
|
|
std::ostringstream ss;
|
|
ss.setf(std::ios::fixed);
|
|
ss.precision(1);
|
|
ss << value << "°C";
|
|
return ss.str();
|
|
}
|
|
} // namespace
|
|
|
|
WeatherWidget::WeatherWidget()
|
|
: Gtk::Box(Gtk::Orientation::VERTICAL) {
|
|
this->set_orientation(Gtk::Orientation::VERTICAL);
|
|
this->set_spacing(6);
|
|
this->set_margin_top(4);
|
|
this->set_margin_bottom(4);
|
|
this->set_margin_start(4);
|
|
this->set_margin_end(4);
|
|
|
|
this->titleLabel.set_text("Weather");
|
|
this->currentLabel.set_text("Now: --");
|
|
this->todayLabel.set_text("Today: -- / --");
|
|
|
|
this->append(this->titleLabel);
|
|
this->append(this->currentLabel);
|
|
this->append(this->todayLabel);
|
|
|
|
this->fetchWeather();
|
|
Glib::signal_timeout().connect_seconds(
|
|
sigc::mem_fun(*this, &WeatherWidget::onRefreshTick),
|
|
3600);
|
|
}
|
|
|
|
bool WeatherWidget::onRefreshTick() {
|
|
this->fetchWeather();
|
|
return true;
|
|
}
|
|
|
|
void WeatherWidget::fetchWeather() {
|
|
std::thread([this]() {
|
|
std::string buffer;
|
|
|
|
CURL *curl = curl_easy_init();
|
|
if (!curl) {
|
|
Glib::signal_idle().connect_once([this]() {
|
|
this->applyWeatherText("Now: --", "Today: -- / --");
|
|
});
|
|
return;
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, kWeatherUrl);
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_buffer);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "bar/1.0");
|
|
|
|
auto res = curl_easy_perform(curl);
|
|
curl_easy_cleanup(curl);
|
|
|
|
if (res != CURLE_OK || buffer.empty()) {
|
|
Glib::signal_idle().connect_once([this]() {
|
|
this->applyWeatherText("Now: --", "Today: -- / --");
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
auto json = nlohmann::json::parse(buffer);
|
|
double current = json.at("current").at("temperature_2m").get<double>();
|
|
double minTemp = json.at("daily").at("temperature_2m_min").at(0).get<double>();
|
|
double maxTemp = json.at("daily").at("temperature_2m_max").at(0).get<double>();
|
|
|
|
std::string currentText = "Now: " + formatTemp(current);
|
|
std::string todayText = "Today: " + formatTemp(minTemp) + " / " + formatTemp(maxTemp);
|
|
|
|
Glib::signal_idle().connect_once([this, currentText, todayText]() {
|
|
this->applyWeatherText(currentText, todayText);
|
|
});
|
|
} catch (const std::exception &ex) {
|
|
spdlog::error("Weather parse error: {}", ex.what());
|
|
Glib::signal_idle().connect_once([this]() {
|
|
this->applyWeatherText("Now: --", "Today: -- / --");
|
|
});
|
|
}
|
|
}).detach();
|
|
}
|
|
|
|
void WeatherWidget::applyWeatherText(const std::string ¤t_text,
|
|
const std::string &today_text) {
|
|
this->currentLabel.set_text(current_text);
|
|
this->todayLabel.set_text(today_text);
|
|
}
|