LED ON · NOISE CANCELLATION
Simulated — click the key. The real app moves your actual AirPods.
Caps Lock now controls your AirPods.
AirCaps remaps the physical Caps Lock key into a hardware switch for noise mode. LED on is Noise Cancellation. LED off is Adaptive or Transparency, your call — synced across the built-in keyboard and every Magic Keyboard on your desk.
Four steps happen before the LED moves.
-
01
Remap
hidutilturns Caps Lock into a no-op, system-wide, the moment the app launches. Text-input locking stops. So does the OS auto-driving the LED — nothing else is watching it anymore. -
02
Listen
IOHIDManagerwatches for the key's raw down-transition anyway, underneath the remap, on every matched keyboard — built-in and every connected Magic Keyboard. -
03
Automate
Accessibility drives Control Center's Sound popover: press Sound, wait for the dialog, expand the AirPods section, press the target mode's checkbox.
-
04
Illuminate
IOHIDDeviceSetValuewrites the Caps Lock LED element directly, on every connected keyboard at once. The app owns the light now — it has to.
Eight things worth knowing.
Raw HID capture
Reads keyboard input reports directly — independent of, and unaffected by, the hidutil remap sitting above it.
Control Center automation
No private API left to call, so it drives the same Sound popover a person would click through by hand.
Multi-keyboard sync
LED state applies to every matched device at once. A keyboard that connects mid-session gets synced and remapped immediately.
Two-permission gate
Accessibility for the popover, Input Monitoring for raw HID. Both re-checked every time the menu opens.
Explicit LED ownership
Once the remap is live, macOS stops driving the light entirely. Every state change is a direct write.
One shared core
Audio, Bluetooth, battery, and noise control live in a library that powers both the CLI and the menu-bar app.
Debounced state machine
Only idle or transitioning. A press mid-transition is dropped, not queued — the popover flow can't overlap itself.
CoreAudio switching
The same menu flips default and system output device between AirPods and built-in speakers.
The usage IDs this app touches.
Three HID usage IDs, one remap, and the three checkbox labels inside Control Center's Sound popover — that's the entire vocabulary.
HID usage table
| Page | Usage | Meaning | Role |
|---|---|---|---|
| 0x01 | 0x06 | Generic Desktop → Keyboard | Matches every connected keyboard |
| 0x07 | 0x39 | Keyboard/Keypad → Caps Lock | The physical key being watched |
| 0x08 | 0x02 | LEDs → Caps Lock indicator | The light being written |
Noise modes
| Code | Control Center label | LED state |
|---|---|---|
| ANC | Noise Cancellation | On |
| AT | Adaptive | Off — default |
| TR | Transparency | Off — alternate |
The remap, verbatim
hidutil property --set '{"UserKeyMapping":[{
"HIDKeyboardModifierMappingSrc": 0x700000039,
"HIDKeyboardModifierMappingDst": 0x700000000
}]}'
Reverted to {"UserKeyMapping":[]} on quit — or by hand, any time, with the same command.
The CLI underneath.
The menu-bar app is built on this. It works standalone too — aircaps [command], no menu required.
| Command | Does |
|---|---|
| status | Shows connection, audio output, and battery — the default |
| on | Switches audio output to AirPods |
| off | Switches audio output to built-in speakers |
| toggle | Switches between the two |
| list | Lists every audio and Bluetooth device |
| ANC | Sets noise control to Noise Cancellation |
| AT | Sets noise control to Adaptive |
| TR | Sets noise control to Transparency |
From clone to lit LED.
-
01
Build the CLI
make install— builds a release binary and copies it to/usr/local/bin/aircaps. -
02
Bundle the app
make bundle-install— wraps the menu-bar app and installs it to/Applications. -
03
Grant permissions
Accessibility for the popover, Input Monitoring for the key. The app's menu links straight to both panes.
-
04
Pick your off-mode
Settings — Adaptive or Transparency for when the LED is off. Adaptive by default.
What's shipped, what's next.
- Caps Lock → noise-mode toggle, LED-synced
- Multi-keyboard sync, live on connect
- CoreAudio output switching
- Control Center automation for ANC/Adaptive/Transparency
- Battery parsing via system_profiler
- Reading current mode without opening the popover
- Automated test suite
- In-app updates
- Bluetooth reconnect handling for
on
What you'll need.
- macOS 13.0+ (Ventura or later)
- AirPods Pro, or other AirPods with ANC/Adaptive/Transparency
- Accessibility permission, for noise-mode switching
- Input Monitoring permission, for the menu-bar app's Caps Lock capture
CoreAudio · IOBluetooth · ApplicationServices · IOKit HID · SwiftUI · Swift Package Manager