mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 03:28:28 -04:00
542 lines
18 KiB
QML
542 lines
18 KiB
QML
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import qs.Common
|
|
import qs.Services
|
|
|
|
Singleton {
|
|
id: root
|
|
readonly property var log: Log.scoped("ThemeAutoService")
|
|
|
|
property bool active: false
|
|
|
|
Component.onCompleted: {
|
|
if (typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled) {
|
|
start();
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: SessionData
|
|
enabled: typeof SessionData !== "undefined"
|
|
|
|
function onThemeModeAutoEnabledChanged() {
|
|
if (SessionData.themeModeAutoEnabled) {
|
|
root.start();
|
|
} else {
|
|
root.stop();
|
|
}
|
|
}
|
|
|
|
function onThemeModeAutoModeChanged() {
|
|
if (root.active) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
root.syncLocationSchedule();
|
|
}
|
|
}
|
|
|
|
function onThemeModeStartHourChanged() {
|
|
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onThemeModeStartMinuteChanged() {
|
|
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onThemeModeEndHourChanged() {
|
|
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onThemeModeEndMinuteChanged() {
|
|
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onThemeModeShareGammaSettingsChanged() {
|
|
if (root.active) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
root.syncLocationSchedule();
|
|
}
|
|
}
|
|
|
|
function onNightModeStartHourChanged() {
|
|
if (root.active && SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onNightModeStartMinuteChanged() {
|
|
if (root.active && SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onNightModeEndHourChanged() {
|
|
if (root.active && SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onNightModeEndMinuteChanged() {
|
|
if (root.active && SessionData.themeModeShareGammaSettings) {
|
|
root.evaluate();
|
|
root.syncTimeSchedule();
|
|
}
|
|
}
|
|
|
|
function onLatitudeChanged() {
|
|
if (root.active && SessionData.themeModeAutoMode === "location") {
|
|
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
|
|
DMSService.sendRequest("wayland.gamma.setLocation", {
|
|
"latitude": SessionData.latitude,
|
|
"longitude": SessionData.longitude
|
|
});
|
|
}
|
|
root.evaluate();
|
|
root.syncLocationSchedule();
|
|
}
|
|
}
|
|
|
|
function onLongitudeChanged() {
|
|
if (root.active && SessionData.themeModeAutoMode === "location") {
|
|
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
|
|
DMSService.sendRequest("wayland.gamma.setLocation", {
|
|
"latitude": SessionData.latitude,
|
|
"longitude": SessionData.longitude
|
|
});
|
|
}
|
|
root.evaluate();
|
|
root.syncLocationSchedule();
|
|
}
|
|
}
|
|
|
|
function onNightModeUseIPLocationChanged() {
|
|
if (root.active && SessionData.themeModeAutoMode === "location") {
|
|
if (typeof DMSService !== "undefined") {
|
|
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
|
"use": SessionData.nightModeUseIPLocation
|
|
}, response => {
|
|
if (!response.error && !SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
|
DMSService.sendRequest("wayland.gamma.setLocation", {
|
|
"latitude": SessionData.latitude,
|
|
"longitude": SessionData.longitude
|
|
});
|
|
}
|
|
});
|
|
}
|
|
root.evaluate();
|
|
root.syncLocationSchedule();
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: DisplayService
|
|
enabled: typeof DisplayService !== "undefined" && typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location" && !root.backendAvailable()
|
|
|
|
function onGammaIsDayChanged() {
|
|
if (Theme.isLightMode !== DisplayService.gammaIsDay) {
|
|
Theme.setLightMode(DisplayService.gammaIsDay, true, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: DMSService
|
|
|
|
function onThemeAutoStateUpdate(data) {
|
|
if (!SessionData.themeModeAutoEnabled) {
|
|
return;
|
|
}
|
|
root.applyBackendState(data);
|
|
}
|
|
|
|
function onConnectionStateChanged() {
|
|
if (DMSService.isConnected && SessionData.themeModeAutoMode === "time") {
|
|
root.syncTimeSchedule();
|
|
}
|
|
|
|
if (DMSService.isConnected && SessionData.themeModeAutoMode === "location") {
|
|
root.syncLocationSchedule();
|
|
}
|
|
|
|
if (root.backendAvailable() && SessionData.themeModeAutoEnabled) {
|
|
DMSService.sendRequest("theme.auto.getState", null, response => {
|
|
if (response && response.result) {
|
|
root.applyBackendState(response.result);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (!SessionData.themeModeAutoEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (DMSService.isConnected && SessionData.themeModeAutoMode === "location") {
|
|
if (SessionData.nightModeUseIPLocation) {
|
|
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
|
"use": true
|
|
}, response => {
|
|
if (!response.error) {
|
|
root.log.info("Theme automation: IP location enabled after connection");
|
|
}
|
|
});
|
|
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
|
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
|
"use": false
|
|
}, response => {
|
|
if (!response.error) {
|
|
DMSService.sendRequest("wayland.gamma.setLocation", {
|
|
"latitude": SessionData.latitude,
|
|
"longitude": SessionData.longitude
|
|
}, locationResponse => {
|
|
if (locationResponse?.error) {
|
|
root.log.warn("Theme automation: Failed to set location", locationResponse.error);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
root.log.warn("Theme automation: No location configured");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: SessionService
|
|
enabled: SessionData.themeModeAutoEnabled
|
|
|
|
function onSessionUnlocked() {
|
|
root.refresh();
|
|
}
|
|
|
|
function onSessionResumed() {
|
|
root.refresh();
|
|
}
|
|
}
|
|
|
|
function refresh() {
|
|
if (!backendAvailable()) {
|
|
evaluate();
|
|
return;
|
|
}
|
|
DMSService.sendRequest("theme.auto.trigger", {});
|
|
}
|
|
|
|
function backendAvailable() {
|
|
return typeof DMSService !== "undefined" && DMSService.isConnected && Array.isArray(DMSService.capabilities) && DMSService.capabilities.includes("theme.auto");
|
|
}
|
|
|
|
function applyBackendState(state) {
|
|
if (!state) {
|
|
return;
|
|
}
|
|
if (state.config && state.config.mode && state.config.mode !== SessionData.themeModeAutoMode) {
|
|
return;
|
|
}
|
|
if (typeof SessionData !== "undefined" && state.nextTransition !== undefined) {
|
|
SessionData.themeModeNextTransition = state.nextTransition || "";
|
|
}
|
|
if (state.isLight !== undefined && Theme.isLightMode !== state.isLight) {
|
|
Theme.setLightMode(state.isLight, true, true);
|
|
}
|
|
}
|
|
|
|
function syncTimeSchedule() {
|
|
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
|
|
return;
|
|
}
|
|
|
|
if (!DMSService.isConnected) {
|
|
return;
|
|
}
|
|
|
|
const timeModeActive = SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "time";
|
|
|
|
if (!timeModeActive) {
|
|
return;
|
|
}
|
|
|
|
DMSService.sendRequest("theme.auto.setMode", {
|
|
"mode": "time"
|
|
});
|
|
|
|
const shareSettings = SessionData.themeModeShareGammaSettings;
|
|
const startHour = shareSettings ? SessionData.nightModeStartHour : SessionData.themeModeStartHour;
|
|
const startMinute = shareSettings ? SessionData.nightModeStartMinute : SessionData.themeModeStartMinute;
|
|
const endHour = shareSettings ? SessionData.nightModeEndHour : SessionData.themeModeEndHour;
|
|
const endMinute = shareSettings ? SessionData.nightModeEndMinute : SessionData.themeModeEndMinute;
|
|
|
|
DMSService.sendRequest("theme.auto.setSchedule", {
|
|
"startHour": startHour,
|
|
"startMinute": startMinute,
|
|
"endHour": endHour,
|
|
"endMinute": endMinute
|
|
}, response => {
|
|
if (response && response.error) {
|
|
log.error("Theme automation: Failed to sync time schedule:", response.error);
|
|
}
|
|
});
|
|
|
|
DMSService.sendRequest("theme.auto.setEnabled", {
|
|
"enabled": true
|
|
});
|
|
DMSService.sendRequest("theme.auto.trigger", {});
|
|
}
|
|
|
|
function syncLocationSchedule() {
|
|
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
|
|
return;
|
|
}
|
|
|
|
if (!DMSService.isConnected) {
|
|
return;
|
|
}
|
|
|
|
const locationModeActive = SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location";
|
|
|
|
if (!locationModeActive) {
|
|
return;
|
|
}
|
|
|
|
DMSService.sendRequest("theme.auto.setMode", {
|
|
"mode": "location"
|
|
});
|
|
|
|
if (SessionData.nightModeUseIPLocation) {
|
|
DMSService.sendRequest("theme.auto.setUseIPLocation", {
|
|
"use": true
|
|
});
|
|
} else {
|
|
DMSService.sendRequest("theme.auto.setUseIPLocation", {
|
|
"use": false
|
|
});
|
|
if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
|
DMSService.sendRequest("theme.auto.setLocation", {
|
|
"latitude": SessionData.latitude,
|
|
"longitude": SessionData.longitude
|
|
});
|
|
}
|
|
}
|
|
|
|
DMSService.sendRequest("theme.auto.setEnabled", {
|
|
"enabled": true
|
|
});
|
|
DMSService.sendRequest("theme.auto.trigger", {});
|
|
}
|
|
|
|
function evaluate() {
|
|
if (typeof SessionData === "undefined" || !SessionData.themeModeAutoEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (backendAvailable()) {
|
|
DMSService.sendRequest("theme.auto.getState", null, response => {
|
|
if (response && response.result) {
|
|
applyBackendState(response.result);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
const mode = SessionData.themeModeAutoMode;
|
|
|
|
if (mode === "location") {
|
|
evaluateLocation();
|
|
} else {
|
|
evaluateTime();
|
|
}
|
|
}
|
|
|
|
function evaluateLocation() {
|
|
if (typeof DisplayService !== "undefined") {
|
|
const shouldBeLight = DisplayService.gammaIsDay;
|
|
if (Theme.isLightMode !== shouldBeLight) {
|
|
Theme.setLightMode(shouldBeLight, true, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
|
const shouldBeLight = calculateIsDaytime(SessionData.latitude, SessionData.longitude);
|
|
if (Theme.isLightMode !== shouldBeLight) {
|
|
Theme.setLightMode(shouldBeLight, true, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (root.active) {
|
|
if (SessionData.nightModeUseIPLocation) {
|
|
log.warn("Theme automation: Waiting for IP location from backend");
|
|
} else {
|
|
log.warn("Theme automation: Location mode requires coordinates");
|
|
}
|
|
}
|
|
}
|
|
|
|
function evaluateTime() {
|
|
const shareSettings = SessionData.themeModeShareGammaSettings;
|
|
|
|
const startHour = shareSettings ? SessionData.nightModeStartHour : SessionData.themeModeStartHour;
|
|
const startMinute = shareSettings ? SessionData.nightModeStartMinute : SessionData.themeModeStartMinute;
|
|
const endHour = shareSettings ? SessionData.nightModeEndHour : SessionData.themeModeEndHour;
|
|
const endMinute = shareSettings ? SessionData.nightModeEndMinute : SessionData.themeModeEndMinute;
|
|
|
|
const now = new Date();
|
|
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
|
const startMinutes = startHour * 60 + startMinute;
|
|
const endMinutes = endHour * 60 + endMinute;
|
|
|
|
let shouldBeLight;
|
|
if (startMinutes < endMinutes) {
|
|
shouldBeLight = currentMinutes < startMinutes || currentMinutes >= endMinutes;
|
|
} else {
|
|
shouldBeLight = currentMinutes >= endMinutes && currentMinutes < startMinutes;
|
|
}
|
|
|
|
if (Theme.isLightMode !== shouldBeLight) {
|
|
Theme.setLightMode(shouldBeLight, true, true);
|
|
}
|
|
}
|
|
|
|
function calculateIsDaytime(lat, lng) {
|
|
const now = new Date();
|
|
const start = new Date(now.getFullYear(), 0, 0);
|
|
const diff = now - start;
|
|
const dayOfYear = Math.floor(diff / 86400000);
|
|
const latRad = lat * Math.PI / 180;
|
|
|
|
const declination = 23.45 * Math.sin((360 / 365) * (dayOfYear - 81) * Math.PI / 180);
|
|
const declinationRad = declination * Math.PI / 180;
|
|
|
|
const cosHourAngle = -Math.tan(latRad) * Math.tan(declinationRad);
|
|
|
|
if (cosHourAngle > 1) {
|
|
return false;
|
|
}
|
|
if (cosHourAngle < -1) {
|
|
return true;
|
|
}
|
|
|
|
const hourAngle = Math.acos(cosHourAngle);
|
|
const hourAngleDeg = hourAngle * 180 / Math.PI;
|
|
|
|
const sunriseHour = 12 - hourAngleDeg / 15;
|
|
const sunsetHour = 12 + hourAngleDeg / 15;
|
|
|
|
const timeZoneOffset = now.getTimezoneOffset() / 60;
|
|
const localSunrise = sunriseHour - lng / 15 - timeZoneOffset;
|
|
const localSunset = sunsetHour - lng / 15 - timeZoneOffset;
|
|
|
|
const currentHour = now.getHours() + now.getMinutes() / 60;
|
|
|
|
const normalizeSunrise = ((localSunrise % 24) + 24) % 24;
|
|
const normalizeSunset = ((localSunset % 24) + 24) % 24;
|
|
|
|
return currentHour >= normalizeSunrise && currentHour < normalizeSunset;
|
|
}
|
|
|
|
function sendLocationToBackend() {
|
|
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
|
|
return false;
|
|
}
|
|
|
|
if (!DMSService.isConnected) {
|
|
return false;
|
|
}
|
|
|
|
if (SessionData.nightModeUseIPLocation) {
|
|
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
|
"use": true
|
|
}, response => {
|
|
if (response?.error) {
|
|
log.warn("Theme automation: Failed to enable IP location", response.error);
|
|
}
|
|
});
|
|
return true;
|
|
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
|
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
|
"use": false
|
|
}, response => {
|
|
if (!response.error) {
|
|
DMSService.sendRequest("wayland.gamma.setLocation", {
|
|
"latitude": SessionData.latitude,
|
|
"longitude": SessionData.longitude
|
|
}, locResp => {
|
|
if (locResp?.error) {
|
|
log.warn("Theme automation: Failed to set location", locResp.error);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Timer {
|
|
id: locationRetryTimer
|
|
interval: 1000
|
|
repeat: true
|
|
running: false
|
|
property int retryCount: 0
|
|
|
|
onTriggered: {
|
|
if (root.sendLocationToBackend()) {
|
|
stop();
|
|
retryCount = 0;
|
|
root.evaluate();
|
|
} else {
|
|
retryCount++;
|
|
if (retryCount >= 10) {
|
|
stop();
|
|
retryCount = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function start() {
|
|
root.active = true;
|
|
|
|
syncTimeSchedule();
|
|
syncLocationSchedule();
|
|
|
|
const sent = sendLocationToBackend();
|
|
|
|
if (!sent && typeof SessionData !== "undefined" && SessionData.themeModeAutoMode === "location") {
|
|
locationRetryTimer.start();
|
|
} else {
|
|
evaluate();
|
|
}
|
|
}
|
|
|
|
function stop() {
|
|
root.active = false;
|
|
if (typeof DMSService !== "undefined" && DMSService.isConnected) {
|
|
DMSService.sendRequest("theme.auto.setEnabled", {
|
|
"enabled": false
|
|
});
|
|
}
|
|
}
|
|
}
|