night light: use hyprsunset <- gammastep

This commit is contained in:
end-4 2025-07-23 09:00:31 +07:00
parent 3d5ed9401c
commit 574a2a11e7
7 changed files with 148 additions and 35 deletions

View file

@ -1,5 +1,5 @@
# Bar, wallpaper
exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh & gammastep
exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh
exec-once = qs -c $qsConfig &
# Input method

View file

@ -8,6 +8,7 @@ Singleton {
id: root
property string filePath: Directories.shellConfigPath
property alias options: configOptionsJsonAdapter
property bool ready: false
function setNestedValue(nestedKey, value) {
let keys = nestedKey.split(".");
@ -41,10 +42,10 @@ Singleton {
FileView {
path: root.filePath
watchChanges: true
onFileChanged: reload()
onAdapterUpdated: writeAdapter()
onLoaded: root.ready = true
onLoadFailed: error => {
if (error == FileViewError.FileNotFound) {
writeAdapter();
@ -171,6 +172,15 @@ Singleton {
}
}
property JsonObject light: JsonObject {
property JsonObject night: JsonObject {
property bool automatic: true
property string from: "19:00" // Format: "HH:mm", 24-hour time
property string to: "06:30" // Format: "HH:mm", 24-hour time
property int colorTemperature: 5000
}
}
property JsonObject networking: JsonObject {
property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
}

View file

@ -1,41 +1,28 @@
import QtQuick
import qs.modules.common
import qs.modules.common.widgets
import qs
import qs.services
import Quickshell.Io
QuickToggleButton {
id: nightLightButton
property bool enabled: false
property bool enabled: Hyprsunset.active
toggled: enabled
buttonIcon: "nightlight"
buttonIcon: Config.options.light.night.automatic ? "night_sight_auto" : "bedtime"
onClicked: {
nightLightButton.enabled = !nightLightButton.enabled
if (enabled) {
nightLightOn.startDetached()
}
else {
nightLightOff.startDetached()
}
Hyprsunset.toggle()
}
Process {
id: nightLightOn
command: ["gammastep"]
altAction: () => {
Config.options.light.night.automatic = !Config.options.light.night.automatic
}
Process {
id: nightLightOff
command: ["pkill", "gammastep"]
}
Process {
id: updateNightLightState
running: true
command: ["pidof", "gammastep"]
stdout: SplitParser {
onRead: (data) => { // if not empty then set toggled to true
nightLightButton.enabled = data.length > 0
}
}
Component.onCompleted: {
Hyprsunset.fetchState()
}
StyledToolTip {
content: Translation.tr("Night Light")
content: Translation.tr("Night Light | Right-click to toggle Auto mode")
}
}

View file

@ -9,16 +9,15 @@ pragma ComponentBehavior: Bound
* A nice wrapper for date and time strings.
*/
Singleton {
property var clock: SystemClock {
id: clock
precision: SystemClock.Minutes
}
property string time: Qt.locale().toString(clock.date, Config.options?.time.format ?? "hh:mm")
property string date: Qt.locale().toString(clock.date, Config.options?.time.dateFormat ?? "dddd, dd/MM")
property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dd MMMM yyyy")
property string uptime: "0h, 0m"
SystemClock {
id: clock
precision: SystemClock.Minutes
}
Timer {
interval: 10
running: true

View file

@ -0,0 +1,117 @@
pragma Singleton
import QtQuick
import qs.modules.common
import Quickshell
import Quickshell.Io
/**
* Simple hyprsunset service with automatic mode.
* In theory we don't need this because hyprsunset has a config file, but it somehow doesn't work.
* It should also be possible to control it via hyprctl, but it doesn't work consistently either so we're just killing and launching.
*/
Singleton {
id: root
property var manualActive
property string from: Config.options?.light?.night?.from ?? "19:00" // Default to 7 PM
property string to: Config.options?.light?.night?.to ?? "06:30" // Default to 6:30 AM
property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true)
property int colorTemperature: Config.options?.light?.night?.colorTemperature ?? 5000 // Default color temperature
property bool shouldBeOn
property bool firstEvaluation: true
property bool active: false
property int fromHour: Number(from.split(":")[0])
property int fromMinute: Number(from.split(":")[1])
property int toHour: Number(to.split(":")[0])
property int toMinute: Number(to.split(":")[1])
property int clockHour: DateTime.clock.hours
property int clockMinute: DateTime.clock.minutes
function isNoLater(hour1, minute1, hour2, minute2) {
if (hour1 < hour2)
return true;
if (hour1 === hour2 && minute1 < minute2)
return true;
return false;
}
onClockMinuteChanged: reEvaluate()
onAutomaticChanged: {
root.manualActive = undefined;
root.firstEvaluation = true;
reEvaluate();
}
function reEvaluate() {
const toHourIsNextDay = !isNoLater(fromHour, fromMinute, toHour, toMinute);
const toHourWrapped = toHourIsNextDay ? toHour + 24 : toHour;
const toMinuteWrapped = toMinute;
root.shouldBeOn = isNoLater(fromHour, fromMinute, clockHour, clockMinute) && isNoLater(clockHour, clockMinute, toHourWrapped, toMinuteWrapped);
if (firstEvaluation) {
firstEvaluation = false;
root.ensureState();
}
}
onShouldBeOnChanged: ensureState()
function ensureState() {
// console.log("[Hyprsunset] Ensuring state:", root.shouldBeOn, "Automatic mode:", root.automatic);
if (!root.automatic || root.manualActive !== undefined)
return;
if (root.shouldBeOn) {
root.enable();
} else {
root.disable();
}
}
function load() { } // Dummy to force init
function enable() {
root.active = true;
// console.log("[Hyprsunset] Enabling");
Quickshell.execDetached(["bash", "-c", `pidof hyprsunset || hyprsunset --temperature ${root.colorTemperature}`]);
}
function disable() {
root.active = false;
// console.log("[Hyprsunset] Disabling");
Quickshell.execDetached(["bash", "-c", `pkill hyprsunset`]);
}
function fetchState() {
fetchProc.running = true;
}
Process {
id: fetchProc
running: true
command: ["bash", "-c", "hyprctl hyprsunset temperature"]
stdout: StdioCollector {
id: stateCollector
onStreamFinished: {
const output = stateCollector.text.trim();
if (output.length == 0 || output.startsWith("Couldn't"))
root.active = false;
else
root.active = (output != "6500");
// console.log("[Hyprsunset] Fetched state:", output, "->", root.active);
}
}
}
function toggle() {
if (root.manualActive === undefined)
root.manualActive = root.active;
root.manualActive = !root.manualActive;
if (root.manualActive) {
root.enable();
} else {
root.disable();
}
}
}

View file

@ -49,9 +49,10 @@ ShellRoot {
// Force initialization of some singletons
Component.onCompleted: {
MaterialThemeLoader.reapplyTheme()
Cliphist.refresh()
FirstRunExperience.load()
Hyprsunset.load()
MaterialThemeLoader.reapplyTheme()
}
LazyLoader { active: enableBar; component: Bar {} }

View file

@ -5,7 +5,6 @@ pkgdesc='Illogical Impulse Backlight Dependencies'
arch=(any)
license=(None)
depends=(
gammastep
geoclue
brightnessctl
ddcutil