pragma Singleton pragma ComponentBehavior: Bound import Quickshell import Quickshell.Io import QtQuick import QtPositioning import qs.modules.common Singleton { id: root // 10 minute readonly property int fetchInterval: Config.options.bar.weather.fetchInterval * 60 * 1000 readonly property string city: Config.options.bar.weather.city readonly property bool useUSCS: Config.options.bar.weather.useUSCS property bool gpsActive: Config.options.bar.weather.enableGPS property var location: ({ valid: false, lat: 0, lon: 0 }) property var data: ({ uv: 0, humidity: 0, sunrise: 0, sunset: 0, windDir: 0, wCode: 0, city: 0, wind: 0, precip: 0, visib: 0, press: 0, temp: 0 }) function refineData(data) { let temp = {}; temp.uv = data?.current?.uvIndex || 0; temp.humidity = (data?.current?.humidity || 0) + "%"; temp.sunrise = data?.astronomy?.sunrise || "0.0"; temp.sunset = data?.astronomy?.sunset || "0.0"; temp.windDir = data?.current?.winddir16Point || "N"; temp.wCode = data?.current?.weatherCode || "113"; temp.city = data?.location?.areaName[0]?.value || "City"; temp.temp = ""; if (root.useUSCS) { temp.wind = (data?.current?.windspeedMiles || 0) + " mph"; temp.precip = (data?.current?.precipInches || 0) + " in"; temp.visib = (data?.current?.visibilityMiles || 0) + " m"; temp.press = (data?.current?.pressureInches || 0) + " psi"; temp.temp += (data?.current?.temp_F || 0); temp.temp += " (" + (data?.current?.FeelsLikeF || 0) + ") "; temp.temp += "\u{02109}"; } else { temp.wind = (data?.current?.windspeedKmph || 0) + " km/h"; temp.precip = (data?.current?.precipMM || 0) + " mm"; temp.visib = (data?.current?.visibility || 0) + " km"; temp.press = (data?.current?.pressure || 0) + " hPa"; temp.temp += (data?.current?.temp_C || 0); temp.temp += " (" + (data?.current?.FeelsLikeC || 0) + ") "; temp.temp += "\u{02103}"; } root.data = temp; } function getData() { let command = "curl -s wttr.in"; if (root.gpsActive && root.location.valid) { command += `/${root.location.lat},${root.location.long}`; } else { command += `/${formatCityName(root.city)}`; } // format as json command += "?format=j1"; command += " | "; // only take the current weather, location, asytronmy data command += "jq '{current: .current_condition[0], location: .nearest_area[0], astronomy: .weather[0].astronomy[0]}'"; fetcher.command[2] = command; fetcher.running = true; } function formatCityName(cityName) { return cityName.trim().split(/\s+/).join('+'); } Component.onCompleted: { if (!root.gpsActive) return; console.info("[WeatherService] Starting the GPS service."); positionSource.start(); } Process { id: fetcher command: ["bash", "-c", ""] stdout: StdioCollector { onStreamFinished: { if (text.length === 0) return; try { const parsedData = JSON.parse(text); root.refineData(parsedData); // console.info(`[ data: ${JSON.stringify(parsedData)}`); } catch (e) { console.error(`[WeatherService] ${e.message}`); } } } } PositionSource { id: positionSource updateInterval: root.fetchInterval onPositionChanged: { // update the location if the given location is valid // if it fails getting the location, use the last valid location if (position.latitudeValid && position.longitudeValid) { root.location.lat = position.coordinate.latitude; root.location.long = position.coordinate.longitude; root.location.valid = true; // console.info(`📍 Location: ${position.coordinate.latitude}, ${position.coordinate.longitude}`); root.getData(); // if can't get initialized with valid location deactivate the GPS } else { root.gpsActive = root.location.valid ? true : false; console.error("[WeatherService] Failed to get the GPS location."); } } onValidityChanged: { if (!positionSource.valid) { positionSource.stop(); root.location.valid = false; root.gpsActive = false; Quickshell.execDetached(["notify-send", Translation.tr("Weather Service"), Translation.tr("Cannot find a GPS service. Using the fallback method instead."), "-a", "Shell"]); console.error("[WeatherService] Could not aquire a valid backend plugin."); } } } Timer { running: !root.gpsActive repeat: true interval: root.fetchInterval triggeredOnStart: !root.gpsActive onTriggered: root.getData() } }