dots-hyprland/.config/quickshell/ii/services/Weather.qml

154 lines
5.3 KiB
QML

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()
}
}