{"id":51294273,"url":"https://github.com/schappim/macropad","last_synced_at":"2026-06-30T13:01:27.317Z","repository":{"id":360592663,"uuid":"1250863054","full_name":"schappim/macropad","owner":"schappim","description":"Karabiner-Elements complex_modifications for a USB macropad (VID 20812 / PID 34897)","archived":false,"fork":false,"pushed_at":"2026-05-27T04:10:19.000Z","size":79,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-27T05:14:10.740Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/schappim.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-27T03:15:22.000Z","updated_at":"2026-05-27T04:10:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/schappim/macropad","commit_stats":null,"previous_names":["schappim/macropad"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/schappim/macropad","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schappim%2Fmacropad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schappim%2Fmacropad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schappim%2Fmacropad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schappim%2Fmacropad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/schappim","download_url":"https://codeload.github.com/schappim/macropad/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schappim%2Fmacropad/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34967627,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-30T13:01:26.030Z","updated_at":"2026-06-30T13:01:27.309Z","avatar_url":"https://github.com/schappim.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# macropad\n\nKarabiner-Elements rules for a cheap USB macropad (4 keys + a rotary encoder), wired up to drive [cmux](https://cmux.com/) and [MacWhisper](https://goodsnooze.gumroad.com/l/macwhisper).\n\n![Fafeicy 4-key macropad with rotary encoder](macropad.jpg)\n\nThe hardware is the [Fafeicy 4 Key 1 Knob Programmable Macro Keypad](https://www.amazon.com.au/Fafeicy-Keyboard-Programmable-Switches-Productivity/dp/B0FZCGX42T/) (~AU$20 on Amazon AU) — blue mechanical switches and a clicky rotary encoder.\n\nIt reports as a USB keyboard with VID `20812` / PID `34897`.\n\n## Hardware layout\n\n| Physical input | Sends keycode |\n|---|---|\n| Encoder rotated left (CCW) | `1` |\n| Encoder pressed (push-down) | `2` |\n| Encoder rotated right (CW) | `3` |\n| Top key of the T-cluster | `a` |\n| Left key | `b` |\n| Bottom key | `c` |\n| Right key | `d` |\n\n## What it does\n\n| Macropad input | Global | In cmux.app | In Chrome |\n|---|---|---|---|\n| Encoder CCW (`1`) | — | Previous workspace (Ctrl+Cmd+[) | Page Up |\n| Centre press (`2`) | **F18** → MacWhisper dictation | F18 | F18 |\n| Encoder CW (`3`) | — | Next workspace (Ctrl+Cmd+]) | Page Down |\n| Button `a` | — | Focus pane up (Cmd+Opt+↑) | — |\n| Button `b` | — | Focus pane left (Cmd+Opt+←) | — |\n| Button `c` | — | Focus pane down (Cmd+Opt+↓) | — |\n| Button `d` | — | Focus pane right (Cmd+Opt+→) | — |\n| Cmd held + encoder CCW | Previous app in switcher (Cmd+Shift+Tab) | | |\n| Cmd held + encoder CW | Next app in switcher (Cmd+Tab) | | |\n\nOutside cmux/Chrome, the keys (other than `2`) pass through unchanged.\n\n## The two apps\n\n### [cmux](https://cmux.com/)\n\nA native macOS terminal designed for running multiple coding agents in parallel. It has vertical tabs, split panes, notification rings when an agent wants attention, an embedded scriptable browser, and a socket API — built on libghostty's renderer. The macropad bindings drive its tab/pane navigation:\n\n- **Ctrl+Cmd+[ / ]** — previous / next workspace\n- **Cmd+Opt+arrows** — move focus between split panes\n\nHolding Cmd on the main keyboard and twirling the encoder gives a tactile Cmd+Tab — handy when you live in cmux but need to flick to another app for a second.\n\n### [MacWhisper](https://goodsnooze.gumroad.com/l/macwhisper)\n\nA macOS app for transcribing audio with OpenAI Whisper models, locally on your Mac. It supports a global push-to-talk hotkey for dictation — set it to **F18** in MacWhisper's preferences and the centre button on the macropad becomes a dedicated dictation key that works in any app.\n\n## Installation\n\n1. **Install [Karabiner-Elements](https://karabiner-elements.pqrs.org/)** if you don't have it. Grant the Input Monitoring / Accessibility permissions it asks for.\n\n2. **Drop the rules file into Karabiner's import folder:**\n\n   ```sh\n   curl -L -o ~/.config/karabiner/assets/complex_modifications/macropad.json \\\n     https://raw.githubusercontent.com/schappim/macropad/master/macropad.json\n   ```\n\n3. **Import via the Karabiner UI:**\n   - Open Karabiner-Elements → **Complex Modifications** tab → **Add predefined rule**\n   - Find **Macropad mappings (VID 20812 / PID 34897)**\n   - Click **Enable All** (or enable rules individually)\n\n4. **Configure MacWhisper** (optional): in its preferences, bind the dictation / push-to-talk shortcut to **F18**.\n\n## RGB backlight control\n\nThe 4 buttons have addressable backlight LEDs. (The knob doesn't.) The firmware uses the same wire protocol as the `k884x` family from [kriomant/ch57x-keyboard-tool](https://github.com/kriomant/ch57x-keyboard-tool), but on macOS we can't claim the USB interface via libusb because IOHIDFamily owns it — so [`set_led.py`](set_led.py) talks to the vendor-specific HID interface directly using `hidapi`.\n\n### Setup\n\n```sh\nbrew install hidapi\npython3 -m venv .venv\n.venv/bin/pip install hid\n```\n\n### Usage\n\n```sh\n.venv/bin/python set_led.py off                  # all LEDs off\n.venv/bin/python set_led.py backlight blue       # always-on blue\n.venv/bin/python set_led.py backlight white      # always-on white\n.venv/bin/python set_led.py press red            # only lights pressed key\n.venv/bin/python set_led.py shock cyan           # \"shock\" effect on press\n.venv/bin/python set_led.py shock2 green         # alternate shock effect\n```\n\n**Modes:** `off`, `backlight`, `press`, `shock`, `shock2`.\n**Colors:** `red`, `orange`, `yellow`, `green`, `cyan`, `blue`, `purple` (looks more pink), plus `white` (backlight mode only).\n\nThe wire format (for reference):\n\n```\nprogram LED:    03 fe b0 \u003clayer+1\u003e 08 00 00 00 00 00 01 00 \u003ccode\u003e   (pad to 64 bytes)\ncommit:         03 fd fe ff                                          (pad to 64 bytes)\n\ncode = (color \u003c\u003c 4) | mode\nmodes:   0=off  1=backlight-colour  2=shock  3=shock2  4=press  5=backlight-white\ncolours: 1=red  2=orange  3=yellow  4=green  5=cyan  6=blue  7=purple\n```\n\nSent as HID output reports (report ID `0x00`) to the vendor-specific interface (UsagePage `0xFF00`, endpoint `0x02 OUT`).\n\n**No brightness / PWM:** the firmware on this device only exposes the 7 named colors plus white, with no intensity control. The related K8850 (PID `0x8850`, 16 keys) has a different firmware that supports full per-key RGB ([upstream PR #175](https://github.com/kriomant/ch57x-keyboard-tool/pull/175)) — that protocol does *not* work on this 4-key 0x8851 variant. Host-side PWM toggling via HID writes is too coarse to look smooth (firmware applies updates well below flicker fusion), so brightness pulsing isn't practical here.\n\n## Adapting to a different macropad\n\nIf your macropad reports a different VID/PID, you'll need to update them in `macropad.json`. To find yours:\n\n1. Open Karabiner-Elements → **Devices** tab — the VID and PID are listed next to each connected keyboard.\n2. In `macropad.json`, find/replace every `\"vendor_id\": 20812, \"product_id\": 34897` with your values.\n3. Re-import via the Complex Modifications tab.\n\nIf your macropad sends different key codes than `a`/`b`/`c`/`d`/`1`/`2`/`3`, use Karabiner's **EventViewer** (under the menu bar icon) to see what each button sends, then update the `\"key_code\"` values in the rules accordingly.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschappim%2Fmacropad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschappim%2Fmacropad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschappim%2Fmacropad/lists"}