Last active
July 11, 2026 19:30
-
-
Save yagop/33355898452881d8ce3f89ce89e7236a to your computer and use it in GitHub Desktop.
Ambient Dark Mode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # ambient_adaptive.sh | |
| # | |
| # Reads the ambient light sensor (CurrentLux) and: | |
| # 1. Switches macOS Light/Dark mode based on a lux threshold (with hysteresis) | |
| # 2. Sets display brightness using a custom curve with a guaranteed minimum | |
| # | |
| # Requirements: | |
| # - Self-compiling brightness helper at ~/.local/bin/brightness | |
| # - Disable "Automatically adjust brightness" in System Settings > Displays | |
| # (otherwise macOS will fight this script) | |
| # ---- Configuration ---------------------------------------------------- | |
| # Dark mode thresholds (hysteresis: dark below DARK_BELOW, light above LIGHT_ABOVE, | |
| # no change in between — prevents flip-flopping around a single threshold) | |
| DARK_BELOW=300 | |
| LIGHT_ABOVE=350 | |
| # Brightness curve | |
| MIN_BRIGHTNESS=0.78 # your floor — brightness never goes below this | |
| MAX_BRIGHTNESS=1.00 # brightness at LUX_MAX and above | |
| LUX_MIN=0 # lux value at (or below) which brightness = MIN_BRIGHTNESS | |
| LUX_MAX=500 # lux value at (or above) which brightness = MAX_BRIGHTNESS | |
| # Path to the self-compiled brightness helper | |
| BRIGHTNESS_BIN="$HOME/.local/bin/brightness" | |
| LOG_FILE="$HOME/Library/Logs/ambient_adaptive.log" | |
| # ---- Skip when lid is closed -------------------------------------------- | |
| # The ambient light sensor sits in the lid; when closed it reads ~0 lux, | |
| # which would wrongly force dark mode / minimum brightness in clamshell mode. | |
| CLAMSHELL=$(ioreg -r -k AppleClamshellState -d 1 2>/dev/null | grep AppleClamshellState | grep -c "Yes") | |
| if [ "$CLAMSHELL" = "1" ]; then | |
| echo "$(date): Lid closed (clamshell) -> skipping." >> "$LOG_FILE" 2>/dev/null | |
| exit 0 | |
| fi | |
| # ---- Read the sensor --------------------------------------------------- | |
| LUX=$(ioreg -ark CurrentLux | plutil -extract 0.CurrentLux raw - 2>/dev/null) | |
| if ! [[ "$LUX" =~ ^[0-9]+$ ]]; then | |
| echo "$(date): Could not read CurrentLux (got: '$LUX')." >> "$LOG_FILE" 2>/dev/null | |
| exit 1 | |
| fi | |
| # ---- Dark / Light mode (with hysteresis) -------------------------------- | |
| CURRENT_MODE=$(osascript -e 'tell application "System Events" to tell appearance preferences to get dark mode') | |
| TARGET_MODE="" | |
| if [ "$LUX" -lt "$DARK_BELOW" ]; then | |
| TARGET_MODE="true" | |
| elif [ "$LUX" -gt "$LIGHT_ABOVE" ]; then | |
| TARGET_MODE="false" | |
| fi | |
| # If lux is between DARK_BELOW and LIGHT_ABOVE, TARGET_MODE stays empty -> no change | |
| if [ -n "$TARGET_MODE" ] && [ "$CURRENT_MODE" != "$TARGET_MODE" ]; then | |
| osascript -e "tell application \"System Events\" to tell appearance preferences to set dark mode to $TARGET_MODE" | |
| echo "$(date): Lux=$LUX -> dark mode set to $TARGET_MODE" >> "$LOG_FILE" 2>/dev/null | |
| fi | |
| # ---- Brightness curve ---------------------------------------------------- | |
| if [ ! -x "$BRIGHTNESS_BIN" ]; then | |
| echo "$(date): set_brightness binary not found at $BRIGHTNESS_BIN (compile set_brightness.m first)" >> "$LOG_FILE" 2>/dev/null | |
| exit 1 | |
| fi | |
| # Linear interpolation between (LUX_MIN, MIN_BRIGHTNESS) and (LUX_MAX, MAX_BRIGHTNESS), | |
| # clamped to [MIN_BRIGHTNESS, MAX_BRIGHTNESS]. | |
| TARGET_BRIGHTNESS=$(awk -v lux="$LUX" \ | |
| -v lmin="$LUX_MIN" -v lmax="$LUX_MAX" \ | |
| -v bmin="$MIN_BRIGHTNESS" -v bmax="$MAX_BRIGHTNESS" ' | |
| BEGIN { | |
| if (lux <= lmin) b = bmin; | |
| else if (lux >= lmax) b = bmax; | |
| else b = bmin + (bmax - bmin) * (lux - lmin) / (lmax - lmin); | |
| printf "%.2f", b; | |
| }') | |
| # Read current brightness; set_brightness prints a bare number like "0.50" | |
| CURRENT_BRIGHTNESS=$("$BRIGHTNESS_BIN" 2>/dev/null) | |
| if ! [[ "$CURRENT_BRIGHTNESS" =~ ^[0-9.]+$ ]]; then | |
| echo "$(date): Could not read current brightness (got: '$CURRENT_BRIGHTNESS')." >> "$LOG_FILE" 2>/dev/null | |
| exit 1 | |
| fi | |
| # Only adjust if the difference is meaningful (avoids constant tiny writes) | |
| NEEDS_CHANGE=$(awk -v c="$CURRENT_BRIGHTNESS" -v t="$TARGET_BRIGHTNESS" ' | |
| BEGIN { d = c - t; if (d < 0) d = -d; print (d > 0.03) ? 1 : 0 }') | |
| if [ "$NEEDS_CHANGE" = "1" ]; then | |
| "$BRIGHTNESS_BIN" "$TARGET_BRIGHTNESS" >/dev/null 2>&1 | |
| echo "$(date): Lux=$LUX -> brightness $CURRENT_BRIGHTNESS -> $TARGET_BRIGHTNESS" >> "$LOG_FILE" 2>/dev/null | |
| else | |
| echo "$(date): Lux=$LUX -> brightness OK ($CURRENT_BRIGHTNESS, target $TARGET_BRIGHTNESS)" >> "$LOG_FILE" 2>/dev/null | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment