add crypto
This commit is contained in:
parent
90cbe489f6
commit
af6a3bce3e
120 changed files with 24616 additions and 462 deletions
269
modules/home/services/quickshell/qml/Services/WeatherService.qml
Normal file
269
modules/home/services/quickshell/qml/Services/WeatherService.qml
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
import QtQuick
|
||||
import "root:/Data" as Data
|
||||
|
||||
// Weather service using Open-Meteo API
|
||||
Item {
|
||||
id: service
|
||||
|
||||
property var shell
|
||||
|
||||
property string city: Data.Settings.weatherLocation
|
||||
property bool isAmerican: Data.Settings.useFahrenheit
|
||||
property int updateInterval: 3600 // 1 hour to reduce API calls
|
||||
property string weatherDescription: ""
|
||||
property var weather: null
|
||||
|
||||
property Timer retryTimer: Timer {
|
||||
interval: 30000
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: getGeocoding()
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: service.updateInterval * 1000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: getGeocoding()
|
||||
}
|
||||
|
||||
// Watch for settings changes and refresh weather data
|
||||
Connections {
|
||||
target: Data.Settings
|
||||
function onWeatherLocationChanged() {
|
||||
console.log("Weather location changed to:", Data.Settings.weatherLocation)
|
||||
retryTimer.stop()
|
||||
getGeocoding()
|
||||
}
|
||||
|
||||
function onUseFahrenheitChanged() {
|
||||
console.log("Temperature unit changed to:", Data.Settings.useFahrenheit ? "Fahrenheit" : "Celsius")
|
||||
retryTimer.stop()
|
||||
getGeocoding()
|
||||
}
|
||||
}
|
||||
|
||||
// WMO weather code descriptions (Open-Meteo standard)
|
||||
property var weatherConsts: {
|
||||
"omapiCodeDesc": {
|
||||
0: "Clear sky",
|
||||
1: "Mainly clear",
|
||||
2: "Partly cloudy",
|
||||
3: "Overcast",
|
||||
45: "Fog",
|
||||
48: "Depositing rime fog",
|
||||
51: "Light drizzle",
|
||||
53: "Moderate drizzle",
|
||||
55: "Dense drizzle",
|
||||
56: "Light freezing drizzle",
|
||||
57: "Dense freezing drizzle",
|
||||
61: "Slight rain",
|
||||
63: "Moderate rain",
|
||||
65: "Heavy rain",
|
||||
66: "Light freezing rain",
|
||||
67: "Heavy freezing rain",
|
||||
71: "Slight snow fall",
|
||||
73: "Moderate snow fall",
|
||||
75: "Heavy snow fall",
|
||||
77: "Snow grains",
|
||||
80: "Slight rain showers",
|
||||
81: "Moderate rain showers",
|
||||
82: "Violent rain showers",
|
||||
85: "Slight snow showers",
|
||||
86: "Heavy snow showers",
|
||||
95: "Thunderstorm",
|
||||
96: "Thunderstorm with slight hail",
|
||||
99: "Thunderstorm with heavy hail"
|
||||
}
|
||||
}
|
||||
|
||||
function getTemp(temp, tempUnit) {
|
||||
return temp + tempUnit;
|
||||
}
|
||||
|
||||
function updateWeather() {
|
||||
if (!weather || !weather.current || !weather.current_units) {
|
||||
console.warn("Weather data incomplete, skipping update");
|
||||
return;
|
||||
}
|
||||
|
||||
const weatherCode = weather.current.weather_code;
|
||||
const temp = getTemp(Math.round(weather.current.temperature_2m || 0), weather.current_units.temperature_2m || "°C");
|
||||
|
||||
// Build 3-day forecast
|
||||
const forecast = [];
|
||||
const today = new Date();
|
||||
|
||||
if (weather.daily && weather.daily.time && weather.daily.weather_code && weather.daily.temperature_2m_min && weather.daily.temperature_2m_max) {
|
||||
for (let i = 0; i < Math.min(3, weather.daily.time.length); i++) {
|
||||
let dayName;
|
||||
if (i === 0) {
|
||||
dayName = "Today";
|
||||
} else if (i === 1) {
|
||||
dayName = "Tomorrow";
|
||||
} else {
|
||||
const futureDate = new Date(today);
|
||||
futureDate.setDate(today.getDate() + i);
|
||||
dayName = Qt.formatDate(futureDate, "ddd MMM d");
|
||||
}
|
||||
|
||||
const dailyWeatherCode = weather.daily.weather_code[i];
|
||||
const condition = weatherConsts.omapiCodeDesc[dailyWeatherCode] || "Unknown";
|
||||
|
||||
forecast.push({
|
||||
dayName: dayName,
|
||||
condition: condition,
|
||||
minTemp: Math.round(weather.daily.temperature_2m_min[i]),
|
||||
maxTemp: Math.round(weather.daily.temperature_2m_max[i])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update shell weather data in expected format
|
||||
shell.weatherData = {
|
||||
location: city,
|
||||
currentTemp: temp,
|
||||
currentCondition: weatherConsts.omapiCodeDesc[weatherCode] || "Unknown",
|
||||
details: [
|
||||
"Wind: " + Math.round(weather.current.wind_speed_10m || 0) + " km/h"
|
||||
],
|
||||
forecast: forecast
|
||||
}
|
||||
|
||||
weatherDescription = weatherConsts.omapiCodeDesc[weatherCode] || "Unknown";
|
||||
shell.weatherLoading = false;
|
||||
}
|
||||
|
||||
// XHR pool to prevent memory leaks
|
||||
property var activeXHRs: []
|
||||
|
||||
function cleanupXHR(xhr) {
|
||||
if (xhr) {
|
||||
xhr.abort();
|
||||
xhr.onreadystatechange = null;
|
||||
xhr.onerror = null;
|
||||
|
||||
const index = activeXHRs.indexOf(xhr);
|
||||
if (index > -1) {
|
||||
activeXHRs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getGeocoding() {
|
||||
if (!city || city.trim() === "") {
|
||||
console.warn("Weather location is empty, skipping weather request");
|
||||
shell.weatherLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
shell.weatherLoading = true;
|
||||
const xhr = new XMLHttpRequest();
|
||||
activeXHRs.push(xhr);
|
||||
|
||||
xhr.open("GET", `https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1&language=en&format=json`);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
const geocoding = JSON.parse(xhr.responseText);
|
||||
if (geocoding.results && geocoding.results.length > 0) {
|
||||
const lat = geocoding.results[0].latitude;
|
||||
const lng = geocoding.results[0].longitude;
|
||||
getWeather(lat, lng);
|
||||
} else {
|
||||
console.warn("No geocoding results found for location:", city);
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to parse geocoding response:", e);
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
}
|
||||
} else if (xhr.status === 0) {
|
||||
// Silent handling of network issues
|
||||
if (!retryTimer.running) {
|
||||
console.warn("Weather service: Network unavailable, will retry automatically");
|
||||
}
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
} else {
|
||||
console.error("Geocoding request failed with status:", xhr.status);
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
}
|
||||
cleanupXHR(xhr);
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
console.error("Geocoding request failed with network error");
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
cleanupXHR(xhr);
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function getWeather(lat, lng) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
activeXHRs.push(xhr);
|
||||
|
||||
xhr.open("GET", `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lng}¤t=temperature_2m,is_day,weather_code,wind_speed_10m&daily=temperature_2m_max,temperature_2m_min,weather_code&forecast_days=3&temperature_unit=` + (isAmerican ? "fahrenheit" : "celsius"));
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
weather = JSON.parse(xhr.responseText);
|
||||
updateWeather();
|
||||
} catch (e) {
|
||||
console.error("Failed to parse weather response:", e);
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
}
|
||||
} else if (xhr.status === 0) {
|
||||
// Silent handling of network issues
|
||||
if (!retryTimer.running) {
|
||||
console.warn("Weather service: Network unavailable for weather data");
|
||||
}
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
} else {
|
||||
console.error("Weather request failed with status:", xhr.status);
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
}
|
||||
cleanupXHR(xhr);
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
console.error("Weather request failed with network error");
|
||||
retryTimer.running = true;
|
||||
shell.weatherLoading = false;
|
||||
cleanupXHR(xhr);
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function loadWeather() {
|
||||
getGeocoding();
|
||||
}
|
||||
|
||||
Component.onCompleted: getGeocoding()
|
||||
|
||||
Component.onDestruction: {
|
||||
// Cleanup all active XHR requests
|
||||
for (let i = 0; i < activeXHRs.length; i++) {
|
||||
if (activeXHRs[i]) {
|
||||
activeXHRs[i].abort();
|
||||
activeXHRs[i].onreadystatechange = null;
|
||||
activeXHRs[i].onerror = null;
|
||||
}
|
||||
}
|
||||
activeXHRs = [];
|
||||
weather = null;
|
||||
shell.weatherData = null;
|
||||
weatherDescription = "";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue