1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

feat: Night Mode Automation

This commit is contained in:
purian23
2025-08-28 21:37:05 -04:00
parent 48a78c39e2
commit 324d6c13b4
5 changed files with 585 additions and 56 deletions

View File

@@ -921,6 +921,40 @@ Item {
anchors.verticalCenter: parent.verticalCenter
}
StyledRect {
width: 60
height: 32
radius: Theme.cornerRadius
color: Theme.surfaceVariant
border.color: Theme.outline
border.width: 1
StyledText {
text: "Clear"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.centerIn: parent
}
StateLayer {
stateColor: Theme.error
cornerRadius: parent.radius
onClicked: {
console.log("PersonalizationTab: Clearing location coordinates via UI")
SessionData.setLatitude(0.0);
SessionData.setLongitude(0.0);
latitudeField.text = "";
longitudeField.text = "";
// Also call the service clear function
if (typeof globalThis !== 'undefined' && globalThis.clearNightModeLocation) {
globalThis.clearNightModeLocation();
}
}
}
anchors.verticalCenter: parent.verticalCenter
}
}
}

View File

@@ -89,7 +89,7 @@ https://github.com/user-attachments/assets/5ad934bb-e7aa-4c04-8d40-149181bd2d29
- **Process List** A process list, with system metrics and information. More detailed modal available via IPC.
- **Notification Center** A center for notifications that has support for grouping.
- **Dock** A dock with pinned apps support, recent apps support, and currently running application support.
- **Control Center** A full control center with user profile information, network, bluetooth, audio input/output, and display controls.
- **Control Center** A full control center with user profile information, network, bluetooth, audio input/output, display controls, and night mode automation.
- **Lock Screen** Using quickshell's WlSessionLock
**Features:**
@@ -100,6 +100,7 @@ https://github.com/user-attachments/assets/5ad934bb-e7aa-4c04-8d40-149181bd2d29
- Audio/media controls
- Grouped notifications
- Brightness control for internal and external displays
- Automated night mode with time-based and location-based scheduling
- Qt and GTK app theming synchronization, as well as [Ghostty](https://ghostty.org/) auto-theme support.
## Installation
@@ -323,6 +324,10 @@ binds {
XF86MonBrightnessDown allow-when-locked=true {
spawn "qs" "-c" "dms" "ipc" "call" "brightness" "decrement" "5" "";
}
// Night mode toggle
Mod+Shift+N allow-when-locked=true {
spawn "qs" "-c" "dms" "ipc" "call" "night" "toggle";
}
}
```
@@ -366,6 +371,9 @@ bindl = , XF86AudioMicMute, exec, qs -c dms ipc call audio micmute
bindl = , XF86MonBrightnessUp, exec, qs -c dms ipc call brightness increment 5 ""
# You can override the default device for e.g. keyboards by adding the device name to the last param
bindl = , XF86MonBrightnessDown, exec, qs -c dms ipc call brightness decrement 5 ""
# Night mode toggle
bind = SUPERSHIFT, N, exec, qs -c dms ipc call night toggle
```
### IPC Commands
@@ -388,6 +396,7 @@ qs -c dms ipc call powermenu toggle
```
qs -c dms ipc call wallpaper set /path/to/image.jpg
qs -c dms ipc call theme toggle
qs -c dms ipc call night toggle
qs -c dms ipc call lock lock
```
# Media control

View File

@@ -18,14 +18,80 @@ Singleton {
property real latitude: 0.0
property real longitude: 0.0
// Expose these functions to global scope for console debugging
function testGeoclueConnection() {
console.log("NightModeAutomationService: Testing geoclue2 connection...")
if (geoclueTestProcess.running) {
geoclueTestProcess.running = false
}
geoclueTestProcess.command = [
"timeout", "32", // Increased to 32 seconds for geoclue location fix
"gammastep",
"-m", "wayland",
"-l", "geoclue2",
"-O", "6500", // One-shot mode to test location quickly
"-v"
]
geoclueTestProcess.running = true
}
function debugTimeBasedMode() {
const now = new Date()
const currentHour = now.getHours()
const currentMinute = now.getMinutes()
const currentTime = currentHour * 60 + currentMinute
const startTime = SessionData.nightModeStartTime || "20:00"
const endTime = SessionData.nightModeEndTime || "06:00"
console.log("=== DEBUG TIME BASED MODE ===")
console.log("Current time:", now.toLocaleTimeString())
console.log("Current minutes since midnight:", currentTime)
console.log("Night mode start time:", startTime)
console.log("Night mode end time:", endTime)
console.log("Auto enabled:", SessionData.nightModeAutoEnabled)
console.log("Auto mode:", SessionData.nightModeAutoMode)
console.log("Temperature:", SessionData.nightModeTemperature + "K")
console.log("isAutomaticNightTime:", isAutomaticNightTime)
console.log("automationTimer running:", automationTimer.running)
console.log("gammaStepProcess running:", gammaStepProcess.running)
console.log("gammaStepProcess type:", gammaStepProcess.processType)
// Force a check
console.log("Forcing checkTimeBasedMode()...")
checkTimeBasedMode()
}
Component.onCompleted: {
console.log("NightModeAutomationService: Component completed")
// Kill any straggling gammastep processes to prevent conflicts
pkillProcess.running = true
checkAvailability()
updateFromSessionData()
if (SessionData.nightModeAutoEnabled) {
console.log("NightModeAutomationService: Auto-starting automation on init")
startAutomation()
// Debug current SessionData values
console.log("NightModeAutomationService: === SESSIONDATA DEBUG ===")
console.log("NightModeAutomationService: nightModeAutoEnabled:", SessionData.nightModeAutoEnabled)
console.log("NightModeAutomationService: nightModeAutoMode:", SessionData.nightModeAutoMode)
console.log("NightModeAutomationService: nightModeStartTime:", SessionData.nightModeStartTime)
console.log("NightModeAutomationService: nightModeEndTime:", SessionData.nightModeEndTime)
console.log("NightModeAutomationService: nightModeTemperature:", SessionData.nightModeTemperature)
console.log("NightModeAutomationService: nightModeEnabled:", SessionData.nightModeEnabled)
console.log("NightModeAutomationService: === END SESSIONDATA DEBUG ===")
// Expose debug functions globally
if (typeof globalThis !== 'undefined') {
globalThis.debugNightMode = debugTimeBasedMode
globalThis.testNightMode = manualNightModeTest
globalThis.resetNightMode = manualResetTest
globalThis.clearNightModeLocation = clearLocation
globalThis.debugLocationMode = debugLocationMode
globalThis.nightModeService = root
}
// Don't start automation here - wait for Gammastep availability check to complete
// The gammaStepTestProcess.onExited will start automation when ready
}
function checkAvailability() {
@@ -38,6 +104,9 @@ Singleton {
return
}
// Stop any existing automation processes first
stopAutomation()
const mode = SessionData.nightModeAutoMode || "manual"
switch (mode) {
@@ -57,55 +126,108 @@ Singleton {
function stopAutomation() {
automationTimer.stop()
locationTimer.stop()
if (gammaStepAutomationProcess.running) {
gammaStepAutomationProcess.kill()
// Stop the unified process
if (gammaStepProcess.running) {
gammaStepProcess.running = false
}
isAutomaticNightTime = false
}
function startTimeBasedMode() {
console.log("NightModeAutomationService: Starting time-based automation")
console.log("NightModeAutomationService: === Starting time-based automation ===")
console.log("NightModeAutomationService: automationTimer.running before start:", automationTimer.running)
automationTimer.start()
console.log("NightModeAutomationService: automationTimer.running after start:", automationTimer.running)
console.log("NightModeAutomationService: automationTimer.interval:", automationTimer.interval, "ms")
console.log("NightModeAutomationService: Now calling initial checkTimeBasedMode...")
checkTimeBasedMode()
console.log("NightModeAutomationService: === Time-based automation startup complete ===")
}
function startLocationBasedMode() {
if (!locationProviderAvailable) {
console.warn("NightModeAutomationService: No location provider available, falling back to time-based mode")
startTimeBasedMode()
return
}
console.log("NightModeAutomationService: Starting location-based automation")
// Stop the process first, then change command, then start
if (gammaStepProcess.running) {
gammaStepProcess.running = false
}
const temperature = SessionData.nightModeTemperature || 4500
const dayTemp = 6500
gammaStepProcess.processType = "automation"
// Check manual coordinates first (highest priority)
if (latitude !== 0.0 && longitude !== 0.0) {
gammaStepAutomationProcess.command = [
console.log(`NightModeAutomationService: Using manual coordinates: ${latitude.toFixed(6)},${longitude.toFixed(6)}`)
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-l", `${latitude.toFixed(6)}:${longitude.toFixed(6)}`,
"-t", `${dayTemp}:${temperature}`,
"-v"
]
} else {
gammaStepAutomationProcess.command = [
"gammastep",
"-m", "wayland",
"-l", currentProvider || "manual",
"-t", `${dayTemp}:${temperature}`,
"-v"
]
gammaStepProcess.running = true
locationTimer.start()
return
}
gammaStepAutomationProcess.running = true
locationTimer.start()
// Check if location providers are available
if (!locationProviderAvailable) {
console.warn("NightModeAutomationService: No location provider available, falling back to time-based mode")
SessionData.setNightModeAutoMode("time")
startTimeBasedMode()
return
}
// Use automatic location provider (geoclue2)
if (currentProvider === "geoclue2") {
console.log("NightModeAutomationService: Starting geoclue2 location provider...")
// Kill any existing gammastep processes to prevent conflicts
pkillProcess.running = true
// Wait longer for geoclue2 to be ready and acquire location
cleanupTimer.interval = 5000 // 5 second delay for geoclue2 location acquisition
cleanupTimer.repeat = false // Single shot
cleanupTimer.triggered.connect(function() {
console.log("NightModeAutomationService: Starting geoclue2 after cleanup...")
const temperature = SessionData.nightModeTemperature || 4500
const dayTemp = 6500
gammaStepProcess.processType = "automation"
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-l", "geoclue2",
"-t", `${dayTemp}:${temperature}`,
"-v"
]
console.log("NightModeAutomationService: Geoclue2 command:", gammaStepProcess.command.join(" "))
gammaStepProcess.running = true
locationTimer.start()
})
cleanupTimer.start()
return
} else {
console.warn("NightModeAutomationService: No working location provider, falling back to time-based mode")
SessionData.setNightModeAutoMode("time")
startTimeBasedMode()
return
}
}
function checkTimeBasedMode() {
console.log("NightModeAutomationService: === checkTimeBasedMode CALLED ===")
console.log("NightModeAutomationService: nightModeAutoEnabled:", SessionData.nightModeAutoEnabled)
console.log("NightModeAutomationService: nightModeAutoMode:", SessionData.nightModeAutoMode)
if (!SessionData.nightModeAutoEnabled || SessionData.nightModeAutoMode !== "time") {
console.log("NightModeAutomationService: checkTimeBasedMode - not enabled or wrong mode")
console.log("NightModeAutomationService: - autoEnabled:", SessionData.nightModeAutoEnabled)
console.log("NightModeAutomationService: - autoMode:", SessionData.nightModeAutoMode)
return
}
@@ -117,6 +239,11 @@ Singleton {
const startTime = SessionData.nightModeStartTime || "20:00"
const endTime = SessionData.nightModeEndTime || "06:00"
console.log("NightModeAutomationService: Raw start time from SessionData:", SessionData.nightModeStartTime)
console.log("NightModeAutomationService: Raw end time from SessionData:", SessionData.nightModeEndTime)
console.log("NightModeAutomationService: Using start time:", startTime)
console.log("NightModeAutomationService: Using end time:", endTime)
const startParts = startTime.split(":")
const endParts = endTime.split(":")
@@ -126,25 +253,36 @@ Singleton {
let shouldBeNight = false
if (startMinutes > endMinutes) {
// Crosses midnight (e.g., 20:00 to 06:00)
shouldBeNight = (currentTime >= startMinutes) || (currentTime < endMinutes)
console.log("NightModeAutomationService: Time range crosses midnight")
} else {
// Same day (e.g., 16:00 to 18:36)
shouldBeNight = (currentTime >= startMinutes) && (currentTime < endMinutes)
console.log("NightModeAutomationService: Time range within same day")
}
console.log(`NightModeAutomationService: Time check - Current: ${currentHour}:${currentMinute.toString().padStart(2, '0')} (${currentTime}), Range: ${startTime}-${endTime} (${startMinutes}-${endMinutes}), Should be night: ${shouldBeNight}`)
console.log(`NightModeAutomationService: === TIME CALCULATION ===`)
console.log(`NightModeAutomationService: Current: ${currentHour}:${currentMinute.toString().padStart(2, '0')} (${currentTime} minutes)`)
console.log(`NightModeAutomationService: Range: ${startTime} (${startMinutes} minutes) to ${endTime} (${endMinutes} minutes)`)
console.log(`NightModeAutomationService: Should be night: ${shouldBeNight}`)
console.log(`NightModeAutomationService: Current isAutomaticNightTime: ${isAutomaticNightTime}`)
if (shouldBeNight !== isAutomaticNightTime) {
isAutomaticNightTime = shouldBeNight
console.log("NightModeAutomationService: Automatic night time status changed to:", shouldBeNight)
console.log("NightModeAutomationService: *** NIGHT TIME STATUS CHANGED TO:", shouldBeNight, "***")
if (shouldBeNight) {
console.log("NightModeAutomationService: >>> ACTIVATING NIGHT MODE <<<")
requestNightModeActivation()
} else {
console.log("NightModeAutomationService: >>> DEACTIVATING NIGHT MODE <<<")
requestNightModeDeactivation()
}
} else {
console.log("NightModeAutomationService: No change needed, isAutomaticNightTime already:", isAutomaticNightTime)
}
console.log("NightModeAutomationService: === checkTimeBasedMode END ===")
}
function requestNightModeActivation() {
@@ -152,30 +290,77 @@ Singleton {
const temperature = SessionData.nightModeTemperature || 4500
console.log("NightModeAutomationService: Using temperature:", temperature + "K")
gammaStepOneTimeProcess.command = [
// Stop process first, change command, then start
if (gammaStepProcess.running) {
gammaStepProcess.running = false
}
// For time-based mode, use continuous process like location mode
if (SessionData.nightModeAutoMode === "time") {
gammaStepProcess.processType = "automation" // Use automation type for continuous process
} else {
gammaStepProcess.processType = "activate" // Keep activate for other modes
}
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-O", String(temperature),
"-P"
"-O", String(temperature)
// This runs continuously, no timeout needed
]
console.log("NightModeAutomationService: Running gamma command:", gammaStepOneTimeProcess.command.join(" "))
gammaStepOneTimeProcess.running = true
console.log("NightModeAutomationService: Running gamma command:", gammaStepProcess.command.join(" "))
gammaStepProcess.running = true
SessionData.setNightModeEnabled(true)
// Only update SessionData manual toggle if we're in automation mode
// This prevents automation from interfering with manual UI settings
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode !== "manual") {
console.log("NightModeAutomationService: Updating SessionData nightModeEnabled to true (automation mode)")
SessionData.setNightModeEnabled(true)
} else {
console.log("NightModeAutomationService: Not updating SessionData (manual mode or automation disabled)")
}
}
function requestNightModeDeactivation() {
console.log("NightModeAutomationService: Requesting night mode deactivation")
gammaStepResetProcess.command = [
"gammastep",
"-m", "wayland",
"-O", "6500",
"-P"
]
gammaStepResetProcess.running = true
// Always stop any running process first
if (gammaStepProcess.running) {
console.log("NightModeAutomationService: Stopping current gamma process")
gammaStepProcess.running = false
}
SessionData.setNightModeEnabled(false)
// For time-based mode, we need to explicitly reset gamma
if (SessionData.nightModeAutoMode === "time") {
console.log("NightModeAutomationService: Time-based deactivation - resetting gamma to normal")
// Use automation type for continuous reset process (no timeout)
gammaStepProcess.processType = "automation"
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-O", "6500"
// This will run continuously to maintain normal temperature
]
gammaStepProcess.running = true
} else {
// For other modes, use the reset approach with timeout protection
gammaStepProcess.processType = "reset"
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-O", "6500"
]
gammaStepProcess.running = true
}
// Only update SessionData manual toggle if we're in automation mode
// This prevents automation from interfering with manual UI settings
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode !== "manual") {
console.log("NightModeAutomationService: Updating SessionData nightModeEnabled to false (automation mode)")
SessionData.setNightModeEnabled(false)
} else {
console.log("NightModeAutomationService: Not updating SessionData (manual mode or automation disabled)")
}
}
function setLocation(lat, lon) {
@@ -188,10 +373,77 @@ Singleton {
}
}
function clearLocation() {
console.log("NightModeAutomationService: === CLEARING LOCATION COORDINATES ===")
console.log("NightModeAutomationService: Before - service lat:", latitude, "lng:", longitude)
console.log("NightModeAutomationService: Before - SessionData lat:", SessionData.latitude, "lng:", SessionData.longitude)
// Clear local service coordinates
latitude = 0.0
longitude = 0.0
currentLocation = ""
// Update SessionData to reflect cleared coordinates
SessionData.setLatitude(0.0)
SessionData.setLongitude(0.0)
console.log("NightModeAutomationService: After clearing - service lat:", latitude, "lng:", longitude)
console.log("NightModeAutomationService: After clearing - SessionData lat:", SessionData.latitude, "lng:", SessionData.longitude)
// Force SessionData to save changes
SessionData.saveSettings()
console.log("NightModeAutomationService: Settings saved")
console.log("NightModeAutomationService: Current automation mode:", SessionData.nightModeAutoMode)
console.log("NightModeAutomationService: Automation enabled:", SessionData.nightModeAutoEnabled)
// If location mode is active, restart it (will fallback to provider or time-based)
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "location") {
console.log("NightModeAutomationService: Restarting location automation after clearing coordinates")
startLocationBasedMode()
} else {
console.log("NightModeAutomationService: Not in location mode, coordinates cleared but automation not restarted")
}
console.log("NightModeAutomationService: === LOCATION CLEARING COMPLETE ===")
}
function debugLocationMode() {
console.log("=== DEBUG LOCATION MODE ===")
console.log("Manual coordinates - lat:", latitude, "lng:", longitude)
console.log("SessionData coordinates - lat:", SessionData.latitude, "lng:", SessionData.longitude)
console.log("Current location string:", currentLocation)
console.log("Location providers available:", locationProviderAvailable)
console.log("Available providers:", availableProviders)
console.log("Current provider:", currentProvider)
console.log("Auto enabled:", SessionData.nightModeAutoEnabled)
console.log("Auto mode:", SessionData.nightModeAutoMode)
console.log("Temperature:", SessionData.nightModeTemperature + "K")
console.log("gammaStepProcess running:", gammaStepProcess.running)
console.log("gammaStepProcess type:", gammaStepProcess.processType)
console.log("gammaStepProcess command:", gammaStepProcess.command)
// Force location mode test if enabled
if (SessionData.nightModeAutoMode === "location") {
console.log("Forcing startLocationBasedMode()...")
startLocationBasedMode()
}
console.log("=== END DEBUG LOCATION MODE ===")
}
function updateFromSessionData() {
console.log("NightModeAutomationService: Updating from SessionData - lat:", SessionData.latitude, "lng:", SessionData.longitude)
// Only update coordinates if they're non-zero (user has set manual coordinates)
// If they're 0.0, leave them cleared to allow automatic location detection
if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
console.log("NightModeAutomationService: Loading manual coordinates from SessionData")
setLocation(SessionData.latitude, SessionData.longitude)
} else {
console.log("NightModeAutomationService: No manual coordinates in SessionData, keeping coordinates cleared for auto-detection")
latitude = 0.0
longitude = 0.0
currentLocation = ""
}
}
@@ -202,19 +454,73 @@ Singleton {
function testAutomationNow() {
console.log("NightModeAutomationService: Manual test triggered")
console.log("NightModeAutomationService: Current settings - autoEnabled:", SessionData.nightModeAutoEnabled, "mode:", SessionData.nightModeAutoMode)
console.log("NightModeAutomationService: Time range:", SessionData.nightModeStartTime, "to", SessionData.nightModeEndTime)
console.log("NightModeAutomationService: Temperature:", SessionData.nightModeTemperature + "K")
if (SessionData.nightModeAutoMode === "time") {
console.log("NightModeAutomationService: Testing time-based mode now...")
checkTimeBasedMode()
} else if (SessionData.nightModeAutoMode === "location") {
console.log("NightModeAutomationService: Location mode - coordinates:", latitude, longitude)
}
}
function manualNightModeTest() {
console.log("NightModeAutomationService: Manual night mode test - forcing activation")
const temperature = SessionData.nightModeTemperature || 4500
if (gammaStepProcess.running) {
console.log("NightModeAutomationService: Stopping existing process first...")
gammaStepProcess.running = false
}
gammaStepProcess.processType = "test"
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-O", String(temperature)
// Removed -P flag to prevent hanging
]
console.log("NightModeAutomationService: Test gamma command:", gammaStepProcess.command.join(" "))
gammaStepProcess.running = true
// Use the existing timer approach
testFeedbackTimer.interval = 2000
testFeedbackTimer.feedbackMessage = "NightModeAutomationService: Night mode test command sent. Check if screen temperature changed."
testFeedbackTimer.start()
}
function manualResetTest() {
console.log("NightModeAutomationService: Manual reset test - forcing reset to 6500K")
if (gammaStepProcess.running) {
console.log("NightModeAutomationService: Stopping existing process first...")
gammaStepProcess.running = false
}
gammaStepProcess.processType = "test"
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-O", "6500"
// Removed -P flag to prevent hanging
]
console.log("NightModeAutomationService: Test reset command:", gammaStepProcess.command.join(" "))
gammaStepProcess.running = true
// Use the existing timer approach
testFeedbackTimer.interval = 2000
testFeedbackTimer.feedbackMessage = "NightModeAutomationService: Reset test command sent. Screen should return to normal temperature."
testFeedbackTimer.start()
}
Timer {
id: automationTimer
interval: 60000
running: false
repeat: true
onTriggered: {
console.log("NightModeAutomationService: *** AUTOMATION TIMER FIRED ***", new Date().toLocaleTimeString())
checkTimeBasedMode()
}
}
@@ -231,6 +537,16 @@ Singleton {
}
}
Process {
id: pkillProcess
command: ["pkill", "gammastep"]
running: false
onExited: function(exitCode) {
console.log("NightModeAutomationService: Cleaned up straggling gammastep processes")
}
}
Process {
id: gammaStepTestProcess
command: ["which", "gammastep"]
@@ -241,6 +557,12 @@ Singleton {
if (automationAvailable) {
console.log("NightModeAutomationService: Gammastep available")
detectLocationProviders()
// Start automation now that Gammastep is confirmed available
if (SessionData.nightModeAutoEnabled) {
console.log("NightModeAutomationService: Starting automation after confirming Gammastep availability")
startAutomation()
}
} else {
console.warn("NightModeAutomationService: Gammastep not available")
}
@@ -255,14 +577,27 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
availableProviders = text.trim().split('\n').filter(line => line.trim().length > 0)
locationProviderAvailable = availableProviders.length > 0
// Parse provider names - they start with whitespace and are single words
const lines = text.trim().split('\n')
const providers = lines.filter(line => {
const trimmed = line.trim()
// Provider names are single words that start with whitespace in original line
return line.startsWith(' ') &&
trimmed.length > 0 &&
!trimmed.includes(' ') &&
!trimmed.includes(':') &&
!trimmed.includes('.')
}).map(line => line.trim())
availableProviders = providers
locationProviderAvailable = providers.length > 0
if (locationProviderAvailable && !currentProvider) {
currentProvider = availableProviders[0]
currentProvider = providers[0]
}
console.log("NightModeAutomationService: Available providers:", availableProviders)
console.log("NightModeAutomationService: Parsed providers:", providers)
console.log("NightModeAutomationService: Location provider available:", locationProviderAvailable)
}
}
}
@@ -276,35 +611,140 @@ Singleton {
}
Process {
id: gammaStepAutomationProcess
id: geoclueTestProcess
running: false
onExited: function(exitCode) {
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "location" && exitCode !== 0) {
console.warn("NightModeAutomationService: Location-based automation failed, exit code:", exitCode)
restartTimer.start()
console.log("NightModeAutomationService: Geoclue2 test exited with code:", exitCode)
if (exitCode === 0) {
console.log("NightModeAutomationService: Geoclue2 working, starting location automation")
// Start the actual location automation
const temperature = SessionData.nightModeTemperature || 4500
const dayTemp = 6500
gammaStepProcess.processType = "automation"
gammaStepProcess.command = [
"gammastep",
"-m", "wayland",
"-l", "geoclue2",
"-t", `${dayTemp}:${temperature}`,
"-v"
]
gammaStepProcess.running = true
locationTimer.start()
} else {
console.warn(`NightModeAutomationService: Geoclue2 test failed with exit code ${exitCode}, falling back to time-based mode`)
if (exitCode === 124) {
console.warn("NightModeAutomationService: Geoclue2 timed out - likely no location services available")
} else if (exitCode === 15) {
console.warn("NightModeAutomationService: Geoclue2 terminated - permission or service issues")
}
// Fallback to time-based mode
SessionData.setNightModeAutoMode("time")
startTimeBasedMode()
}
}
}
Process {
id: gammaStepOneTimeProcess
id: gammaStepProcess
running: false
property string processType: "" // "automation", "activate", "reset", "test"
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
console.log("NightModeAutomationService: Gammastep stdout:", text.trim())
}
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text.trim()) {
console.warn("NightModeAutomationService: Gammastep stderr:", text.trim())
}
}
}
onRunningChanged: {
if (running) {
// Start timeout only for one-shot commands, not continuous location automation
if (processType !== "automation") {
processTimeoutTimer.start()
}
} else {
// Stop timeout when process ends
processTimeoutTimer.stop()
}
}
onExited: function(exitCode) {
console.log(`NightModeAutomationService: Process ${processType} exited with code:`, exitCode)
processTimeoutTimer.stop() // Ensure timeout is stopped
if (exitCode !== 0) {
console.warn("NightModeAutomationService: Failed to enable night mode, exit code:", exitCode)
switch(processType) {
case "automation":
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "location") {
console.warn("NightModeAutomationService: Location-based automation failed, exit code:", exitCode)
if (exitCode === 15 || exitCode === 124) {
console.warn("NightModeAutomationService: Location service issues detected, falling back to time-based mode")
SessionData.setNightModeAutoMode("time")
startTimeBasedMode()
} else {
console.warn("NightModeAutomationService: Attempting to restart location automation in 10 seconds")
restartTimer.start()
}
} else if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "time") {
console.warn("NightModeAutomationService: Time-based automation process exited unexpectedly, restarting...")
restartTimer.start()
}
break
case "activate":
console.warn("NightModeAutomationService: Failed to enable night mode, exit code:", exitCode)
break
case "reset":
console.warn("NightModeAutomationService: Failed to reset gamma, exit code:", exitCode)
break
case "test":
console.warn("NightModeAutomationService: Test command failed, exit code:", exitCode)
break
}
} else {
// Success case
switch(processType) {
case "activate":
console.log("NightModeAutomationService: Night mode activated successfully")
break
case "reset":
console.log("NightModeAutomationService: Gamma reset successfully")
break
case "test":
console.log("NightModeAutomationService: Test command completed successfully")
break
}
}
}
}
Process {
id: gammaStepResetProcess
Timer {
id: processTimeoutTimer
interval: 30000 // 30 second timeout (increased from 10s for one-shot commands)
running: false
onExited: function(exitCode) {
if (exitCode !== 0) {
console.warn("NightModeAutomationService: Failed to reset gamma, exit code:", exitCode)
repeat: false
onTriggered: {
if (gammaStepProcess.running) {
console.warn("NightModeAutomationService: Process", gammaStepProcess.processType, "timed out, killing process")
// Only timeout one-shot commands, not continuous automation
if (gammaStepProcess.processType !== "automation") {
gammaStepProcess.running = false
} else {
console.warn("NightModeAutomationService: Automation process running longer than expected, but not killing continuous process")
}
}
}
}
@@ -321,6 +761,27 @@ Singleton {
}
}
Timer {
id: testFeedbackTimer
interval: 2000
running: false
repeat: false
property string feedbackMessage: ""
onTriggered: {
if (feedbackMessage.length > 0) {
console.log(feedbackMessage)
feedbackMessage = ""
}
}
}
Timer {
id: cleanupTimer
interval: 1000
running: false
repeat: false
}
Connections {
target: SessionData
function onNightModeAutoEnabledChanged() {

View File

@@ -119,10 +119,33 @@ Night mode (gamma/color temperature) control.
- `value` - Optional temperature in Kelvin (2500-6000, steps of 500)
- Returns: Current or newly set temperature
**`automation [mode]`**
- Get or set night mode automation mode
- Parameters:
- `mode` - Optional automation mode: "manual", "time", or "location"
- Returns: Current or newly set automation mode
**`schedule <start> <end>`**
- Set time-based automation schedule
- Parameters:
- `start` - Start time in HH:MM format (e.g., "20:00")
- `end` - End time in HH:MM format (e.g., "06:00")
- Returns: Confirmation of schedule update
**`location <latitude> <longitude>`**
- Set manual coordinates for location-based automation
- Parameters:
- `latitude` - Latitude coordinate (e.g., 40.7128)
- `longitude` - Longitude coordinate (e.g., -74.0060)
- Returns: Confirmation of coordinates update
### Examples
```bash
qs -c dms ipc call night toggle
qs -c dms ipc call night temperature 4000
qs -c dms ipc call night automation time
qs -c dms ipc call night schedule 20:00 06:00
qs -c dms ipc call night location 40.7128 -74.0060
```
## Target: `mpris`

View File

@@ -25,6 +25,8 @@ ShellRoot {
Component.onCompleted: {
PortalService.init()
// Initialize NightModeAutomationService to trigger its Component.onCompleted
NightModeAutomationService.automationAvailable
}
WallpaperBackground {