#include "widgets/weather.hpp" #include #include #include #include #include #include #include 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(userp); buffer->append(static_cast(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 minTemp = json.at("daily").at("temperature_2m_min").at(0).get(); double maxTemp = json.at("daily").at("temperature_2m_max").at(0).get(); 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); }