A macOS menu bar app to control a Luxafor Bluetooth PRO status light. Automatically changes the light color based on your meeting status and calendar events, with manual override support. Tested on MacOS Tahoe Version 26.3.1 (a).
- USB Dongle: communicates via IOKit HID with the Luxafor USB dongle
- Bluetooth: direct BLE connection to Luxafor Bluetooth PRO (no dongle needed)
Detects active calls and meetings by monitoring window titles via the macOS Accessibility API. Currently supported apps:
- Zoom: detects active meetings
- Slack: detects Huddle sessions
- Microsoft Teams: detects active calls
Monitors your macOS calendars (via EventKit) and changes the light color when you have an event. You can select which calendars to monitor.
- Auto mode: automatically switches between green (available), yellow (calendar event), and red (in a meeting)
- Manual modes: force Red, Green, Yellow, Police (police light show!), or Off
- Per-state settings: each color mode has independent brightness and flash/strobe controls
- Off mode — turn the light off completely (both USB and Bluetooth)
- Battery level monitoring (polled every 30 minutes to conserve device battery)
- Firmware version display
- Auto-reconnect on disconnect
| Feature | Description |
|---|---|
| Connection toggle | Switch between USB Dongle and Bluetooth modes |
| Status indicator | Shows current color, mode (Auto/Manual), and meeting status |
| Mode buttons | Auto, Red, Green, Yellow, Police, Off |
| Light settings | Per-state brightness and flash/interval controls |
| Battery & firmware | Shown in header when connected via Bluetooth |
| Calendar section | Shows current event status with calendar picker |
- macOS 14.0 (Sonoma) or later
- USB mode: Luxafor USB light (Vendor ID:
0x04d8, Product ID:0xfc92) - Bluetooth mode: Luxafor Bluetooth PRO
- Accessibility permission (for meeting detection — prompted on first launch)
- Calendar permission (for calendar event detection — prompted on first launch)
- Bluetooth permission (prompted on first launch, Bluetooth mode only)
Note: This is an unsandboxed version for personal use. One can modify it to be sandboxed but then the Auto mode meeting detection would not work as expected.
- Open
LuxaforStatusBar.xcodeprojin Xcode - Build and run (Cmd+R)
- Grant Accessibility permission when prompted (requires app relaunch)
- Grant Calendar permission when prompted
- Grant Bluetooth permission when prompted
- Select USB Dongle in the Connection section
- Plug in your Luxafor USB dongle
- The light will respond automatically
- Select Bluetooth in the Connection section
- Power on your Luxafor Bluetooth PRO (insert the Luxafor Bluetooth PRO into the Power Bank or USB power source)
- When not connected, the Dot blinks red every ~2 seconds (advertising mode)
- Tap Scan — the device appears as "LUX DOTxxx". Here, the xxx can be a random number.
- Select it to connect (may take up to 2 minutes on first connection)
- Once connected, battery level and firmware version are displayed in the header
Tip: The Luxafor Bluetooth PRO does not have a pairing button. It is always discoverable when powered on and not connected. If unresponsive, unplug the Luxafor Bluetooth PRO, wait a few seconds, and plug it back in to reset.
The app polls every 5 seconds using the macOS Accessibility API to check window titles of supported meeting apps:
| App | Detection Method |
|---|---|
| Zoom | Window title contains "Meeting" or "Webinar" |
| Slack | Window title contains "Huddle" |
| MS Teams | Window title indicates an active call |
Note: Accessibility permission is required for meeting detection. After granting permission in System Settings → Privacy & Security → Accessibility, you must relaunch the app.
- 🔴 Active meeting (Zoom/Slack Huddle/Teams call) → Red
- 🟡 Calendar event (current event in selected calendars) → Yellow
- 🟢 Available (no meeting or event) → Green
- SwiftUI menu bar app with
MenuBarExtrapopover (LSUIElement) - IOKit HID Manager for USB device communication
- CoreBluetooth for BLE device communication
- Accessibility API (AXUIElement) for meeting detection
- EventKit for calendar monitoring
- @Observable reactive state management
This protocol was reverse-engineered from the official Luxafor Android app (v1.5.1, com.greynut.luxafor.bt_luxofor).
| Service | UUID | Purpose |
|---|---|---|
| Generic Access | 00001800-0000-1000-8000-00805F9B34FB |
Device name |
| Device Information | 0000180A-0000-1000-8000-00805F9B34FB |
Firmware version |
| Battery Service | 0000180F-0000-1000-8000-00805F9B34FB |
Battery level (0–100%) |
| Custom Service | 00001234-0000-1000-8000-00805F9B34FB |
Light control |
| Characteristic | UUID | Direction | Purpose |
|---|---|---|---|
| Device Name | 00002A00 |
Read/Write | BLE device name |
| Firmware Rev | 00002A26 |
Read | Firmware version string |
| Battery Level | 00002A19 |
Read/Notify | Battery percentage (1 byte) |
| Custom RX | 00001235 |
Write | Send commands to device |
| Custom TX | 00001236 |
Read/Notify | Receive responses from device |
Commands are sent as 8-byte arrays to the Custom RX characteristic (00001235).
A1 <LED> <R> <G> <B> 10 10 10
LED:01= LED 1,02= LED 2,FF= all LEDsR,G,B: color values00–FF- Example — red:
A1 FF FF 00 00 10 10 10 - Example — off:
A1 FF 00 00 00 10 10 10
A3 FF <R> <G> <B> <SPEED> 00 <REPEAT>
A6 0<ID> <REPEAT> 10 10 10 10 10
- Pattern IDs: 1–3 = Random, 4 = Blue blink, 5 = Police, 6–7 = Random, 8 = Rainbow
| Command | USB HID | BLE |
|---|---|---|
| Solid color | 01 FF R G B 00 00 00 |
A1 FF R G B 10 10 10 |
| Strobe | 03 FF R G B SPD 00 FF |
A3 FF R G B SPD 00 RPT |
| Pattern | 06 ID R G B 00 00 00 |
A6 0ID RPT 10 10 10 10 10 |
- Write type: Commands MUST use
writeWithoutResponse. UsingwriteWithResponsecompletes without error but the light does not turn on. - Device Information service is required: Read the firmware version (
00002A26) during setup. Skipping this may prevent the device from accepting commands. - Wake-up sequence: Send an OFF command before the actual color command after connecting.
- Sleep signals: The device sends
310Dvia TX after each command, and300D/330D/320Dwhen entering sleep. Re-send the current color in response.
MIT
