diff --git a/bin/omarchy-menu b/bin/omarchy-menu index 6f372be2e3..a5185e550c 100755 --- a/bin/omarchy-menu +++ b/bin/omarchy-menu @@ -342,7 +342,7 @@ show_font_menu() { } show_setup_menu() { - local options=" Audio\n Wifi\n󰂯 Bluetooth\n󱐋 Power Profile\n System Sleep\n󰍹 Monitors" + local options=" Audio\n Wifi\n󰂯 Bluetooth\n󱐋 Power Profile\n󰸌 Theme Schedule\n System Sleep\n󰍹 Monitors" [[ -f ~/.config/hypr/bindings.lua ]] && options="$options\n Keybindings" [[ -f ~/.config/hypr/input.lua ]] && options="$options\n Input" options="$options\n Defaults\n󰱔 DNS\n Security\n Config" @@ -352,6 +352,7 @@ show_setup_menu() { *Wifi*) omarchy-launch-wifi ;; *Bluetooth*) omarchy-launch-bluetooth ;; *Power*) show_setup_power_menu ;; + *"Theme Schedule"*) show_setup_theme_schedule_menu ;; *System*) show_setup_system_menu ;; *Monitors*) open_in_editor "$(hypr_config_file monitors)" ;; *Keybindings*) open_in_editor "$(hypr_config_file bindings)" ;; @@ -364,13 +365,53 @@ show_setup_menu() { esac } +show_setup_theme_schedule_menu() { + local config_file="$HOME/.config/omarchy/theme-schedule.conf" + + # Default values + local MODE="time" LIGHT_THEME="" DARK_THEME="" LIGHT_TIME="07:00" DARK_TIME="19:00" + + if [[ -f "$config_file" ]]; then + source <(grep -E '^(MODE|LIGHT_TIME|DARK_TIME|LIGHT_THEME|DARK_THEME)=' "$config_file") + fi + + local options="󰔎 Mode: ${MODE^}\n󰸌 Set Day Theme\n󰸌 Set Night Theme" + [[ "$MODE" == "time" ]] && options="$options\n󰔚 Set Day Start ($LIGHT_TIME)\n󰔚 Set Night Start ($DARK_TIME)" + options="$options\n Check & Apply Now" + + case $(menu "Theme Schedule" "$options") in + *Mode*) + local new_mode="time"; [[ "$MODE" == "time" ]] && new_mode="auto" + omarchy-theme-schedule --mode "$new_mode" && show_setup_theme_schedule_menu ;; + *"Day Theme"*) + local theme; theme=$(menu "Select Day Theme" "$(omarchy-theme-list)" "" "$LIGHT_THEME") + [[ -n $theme && $theme != "CNCLD" ]] && omarchy-theme-schedule --light "$theme" + show_setup_theme_schedule_menu ;; + *"Night Theme"*) + local theme; theme=$(menu "Select Night Theme" "$(omarchy-theme-list)" "" "$DARK_THEME") + [[ -n $theme && $theme != "CNCLD" ]] && omarchy-theme-schedule --dark "$theme" + show_setup_theme_schedule_menu ;; + *"Day Start"*) + local time; time=$(omarchy-menu-input "Day Start (HH:MM)" "$LIGHT_TIME") + [[ $time =~ ^([01][0-9]|2[0-3]):[0-5][0-9]$ ]] && omarchy-theme-schedule --light-time "$time" + show_setup_theme_schedule_menu ;; + *"Night Start"*) + local time; time=$(omarchy-menu-input "Night Start (HH:MM)" "$DARK_TIME") + [[ $time =~ ^([01][0-9]|2[0-3]):[0-5][0-9]$ ]] && omarchy-theme-schedule --dark-time "$time" + show_setup_theme_schedule_menu ;; + *"Apply Now"*) + omarchy-theme-schedule --check && show_setup_theme_schedule_menu ;; + *) show_setup_menu ;; + esac +} + show_setup_power_menu() { - profile=$(menu "Power Profile" "$(omarchy-powerprofiles-list)" "" "$(powerprofilesctl get)") + local current; current=$(powerprofilesctl get) + local profile; profile=$(menu "Power Profile" "$(omarchy-powerprofiles-list)" "" "$current") - if [[ $profile == "CNCLD" || -z $profile ]]; then - back_to show_setup_menu - else + if [[ $profile != "CNCLD" && -n $profile && "$profile" != "$current" ]]; then powerprofilesctl set "$profile" + omarchy-notification-send -g 󱐋 "Power Profile" "Switched to $profile" fi } diff --git a/bin/omarchy-theme-schedule b/bin/omarchy-theme-schedule new file mode 100644 index 0000000000..40b78a4337 --- /dev/null +++ b/bin/omarchy-theme-schedule @@ -0,0 +1,204 @@ +#!/bin/bash + +# omarchy:summary=Schedule dark and light themes based on time or sunrise/sunset +# omarchy:args=[--light ] [--dark ] [--mode ] [--light-time ] [--dark-time ] [--check] +# omarchy:examples=omarchy theme schedule --mode auto --light "Catppuccin Latte" --dark "Tokyo Night" + +set -o pipefail + +# --- Infrastructure --- +CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy" +CONFIG_FILE="$CONFIG_DIR/theme-schedule.conf" +LOG_FILE="$CONFIG_DIR/theme-schedule.log" +LOCK_DIR="/tmp/omarchy-theme-schedule.lock.dir" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" +} + +# --- Atomic Locking --- +if ! mkdir "$LOCK_DIR" 2>/dev/null; then + if [[ $(find "$LOCK_DIR" -maxdepth 0 -mmin +30 2>/dev/null) ]]; then + rmdir "$LOCK_DIR" + mkdir "$LOCK_DIR" || exit 0 + else + exit 0 + fi +fi +trap 'rmdir "$LOCK_DIR" 2>/dev/null' EXIT + +# --- Helpers --- +show_help() { + cat < Theme to use for daytime + --dark Theme to use for nighttime + --mode Switching mode: 'time' (fixed) or 'auto' (astronomy) + --light-time Time to switch to light theme (default: 07:00) + --dark-time Time to switch to dark theme (default: 19:00) + --check Evaluate conditions and apply the correct theme + --help Show this help message + +Examples: + omarchy theme schedule --mode auto --light "Catppuccin Latte" --dark "Tokyo Night" + omarchy theme schedule --mode time --light-time 08:30 --dark-time 20:15 +EOF +} + +validate_theme() { + local theme="$1" + if ! omarchy-theme-list | grep -qFx "$theme"; then + echo "Error: Theme '$theme' is not a valid Omarchy theme." + return 1 + fi + return 0 +} + +load_config() { + MODE="time" + LIGHT_TIME="07:00" + DARK_TIME="19:00" + LIGHT_THEME="" + DARK_THEME="" + + if [[ -f "$CONFIG_FILE" ]]; then + # shellcheck disable=SC1090 + source <(grep -E '^(MODE|LIGHT_TIME|DARK_TIME|LIGHT_THEME|DARK_THEME)=' "$CONFIG_FILE") + fi +} + +save_config() { + mkdir -p "$CONFIG_DIR" + cat > "$CONFIG_FILE" </dev/null 2>&1 + log "Systemd units synchronized." +} + +notify() { + omarchy-notification-send -g "󰸌" "Theme Schedule" "$1" +} + +check_and_apply() { + load_config + + if [[ -z "$LIGHT_THEME" || -z "$DARK_THEME" ]]; then + return + fi + + local target_mode="light" + + if [[ "$MODE" == "auto" ]]; then + local data + data=$(curl -fsS --max-time 10 "https://wttr.in?format=j1" 2>/dev/null | jq -er '[.weather[0].astronomy[0].sunrise, .weather[0].astronomy[0].sunset] | select(all(. != null and . != "")) | @tsv' 2>/dev/null) + + if [[ $? -eq 0 ]]; then + local sunrise sunset + IFS=$'\t' read -r sunrise sunset <<< "$data" + + local now_epoch sunrise_epoch sunset_epoch + now_epoch=$(date +%s) + sunrise_epoch=$(date -d "today $sunrise" +%s 2>/dev/null || echo 0) + sunset_epoch=$(date -d "today $sunset" +%s 2>/dev/null || echo 0) + + if (( sunrise_epoch > 0 && sunset_epoch > 0 )); then + if (( now_epoch < sunrise_epoch || now_epoch >= sunset_epoch )); then + target_mode="dark" + else + target_mode="light" + fi + else + MODE="time" + fi + else + MODE="time" + fi + fi + + if [[ "$MODE" == "time" ]]; then + local current_time + current_time=$(date +%H:%M) + if [[ "$current_time" < "$LIGHT_TIME" ]] || [[ "$current_time" > "$DARK_TIME" ]] || [[ "$current_time" == "$DARK_TIME" ]]; then + target_mode="dark" + else + target_mode="light" + fi + fi + + local target_theme + [[ "$target_mode" == "light" ]] && target_theme="$LIGHT_THEME" || target_theme="$DARK_THEME" + + local current_theme + current_theme=$(omarchy-theme-current) + + if [[ "$current_theme" != "$target_theme" ]]; then + log "Applying theme change: $current_theme -> $target_theme ($target_mode mode)" + omarchy-theme-set "$target_theme" + notify "Switched to $target_theme ($target_mode mode)" + else + # Redundancy Check: Ensure only one waybar is running + if [[ $(pgrep -x waybar | wc -l) -gt 1 ]]; then + log "Cleaning up duplicate Waybar instances." + omarchy-restart-waybar + fi + fi +} + +# --- Entry Point --- +load_config + +if [[ $# -eq 0 ]]; then + show_help + exit 0 +fi + +NEED_SAVE=false +MSG="" + +while [[ $# -gt 0 ]]; do + case $1 in + --light) + validate_theme "$2" || exit 1 + LIGHT_THEME="$2"; NEED_SAVE=true + MSG="${MSG:+$MSG\n}Day theme: $2"; shift 2 ;; + --dark) + validate_theme "$2" || exit 1 + DARK_THEME="$2"; NEED_SAVE=true + MSG="${MSG:+$MSG\n}Night theme: $2"; shift 2 ;; + --mode) + MODE="$2"; NEED_SAVE=true + MSG="${MSG:+$MSG\n}Mode: ${2^}"; shift 2 ;; + --light-time) + LIGHT_TIME="$2"; NEED_SAVE=true + MSG="${MSG:+$MSG\n}Day start: $2"; shift 2 ;; + --dark-time) + DARK_TIME="$2"; NEED_SAVE=true + MSG="${MSG:+$MSG\n}Night start: $2"; shift 2 ;; + --check) + check_and_apply; exit 0 ;; + --help) + show_help; exit 0 ;; + *) + echo "Unknown option: $1"; show_help; exit 1 ;; + esac +done + +if [[ "$NEED_SAVE" == "true" ]]; then + save_config + setup_systemd + [[ -n "$MSG" ]] && notify "Schedule Updated:\n$MSG" + check_and_apply +fi diff --git a/config/systemd/user/omarchy-theme-schedule.service b/config/systemd/user/omarchy-theme-schedule.service new file mode 100644 index 0000000000..56424a4f63 --- /dev/null +++ b/config/systemd/user/omarchy-theme-schedule.service @@ -0,0 +1,11 @@ +[Unit] +Description=Omarchy Theme Scheduler +After=graphical-session.target + +[Service] +Type=oneshot +ExecStartPre=/usr/bin/systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE +ExecStartPre=/usr/bin/mkdir -p %h/.config/omarchy +ExecStart=%h/.local/share/omarchy/bin/omarchy-theme-schedule --check +StandardOutput=append:%h/.config/omarchy/theme-schedule.log +StandardError=append:%h/.config/omarchy/theme-schedule.log diff --git a/config/systemd/user/omarchy-theme-schedule.timer b/config/systemd/user/omarchy-theme-schedule.timer new file mode 100644 index 0000000000..deb6feaf17 --- /dev/null +++ b/config/systemd/user/omarchy-theme-schedule.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Omarchy Theme Scheduler Timer + +[Timer] +OnCalendar=*:0/15 +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/migrations/1778733053.sh b/migrations/1778733053.sh new file mode 100644 index 0000000000..5e4130dd89 --- /dev/null +++ b/migrations/1778733053.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Enable the theme scheduler timer if it was recently added +if [[ -f "$HOME/.config/systemd/user/omarchy-theme-schedule.timer" ]]; then + systemctl --user enable --now omarchy-theme-schedule.timer || true +fi