Compare commits
13
Commits
ab66fe9fca
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8509f2ad1c | ||
|
|
92308d3147 | ||
|
|
6903aeec29 | ||
|
|
706a602e33 | ||
|
|
77c616227e | ||
|
|
a42b741022 | ||
|
|
8df49bd750 | ||
|
|
cdc751cae8 | ||
|
|
2d2b176b85 | ||
|
|
9344f1243d | ||
|
|
014f6f165c | ||
|
|
aed8cf9dd3 | ||
|
|
2e546e1ebb |
@@ -119,6 +119,7 @@ packages:
|
|||||||
- grim
|
- grim
|
||||||
- jdk-openjdk
|
- jdk-openjdk
|
||||||
- jq
|
- jq
|
||||||
|
- yq
|
||||||
- kdeconnect
|
- kdeconnect
|
||||||
- kitty
|
- kitty
|
||||||
- librewolf
|
- librewolf
|
||||||
@@ -172,8 +173,12 @@ packages:
|
|||||||
yay:
|
yay:
|
||||||
- teams-for-linux-bin
|
- teams-for-linux-bin
|
||||||
- docker
|
- docker
|
||||||
|
- docker-credential-secretservice-bin
|
||||||
|
- docker-compose
|
||||||
- claude
|
- claude
|
||||||
- claude-code
|
- claude-code
|
||||||
- webstorm
|
- webstorm
|
||||||
- webstorm-jre
|
- webstorm-jre
|
||||||
- azure-cli
|
- azure-cli
|
||||||
|
- terraform
|
||||||
|
- postgresql-libs
|
||||||
|
|||||||
+7
-5
@@ -76,15 +76,17 @@
|
|||||||
.config/spicetify
|
.config/spicetify
|
||||||
.config/spotify
|
.config/spotify
|
||||||
.config/spotify-launcher.conf
|
.config/spotify-launcher.conf
|
||||||
.config/systemd
|
# Ignore only the symlink farms generated by `systemctl --user enable`,
|
||||||
|
# so that real unit files can still be managed.
|
||||||
|
.config/systemd/user/*.wants
|
||||||
.config/teams-for-linux
|
.config/teams-for-linux
|
||||||
.config/yay
|
.config/yay
|
||||||
|
|
||||||
.gnupg
|
# Ignore only the keyring and runtime state that GnuPG generates itself,
|
||||||
|
# so that the real config files can still be managed. Do not ignore
|
||||||
|
# `.gnupg` wholesale: chezmoi never descends into an ignored directory, so
|
||||||
|
# a `!` negation for a file inside it can never re-include that file.
|
||||||
.gnupg/crls.d
|
.gnupg/crls.d
|
||||||
!.gnupg/dirmngr.conf
|
|
||||||
!.gnupg/gpg-agent.conf
|
|
||||||
!.gnupg/gpg.conf
|
|
||||||
.gnupg/openpgp-revocs.d
|
.gnupg/openpgp-revocs.d
|
||||||
.gnupg/private-keys-v1.d
|
.gnupg/private-keys-v1.d
|
||||||
.gnupg/public-keys.d
|
.gnupg/public-keys.d
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{{- if eq .chezmoi.os "linux" -}}
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Enable the battery charge-controller watchdog.
|
||||||
|
#
|
||||||
|
# Re-runs whenever the unit or the watchdog script changes, so that an
|
||||||
|
# already-running instance picks up the new version.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
[ "${CHEZMOI}" -eq 1 ] || exit 1
|
||||||
|
|
||||||
|
# unit hash: {{ include (joinPath .chezmoi.sourceDir "dot_config/systemd/user/battery-charge-watchdog.service") | sha256sum }}
|
||||||
|
# script hash: {{ include (joinPath .chezmoi.sourceDir "dot_config/scripts/executable_battery-charge-watchdog.sh") | sha256sum }}
|
||||||
|
|
||||||
|
# Applying from a TTY or a container has no user bus; skip rather than
|
||||||
|
# aborting the whole apply.
|
||||||
|
if ! systemctl --user show-environment > /dev/null 2>&1; then
|
||||||
|
echo "No systemd user session, skipping battery-charge-watchdog activation"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
|
||||||
|
# The unit carries ConditionPathExists on the battery, so enabling it on a
|
||||||
|
# machine without one is harmless: it simply never starts.
|
||||||
|
systemctl --user enable --now battery-charge-watchdog.service
|
||||||
|
|
||||||
|
# enable --now will not restart an instance that is already running, so an
|
||||||
|
# updated script or unit would otherwise not take effect until next login.
|
||||||
|
systemctl --user try-restart battery-charge-watchdog.service
|
||||||
|
|
||||||
|
# vim: syntax=bash ft=template
|
||||||
|
{{- end -}}
|
||||||
+9
@@ -23,6 +23,15 @@ fi
|
|||||||
# spotify hash: <not installed>
|
# spotify hash: <not installed>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
|
{{ $spotifyPath := "/usr/bin/spotify" }}
|
||||||
|
{{ $s := stat $spotifyPath }}
|
||||||
|
{{ if and $s (hasKey $s "Exists") $s.Exists }}
|
||||||
|
# spotify hash: {{ include $spotifyPath | sha256sum }}
|
||||||
|
{{ else }}
|
||||||
|
# spotify hash: <not installed>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ $catppuccinPath := joinPath .chezmoi.homeDir ".config/spicetify-catppuccin-theme/catppuccin/app.scss" }}
|
{{ $catppuccinPath := joinPath .chezmoi.homeDir ".config/spicetify-catppuccin-theme/catppuccin/app.scss" }}
|
||||||
{{ $c := stat $catppuccinPath }}
|
{{ $c := stat $catppuccinPath }}
|
||||||
{{ if and $c (hasKey $c "Exists") $c.Exists }}
|
{{ if and $c (hasKey $c "Exists") $c.Exists }}
|
||||||
@@ -364,24 +364,24 @@
|
|||||||
"acLockTimeout": 1800,
|
"acLockTimeout": 1800,
|
||||||
"acSuspendTimeout": 0,
|
"acSuspendTimeout": 0,
|
||||||
"acSuspendBehavior": 0,
|
"acSuspendBehavior": 0,
|
||||||
"acProfileName": "",
|
"acProfileName": "2",
|
||||||
"acPostLockMonitorTimeout": 3600,
|
"acPostLockMonitorTimeout": 3600,
|
||||||
"batteryMonitorTimeout": 600,
|
"batteryMonitorTimeout": 600,
|
||||||
"batteryLockTimeout": 300,
|
"batteryLockTimeout": 300,
|
||||||
"batterySuspendTimeout": 3600,
|
"batterySuspendTimeout": 3600,
|
||||||
"batterySuspendBehavior": 2,
|
"batterySuspendBehavior": 2,
|
||||||
"batteryProfileName": "",
|
"batteryProfileName": "0",
|
||||||
"batteryPostLockMonitorTimeout": 300,
|
"batteryPostLockMonitorTimeout": 300,
|
||||||
"batteryChargeLimit": 100,
|
"batteryChargeLimit": 100,
|
||||||
"batteryNotifyChargeLimit": false,
|
"batteryNotifyChargeLimit": true,
|
||||||
"batteryCriticalThreshold": 5,
|
"batteryCriticalThreshold": 10,
|
||||||
"batteryNotifyCritical": true,
|
"batteryNotifyCritical": true,
|
||||||
"batteryLowThreshold": 15,
|
"batteryLowThreshold": 15,
|
||||||
"batteryNotifyLow": true,
|
"batteryNotifyLow": true,
|
||||||
"batteryChargeLimitNotificationType": 0,
|
"batteryChargeLimitNotificationType": 1,
|
||||||
"batteryLowNotificationType": 1,
|
"batteryLowNotificationType": 1,
|
||||||
"batteryCriticalNotificationType": 1,
|
"batteryCriticalNotificationType": 1,
|
||||||
"batteryAutoPowerSaver": false,
|
"batteryAutoPowerSaver": true,
|
||||||
"lockBeforeSuspend": true,
|
"lockBeforeSuspend": true,
|
||||||
"loginctlLockIntegration": true,
|
"loginctlLockIntegration": true,
|
||||||
"fadeToLockEnabled": true,
|
"fadeToLockEnabled": true,
|
||||||
@@ -661,10 +661,10 @@
|
|||||||
"enabled": true
|
"enabled": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"spacing": 4,
|
"spacing": 0,
|
||||||
"innerPadding": 6,
|
"innerPadding": 6,
|
||||||
"barInsetPadding": 4,
|
"barInsetPadding": 4,
|
||||||
"bottomGap": 0,
|
"bottomGap": -16,
|
||||||
"transparency": 0,
|
"transparency": 0,
|
||||||
"widgetTransparency": 1,
|
"widgetTransparency": 1,
|
||||||
"squareCorners": false,
|
"squareCorners": false,
|
||||||
|
|||||||
+71
-8
@@ -1,10 +1,73 @@
|
|||||||
|
local workspace_rules = {}
|
||||||
|
|
||||||
|
{{ if eq .chezmoi.hostname "ia-frank-xps13" -}}
|
||||||
hl.monitor({
|
hl.monitor({
|
||||||
output = "eDP-1",
|
output = "eDP-1",
|
||||||
scale = 1,
|
scale = 1,
|
||||||
position = "0x0",
|
position = "0x0",
|
||||||
mode = "1920x1200@60"
|
mode = "1920x1200@120"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
hl.monitor({
|
||||||
|
output = "desc:Dell Inc. DELL U2724DE BD19MF4",
|
||||||
|
scale = 1,
|
||||||
|
position = "-2560x0",
|
||||||
|
mode = "2560x1440@120"
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.monitor({
|
||||||
|
output = "desc:Dell Inc. DELL U2724D 1CQFMF4",
|
||||||
|
scale = 1,
|
||||||
|
position = "-5120x0",
|
||||||
|
mode = "2560x1440@120"
|
||||||
|
})
|
||||||
|
local workspace_rules = {
|
||||||
|
{ workspace = "6", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", default = true, persistent = true },
|
||||||
|
{ workspace = "7", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", persistent = true },
|
||||||
|
{ workspace = "8", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", persistent = true },
|
||||||
|
{ workspace = "1", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", default = true, persistent = true },
|
||||||
|
{ workspace = "2", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
|
{ workspace = "3", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
|
{ workspace = "4", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
|
{ workspace = "5", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
|
{ workspace = "9", monitor = "eDP-1", default = true, persistent = true },
|
||||||
|
{ workspace = "10", monitor = "eDP-1", persistent = true },
|
||||||
|
}
|
||||||
|
{{- else if eq .chezmoi.hostname "arch-next" -}}
|
||||||
|
hl.monitor({
|
||||||
|
output = "desc:GIGA-BYTE TECHNOLOGY CO. LTD. M34WQ 0x00000B52",
|
||||||
|
scale = 1,
|
||||||
|
position = "0x0",
|
||||||
|
mode = "3440x1440@120",
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.monitor({
|
||||||
|
output = "desc:Dell Inc. DELL U2724D DL7R834",
|
||||||
|
scale = 1,
|
||||||
|
position = "3440x0",
|
||||||
|
mode = "2560x1440@120",
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.monitor({
|
||||||
|
output = "desc:Dell Inc. DELL U2414H 292K475V26JL",
|
||||||
|
scale = 1,
|
||||||
|
position = "760x-1080",
|
||||||
|
mode = "1920x1080@60",
|
||||||
|
})
|
||||||
|
local workspace_rules = {
|
||||||
|
{ workspace = "1", monitor = "desc:GIGA-BYTE TECHNOLOGY CO. LTD. M34WQ 0x00000B52", default = true, persistent = true},
|
||||||
|
{ workspace = "9", monitor = "desc:Dell Inc. DELL U2724D DL7R834", default = true, persistent = true},
|
||||||
|
{ workspace = "10", monitor = "desc:Dell Inc. DELL U2414H 292K475V26JL", default = true, persistent = true},
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for _, rule in ipairs(workspace_rules) do
|
||||||
|
hl.workspace_rule(rule)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- if multiple profiles match, profile with highest priority wins.
|
-- if multiple profiles match, profile with highest priority wins.
|
||||||
-- if no profile match at all, the fallback_profile will be applied anyways.
|
-- if no profile match at all, the fallback_profile will be applied anyways.
|
||||||
local fallback_profile="laptop"
|
local fallback_profile="laptop"
|
||||||
@@ -47,25 +110,25 @@ local profiles = {
|
|||||||
work = {
|
work = {
|
||||||
priority = 10,
|
priority = 10,
|
||||||
monitors = {
|
monitors = {
|
||||||
"eDP-1",
|
|
||||||
"desc:Dell Inc. DELL U2724DE BD19MF4",
|
|
||||||
"desc:Dell Inc. DELL U2724D 1CQFMF4",
|
"desc:Dell Inc. DELL U2724D 1CQFMF4",
|
||||||
|
"desc:Dell Inc. DELL U2724DE BD19MF4",
|
||||||
|
"eDP-1",
|
||||||
},
|
},
|
||||||
disable_other_monitors = false,
|
disable_other_monitors = false,
|
||||||
monitor_config = {
|
monitor_config = {
|
||||||
|
["desc:Dell Inc. DELL U2724D 1CQFMF4"] = {disabled=false, scale=1, position="-5120x0", mode="2560x1440@120"},
|
||||||
|
["desc:Dell Inc. DELL U2724DE BD19MF4"] = {disabled=false, scale=1, position="-2560x0", mode="2560x1440@120"},
|
||||||
["eDP-1"] = {disabled=false, scale=1, position="0x0", mode="1920x1200@120"},
|
["eDP-1"] = {disabled=false, scale=1, position="0x0", mode="1920x1200@120"},
|
||||||
["desc:Dell Inc. DELL U2724DE BD19MF4"] = {disabled=false, scale=1, position="1920x0", mode="2560x1440@120"},
|
|
||||||
["desc:Dell Inc. DELL U2724D 1CQFMF4"] = {disabled=false, scale=1, position="4480x0", mode="2560x1440@120"},
|
|
||||||
},
|
},
|
||||||
workspace_rules = {
|
workspace_rules = {
|
||||||
|
{ workspace = "6", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", default = true, persistent = true },
|
||||||
|
{ workspace = "7", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", persistent = true },
|
||||||
|
{ workspace = "8", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", persistent = true },
|
||||||
{ workspace = "1", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", default = true, persistent = true },
|
{ workspace = "1", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", default = true, persistent = true },
|
||||||
{ workspace = "2", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
{ workspace = "2", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
{ workspace = "3", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
{ workspace = "3", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
{ workspace = "4", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
{ workspace = "4", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
{ workspace = "5", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
{ workspace = "5", monitor = "desc:Dell Inc. DELL U2724DE BD19MF4", persistent = true },
|
||||||
{ workspace = "6", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", default = true, persistent = true },
|
|
||||||
{ workspace = "7", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", persistent = true },
|
|
||||||
{ workspace = "8", monitor = "desc:Dell Inc. DELL U2724D 1CQFMF4", persistent = true },
|
|
||||||
{ workspace = "9", monitor = "eDP-1", default = true, persistent = true },
|
{ workspace = "9", monitor = "eDP-1", default = true, persistent = true },
|
||||||
{ workspace = "10", monitor = "eDP-1", persistent = true },
|
{ workspace = "10", monitor = "eDP-1", persistent = true },
|
||||||
},
|
},
|
||||||
@@ -221,6 +221,10 @@ rule(
|
|||||||
{ matches_class("^(hyprland-share-picker)$") },
|
{ matches_class("^(hyprland-share-picker)$") },
|
||||||
{ float(), center(), pin() }
|
{ float(), center(), pin() }
|
||||||
)
|
)
|
||||||
|
rule(
|
||||||
|
{ matches_class("^(gcr-prompter)$") },
|
||||||
|
{ float(), center(), pin() }
|
||||||
|
)
|
||||||
|
|
||||||
-- Nemo
|
-- Nemo
|
||||||
rule(
|
rule(
|
||||||
@@ -293,3 +297,4 @@ rule(
|
|||||||
{ matches_initial_class("^(jetbrains-toolbox)$") },
|
{ matches_initial_class("^(jetbrains-toolbox)$") },
|
||||||
{ float(), center(), pin() }
|
{ float(), center(), pin() }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
(sleep 1 ; hyprctl eval 'request_apply_monitor_profile()') &
|
||||||
(sleep 2 ; hyprctl eval 'request_apply_monitor_profile()') &
|
(sleep 2 ; hyprctl eval 'request_apply_monitor_profile()') &
|
||||||
|
(sleep 3 ; hyprctl eval 'request_apply_monitor_profile()') &
|
||||||
|
|
||||||
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- thunderbird", {workspace = 10})'
|
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- thunderbird", {workspace = 10})'
|
||||||
{{ if eq .work_env true -}}
|
{{ if eq .work_env true -}}
|
||||||
|
# outlook
|
||||||
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- /home/frank/.local/share/applications/librewolf.webapp-e7db0a1c-02e9-42cc-a5da-8fdc222e89dc.desktop", {workspace = 9})'
|
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- /home/frank/.local/share/applications/librewolf.webapp-e7db0a1c-02e9-42cc-a5da-8fdc222e89dc.desktop", {workspace = 9})'
|
||||||
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- jetbrains-toolbox.desktop")'
|
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- nextcloud")'
|
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- nextcloud")'
|
||||||
sleep 3
|
sleep 3
|
||||||
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- Telegram --ozone-platform=wayland", {workspace = 10})'
|
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- Telegram --ozone-platform=wayland", {workspace = 10})'
|
||||||
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- signal-desktop --ozone-platform=wayland", {workspace = 10})'
|
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- signal-desktop --ozone-platform=wayland", {workspace = 10})'
|
||||||
{{ if eq .work_env true -}}
|
{{ if eq .work_env true -}}
|
||||||
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- /home/frank/.local/share/applications/librewolf.webapp-daf88433-cdaa-4a10-9bfb-af0a20fb7b71.desktop", {workspace = 9})'
|
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm app -- teams-for-linux.desktop", {workspace = 9})'
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
sleep 2
|
||||||
|
hyprctl dispatch 'hl.dsp.focus({workspace = 10})'
|
||||||
|
hyprctl dispatch 'hl.dsp.focus({workspace = 9})'
|
||||||
|
hyprctl dispatch 'hl.dsp.focus({workspace = 1})'
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
{{ $rss := "x" }}
|
{{- $rss := "x" -}}
|
||||||
{{ $cal := "x" }}
|
{{- $cal := "x" -}}
|
||||||
{{ $mail := "x" }}
|
{{- $mail := "x" -}}
|
||||||
{{ $tb := "x" }}
|
{{- $tb := "x" -}}
|
||||||
{{ $news := "x" }}
|
{{- $news := "x" -}}
|
||||||
|
|
||||||
{{- if eq .chezmoi.hostname "arch-next" -}}
|
{{- if eq .chezmoi.hostname "arch-next" -}}
|
||||||
|
|
||||||
{{- $rss = "userapp-Thunderbird-Y886T3.desktop" -}}
|
{{- $rss = "userapp-Thunderbird-Y886T3.desktop" -}}
|
||||||
{{- $cal = "userapp-Thunderbird-NA25T3.desktop" -}}
|
{{- $cal = "userapp-Thunderbird-NA25T3.desktop" -}}
|
||||||
{{- $mail = "userapp-Thunderbird-Q3N5T3.desktop" -}}
|
{{- $mail = "userapp-Thunderbird-Q3N5T3.desktop" -}}
|
||||||
{{- $tb = "userapp-Thunderbird-SIG6T3.desktop" }}
|
{{- $tb = "userapp-Thunderbird-SIG6T3.desktop" -}}
|
||||||
{{- $news = "userapp-Thunderbird-A0Q6T3.desktop" }}
|
{{- $news = "userapp-Thunderbird-A0Q6T3.desktop" -}}
|
||||||
|
{{- else if eq .chezmoi.hostname "ia-frank-xps13" -}}
|
||||||
{{- else if eq .chezmoi.hostname "xps13-ia" -}}
|
|
||||||
|
|
||||||
{{- $rss = "userapp-Thunderbird-WTV1S3.desktop" -}}
|
{{- $rss = "userapp-Thunderbird-WTV1S3.desktop" -}}
|
||||||
{{- $cal = "userapp-Thunderbird-X7G9S3.desktop" -}}
|
{{- $cal = "userapp-Thunderbird-X7G9S3.desktop" -}}
|
||||||
{{- $mail = "userapp-Thunderbird-3QI0S3.desktop" -}}
|
{{- $mail = "userapp-Thunderbird-3QI0S3.desktop" -}}
|
||||||
{{- $tb = "userapp-Thunderbird-U229S3.desktop" -}}
|
{{- $tb = "userapp-Thunderbird-U229S3.desktop" -}}
|
||||||
{{- $news = "userapp-Thunderbird-AH40S3.desktop" -}}
|
{{- $news = "userapp-Thunderbird-AH40S3.desktop" -}}
|
||||||
|
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
[Default Applications]
|
[Default Applications]
|
||||||
application/pdf=librewolf.desktop
|
application/pdf=librewolf.desktop
|
||||||
application/rss+xml={{ $rss }}
|
application/rss+xml={{ $rss }}
|
||||||
@@ -89,9 +83,9 @@ x-scheme-handler/feed={{ $rss }};
|
|||||||
x-scheme-handler/mailto={{ $mail }};
|
x-scheme-handler/mailto={{ $mail }};
|
||||||
x-scheme-handler/mid={{ $mail }};
|
x-scheme-handler/mid={{ $mail }};
|
||||||
x-scheme-handler/net.thunderbird={{ $tb }};
|
x-scheme-handler/net.thunderbird={{ $tb }};
|
||||||
x-scheme-handler/news={{ $rss }};
|
x-scheme-handler/news={{ $news }};
|
||||||
x-scheme-handler/nntp={{ $rss }};
|
x-scheme-handler/nntp={{ $news }};
|
||||||
x-scheme-handler/snews={{ $rss }};
|
x-scheme-handler/snews={{ $news }};
|
||||||
x-scheme-handler/webcal={{ $cal }};
|
x-scheme-handler/webcal={{ $cal }};
|
||||||
x-scheme-handler/webcals={{ $cal }};
|
x-scheme-handler/webcals={{ $cal }};
|
||||||
application/xhtml+xml=librewolf.desktop;
|
application/xhtml+xml=librewolf.desktop;
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# battery-charge-watchdog
|
||||||
|
#
|
||||||
|
# Detects the Dell/UCSI fault where the battery reports a non-discharging
|
||||||
|
# status (Charging / Full / Not charging) while charge_now is actually
|
||||||
|
# falling. In that state UPower never fires CriticalPowerAction, so the
|
||||||
|
# machine runs to 0% and dies with no warning and no clean shutdown.
|
||||||
|
#
|
||||||
|
# The only trustworthy signal is the charge_now trend: current_now is
|
||||||
|
# reported unsigned on this hardware, so its sign says nothing about
|
||||||
|
# direction, and `status` is precisely what lies during the fault.
|
||||||
|
#
|
||||||
|
# Raises a critical desktop notification and keeps re-raising it until
|
||||||
|
# charging genuinely resumes or the status turns honest (Discharging,
|
||||||
|
# where UPower's own safety net takes over again).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
BAT=${BAT:-/sys/class/power_supply/BAT0}
|
||||||
|
AC=${AC:-/sys/class/power_supply/AC}
|
||||||
|
|
||||||
|
# How often to sample.
|
||||||
|
POLL_INTERVAL=${POLL_INTERVAL:-20}
|
||||||
|
# How often to re-raise the alert while the fault persists.
|
||||||
|
REPEAT_INTERVAL=${REPEAT_INTERVAL:-60}
|
||||||
|
# Drop below the running peak, in 1/1000ths of full charge, before we
|
||||||
|
# call it a fault. 15 = 1.5% of full charge, comfortably above gauge jitter.
|
||||||
|
DROP_PERMILLE=${DROP_PERMILLE:-15}
|
||||||
|
# Emergency hibernate floor, in percent. 0 disables it (default: notify only).
|
||||||
|
HIBERNATE_AT=${HIBERNATE_AT:-0}
|
||||||
|
|
||||||
|
# Statuses that mean "UPower will not treat this as discharging", and so
|
||||||
|
# will not act on PercentageAction no matter how low the battery gets.
|
||||||
|
is_lying_status() {
|
||||||
|
case "$1" in
|
||||||
|
Charging | Full | "Not charging" | Unknown) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# sysfs reads can block for minutes when the UCSI/PD controller wedges,
|
||||||
|
# which is the very condition we are watching for. Never read unguarded.
|
||||||
|
read_attr() {
|
||||||
|
local v
|
||||||
|
v=$(timeout 5 cat "$1" 2>/dev/null) || return 1
|
||||||
|
[[ -n $v ]] || return 1
|
||||||
|
printf '%s' "$v"
|
||||||
|
}
|
||||||
|
|
||||||
|
read_int() {
|
||||||
|
local v
|
||||||
|
v=$(read_attr "$1") || return 1
|
||||||
|
[[ $v =~ ^-?[0-9]+$ ]] || return 1
|
||||||
|
printf '%s' "$v"
|
||||||
|
}
|
||||||
|
|
||||||
|
log() { printf '%s %s\n' "$(date '+%F %T')" "$*"; }
|
||||||
|
|
||||||
|
notify_id=0
|
||||||
|
notify() {
|
||||||
|
local urgency=$1 timeout_ms=$2 title=$3 body=$4 fresh=${5:-0}
|
||||||
|
local args=(-a "Battery Watchdog" -u "$urgency" -t "$timeout_ms"
|
||||||
|
-h string:category:device -i battery-caution)
|
||||||
|
# Reuse one notification so the alert updates in place instead of
|
||||||
|
# stacking, except when we deliberately want a new one to pop.
|
||||||
|
if [[ $fresh -eq 0 && $notify_id -gt 0 ]]; then
|
||||||
|
notify-send "${args[@]}" -r "$notify_id" "$title" "$body" >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
local id
|
||||||
|
id=$(notify-send "${args[@]}" -p "$title" "$body" 2>/dev/null) && notify_id=$id
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt_duration() {
|
||||||
|
local s=$1
|
||||||
|
((s < 0)) && s=0
|
||||||
|
if ((s < 3600)); then
|
||||||
|
printf '%dm' $(((s + 30) / 60))
|
||||||
|
else
|
||||||
|
printf '%dh%02dm' $((s / 3600)) $(((s % 3600) / 60))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
charge_full=$(read_int "$BAT/charge_full") || {
|
||||||
|
log "FATAL: cannot read $BAT/charge_full"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
((charge_full > 0)) || {
|
||||||
|
log "FATAL: charge_full is $charge_full"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
drop_threshold=$((charge_full * DROP_PERMILLE / 1000))
|
||||||
|
# Clearing the alert needs less movement than raising it: real charging
|
||||||
|
# adds roughly 1%/min here, so this confirms recovery within a minute.
|
||||||
|
recover_threshold=$((drop_threshold / 3))
|
||||||
|
((recover_threshold > 0)) || recover_threshold=1
|
||||||
|
|
||||||
|
log "started: charge_full=${charge_full}uAh threshold=${drop_threshold}uAh" \
|
||||||
|
"poll=${POLL_INTERVAL}s repeat=${REPEAT_INTERVAL}s hibernate_at=${HIBERNATE_AT}%"
|
||||||
|
|
||||||
|
peak=0 # highest charge_now seen in the current non-discharging run
|
||||||
|
trough=0 # low-water mark within the current fault, for recovery
|
||||||
|
alerting=0 # are we currently in the fault state
|
||||||
|
alert_start_t=0 # when the fault was first detected
|
||||||
|
alert_start_c=0 # charge_now at that moment
|
||||||
|
last_notify=0
|
||||||
|
last_escalation=100
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
now=$(date +%s)
|
||||||
|
status=$(read_attr "$BAT/status") || status="Unreadable"
|
||||||
|
charge=$(read_int "$BAT/charge_now") || charge=""
|
||||||
|
capacity=$(read_int "$BAT/capacity") || capacity=""
|
||||||
|
ac=$(read_int "$AC/online") || ac="?"
|
||||||
|
|
||||||
|
if [[ -z $charge ]]; then
|
||||||
|
# A hung or unreadable gauge is itself a symptom worth surfacing.
|
||||||
|
log "WARN: charge_now unreadable (status=$status)"
|
||||||
|
sleep "$POLL_INTERVAL"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_lying_status "$status"; then
|
||||||
|
if ((alerting)); then
|
||||||
|
# Recovery is measured against the low-water mark of this fault,
|
||||||
|
# not the pre-fault peak: after draining 90%->50%, real charging
|
||||||
|
# must clear the alert promptly, not on the way back up to 90%.
|
||||||
|
((charge < trough)) && trough=$charge
|
||||||
|
if ((charge - trough >= recover_threshold)); then
|
||||||
|
log "RESOLVED: charging resumed (status=$status capacity=${capacity}%)"
|
||||||
|
notify normal 10000 "Battery charging restored" \
|
||||||
|
"Charge is rising again at ${capacity}%. Status: ${status}." 1
|
||||||
|
alerting=0
|
||||||
|
peak=$charge
|
||||||
|
last_escalation=100
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
((charge > peak)) && peak=$charge
|
||||||
|
deficit=$((peak - charge))
|
||||||
|
if ((deficit >= drop_threshold)); then
|
||||||
|
alerting=1
|
||||||
|
trough=$charge
|
||||||
|
alert_start_t=$now
|
||||||
|
alert_start_c=$charge
|
||||||
|
last_notify=0
|
||||||
|
log "FAULT: status=$status but charge fell ${deficit}uAh from peak" \
|
||||||
|
"(capacity=${capacity}% ac_online=${ac})"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((alerting)); then
|
||||||
|
deficit=$((peak - charge))
|
||||||
|
if ((now - last_notify >= REPEAT_INTERVAL)); then
|
||||||
|
elapsed=$((now - alert_start_t))
|
||||||
|
drained=$((alert_start_c - charge))
|
||||||
|
eta=""
|
||||||
|
# Need a wide enough window for the rate to mean anything;
|
||||||
|
# over a few seconds the gauge quantisation dominates.
|
||||||
|
if ((elapsed >= 60 && drained > 0)); then
|
||||||
|
# seconds until charge_now hits zero at the observed rate
|
||||||
|
eta=$(fmt_duration $((charge * elapsed / drained)))
|
||||||
|
eta=" Est. ${eta} to empty."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pop a brand-new notification when crossing a danger step,
|
||||||
|
# so it is not silently folded into the existing one.
|
||||||
|
fresh=0
|
||||||
|
for step in 20 10 5; do
|
||||||
|
if [[ -n $capacity ]] && ((capacity <= step && last_escalation > step)); then
|
||||||
|
last_escalation=$step
|
||||||
|
fresh=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
notify critical 0 "⚠ Battery draining while reporting \"${status}\"" \
|
||||||
|
"Charge controller fault: ${capacity}% and falling, but status reads \"${status}\" and AC online=${ac}.${eta}
|
||||||
|
UPower will NOT hibernate in this state. Reseat the USB-C/dock cable or save your work now." \
|
||||||
|
"$fresh"
|
||||||
|
|
||||||
|
log "ALERT: capacity=${capacity}% status=$status deficit=${deficit}uAh${eta}"
|
||||||
|
last_notify=$now
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((HIBERNATE_AT > 0)) && [[ -n $capacity ]] && ((capacity <= HIBERNATE_AT)); then
|
||||||
|
log "EMERGENCY: capacity=${capacity}% <= ${HIBERNATE_AT}%, hibernating"
|
||||||
|
notify critical 0 "Hibernating now" \
|
||||||
|
"Battery at ${capacity}% with a charge-controller fault." 1
|
||||||
|
systemctl hibernate
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Discharging or similar: honest reporting, UPower handles it.
|
||||||
|
if ((alerting)); then
|
||||||
|
log "RESOLVED: status turned honest ($status) - UPower safety net active again"
|
||||||
|
notify normal 10000 "Battery status corrected" \
|
||||||
|
"Now reporting \"${status}\" at ${capacity}%. Normal low-battery handling applies." 1
|
||||||
|
alerting=0
|
||||||
|
last_escalation=100
|
||||||
|
fi
|
||||||
|
peak=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "$POLL_INTERVAL"
|
||||||
|
done
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Battery charge-controller fault watchdog
|
||||||
|
Documentation=man:notify-send(1)
|
||||||
|
# Needs the session bus for notifications.
|
||||||
|
After=graphical-session.target
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
# Makes the unit inert on machines without a battery, so it can be
|
||||||
|
# enabled unconditionally on the desktop as well as the laptop.
|
||||||
|
ConditionPathExists=/sys/class/power_supply/BAT0/charge_now
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=%h/.config/scripts/battery-charge-watchdog.sh
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
# Set HIBERNATE_AT=5 to add an emergency hibernate floor (0 = notify only).
|
||||||
|
Environment=POLL_INTERVAL=20
|
||||||
|
Environment=REPEAT_INTERVAL=60
|
||||||
|
Environment=DROP_PERMILLE=15
|
||||||
|
Environment=HIBERNATE_AT=0
|
||||||
|
|
||||||
|
# Read-only monitoring; no need for any write access to the system.
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=read-only
|
||||||
|
PrivateTmp=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
MemoryMax=64M
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical-session.target
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"credStore": "secretservice",
|
||||||
|
"auths": {},
|
||||||
|
"currentContext": "default",
|
||||||
|
"plugins": {
|
||||||
|
"-x-cli-hints": {
|
||||||
|
"enabled": "true"
|
||||||
|
},
|
||||||
|
"ai": {
|
||||||
|
"hooks": "build,buildx build,run,compose"
|
||||||
|
},
|
||||||
|
"compose": {
|
||||||
|
"hooks": "logs,compose logs,compose attach,compose up"
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"hooks": "exec"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"hooks": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user