quic
This commit is contained in:
+122
-49
@@ -1,16 +1,21 @@
|
||||
#include "widgets/weather.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <curl/curl.h>
|
||||
#include <glibmm/main.h>
|
||||
#include <ios>
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "components/weather.hpp"
|
||||
|
||||
#include "gtkmm/object.h"
|
||||
|
||||
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";
|
||||
"https://api.open-meteo.com/v1/forecast?latitude=49.0094&longitude=8.4044&daily=temperature_2m_max,temperature_2m_min,apparent_temperature_min,apparent_temperature_max,precipitation_sum,weather_code&hourly=temperature_2m,apparent_temperature,weather_code,precipitation_probability,precipitation¤t=temperature_2m,apparent_temperature,weather_code,precipitation&timezone=Europe%2FBerlin&past_days=0&forecast_days=7";
|
||||
|
||||
size_t write_to_buffer(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
size_t total = size * nmemb;
|
||||
@@ -19,32 +24,42 @@ size_t write_to_buffer(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
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);
|
||||
WeatherWidget::WeatherWidget() : Gtk::Box(Gtk::Orientation::VERTICAL) {
|
||||
set_spacing(10);
|
||||
|
||||
this->titleLabel.set_text("Weather");
|
||||
this->currentLabel.set_text("Now: --");
|
||||
this->todayLabel.set_text("Today: -- / --");
|
||||
currentScroll.set_policy(Gtk::PolicyType::ALWAYS, Gtk::PolicyType::NEVER);
|
||||
currentScroll.set_propagate_natural_height(true);
|
||||
currentScroll.add_css_class("current-weather-scroll");
|
||||
currentScroll.set_valign(Gtk::Align::START);
|
||||
currentScroll.set_halign(Gtk::Align::FILL);
|
||||
|
||||
this->append(this->titleLabel);
|
||||
this->append(this->currentLabel);
|
||||
this->append(this->todayLabel);
|
||||
hourlyScroll.set_policy(Gtk::PolicyType::ALWAYS, Gtk::PolicyType::NEVER);
|
||||
hourlyScroll.set_propagate_natural_height(true);
|
||||
hourlyScroll.add_css_class("hourly-weather-scroll");
|
||||
hourlyScroll.set_valign(Gtk::Align::START);
|
||||
hourlyScroll.set_halign(Gtk::Align::FILL);
|
||||
|
||||
this->fetchWeather();
|
||||
Glib::signal_timeout().connect_seconds(
|
||||
sigc::mem_fun(*this, &WeatherWidget::onRefreshTick),
|
||||
3600);
|
||||
dailyScroll.set_policy(Gtk::PolicyType::ALWAYS, Gtk::PolicyType::NEVER);
|
||||
dailyScroll.set_propagate_natural_height(true);
|
||||
dailyScroll.add_css_class("daily-weather-scroll");
|
||||
dailyScroll.set_valign(Gtk::Align::START);
|
||||
dailyScroll.set_halign(Gtk::Align::FILL);
|
||||
|
||||
|
||||
this->append(currentScroll);
|
||||
this->append(hourlyScroll);
|
||||
this->append(dailyScroll);
|
||||
|
||||
m_dispatcher.connect([this]() {
|
||||
this->refreshCurrentWeather();
|
||||
this->refreshHourlyWeather();
|
||||
this->refreshDailyWeather();
|
||||
});
|
||||
|
||||
Glib::signal_timeout().connect(sigc::mem_fun(*this, &WeatherWidget::onRefreshTick), 15 * 60 * 1000);
|
||||
this->onRefreshTick();
|
||||
}
|
||||
|
||||
bool WeatherWidget::onRefreshTick() {
|
||||
@@ -52,15 +67,45 @@ bool WeatherWidget::onRefreshTick() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void WeatherWidget::refreshCurrentWeather() {
|
||||
auto current_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
||||
current_box->set_halign(Gtk::Align::CENTER);
|
||||
current_box->set_valign(Gtk::Align::CENTER);
|
||||
this->currentScroll.set_child(*current_box);
|
||||
|
||||
auto &cw = this->current_weather;
|
||||
auto weatherPill = Gtk::make_managed<WeatherInfo>(cw);
|
||||
current_box->append(*weatherPill);
|
||||
}
|
||||
|
||||
void WeatherWidget::refreshHourlyWeather() {
|
||||
auto hourly_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
||||
this->hourlyScroll.set_child(*hourly_box);
|
||||
hourly_box->set_spacing(5);
|
||||
|
||||
for (auto &hw : this->hourly_weather) {
|
||||
auto weatherPill = Gtk::make_managed<WeatherInfo>(hw);
|
||||
hourly_box->append(*weatherPill);
|
||||
}
|
||||
}
|
||||
|
||||
void WeatherWidget::refreshDailyWeather() {
|
||||
auto daily_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
||||
this->dailyScroll.set_child(*daily_box);
|
||||
daily_box->set_spacing(5);
|
||||
|
||||
for (auto &dw : this->daily_weather) {
|
||||
auto weatherPill = Gtk::make_managed<WeatherInfo>(dw);
|
||||
daily_box->append(*weatherPill);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -68,41 +113,69 @@ void WeatherWidget::fetchWeather() {
|
||||
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");
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3");
|
||||
|
||||
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>();
|
||||
auto json = nlohmann::json::parse(buffer);
|
||||
|
||||
std::string currentText = "Now: " + formatTemp(current);
|
||||
std::string todayText = "Today: " + formatTemp(minTemp) + " / " + formatTemp(maxTemp);
|
||||
auto current = json["current"];
|
||||
|
||||
this->current_weather.temperature = current["temperature_2m"].get<double>();
|
||||
this->current_weather.apparent_temperature = current["apparent_temperature"].get<double>();
|
||||
this->current_weather.weather_code = current["weather_code"].get<int>();
|
||||
this->current_weather.precipitation = current["precipitation"].get<double>();
|
||||
|
||||
auto daily = json["daily"];
|
||||
|
||||
for (size_t i = 0; i < daily["time"].size(); i++) {
|
||||
DailyWeather dw{};
|
||||
dw.temperature_2m_max = daily["temperature_2m_max"][i].get<double>();
|
||||
dw.temperature_2m_min = daily["temperature_2m_min"][i].get<double>();
|
||||
dw.apparent_temperature_min = daily["apparent_temperature_min"][i].get<double>();
|
||||
dw.apparent_temperature_max = daily["apparent_temperature_max"][i].get<double>();
|
||||
dw.precipitation_sum = daily["precipitation_sum"][i].get<double>();
|
||||
dw.weather_code = daily["weather_code"][i].get<int>();
|
||||
auto time_str = daily["time"][i].get<std::string>();
|
||||
std::tm tm = {};
|
||||
std::istringstream ss(time_str);
|
||||
ss >> std::get_time(&tm, "%Y-%m-%d");
|
||||
|
||||
std::mktime(&tm);
|
||||
char buffer[10];
|
||||
std::strftime(buffer, sizeof(buffer), "%a", &tm);
|
||||
dw.time = buffer;
|
||||
|
||||
this->daily_weather[i] = dw;
|
||||
}
|
||||
|
||||
auto hourly = json["hourly"];
|
||||
auto current_time = std::time(nullptr);
|
||||
auto nextHour = std::localtime(¤t_time)->tm_hour + 1;
|
||||
|
||||
for (int i = nextHour; i < nextHour + 24; i ++) {
|
||||
HourlyWeather hw{};
|
||||
hw.temperature_2m = hourly["temperature_2m"][i].get<double>();
|
||||
hw.apparent_temperature = hourly["apparent_temperature"][i].get<double>();
|
||||
hw.weather_code = hourly["weather_code"][i].get<int>();
|
||||
hw.precipitation_probability = hourly["precipitation_probability"][i].get<double>();
|
||||
hw.precipitation = hourly["precipitation"][i].get<double>();
|
||||
hw.time = hourly["time"][i].get<std::string>().substr(11, 5);
|
||||
|
||||
this->hourly_weather[i - nextHour] = hw;
|
||||
}
|
||||
|
||||
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: -- / --");
|
||||
});
|
||||
}
|
||||
|
||||
m_dispatcher.emit();
|
||||
}).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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user