Last active
August 5, 2025 00:30
-
-
Save lynaghk/e549eb86d59dac59e68ce7389a87a971 to your computer and use it in GitHub Desktop.
control macos brightness of monitor(s) via MIDI input
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
#!/usr/bin/env python3 | |
# launch with `uv run`. https://docs.astral.sh/uv/ | |
# uses https://github.com/waydabber/betterdisplay/ to control the monitors | |
# /// script | |
# dependencies = [ | |
# "python-rtmidi==1.5.8", | |
# "requests==2.32.4", | |
# ] | |
# /// | |
import requests | |
import rtmidi | |
import subprocess | |
import sys | |
import time | |
from contextlib import contextmanager | |
@contextmanager | |
def timer(label): | |
start = time.perf_counter() | |
try: | |
yield | |
finally: | |
print(f"{label} took {time.perf_counter() - start:.4f} seconds") | |
# MIDI CC to display mapping (adjust these for your setup) | |
CC_TO_DISPLAY = { | |
# vertical monitor is ID = 3 | |
72: 3, | |
76: 3, | |
# top | |
73: 1, | |
# bottom | |
77: 2, | |
} | |
def set_brightness(display, brightness): | |
# curl -X GET -G http://localhost:55777/set -d "brightness=80%" -d "displayID=1" | |
requests.get("http://localhost:55777/set", | |
params={"displayID": display, | |
"brightness": brightness}) | |
def midi_callback(message, data): | |
msg, deltatime = message | |
# print(msg) | |
if len(msg) == 3 and msg[0] == 0xB0: # Control Change on channel 1 | |
cc_number = msg[1] | |
cc_value = msg[2] | |
if cc_number in CC_TO_DISPLAY: | |
display = CC_TO_DISPLAY[cc_number] | |
brightness = cc_value / 127 | |
set_brightness(display, brightness) | |
# print(f"CC {cc_number} → Display {display}: {brightness}") | |
def main(): | |
midi_in = rtmidi.MidiIn() | |
# Find LPD8 port | |
ports = midi_in.get_ports() | |
lpd8_port = None | |
for i, port in enumerate(ports): | |
if "LPD8" in port or "Akai" in port: | |
lpd8_port = i | |
break | |
if lpd8_port is None: | |
print("LPD8 not found. Available ports:") | |
for i, port in enumerate(ports): | |
print(f" {i}: {port}") | |
sys.exit(1) | |
print(f"Connecting to: {ports[lpd8_port]}") | |
midi_in.open_port(lpd8_port) | |
midi_in.set_callback(midi_callback) | |
print("MIDI brightness control running... Press Ctrl+C to stop") | |
try: | |
while True: | |
time.sleep(1) | |
except KeyboardInterrupt: | |
print("\nShutting down...") | |
finally: | |
midi_in.close_port() | |
if __name__ == "__main__": | |
main() | |
# Tried to have this auto start but couldn't get it working in 20 minutes, so I just launch it myself in a terminal window like an animal ¯\_(ツ)_/¯ | |
# PLISTPATH=~/Library/LaunchAgents/com.keminglabs.midi-brightness.plist | |
# cat << 'EOF' > $PLISTPATH | |
# <?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.keminglabs.midi-brightness</string> | |
# <key>ProgramArguments</key> | |
# <array> | |
# <string>/Users/dev/.dotfiles/uv_shim</string> | |
# <string>run</string> | |
# <string>/Users/dev/.dotfiles/bin/midi-brightness.py</string> | |
# </array> | |
# <key>RunAtLoad</key> | |
# <true/> | |
# <key>KeepAlive</key> | |
# <true/> | |
# <key>StandardOutPath</key> | |
# <string>/Users/dev/Library/Logs/midi-brightness.log</string> | |
# <key>StandardErrorPath</key> | |
# <string>/Users/dev/Library/Logs/midi-brightness.log</string> | |
# </dict> | |
# </plist> | |
# EOF | |
# launchctl load -w $PLISTPATH |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment