Skip to content

Instantly share code, notes, and snippets.

@yagop
Last active July 11, 2026 19:30
Show Gist options
  • Select an option

  • Save yagop/33355898452881d8ce3f89ce89e7236a to your computer and use it in GitHub Desktop.

Select an option

Save yagop/33355898452881d8ce3f89ce89e7236a to your computer and use it in GitHub Desktop.
Ambient Dark Mode
#!/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