notif: send proper wifi on/off and bt on/off notif without a need for hooks, make Hooks documentation a bit more generic.

This commit is contained in:
Lemmy
2026-04-30 14:53:37 -04:00
parent 829e73df63
commit fad660f2ce
7 changed files with 75 additions and 35 deletions
+6
View File
@@ -487,6 +487,12 @@
},
"internal": {
"dbus-disabled": "DBus notifications disabled",
"network": "Network",
"wifi-enabled": "Wi-Fi is on",
"wifi-disabled": "Wi-Fi is off",
"bluetooth": "Bluetooth",
"bluetooth-enabled": "Bluetooth is on",
"bluetooth-disabled": "Bluetooth is off",
"session-bus-unavailable": "Session bus unavailable",
"mpris-disabled": "MPRIS disabled"
}
+2 -2
View File
@@ -143,7 +143,7 @@ Idle behaviors are named entries under `[idle.behavior.*]`. When no `config.toml
```toml
[idle.behavior.lock]
timeout = 660
command = "noctalia:lock"
command = "noctalia:screen-lock"
enabled = false # explicitly disabled in the default config
[idle.behavior.screen-off]
@@ -166,7 +166,7 @@ command = "notify-send 'Idle' 'Going idle'"
The `noctalia:` prefix runs the rest of the string through the IPC command registry — the same as `noctalia msg <subcommand>`. Examples:
```
noctalia:lock
noctalia:screen-lock
noctalia:dpms-off
noctalia:dpms-on
noctalia:enable-idle-inhibitor
+39 -15
View File
@@ -99,7 +99,13 @@ Each action accepts a single string chord or an array of chords.
## Hooks
Optional shell hooks run commands when specific events happen. Define them under `[hooks]` in `config.toml`. Each event is a string (one command) or an array of strings (run in order). The same `noctalia:` prefix rules apply as in [idle behaviors](services.md#idle).
Optional shell hooks run extra commands when specific events happen. Define them under `[hooks]` in `config.toml`.
Each event is a string (one command) or an array of strings (run in order). The same `noctalia:` prefix rules apply as
in [idle behaviors](services.md#idle).
Hooks do not replace Noctalia's built-in behavior. For example, Wi-Fi and Bluetooth power changes still create internal
Noctalia notifications even when `wifi_*` / `bluetooth_*` hooks are not configured. `notify-send` can be useful while
testing that a hook fires, but it is not required for Noctalia's own notifications.
| Key | When it fires |
|-----|---------------|
@@ -122,24 +128,42 @@ Optional shell hooks run commands when specific events happen. Define them under
```toml
[hooks]
started = "notify-send 'Noctalia' 'Shell started'"
wallpaper_changed = ["touch /tmp/noctalia-wallpaper"]
colors_changed = "notify-send 'Theme' 'Palette updated'"
# Start a user unit after Noctalia IPC is ready.
started = "systemctl --user start noctalia-ready.target"
session_locked = "notify-send 'Session' 'Locked'"
session_unlocked = "notify-send 'Session' 'Unlocked'"
# Reload tools that consume generated wallpaper/theme files.
wallpaper_changed = "systemctl --user restart wallpaper-sync.service"
colors_changed = [
"systemctl --user reload foot-server.service",
"logger -t noctalia-hooks 'theme colors changed'",
]
logging_out = "echo logging out >> /tmp/noctalia-hooks.log"
rebooting = "notify-send 'System' 'Rebooting'"
shutting_down = "notify-send 'System' 'Shutting down'"
# Combine normal shell commands with Noctalia IPC commands.
session_locked = [
"playerctl pause",
"noctalia:bar-hide",
]
session_unlocked = [
"noctalia:bar-show",
"noctalia:dpms-on",
]
wifi_enabled = "notify-send 'Network' 'Wi-Fi on'"
wifi_disabled = "notify-send 'Network' 'Wi-Fi off'"
logging_out = "logger -t noctalia-hooks 'logout requested'"
rebooting = "systemctl --user stop backup-sync.service"
shutting_down = "systemctl --user stop backup-sync.service"
bluetooth_enabled = "notify-send 'BT' 'Bluetooth on'"
bluetooth_disabled = "notify-send 'BT' 'Bluetooth off'"
# Wi-Fi/Bluetooth internal notifications are emitted without these hooks;
# use hooks only for extra side effects.
wifi_enabled = "logger -t noctalia-hooks 'Wi-Fi radio enabled'"
wifi_disabled = "logger -t noctalia-hooks 'Wi-Fi radio disabled'"
bluetooth_enabled = "logger -t noctalia-hooks 'Bluetooth powered on'"
bluetooth_disabled = "logger -t noctalia-hooks 'Bluetooth powered off'"
battery_low_percent_threshold = 15
battery_state_changed = "notify-send 'Power' \"Battery: $NOCTALIA_BATTERY_STATE\""
battery_under_threshold = "notify-send 'Power' \"Battery at ${NOCTALIA_BATTERY_PERCENT}%\""
battery_state_changed = "logger -t noctalia-hooks \"Battery: $NOCTALIA_BATTERY_STATE\""
battery_under_threshold = "systemctl --user start battery-low.target"
# Quick test while developing a hook; not needed for Noctalia's own notifications.
# started = "notify-send 'Noctalia hook' 'started fired'"
```
+1 -1
View File
@@ -142,7 +142,7 @@ temperature_night = 4000 # Kelvin
[idle.behavior.lock]
timeout = 660
command = "noctalia:lock"
command = "noctalia:screen-lock"
enabled = false
# [idle.behavior.screen-off]
+22 -12
View File
@@ -474,8 +474,9 @@ void Application::initServices() {
try {
m_networkService = std::make_unique<NetworkService>(*m_systemBus);
m_prevWirelessEnabledForEvents = m_networkService->state().wirelessEnabled;
m_networkService->setChangeCallback([this, shouldRefreshControlCenter](const NetworkState& state) {
onNetworkStateChangedForHooks(state);
onNetworkStateChangedForEvents(state);
m_bar.refresh();
if (shouldRefreshControlCenter()) {
m_panelManager.refresh();
@@ -498,6 +499,7 @@ void Application::initServices() {
try {
m_bluetoothService = std::make_unique<BluetoothService>(*m_systemBus);
m_prevBluetoothPoweredForEvents = m_bluetoothService->state().powered;
auto refreshBluetoothUi = [this, shouldRefreshControlCenter]() {
m_bar.refresh();
if (shouldRefreshControlCenter()) {
@@ -505,7 +507,7 @@ void Application::initServices() {
}
};
m_bluetoothService->setStateCallback([this, refreshBluetoothUi](const BluetoothState& state) {
onBluetoothStateChangedForHooks(state);
onBluetoothStateChangedForEvents(state);
refreshBluetoothUi();
});
m_bluetoothService->setDevicesCallback(
@@ -1112,36 +1114,44 @@ void Application::onUpowerStateChangedForHooks() {
m_prevUpowerForHooks = next;
}
void Application::onNetworkStateChangedForHooks(const NetworkState& state) {
if (!m_prevWirelessEnabledForHooks.has_value()) {
m_prevWirelessEnabledForHooks = state.wirelessEnabled;
void Application::onNetworkStateChangedForEvents(const NetworkState& state) {
if (!m_prevWirelessEnabledForEvents.has_value()) {
m_prevWirelessEnabledForEvents = state.wirelessEnabled;
return;
}
const bool prev = *m_prevWirelessEnabledForHooks;
const bool prev = *m_prevWirelessEnabledForEvents;
if (prev != state.wirelessEnabled) {
if (state.wirelessEnabled) {
m_notificationManager.addInternal(i18n::tr("notifications.internal.network"),
i18n::tr("notifications.internal.wifi-enabled"), "");
m_hookManager.fire(HookKind::WifiEnabled);
} else {
m_notificationManager.addInternal(i18n::tr("notifications.internal.network"),
i18n::tr("notifications.internal.wifi-disabled"), "");
m_hookManager.fire(HookKind::WifiDisabled);
}
}
m_prevWirelessEnabledForHooks = state.wirelessEnabled;
m_prevWirelessEnabledForEvents = state.wirelessEnabled;
}
void Application::onBluetoothStateChangedForHooks(const BluetoothState& state) {
if (!m_prevBluetoothPoweredForHooks.has_value()) {
m_prevBluetoothPoweredForHooks = state.powered;
void Application::onBluetoothStateChangedForEvents(const BluetoothState& state) {
if (!m_prevBluetoothPoweredForEvents.has_value()) {
m_prevBluetoothPoweredForEvents = state.powered;
return;
}
const bool prev = *m_prevBluetoothPoweredForHooks;
const bool prev = *m_prevBluetoothPoweredForEvents;
if (prev != state.powered) {
if (state.powered) {
m_notificationManager.addInternal(i18n::tr("notifications.internal.bluetooth"),
i18n::tr("notifications.internal.bluetooth-enabled"), "");
m_hookManager.fire(HookKind::BluetoothEnabled);
} else {
m_notificationManager.addInternal(i18n::tr("notifications.internal.bluetooth"),
i18n::tr("notifications.internal.bluetooth-disabled"), "");
m_hookManager.fire(HookKind::BluetoothDisabled);
}
}
m_prevBluetoothPoweredForHooks = state.powered;
m_prevBluetoothPoweredForEvents = state.powered;
}
std::vector<PollSource*> Application::currentPollSources() {
+4 -4
View File
@@ -106,8 +106,8 @@ private:
bool runIdleCommand(const std::string& command);
void onIconThemeChanged();
void onUpowerStateChangedForHooks();
void onNetworkStateChangedForHooks(const NetworkState& state);
void onBluetoothStateChangedForHooks(const BluetoothState& state);
void onNetworkStateChangedForEvents(const NetworkState& state);
void onBluetoothStateChangedForEvents(const BluetoothState& state);
[[nodiscard]] std::vector<PollSource*> currentPollSources();
[[nodiscard]] std::vector<PollSource*> buildPollSources();
@@ -137,8 +137,8 @@ private:
std::unique_ptr<PolkitAgent> m_polkitAgent;
std::unique_ptr<UPowerService> m_upowerService;
std::optional<UPowerState> m_prevUpowerForHooks;
std::optional<bool> m_prevWirelessEnabledForHooks;
std::optional<bool> m_prevBluetoothPoweredForHooks;
std::optional<bool> m_prevWirelessEnabledForEvents;
std::optional<bool> m_prevBluetoothPoweredForEvents;
SessionActionHooks m_sessionActionHooks;
std::unique_ptr<BrightnessService> m_brightnessService;
std::unique_ptr<TrayService> m_trayService;
+1 -1
View File
@@ -642,7 +642,7 @@ void ConfigService::loadAll() {
.name = "lock",
.enabled = false,
.timeoutSeconds = 660,
.command = "noctalia:lock",
.command = "noctalia:screen-lock",
});
m_config.bars.push_back(BarConfig{});
return;