Location service with caching

This commit is contained in:
quadbyte 2025-08-11 17:57:09 -04:00
parent 330f2e964c
commit 644f647653
2 changed files with 63 additions and 43 deletions

View file

@ -9,13 +9,8 @@ Singleton {
id: root id: root
property string locationFile: Quickshell.env("NOCTALIA_WEATHER_FILE") || (Settings.cacheDir + "location.json") property string locationFile: Quickshell.env("NOCTALIA_WEATHER_FILE") || (Settings.cacheDir + "location.json")
property int weatherUpdateFrequency: 30 * 60 * 1000 // 30 minutes expressed in milliseconds
// Used to access via Location.data.xxx.yyy property var data: adapter // Used to access via Location.data.xxx.yyy
property var data: adapter
function quickstart() {
console.log(locationFile)
}
FileView { FileView {
path: locationFile path: locationFile
@ -25,44 +20,70 @@ Singleton {
Component.onCompleted: function () { Component.onCompleted: function () {
reload() reload()
} }
onLoaded: function () {} onLoaded: function () {
updateWeather()
}
onLoadFailed: function (error) { onLoadFailed: function (error) {
if (error.toString().includes("No such file") || error === 2) if (error.toString().includes("No such file") || error === 2) {
// File doesn't exist, create it with default values // File doesn't exist, create it with default values
writeAdapter() writeAdapter()
}
} }
JsonAdapter { JsonAdapter {
id: adapter id: adapter
// main property string latitude: ""
property JsonObject main property string longitude: ""
property string weatherLastFetch: ""
property var weather: null
}
}
main: JsonObject {
property string latitude: ""
property string longitude: ""
property int weatherLastFetched: 0
}
// weather Timer {
property JsonObject weather id: updateTimer
interval: 60 * 1000
weather: JsonObject { repeat: true
} onTriggered: {
updateWeather();
} }
} }
// -------------------------------- // --------------------------------
function getWeather() { function init() {
if (data.main.latitude === "" || data.main.longitude === "") { // does nothing but ensure the singleton is created
geocodeLocation(Settings.data.location.name, function (lat, lon) { }
console.log(Settings.data.location.name + ": " + lat + " / " + lon);
})
// --------------------------------
function updateWeather() {
var now = Date.now()
if ((data.weatherLastFetch === "") || (now >= data.weatherLastFetch + weatherUpdateFrequency)) {
getFreshWeather()
} }
} }
// -------------------------------- // --------------------------------
function geocodeLocation(locationName, callback, errorCallback) { function getFreshWeather() {
if (data.latitude === "" || data.longitude === "") {
console.log("Geocoding location")
_geocodeLocation(Settings.data.location.name, function (lat, lon) {
console.log("Geocoded " + Settings.data.location.name + " to : " + lat + " / " + lon)
// Save GPS coordinates
data.latitude = lat
data.longitude = lon
_fetchWeather(data.latitude, data.longitude, errorCallback)
}, errorCallback)
} else {
_fetchWeather(data.latitude, data.longitude, errorCallback)
}
}
// --------------------------------
function _geocodeLocation(locationName, callback, errorCallback) {
var geoUrl = "https://geocoding-api.open-meteo.com/v1/search?name=" + encodeURIComponent( var geoUrl = "https://geocoding-api.open-meteo.com/v1/search?name=" + encodeURIComponent(
locationName) + "&language=en&format=json" locationName) + "&language=en&format=json"
var xhr = new XMLHttpRequest() var xhr = new XMLHttpRequest()
@ -89,7 +110,7 @@ Singleton {
} }
// -------------------------------- // --------------------------------
function fetchWeather(latitude, longitude, callback, errorCallback) { function _fetchWeather(latitude, longitude, errorCallback) {
var url = "https://api.open-meteo.com/v1/forecast?latitude=" + latitude + "&longitude=" + longitude var url = "https://api.open-meteo.com/v1/forecast?latitude=" + latitude + "&longitude=" + longitude
+ "&current_weather=true&current=relativehumidity_2m,surface_pressure&daily=temperature_2m_max,temperature_2m_min,weathercode&timezone=auto" + "&current_weather=true&current=relativehumidity_2m,surface_pressure&daily=temperature_2m_max,temperature_2m_min,weathercode&timezone=auto"
var xhr = new XMLHttpRequest() var xhr = new XMLHttpRequest()
@ -98,7 +119,11 @@ Singleton {
if (xhr.status === 200) { if (xhr.status === 200) {
try { try {
var weatherData = JSON.parse(xhr.responseText) var weatherData = JSON.parse(xhr.responseText)
callback(weatherData)
// Save to json
data.weather = weatherData
data.weatherLastFetch = Date.now()
console.log("Cached weather to disk")
} catch (e) { } catch (e) {
errorCallback("Failed to parse weather data.") errorCallback("Failed to parse weather data.")
} }
@ -111,18 +136,8 @@ Singleton {
xhr.send() xhr.send()
} }
// --------------------------------
function errorCallback(message) {
// function fetchCityWeather(city, callback, errorCallback) { console.error(message)
// fetchCoordinates(city, function (lat, lon) { }
// fetchWeather(lat, lon, function (weatherData) {
// callback({
// "city": city,
// "latitude": lat,
// "longitude": lon,
// "weather": weatherData
// })
// }, errorCallback)
// }, errorCallback)
// }
} }

View file

@ -38,4 +38,9 @@ ShellRoot {
Calendar { Calendar {
id: calendar id: calendar
} }
Component.onCompleted: {
// On startup, check if we need to get fresh weather data
Location.init()
}
} }