From 330f2e964c9262538be8a060a4777f170163244f Mon Sep 17 00:00:00 2001 From: quadbyte Date: Mon, 11 Aug 2025 16:35:48 -0400 Subject: [PATCH] Location Service WIP --- Services/Location.qml | 126 ++++++++++++++++++++++++++++++++++++++++-- Services/Settings.qml | 29 ++++++---- 2 files changed, 139 insertions(+), 16 deletions(-) diff --git a/Services/Location.qml b/Services/Location.qml index 64d8820..95336ab 100644 --- a/Services/Location.qml +++ b/Services/Location.qml @@ -1,10 +1,128 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.Services pragma Singleton -import Quickshell -import qs.Services - // Weather logic and caching -// Calendar Hollidays logic and caching Singleton { id: root + + property string locationFile: Quickshell.env("NOCTALIA_WEATHER_FILE") || (Settings.cacheDir + "location.json") + + // Used to access via Location.data.xxx.yyy + property var data: adapter + + function quickstart() { + console.log(locationFile) + } + + FileView { + path: locationFile + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: writeAdapter() + Component.onCompleted: function () { + reload() + } + onLoaded: function () {} + onLoadFailed: function (error) { + if (error.toString().includes("No such file") || error === 2) + // File doesn't exist, create it with default values + writeAdapter() + } + + JsonAdapter { + id: adapter + + // main + property JsonObject main + + main: JsonObject { + property string latitude: "" + property string longitude: "" + property int weatherLastFetched: 0 + } + + // weather + property JsonObject weather + + weather: JsonObject { + } + } + } + + // -------------------------------- + function getWeather() { + if (data.main.latitude === "" || data.main.longitude === "") { + geocodeLocation(Settings.data.location.name, function (lat, lon) { + console.log(Settings.data.location.name + ": " + lat + " / " + lon); + }) + } + } + + // -------------------------------- + function geocodeLocation(locationName, callback, errorCallback) { + var geoUrl = "https://geocoding-api.open-meteo.com/v1/search?name=" + encodeURIComponent( + locationName) + "&language=en&format=json" + var xhr = new XMLHttpRequest() + xhr.onreadystatechange = function () { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + try { + var geoData = JSON.parse(xhr.responseText) + if (geoData.results && geoData.results.length > 0) { + callback(geoData.results[0].latitude, geoData.results[0].longitude) + } else { + errorCallback("Location not found.") + } + } catch (e) { + errorCallback("Failed to parse geocoding data.") + } + } else { + errorCallback("Geocoding error: " + xhr.status) + } + } + } + xhr.open("GET", geoUrl) + xhr.send() + } + + // -------------------------------- + function fetchWeather(latitude, longitude, callback, errorCallback) { + var url = "https://api.open-meteo.com/v1/forecast?latitude=" + latitude + "&longitude=" + longitude + + "¤t_weather=true¤t=relativehumidity_2m,surface_pressure&daily=temperature_2m_max,temperature_2m_min,weathercode&timezone=auto" + var xhr = new XMLHttpRequest() + xhr.onreadystatechange = function () { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + try { + var weatherData = JSON.parse(xhr.responseText) + callback(weatherData) + } catch (e) { + errorCallback("Failed to parse weather data.") + } + } else { + errorCallback("Weather fetch error: " + xhr.status) + } + } + } + xhr.open("GET", url) + xhr.send() + } + + + + // function fetchCityWeather(city, callback, errorCallback) { + // fetchCoordinates(city, function (lat, lon) { + // fetchWeather(lat, lon, function (weatherData) { + // callback({ + // "city": city, + // "latitude": lat, + // "longitude": lon, + // "weather": weatherData + // }) + // }, errorCallback) + // }, errorCallback) + // } } diff --git a/Services/Settings.qml b/Services/Settings.qml index 5d20d13..647bdec 100644 --- a/Services/Settings.qml +++ b/Services/Settings.qml @@ -7,23 +7,30 @@ pragma Singleton Singleton { id: root + // Define our app directories + // Default config directory: ~/.config/noctalia + // Default cache directory: ~/.cache/noctalia property string shellName: "noctalia" - property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") - || (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env( - "HOME") + "/.config") + "/" + shellName + "/" - property string cacheDir: Quickshell.env("NOCTALIA_CACHE_DIR") - || (Quickshell.env("XDG_CACHE_HOME") || Quickshell.env( - "HOME") + "/.cache") + "/" + shellName + "/" + property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") + || Quickshell.env( + "HOME") + "/.config") + "/" + shellName + "/" + property string cacheDir: Quickshell.env("NOCTALIA_CACHE_DIR") || (Quickshell.env("XDG_CACHE_HOME") || Quickshell.env( + "HOME") + "/.cache") + "/" + shellName + "/" + property string settingsFile: Quickshell.env("NOCTALIA_SETTINGS_FILE") || (configDir + "settings.json") property string colorsFile: Quickshell.env("NOCTALIA_COLORS_FILE") || (configDir + "colors.json") - property var data: settingAdapter property string defaultWallpaper: Qt.resolvedUrl("../Assets/Tests/wallpaper.png") + property string defaultAvatar: Quickshell.env("HOME") + "/.face" + + // Used to access via Settings.data.xxx.yyy + property var data: adapter // Needed to only have one NPanel loaded at a time. // property var openPanel: null Item { Component.onCompleted: { + // ensure settings dir exists Quickshell.execDetached(["mkdir", "-p", configDir]) Quickshell.execDetached(["mkdir", "-p", cacheDir]) @@ -36,8 +43,6 @@ Singleton { // Qt.callLater(function () { // WallpaperManager.setCurrentWallpaper(settings.currentWallpaper, true); // }) - id: settingFileView - path: settingsFile watchChanges: true onFileChanged: reload() @@ -53,7 +58,7 @@ Singleton { } JsonAdapter { - id: settingAdapter + id: adapter // bar property JsonObject bar @@ -70,7 +75,7 @@ Singleton { property JsonObject general general: JsonObject { - property string avatarImage: Quickshell.env("HOME") + "/.face" + property string avatarImage: defaultAvatar property bool dimDesktop: true property bool showScreenCorners: false property bool showDock: false @@ -80,7 +85,7 @@ Singleton { property JsonObject location location: JsonObject { - property bool name: true + property string name: "Dinslaken" property bool useFahrenheit: false property bool reverseDayMonth: false property bool use12HourClock: false