quick commit
This commit is contained in:
@@ -38,6 +38,11 @@ target_sources(bar_lib
|
|||||||
src/app.cpp
|
src/app.cpp
|
||||||
src/bar/bar.cpp
|
src/bar/bar.cpp
|
||||||
|
|
||||||
|
src/connection/httpConnection.cpp
|
||||||
|
src/connection/dbus/notification.cpp
|
||||||
|
src/connection/dbus/mpris.cpp
|
||||||
|
src/connection/dbus/tray.cpp
|
||||||
|
|
||||||
src/widgets/clock.cpp
|
src/widgets/clock.cpp
|
||||||
src/widgets/date.cpp
|
src/widgets/date.cpp
|
||||||
src/widgets/notification/baseNotification.cpp
|
src/widgets/notification/baseNotification.cpp
|
||||||
@@ -45,14 +50,12 @@ target_sources(bar_lib
|
|||||||
src/widgets/notification/notificationWindow.cpp
|
src/widgets/notification/notificationWindow.cpp
|
||||||
src/widgets/notification/spotifyNotification.cpp
|
src/widgets/notification/spotifyNotification.cpp
|
||||||
src/widgets/volumeWidget.cpp
|
src/widgets/volumeWidget.cpp
|
||||||
|
src/widgets/weather.cpp
|
||||||
src/widgets/webWidget.cpp
|
src/widgets/webWidget.cpp
|
||||||
|
|
||||||
src/services/hyprland.cpp
|
src/services/hyprland.cpp
|
||||||
src/services/notificationController.cpp
|
src/services/notificationController.cpp
|
||||||
src/services/textureCache.cpp
|
src/services/textureCache.cpp
|
||||||
src/services/tray.cpp
|
|
||||||
src/services/dbus/notification.cpp
|
|
||||||
src/services/dbus/mpris.cpp
|
|
||||||
|
|
||||||
src/widgets/tray.cpp
|
src/widgets/tray.cpp
|
||||||
src/widgets/controlCenter/controlCenter.cpp
|
src/widgets/controlCenter/controlCenter.cpp
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
#include "bar/bar.hpp"
|
#include "bar/bar.hpp"
|
||||||
#include "services/hyprland.hpp"
|
#include "services/hyprland.hpp"
|
||||||
#include "services/dbus/notification.hpp"
|
#include "connection/dbus/notification.hpp"
|
||||||
|
#include "connection/dbus/tray.hpp"
|
||||||
|
|
||||||
#include "glibmm/refptr.h"
|
#include "glibmm/refptr.h"
|
||||||
#include "gtkmm/application.h"
|
#include "gtkmm/application.h"
|
||||||
|
|||||||
24
include/connection/dbus/dbus.hpp
Normal file
24
include/connection/dbus/dbus.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <giomm.h>
|
||||||
|
#include <sigc++/sigc++.h>
|
||||||
|
|
||||||
|
class DbusConnection {
|
||||||
|
public:
|
||||||
|
virtual ~DbusConnection() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Glib::RefPtr<Gio::DBus::Connection> connection;
|
||||||
|
|
||||||
|
void connect_session_async(const sigc::slot<void(const Glib::RefPtr<Gio::AsyncResult> &)> &callback) {
|
||||||
|
Gio::DBus::Connection::get(Gio::DBus::BusType::SESSION, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ensure_gio_init() {
|
||||||
|
try {
|
||||||
|
Gio::init();
|
||||||
|
} catch (const Glib::Error &) {
|
||||||
|
// Already initialized.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -10,8 +10,6 @@
|
|||||||
#include "gdkmm/pixbuf.h"
|
#include "gdkmm/pixbuf.h"
|
||||||
#include "glibmm/variant.h"
|
#include "glibmm/variant.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct MprisPlayer2Message {
|
struct MprisPlayer2Message {
|
||||||
std::string title;
|
std::string title;
|
||||||
std::vector<std::string> artist;
|
std::vector<std::string> artist;
|
||||||
@@ -5,9 +5,10 @@
|
|||||||
#include <sigc++/sigc++.h>
|
#include <sigc++/sigc++.h>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "services/dbus/messages.hpp"
|
#include "connection/dbus/dbus.hpp"
|
||||||
|
#include "connection/dbus/messages.hpp"
|
||||||
|
|
||||||
class MprisController {
|
class MprisController : public DbusConnection {
|
||||||
public:
|
public:
|
||||||
struct PlayerState {
|
struct PlayerState {
|
||||||
std::string title;
|
std::string title;
|
||||||
@@ -51,7 +52,6 @@ class MprisController {
|
|||||||
|
|
||||||
PlaybackStatus currentPlaybackStatus = PlaybackStatus::Stopped;
|
PlaybackStatus currentPlaybackStatus = PlaybackStatus::Stopped;
|
||||||
|
|
||||||
Glib::RefPtr<Gio::DBus::Connection> m_connection;
|
|
||||||
Glib::RefPtr<Gio::DBus::Proxy> m_proxy;
|
Glib::RefPtr<Gio::DBus::Proxy> m_proxy;
|
||||||
Glib::RefPtr<Gio::DBus::Proxy> m_dbus_proxy;
|
Glib::RefPtr<Gio::DBus::Proxy> m_dbus_proxy;
|
||||||
std::string m_player_bus_name = "org.mpris.MediaPlayer2.spotify";
|
std::string m_player_bus_name = "org.mpris.MediaPlayer2.spotify";
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <gtkmm.h>
|
#include <gtkmm.h>
|
||||||
#include <sigc++/sigc++.h>
|
#include <sigc++/sigc++.h>
|
||||||
|
|
||||||
|
#include "connection/dbus/dbus.hpp"
|
||||||
#include "giomm/dbusconnection.h"
|
#include "giomm/dbusconnection.h"
|
||||||
#include "giomm/dbusownname.h"
|
#include "giomm/dbusownname.h"
|
||||||
#include "glib.h"
|
#include "glib.h"
|
||||||
@@ -39,7 +40,7 @@ const Glib::ustring introspection_xml = R"(
|
|||||||
</node>
|
</node>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
class NotificationService {
|
class NotificationService : public DbusConnection {
|
||||||
public:
|
public:
|
||||||
NotificationService() : notificationIdCounter(1) {
|
NotificationService() : notificationIdCounter(1) {
|
||||||
Gio::DBus::own_name(
|
Gio::DBus::own_name(
|
||||||
@@ -15,7 +15,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class TrayService {
|
#include "connection/dbus/dbus.hpp"
|
||||||
|
|
||||||
|
class TrayService : public DbusConnection {
|
||||||
inline static TrayService *instance = nullptr;
|
inline static TrayService *instance = nullptr;
|
||||||
public:
|
public:
|
||||||
struct Item {
|
struct Item {
|
||||||
@@ -85,7 +87,6 @@ class TrayService {
|
|||||||
bool addSignalPending = false;
|
bool addSignalPending = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Glib::RefPtr<Gio::DBus::Connection> connection;
|
|
||||||
Glib::RefPtr<Gio::DBus::NodeInfo> nodeInfo;
|
Glib::RefPtr<Gio::DBus::NodeInfo> nodeInfo;
|
||||||
Gio::DBus::InterfaceVTable vtable;
|
Gio::DBus::InterfaceVTable vtable;
|
||||||
|
|
||||||
36
include/connection/httpConnection.hpp
Normal file
36
include/connection/httpConnection.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct HttpResponse {
|
||||||
|
long status_code = 0;
|
||||||
|
std::string body;
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
std::string error;
|
||||||
|
|
||||||
|
bool ok() const {
|
||||||
|
return error.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpConnection {
|
||||||
|
public:
|
||||||
|
static HttpResponse get(const std::string &url,
|
||||||
|
const std::map<std::string, std::string> &headers = {},
|
||||||
|
long timeout_ms = 0);
|
||||||
|
|
||||||
|
static HttpResponse post(const std::string &url,
|
||||||
|
const std::string &body,
|
||||||
|
const std::map<std::string, std::string> &headers = {},
|
||||||
|
const std::string &content_type = "application/json",
|
||||||
|
long timeout_ms = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static HttpResponse performRequest(const std::string &method,
|
||||||
|
const std::string &url,
|
||||||
|
const std::string &body,
|
||||||
|
const std::map<std::string, std::string> &headers,
|
||||||
|
const std::string &content_type,
|
||||||
|
long timeout_ms);
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "services/dbus/messages.hpp"
|
#include "connection/dbus/messages.hpp"
|
||||||
#include "widgets/notification/baseNotification.hpp"
|
#include "widgets/notification/baseNotification.hpp"
|
||||||
|
|
||||||
#include "gdkmm/monitor.h"
|
#include "gdkmm/monitor.h"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "gtkmm/label.h"
|
#include "gtkmm/label.h"
|
||||||
#include "gtkmm/stack.h"
|
#include "gtkmm/stack.h"
|
||||||
#include "widgets/controlCenter/mediaControl.hpp"
|
#include "widgets/controlCenter/mediaControl.hpp"
|
||||||
|
#include "widgets/weather.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ class ControlCenter : public Popover {
|
|||||||
Gtk::Box tabRow;
|
Gtk::Box tabRow;
|
||||||
Gtk::Stack contentStack;
|
Gtk::Stack contentStack;
|
||||||
Gtk::Box controlCenterContainer;
|
Gtk::Box controlCenterContainer;
|
||||||
Gtk::Label testLabel;
|
WeatherWidget weatherWidget;
|
||||||
Gtk::Button mediaControl;
|
Gtk::Button mediaControl;
|
||||||
Gtk::Button testTabButton;
|
Gtk::Button testTabButton;
|
||||||
std::shared_ptr<MprisController> mprisController = MprisController::getInstance();
|
std::shared_ptr<MprisController> mprisController = MprisController::getInstance();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "gtkmm/scale.h"
|
#include "gtkmm/scale.h"
|
||||||
#include "gtkmm/scrolledwindow.h"
|
#include "gtkmm/scrolledwindow.h"
|
||||||
|
|
||||||
#include "services/dbus/mpris.hpp"
|
#include "connection/dbus/mpris.hpp"
|
||||||
|
|
||||||
class MediaControlWidget : public Gtk::Box {
|
class MediaControlWidget : public Gtk::Box {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include "services/dbus/messages.hpp"
|
#include "connection/dbus/messages.hpp"
|
||||||
#include "widgets/notification/baseNotification.hpp"
|
#include "widgets/notification/baseNotification.hpp"
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "services/dbus/messages.hpp"
|
#include "connection/dbus/messages.hpp"
|
||||||
#include "widgets/notification/baseNotification.hpp"
|
#include "widgets/notification/baseNotification.hpp"
|
||||||
|
|
||||||
class NotificationWindow : public BaseNotification {
|
class NotificationWindow : public BaseNotification {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "services/dbus/messages.hpp"
|
#include "connection/dbus/messages.hpp"
|
||||||
#include "widgets/notification/baseNotification.hpp"
|
#include "widgets/notification/baseNotification.hpp"
|
||||||
|
|
||||||
#include "gtkmm/centerbox.h"
|
#include "gtkmm/centerbox.h"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "services/tray.hpp"
|
#include "connection/dbus/tray.hpp"
|
||||||
#include "components/base/button.hpp"
|
#include "components/base/button.hpp"
|
||||||
|
|
||||||
class TrayIconWidget : public Button {
|
class TrayIconWidget : public Button {
|
||||||
|
|||||||
20
include/widgets/weather.hpp
Normal file
20
include/widgets/weather.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtkmm/box.h>
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
#include <sigc++/sigc++.h>
|
||||||
|
|
||||||
|
class WeatherWidget : public Gtk::Box {
|
||||||
|
public:
|
||||||
|
WeatherWidget();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Gtk::Label titleLabel;
|
||||||
|
Gtk::Label currentLabel;
|
||||||
|
Gtk::Label todayLabel;
|
||||||
|
|
||||||
|
bool onRefreshTick();
|
||||||
|
void fetchWeather();
|
||||||
|
void applyWeatherText(const std::string ¤t_text,
|
||||||
|
const std::string &today_text);
|
||||||
|
};
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#include <sigc++/sigc++.h>
|
#include <sigc++/sigc++.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "services/dbus/notification.hpp"
|
#include "connection/dbus/notification.hpp"
|
||||||
#include "services/dbus/mpris.hpp"
|
#include "connection/dbus/mpris.hpp"
|
||||||
#include "services/notificationController.hpp"
|
#include "services/notificationController.hpp"
|
||||||
#include "services/textureCache.hpp"
|
#include "services/textureCache.hpp"
|
||||||
|
|
||||||
|
|||||||
0
src/connection/dbus/dbus.cpp
Normal file
0
src/connection/dbus/dbus.cpp
Normal file
@@ -1,4 +1,4 @@
|
|||||||
#include "services/dbus/mpris.hpp"
|
#include "connection/dbus/mpris.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
@@ -19,16 +19,12 @@ std::shared_ptr<MprisController> MprisController::createForPlayer(const std::str
|
|||||||
}
|
}
|
||||||
|
|
||||||
MprisController::MprisController() {
|
MprisController::MprisController() {
|
||||||
Gio::DBus::Connection::get(
|
connect_session_async(sigc::mem_fun(*this, &MprisController::on_bus_connected));
|
||||||
Gio::DBus::BusType::SESSION,
|
|
||||||
sigc::mem_fun(*this, &MprisController::on_bus_connected));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MprisController::MprisController(const std::string &bus_name)
|
MprisController::MprisController(const std::string &bus_name)
|
||||||
: m_player_bus_name(bus_name) {
|
: m_player_bus_name(bus_name) {
|
||||||
Gio::DBus::Connection::get(
|
connect_session_async(sigc::mem_fun(*this, &MprisController::on_bus_connected));
|
||||||
Gio::DBus::BusType::SESSION,
|
|
||||||
sigc::mem_fun(*this, &MprisController::on_bus_connected));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> MprisController::get_registered_players() const {
|
std::vector<std::string> MprisController::get_registered_players() const {
|
||||||
@@ -65,11 +61,11 @@ void MprisController::on_bus_connected(const Glib::RefPtr<Gio::AsyncResult> &res
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
m_connection = Gio::DBus::Connection::get_finish(result);
|
connection = Gio::DBus::Connection::get_finish(result);
|
||||||
|
|
||||||
if (!m_dbus_proxy) {
|
if (!m_dbus_proxy) {
|
||||||
m_dbus_proxy = Gio::DBus::Proxy::create_sync(
|
m_dbus_proxy = Gio::DBus::Proxy::create_sync(
|
||||||
m_connection,
|
connection,
|
||||||
"org.freedesktop.DBus",
|
"org.freedesktop.DBus",
|
||||||
"/org/freedesktop/DBus",
|
"/org/freedesktop/DBus",
|
||||||
"org.freedesktop.DBus");
|
"org.freedesktop.DBus");
|
||||||
@@ -131,10 +127,10 @@ void MprisController::handle_player_registered(const std::string &bus_name) {
|
|||||||
|
|
||||||
registeredPlayers.insert(bus_name);
|
registeredPlayers.insert(bus_name);
|
||||||
|
|
||||||
if (bus_name == m_player_bus_name && !m_proxy && m_connection) {
|
if (bus_name == m_player_bus_name && !m_proxy && connection) {
|
||||||
try {
|
try {
|
||||||
m_proxy = Gio::DBus::Proxy::create_sync(
|
m_proxy = Gio::DBus::Proxy::create_sync(
|
||||||
m_connection,
|
connection,
|
||||||
m_player_bus_name,
|
m_player_bus_name,
|
||||||
"/org/mpris/MediaPlayer2",
|
"/org/mpris/MediaPlayer2",
|
||||||
"org.mpris.MediaPlayer2.Player");
|
"org.mpris.MediaPlayer2.Player");
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
#include "services/dbus/notification.hpp"
|
#include "connection/dbus/notification.hpp"
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "helpers/string.hpp"
|
|
||||||
#include "services/notificationController.hpp"
|
#include "services/notificationController.hpp"
|
||||||
#include "widgets/notification/copyNotification.hpp"
|
|
||||||
|
|
||||||
#include "glib.h"
|
#include "glib.h"
|
||||||
#include "glibconfig.h"
|
#include "glibconfig.h"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "services/tray.hpp"
|
#include "connection/dbus/tray.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -277,11 +277,7 @@ void TrayService::start() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
ensure_gio_init();
|
||||||
Gio::init();
|
|
||||||
} catch (const Glib::Error &) {
|
|
||||||
// Already initialised; ignore.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nodeInfo) {
|
if (!nodeInfo) {
|
||||||
try {
|
try {
|
||||||
119
src/connection/httpConnection.cpp
Normal file
119
src/connection/httpConnection.cpp
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#include "connection/httpConnection.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
size_t write_to_string(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 trim(std::string value) {
|
||||||
|
auto not_space = [](unsigned char c) { return std::isspace(c) == 0; };
|
||||||
|
value.erase(value.begin(),
|
||||||
|
std::find_if(value.begin(), value.end(), not_space));
|
||||||
|
value.erase(std::find_if(value.rbegin(), value.rend(), not_space).base(),
|
||||||
|
value.end());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t header_to_map(char *buffer, size_t size, size_t nitems, void *userdata) {
|
||||||
|
size_t total = size * nitems;
|
||||||
|
auto *header_map = static_cast<std::map<std::string, std::string> *>(userdata);
|
||||||
|
|
||||||
|
std::string line(buffer, total);
|
||||||
|
auto colon = line.find(':');
|
||||||
|
if (colon != std::string::npos) {
|
||||||
|
auto key = trim(line.substr(0, colon));
|
||||||
|
auto value = trim(line.substr(colon + 1));
|
||||||
|
if (!key.empty()) {
|
||||||
|
header_map->insert_or_assign(std::move(key), std::move(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse HttpConnection::get(const std::string &url,
|
||||||
|
const std::map<std::string, std::string> &headers,
|
||||||
|
long timeout_ms) {
|
||||||
|
return performRequest("GET", url, std::string(), headers, std::string(), timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse HttpConnection::post(const std::string &url,
|
||||||
|
const std::string &body,
|
||||||
|
const std::map<std::string, std::string> &headers,
|
||||||
|
const std::string &content_type,
|
||||||
|
long timeout_ms) {
|
||||||
|
return performRequest("POST", url, body, headers, content_type, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse HttpConnection::performRequest(const std::string &method,
|
||||||
|
const std::string &url,
|
||||||
|
const std::string &body,
|
||||||
|
const std::map<std::string, std::string> &headers,
|
||||||
|
const std::string &content_type,
|
||||||
|
long timeout_ms) {
|
||||||
|
HttpResponse response;
|
||||||
|
|
||||||
|
CURL *curl = curl_easy_init();
|
||||||
|
if (!curl) {
|
||||||
|
response.error = "curl_easy_init failed";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response.body);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_to_map);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response.headers);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "bar/1.0");
|
||||||
|
|
||||||
|
if (timeout_ms > 0) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == "POST") {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(body.size()));
|
||||||
|
} else {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct curl_slist *header_list = nullptr;
|
||||||
|
for (const auto &pair : headers) {
|
||||||
|
std::string header = pair.first + ": " + pair.second;
|
||||||
|
header_list = curl_slist_append(header_list, header.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == "POST" && !content_type.empty()) {
|
||||||
|
std::string content_header = "Content-Type: " + content_type;
|
||||||
|
header_list = curl_slist_append(header_list, content_header.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header_list) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = curl_easy_perform(curl);
|
||||||
|
if (result != CURLE_OK) {
|
||||||
|
response.error = curl_easy_strerror(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.status_code);
|
||||||
|
|
||||||
|
if (header_list) {
|
||||||
|
curl_slist_free_all(header_list);
|
||||||
|
}
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "services/dbus/messages.hpp"
|
#include "connection/dbus/messages.hpp"
|
||||||
#include "widgets/notification/baseNotification.hpp"
|
#include "widgets/notification/baseNotification.hpp"
|
||||||
#include "widgets/notification/copyNotification.hpp"
|
#include "widgets/notification/copyNotification.hpp"
|
||||||
#include "widgets/notification/notificationWindow.hpp"
|
#include "widgets/notification/notificationWindow.hpp"
|
||||||
|
|||||||
@@ -34,10 +34,8 @@ ControlCenter::ControlCenter(std::string icon, std::string name)
|
|||||||
this->controlCenterContainer.set_orientation(Gtk::Orientation::VERTICAL);
|
this->controlCenterContainer.set_orientation(Gtk::Orientation::VERTICAL);
|
||||||
this->controlCenterContainer.set_spacing(4);
|
this->controlCenterContainer.set_spacing(4);
|
||||||
|
|
||||||
this->testLabel.set_text("Test tab");
|
|
||||||
|
|
||||||
this->contentStack.add(this->controlCenterContainer, "controls", "Controls");
|
this->contentStack.add(this->controlCenterContainer, "controls", "Controls");
|
||||||
this->contentStack.add(this->testLabel, "test", "Test");
|
this->contentStack.add(this->weatherWidget, "test", "Test");
|
||||||
this->contentStack.set_visible_child("controls");
|
this->contentStack.set_visible_child("controls");
|
||||||
this->setActiveTab("controls");
|
this->setActiveTab("controls");
|
||||||
|
|
||||||
|
|||||||
112
src/widgets/weather.cpp
Normal file
112
src/widgets/weather.cpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
580
weather.json
Normal file
580
weather.json
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
{
|
||||||
|
"latitude": 49.0,
|
||||||
|
"longitude": 8.4,
|
||||||
|
"generationtime_ms": 0.23317337036132812,
|
||||||
|
"utc_offset_seconds": 3600,
|
||||||
|
"timezone": "Europe/Berlin",
|
||||||
|
"timezone_abbreviation": "GMT+1",
|
||||||
|
"elevation": 122.0,
|
||||||
|
"current_units": {
|
||||||
|
"time": "iso8601",
|
||||||
|
"interval": "seconds",
|
||||||
|
"temperature_2m": "°C"
|
||||||
|
},
|
||||||
|
"current": {
|
||||||
|
"time": "2026-02-03T23:00",
|
||||||
|
"interval": 900,
|
||||||
|
"temperature_2m": -0.7
|
||||||
|
},
|
||||||
|
"hourly_units": {
|
||||||
|
"time": "iso8601",
|
||||||
|
"temperature_2m": "°C",
|
||||||
|
"rain": "mm"
|
||||||
|
},
|
||||||
|
"hourly": {
|
||||||
|
"time": [
|
||||||
|
"2026-02-03T00:00",
|
||||||
|
"2026-02-03T01:00",
|
||||||
|
"2026-02-03T02:00",
|
||||||
|
"2026-02-03T03:00",
|
||||||
|
"2026-02-03T04:00",
|
||||||
|
"2026-02-03T05:00",
|
||||||
|
"2026-02-03T06:00",
|
||||||
|
"2026-02-03T07:00",
|
||||||
|
"2026-02-03T08:00",
|
||||||
|
"2026-02-03T09:00",
|
||||||
|
"2026-02-03T10:00",
|
||||||
|
"2026-02-03T11:00",
|
||||||
|
"2026-02-03T12:00",
|
||||||
|
"2026-02-03T13:00",
|
||||||
|
"2026-02-03T14:00",
|
||||||
|
"2026-02-03T15:00",
|
||||||
|
"2026-02-03T16:00",
|
||||||
|
"2026-02-03T17:00",
|
||||||
|
"2026-02-03T18:00",
|
||||||
|
"2026-02-03T19:00",
|
||||||
|
"2026-02-03T20:00",
|
||||||
|
"2026-02-03T21:00",
|
||||||
|
"2026-02-03T22:00",
|
||||||
|
"2026-02-03T23:00",
|
||||||
|
"2026-02-04T00:00",
|
||||||
|
"2026-02-04T01:00",
|
||||||
|
"2026-02-04T02:00",
|
||||||
|
"2026-02-04T03:00",
|
||||||
|
"2026-02-04T04:00",
|
||||||
|
"2026-02-04T05:00",
|
||||||
|
"2026-02-04T06:00",
|
||||||
|
"2026-02-04T07:00",
|
||||||
|
"2026-02-04T08:00",
|
||||||
|
"2026-02-04T09:00",
|
||||||
|
"2026-02-04T10:00",
|
||||||
|
"2026-02-04T11:00",
|
||||||
|
"2026-02-04T12:00",
|
||||||
|
"2026-02-04T13:00",
|
||||||
|
"2026-02-04T14:00",
|
||||||
|
"2026-02-04T15:00",
|
||||||
|
"2026-02-04T16:00",
|
||||||
|
"2026-02-04T17:00",
|
||||||
|
"2026-02-04T18:00",
|
||||||
|
"2026-02-04T19:00",
|
||||||
|
"2026-02-04T20:00",
|
||||||
|
"2026-02-04T21:00",
|
||||||
|
"2026-02-04T22:00",
|
||||||
|
"2026-02-04T23:00",
|
||||||
|
"2026-02-05T00:00",
|
||||||
|
"2026-02-05T01:00",
|
||||||
|
"2026-02-05T02:00",
|
||||||
|
"2026-02-05T03:00",
|
||||||
|
"2026-02-05T04:00",
|
||||||
|
"2026-02-05T05:00",
|
||||||
|
"2026-02-05T06:00",
|
||||||
|
"2026-02-05T07:00",
|
||||||
|
"2026-02-05T08:00",
|
||||||
|
"2026-02-05T09:00",
|
||||||
|
"2026-02-05T10:00",
|
||||||
|
"2026-02-05T11:00",
|
||||||
|
"2026-02-05T12:00",
|
||||||
|
"2026-02-05T13:00",
|
||||||
|
"2026-02-05T14:00",
|
||||||
|
"2026-02-05T15:00",
|
||||||
|
"2026-02-05T16:00",
|
||||||
|
"2026-02-05T17:00",
|
||||||
|
"2026-02-05T18:00",
|
||||||
|
"2026-02-05T19:00",
|
||||||
|
"2026-02-05T20:00",
|
||||||
|
"2026-02-05T21:00",
|
||||||
|
"2026-02-05T22:00",
|
||||||
|
"2026-02-05T23:00",
|
||||||
|
"2026-02-06T00:00",
|
||||||
|
"2026-02-06T01:00",
|
||||||
|
"2026-02-06T02:00",
|
||||||
|
"2026-02-06T03:00",
|
||||||
|
"2026-02-06T04:00",
|
||||||
|
"2026-02-06T05:00",
|
||||||
|
"2026-02-06T06:00",
|
||||||
|
"2026-02-06T07:00",
|
||||||
|
"2026-02-06T08:00",
|
||||||
|
"2026-02-06T09:00",
|
||||||
|
"2026-02-06T10:00",
|
||||||
|
"2026-02-06T11:00",
|
||||||
|
"2026-02-06T12:00",
|
||||||
|
"2026-02-06T13:00",
|
||||||
|
"2026-02-06T14:00",
|
||||||
|
"2026-02-06T15:00",
|
||||||
|
"2026-02-06T16:00",
|
||||||
|
"2026-02-06T17:00",
|
||||||
|
"2026-02-06T18:00",
|
||||||
|
"2026-02-06T19:00",
|
||||||
|
"2026-02-06T20:00",
|
||||||
|
"2026-02-06T21:00",
|
||||||
|
"2026-02-06T22:00",
|
||||||
|
"2026-02-06T23:00",
|
||||||
|
"2026-02-07T00:00",
|
||||||
|
"2026-02-07T01:00",
|
||||||
|
"2026-02-07T02:00",
|
||||||
|
"2026-02-07T03:00",
|
||||||
|
"2026-02-07T04:00",
|
||||||
|
"2026-02-07T05:00",
|
||||||
|
"2026-02-07T06:00",
|
||||||
|
"2026-02-07T07:00",
|
||||||
|
"2026-02-07T08:00",
|
||||||
|
"2026-02-07T09:00",
|
||||||
|
"2026-02-07T10:00",
|
||||||
|
"2026-02-07T11:00",
|
||||||
|
"2026-02-07T12:00",
|
||||||
|
"2026-02-07T13:00",
|
||||||
|
"2026-02-07T14:00",
|
||||||
|
"2026-02-07T15:00",
|
||||||
|
"2026-02-07T16:00",
|
||||||
|
"2026-02-07T17:00",
|
||||||
|
"2026-02-07T18:00",
|
||||||
|
"2026-02-07T19:00",
|
||||||
|
"2026-02-07T20:00",
|
||||||
|
"2026-02-07T21:00",
|
||||||
|
"2026-02-07T22:00",
|
||||||
|
"2026-02-07T23:00",
|
||||||
|
"2026-02-08T00:00",
|
||||||
|
"2026-02-08T01:00",
|
||||||
|
"2026-02-08T02:00",
|
||||||
|
"2026-02-08T03:00",
|
||||||
|
"2026-02-08T04:00",
|
||||||
|
"2026-02-08T05:00",
|
||||||
|
"2026-02-08T06:00",
|
||||||
|
"2026-02-08T07:00",
|
||||||
|
"2026-02-08T08:00",
|
||||||
|
"2026-02-08T09:00",
|
||||||
|
"2026-02-08T10:00",
|
||||||
|
"2026-02-08T11:00",
|
||||||
|
"2026-02-08T12:00",
|
||||||
|
"2026-02-08T13:00",
|
||||||
|
"2026-02-08T14:00",
|
||||||
|
"2026-02-08T15:00",
|
||||||
|
"2026-02-08T16:00",
|
||||||
|
"2026-02-08T17:00",
|
||||||
|
"2026-02-08T18:00",
|
||||||
|
"2026-02-08T19:00",
|
||||||
|
"2026-02-08T20:00",
|
||||||
|
"2026-02-08T21:00",
|
||||||
|
"2026-02-08T22:00",
|
||||||
|
"2026-02-08T23:00",
|
||||||
|
"2026-02-09T00:00",
|
||||||
|
"2026-02-09T01:00",
|
||||||
|
"2026-02-09T02:00",
|
||||||
|
"2026-02-09T03:00",
|
||||||
|
"2026-02-09T04:00",
|
||||||
|
"2026-02-09T05:00",
|
||||||
|
"2026-02-09T06:00",
|
||||||
|
"2026-02-09T07:00",
|
||||||
|
"2026-02-09T08:00",
|
||||||
|
"2026-02-09T09:00",
|
||||||
|
"2026-02-09T10:00",
|
||||||
|
"2026-02-09T11:00",
|
||||||
|
"2026-02-09T12:00",
|
||||||
|
"2026-02-09T13:00",
|
||||||
|
"2026-02-09T14:00",
|
||||||
|
"2026-02-09T15:00",
|
||||||
|
"2026-02-09T16:00",
|
||||||
|
"2026-02-09T17:00",
|
||||||
|
"2026-02-09T18:00",
|
||||||
|
"2026-02-09T19:00",
|
||||||
|
"2026-02-09T20:00",
|
||||||
|
"2026-02-09T21:00",
|
||||||
|
"2026-02-09T22:00",
|
||||||
|
"2026-02-09T23:00"
|
||||||
|
],
|
||||||
|
"temperature_2m": [
|
||||||
|
2.2,
|
||||||
|
2.2,
|
||||||
|
2.2,
|
||||||
|
2.2,
|
||||||
|
2.0,
|
||||||
|
1.9,
|
||||||
|
1.8,
|
||||||
|
1.5,
|
||||||
|
1.2,
|
||||||
|
1.4,
|
||||||
|
1.8,
|
||||||
|
2.2,
|
||||||
|
2.5,
|
||||||
|
1.9,
|
||||||
|
2.6,
|
||||||
|
3.0,
|
||||||
|
2.7,
|
||||||
|
3.0,
|
||||||
|
3.0,
|
||||||
|
2.4,
|
||||||
|
1.7,
|
||||||
|
1.3,
|
||||||
|
0.9,
|
||||||
|
-0.7,
|
||||||
|
-0.2,
|
||||||
|
-0.1,
|
||||||
|
-0.5,
|
||||||
|
-0.7,
|
||||||
|
-1.4,
|
||||||
|
-1.1,
|
||||||
|
-1.0,
|
||||||
|
-1.0,
|
||||||
|
-1.1,
|
||||||
|
-0.9,
|
||||||
|
1.7,
|
||||||
|
3.7,
|
||||||
|
5.0,
|
||||||
|
5.8,
|
||||||
|
6.5,
|
||||||
|
6.9,
|
||||||
|
6.6,
|
||||||
|
5.9,
|
||||||
|
4.8,
|
||||||
|
3.6,
|
||||||
|
2.7,
|
||||||
|
2.0,
|
||||||
|
1.5,
|
||||||
|
1.1,
|
||||||
|
0.5,
|
||||||
|
0.2,
|
||||||
|
0.4,
|
||||||
|
0.9,
|
||||||
|
1.2,
|
||||||
|
1.3,
|
||||||
|
1.5,
|
||||||
|
1.7,
|
||||||
|
1.8,
|
||||||
|
2.0,
|
||||||
|
2.2,
|
||||||
|
2.7,
|
||||||
|
3.4,
|
||||||
|
4.2,
|
||||||
|
5.2,
|
||||||
|
6.1,
|
||||||
|
6.4,
|
||||||
|
5.9,
|
||||||
|
4.5,
|
||||||
|
3.2,
|
||||||
|
2.4,
|
||||||
|
1.9,
|
||||||
|
1.4,
|
||||||
|
1.1,
|
||||||
|
1.0,
|
||||||
|
0.8,
|
||||||
|
0.5,
|
||||||
|
0.3,
|
||||||
|
0.1,
|
||||||
|
-0.1,
|
||||||
|
-0.2,
|
||||||
|
-0.3,
|
||||||
|
-0.3,
|
||||||
|
-0.1,
|
||||||
|
0.7,
|
||||||
|
1.7,
|
||||||
|
2.9,
|
||||||
|
4.1,
|
||||||
|
5.0,
|
||||||
|
5.2,
|
||||||
|
5.1,
|
||||||
|
4.8,
|
||||||
|
4.3,
|
||||||
|
4.1,
|
||||||
|
4.2,
|
||||||
|
4.1,
|
||||||
|
3.8,
|
||||||
|
3.5,
|
||||||
|
3.3,
|
||||||
|
3.4,
|
||||||
|
3.4,
|
||||||
|
3.5,
|
||||||
|
3.5,
|
||||||
|
3.5,
|
||||||
|
3.4,
|
||||||
|
3.4,
|
||||||
|
3.5,
|
||||||
|
3.6,
|
||||||
|
4.1,
|
||||||
|
5.2,
|
||||||
|
6.6,
|
||||||
|
7.8,
|
||||||
|
8.4,
|
||||||
|
8.8,
|
||||||
|
8.8,
|
||||||
|
8.1,
|
||||||
|
6.9,
|
||||||
|
5.9,
|
||||||
|
5.3,
|
||||||
|
4.9,
|
||||||
|
4.6,
|
||||||
|
4.3,
|
||||||
|
4.2,
|
||||||
|
4.0,
|
||||||
|
3.9,
|
||||||
|
3.9,
|
||||||
|
3.8,
|
||||||
|
3.8,
|
||||||
|
3.8,
|
||||||
|
3.9,
|
||||||
|
4.3,
|
||||||
|
4.7,
|
||||||
|
5.3,
|
||||||
|
6.0,
|
||||||
|
6.9,
|
||||||
|
7.5,
|
||||||
|
7.6,
|
||||||
|
7.5,
|
||||||
|
7.2,
|
||||||
|
6.6,
|
||||||
|
5.8,
|
||||||
|
5.0,
|
||||||
|
4.4,
|
||||||
|
3.9,
|
||||||
|
3.5,
|
||||||
|
3.1,
|
||||||
|
2.9,
|
||||||
|
2.8,
|
||||||
|
3.0,
|
||||||
|
3.3,
|
||||||
|
3.6,
|
||||||
|
3.5,
|
||||||
|
3.4,
|
||||||
|
3.4,
|
||||||
|
3.7,
|
||||||
|
4.2,
|
||||||
|
4.7,
|
||||||
|
5.3,
|
||||||
|
6.0,
|
||||||
|
6.5,
|
||||||
|
6.8,
|
||||||
|
6.8,
|
||||||
|
6.6,
|
||||||
|
5.9,
|
||||||
|
4.8,
|
||||||
|
3.9,
|
||||||
|
3.5,
|
||||||
|
3.3,
|
||||||
|
3.1,
|
||||||
|
2.9
|
||||||
|
],
|
||||||
|
"rain": [
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.10,
|
||||||
|
0.40,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.20,
|
||||||
|
0.60,
|
||||||
|
0.30,
|
||||||
|
1.30,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.40,
|
||||||
|
0.10,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.10,
|
||||||
|
0.20,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.10,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.00
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"daily_units": {
|
||||||
|
"time": "iso8601",
|
||||||
|
"temperature_2m_max": "°C",
|
||||||
|
"temperature_2m_min": "°C",
|
||||||
|
"weather_code": "wmo code"
|
||||||
|
},
|
||||||
|
"daily": {
|
||||||
|
"time": [
|
||||||
|
"2026-02-03",
|
||||||
|
"2026-02-04",
|
||||||
|
"2026-02-05",
|
||||||
|
"2026-02-06",
|
||||||
|
"2026-02-07",
|
||||||
|
"2026-02-08",
|
||||||
|
"2026-02-09"
|
||||||
|
],
|
||||||
|
"temperature_2m_max": [
|
||||||
|
3.0,
|
||||||
|
6.9,
|
||||||
|
6.4,
|
||||||
|
5.2,
|
||||||
|
8.8,
|
||||||
|
7.6,
|
||||||
|
6.8
|
||||||
|
],
|
||||||
|
"temperature_2m_min": [
|
||||||
|
-0.7,
|
||||||
|
-1.4,
|
||||||
|
0.2,
|
||||||
|
-0.3,
|
||||||
|
3.3,
|
||||||
|
3.1,
|
||||||
|
2.8
|
||||||
|
],
|
||||||
|
"weather_code": [
|
||||||
|
61,
|
||||||
|
45,
|
||||||
|
45,
|
||||||
|
61,
|
||||||
|
61,
|
||||||
|
3,
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user