Skip to content

Instantly share code, notes, and snippets.

@Nymphium
Last active February 5, 2026 02:07
Show Gist options
  • Select an option

  • Save Nymphium/8a3b15bd7488ddc4dda10e6d8709895d to your computer and use it in GitHub Desktop.

Select an option

Save Nymphium/8a3b15bd7488ddc4dda10e6d8709895d to your computer and use it in GitHub Desktop.
BetterDisplay auto brightness automation for macOS dark mode

BetterDisplay Auto Brightness for macOS Dark Mode

This script automatically adjusts the brightness of your displays via BetterDisplay when macOS switches between Light and Dark mode (e.g., at sunset).

Files

  • AutoBrightness.swift: The main Swift script that listens for theme changes.
  • com.nymphium.autobrightness.plist: LaunchAgent configuration to run the script in the background.

Setup Instructions

  1. Compile the script:

    mkdir -p ~/.local/bin
    swiftc AutoBrightness.swift -o ~/.local/bin/autobrightness
  2. Install the LaunchAgent:

    mkdir -p ~/Library/LaunchAgents
    cp com.nymphium.autobrightness.plist ~/Library/LaunchAgents/
    launchctl load ~/Library/LaunchAgents/com.nymphium.autobrightness.plist
  3. Verify: Check if it's running:

    launchctl list | grep autobrightness

    Logs can be found at /tmp/com.nymphium.autobrightness.out and .err.

Requirements

  • BetterDisplay v2.2.x or newer (Pro features may be required for some CLI controls).
  • BetterDisplay must be running and configured to accept CLI requests.

AGENT.md - BetterDisplay Auto Brightness

Project Overview

This project provides a Swift-based automation tool for macOS to control external display brightness via BetterDisplay based on the system's appearance (Light/Dark mode) and power state (Wake/Sleep).

Key Components

1. AutoBrightness.swift

  • Language: Swift (compiled).
  • Function:
    • Monitors AppleInterfaceThemeChangedNotification (DistributedNotificationCenter).
    • Monitors NSWorkspace.didWakeNotification and screensDidWakeNotification.
    • Executes BetterDisplay CLI commands to set brightness.
  • Configuration:
    • brightnessOnDark: Target brightness for Dark Mode (default: "100%").
    • brightnessOnLight: Target brightness for Light Mode (default: "85%").
    • bdExecutable: Path to BetterDisplay binary.

2. com.nymphium.autobrightness.plist

  • Type: launchd agent configuration.
  • Location: ~/Library/LaunchAgents/.
  • Logs:
    • StdOut: /tmp/com.nymphium.autobrightness.out
    • StdErr: /tmp/com.nymphium.autobrightness.err

Development & Maintenance

Compilation

The script must be compiled after any changes.

swiftc AutoBrightness.swift -o ~/.local/bin/autobrightness

Note: Ensure ~/.local/bin exists and is in the PATH if running manually.

Deployment

To apply changes to the background service:

launchctl unload ~/Library/LaunchAgents/com.nymphium.autobrightness.plist
launchctl load ~/Library/LaunchAgents/com.nymphium.autobrightness.plist

Debugging

Tail the log file to see real-time status:

tail -f /tmp/com.nymphium.autobrightness.out

Context for AI Agents

  • BetterDisplay CLI: Uses set --brightness=<value> syntax.
  • Timing: Delays are used (0.5s after theme change, 3.0s after wake) to ensure the display and system are ready before sending commands.
  • Paths: Hardcoded paths in AutoBrightness.swift and .plist may need adjustment if the user's environment differs (e.g., username).
import Cocoa
import Foundation
// 設定: 明るさのターゲット値 (必要に応じて変更してください)
let brightnessOnDark = "100%"
let brightnessOnLight = "85%"
// BetterDisplayのパス
let bdExecutable = "/Applications/BetterDisplay.app/Contents/MacOS/BetterDisplay"
func setBrightness(_ level: String) {
let task = Process()
task.executableURL = URL(fileURLWithPath: bdExecutable)
task.arguments = ["set", "--brightness=\(level)"]
do {
try task.run()
print("Set brightness to \(level)")
} catch {
print("Failed to run BetterDisplay CLI: \(error)")
}
}
func updateBrightness() {
let style = UserDefaults.standard.persistentDomain(forName: UserDefaults.globalDomain)?["AppleInterfaceStyle"] as? String
let isDark = style == "Dark"
print("Detected Appearance: \(isDark ? "Dark" : "Light")")
setBrightness(isDark ? brightnessOnDark : brightnessOnLight)
}
// 監視の設定
class Observer {
@objc func themeChanged(_ notification: Notification) {
// 変更直後は値が反映されていない場合があるため、わずかに遅延させる
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
updateBrightness()
}
}
@objc func didWake(_ notification: Notification) {
print("System woke up. Updating brightness...")
// スリープ復帰後はディスプレイ認識やBetterDisplayの準備待ちのため、少し長めに待つ
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
updateBrightness()
}
}
}
let observer = Observer()
// テーマ変更の監視 (DistributedNotificationCenter)
DistributedNotificationCenter.default().addObserver(
observer,
selector: #selector(Observer.themeChanged(_:)),
name: NSNotification.Name("AppleInterfaceThemeChangedNotification"),
object: nil
)
// スリープ解除と画面復帰の監視 (NSWorkspace)
NSWorkspace.shared.notificationCenter.addObserver(
observer,
selector: #selector(Observer.didWake(_:)),
name: NSWorkspace.didWakeNotification,
object: nil
)
NSWorkspace.shared.notificationCenter.addObserver(
observer,
selector: #selector(Observer.didWake(_:)),
name: NSWorkspace.screensDidWakeNotification,
object: nil
)
// 起動時に一度実行
updateBrightness()
// メインループの開始
RunLoop.main.run()
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.nymphium.autobrightness</string>
<key>ProgramArguments</key>
<array>
<string>/Users/nymphium/.local/bin/autobrightness</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/com.nymphium.autobrightness.out</string>
<key>StandardErrorPath</key>
<string>/tmp/com.nymphium.autobrightness.err</string>
</dict>
</plist>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment