Sunho Sleep Manager is a macOS menu bar app that sends Slack notifications when your laptop lid closes or opens, and lets you block/allow sleep with one click.
- Lid close / open → Slack notification (via Incoming Webhook)
- Menu bar toggle — block or allow sleep instantly
SL(normal) — sleep allowedSL+ underline — sleep blocked (pmset disablesleep 1)
- Remote HTTP API — control sleep state over Tailscale from any device
- i18n — UI and default Slack messages automatically match your macOS language Supported: English · 한국어 · 日本語 · 简体中文 · 繁體中文 · Français · Deutsch · Español
- Recent history — last 10 sleep/wake events shown in the menu
- Runs at login via LaunchAgent
- macOS 12 Monterey or later
- Python 3 (pre-installed on macOS)
- PyObjC — Cocoa bridge for Python (installed automatically by
setup.sh)
git clone https://github.com/<your-username>/sleep-manager.git
cd sleep-managerGo to https://api.slack.com/apps → create an app → enable Incoming Webhooks → copy the URL.
{
"slack_webhook_url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
"include_computer_name": true,
"computer_name": "",
"language": "",
"messages": {
"sleep": "",
"wake": ""
},
"api_port": 8765,
"api_token": ""
}| Key | Default | Notes |
|---|---|---|
slack_webhook_url |
(required) | Slack Incoming Webhook URL |
include_computer_name |
true |
Prepend hostname to messages |
computer_name |
"" |
Empty = auto-detect via hostname |
language |
"" |
Empty = auto-detect from macOS. Override with "en", "ko", "ja", "zh-Hans", "zh-Hant", "fr", "de", or "es" |
messages.sleep / .wake |
"" |
Empty = use locale default. Supports {computer_name} placeholder |
api_port |
8765 |
HTTP API listen port |
api_token |
"" |
Empty = no auth. Set to require X-Token: <token> header |
bash setup.shThe script will:
- Install PyObjC if not already present
- Register
net.sunho.sleep-manager.plistunder~/Library/LaunchAgents/ - Grant passwordless
sudo pmsetaccess via/etc/sudoers.d/sleep-manager(requires your admin password one time only)
python3 sleep_notifier.py# Check the service is running
launchctl list | grep sleep-manager
# Follow live logs
tail -f ~/Library/Logs/sleep-manager.logPLIST=~/Library/LaunchAgents/net.sunho.sleep-manager.plist
# Stop
launchctl unload "$PLIST"
# Restart
launchctl unload "$PLIST" && launchctl load "$PLIST"The API listens on 0.0.0.0:<api_port> — intended for use over a Tailscale internal network.
| Endpoint | Description |
|---|---|
GET /status |
Returns {"disablesleep": 0|1} |
GET /sleep?value=1 |
Block sleep (pmset disablesleep 1) |
GET /sleep?value=0 |
Allow sleep (pmset disablesleep 0) |
If api_token is set, all requests must include the header X-Token: <token>.
# Example: block sleep remotely
curl -H "X-Token: mytoken" http://<tailscale-ip>:8765/sleep?value=1The app detects your macOS language automatically. To pin a specific language, set "language" in config.json:
{ "language": "ja" }Locale files live in locales/*.json. Each file contains UI strings and default Slack message templates. To customize Slack messages, set non-empty values in messages.sleep / messages.wake in config.json — these override the locale defaults.
sleep-manager/
├── sleep_notifier.py # Main script (Python 3 + PyObjC)
├── config.json # Configuration
├── locales/
│ ├── en.json # English
│ ├── ko.json # Korean (한국어)
│ ├── ja.json # Japanese (日本語)
│ ├── zh-Hans.json # Simplified Chinese (简体中文)
│ ├── zh-Hant.json # Traditional Chinese (繁體中文)
│ ├── fr.json # French (Français)
│ ├── de.json # German (Deutsch)
│ └── es.json # Spanish (Español)
├── net.sunho.sleep-manager.plist # LaunchAgent definition
└── setup.sh # Install script
launchctl unload ~/Library/LaunchAgents/net.sunho.sleep-manager.plist
rm ~/Library/LaunchAgents/net.sunho.sleep-manager.plist
sudo rm /etc/sudoers.d/sleep-manager- With
pmset disablesleep 1active, closing the lid keeps the CPU running — watch your battery. - Before quitting the app, use the menu bar to re-enable sleep if you had it blocked.
MIT