Griffin PowerMate is a fully-customizable Windows 11 application designed to control and interact with the Griffin PowerMate USB knob (originally discontinued in 2018). It supports every interaction of the knob, including rotation left/right, single/double/triple press, long press, and press-while-rotating, with per-application profiles and rich action bindings.
Key Features:
Full Interaction Support: Maps all knob interactions to actions like volume control, media playback, keyboard shortcuts, macros, scrolling, launching apps, or running shell commands.
Per-Application Profiles: Customizes bindings based on the currently focused application, with inheritance from a default profile for consistent behavior across apps.
Rich Action Bindings: Supports system volume, mute, microphone mute, media transport (play/pause, next, previous), scroll, keyboard shortcuts, macros, launching files/URLs, and shell commands.
LED Control: Offers full control over the LED, including solid, pulse, flash, or volume-tracking modes with fade effects.
On-Screen Display (OSD): Features a translucent overlay showing gestures and actions in real-time, customizable for opacity, background color, font family/color/size, and duration.
System Tray Integration: Runs quietly in the system tray with options to pause/resume, access settings, or quit the app.
Audience & Benefit:
Ideal for users of the Griffin PowerMate USB knob who want a modern, customizable experience on Windows 11. It enhances productivity by allowing precise control over device interactions and provides a flexible way to customize workflows across applications.
Griffin PowerMate can be installed via winget, ensuring seamless integration into your system setup.
README
Griffin PowerMate — Windows 11 Controller
A modern, fully‑customizable Windows 11 application for the Griffin PowerMate USB
knob (the original 2001 multifunction controller, discontinued in 2018). Turn, press,
double/triple‑press, long‑press, and press‑while‑turning — every interaction is
mappable, per application, through a clean GUI. The blue LED is fully driven by the app
(solid, software pulse, flash, or volume‑tracking with fade).
This project is a from‑scratch Python reimplementation inspired by Nathan Sweet's
original Java utility (EsotericSoftware/powermate),
rebuilt with native Windows audio, a full gesture engine, per‑app profiles, a
CustomTkinter UI, and a one‑file .exe build. It was reverse‑engineered and verified
against real PowerMate hardware on Windows 11.
Every knob interaction is mappable — rotate left/right, single/double/triple
press, long press, and press‑while‑rotating (both directions).
Per‑application profiles — different bindings depending on the focused app, with
inheritance from a default profile.
Rich action set — system volume, mute, microphone mute, media transport
(play/pause, next, previous), scroll, arbitrary keyboard shortcuts, 3‑step
keyboard macros, launch apps/files/URLs, and run shell commands.
Knob activity / debug panel — a live, timestamped log of incoming knob events and
the action each one triggers.
Translucent on‑screen display (OSD) — a two‑line overlay in the bottom‑right corner
(over the taskbar) that shows the gesture and its bound action on every turn/press.
Fully styleable (opacity, background color, font family/color/size 8–24pt, duration).
Start with Windows — one‑click toggle to launch automatically at login.
Full LED control — off, solid, software pulse, flash, and volume
(brightness follows the system volume, then fades).
Modern GUI (CustomTkinter) — profile sidebar with a running‑app picker, a
trigger→action grid with a "record shortcut" capture field, an LED panel with a live
test button, and a timing panel.
System tray — runs quietly in the background; pause/resume; settings; quit.
Live config reload — edit config.json by hand and changes apply within ~1s.
Single‑file executable — PowerMate.exe bundles Python, all dependencies, and the
native libusb DLL. No Python install required on the target machine.
Hardware & Protocol
Property
Value
USB Vendor ID
0x077D
USB Product ID
0x0410
Interface
0 (HID class, but driven via libusb/WinUSB)
Input endpoint
0x81 — 6‑byte interrupt reports
Output endpoint
0x02 — 1‑byte interrupt (LED brightness)
Input report (endpoint 0x81, 6 bytes):
Byte
Meaning
0
Button state — bit 0 set = pressed
1
Relative rotation delta, signed (+ clockwise, − counter‑clockwise)
2–5
Status/reserved (unused by this app)
LED brightness (endpoint 0x02): a single interrupt‑OUT byte, 0–255. This is the
method this app uses on Windows — see Reverse‑Engineering Notes
for why the vendor control transfer is deliberately avoided there.
When polled slowly during a fast spin, the device coalesces rotation into a single larger
delta (e.g. +15), so the gesture engine accumulates deltas rather than assuming one
detent per packet.
Quick Start
Option A — Run the prebuilt EXE (end users)
Install the WinUSB driver on the PowerMate with Zadig — see
USB Driver Setup. (One time.)
Download / copy PowerMate.exe and double‑click it.
A tray icon appears. Turn the knob — your system volume changes. Right‑click the tray
icon → Settings to customize.
Option B — Run from source (developers)
# 1. Python 3.11+ (64-bit). 3.14 is tested.
python --version
# 2. Install dependencies
pip install -r requirements.txt
# 3. Install the WinUSB driver via Zadig (see below)
# 4. Run
python main.py
USB Driver Setup (Zadig)
By default Windows binds the PowerMate to the generic HID driver, which does not
allow the low‑level USB access this app needs. You must rebind it to WinUSB once.
Download Zadig: (portable, no install).
Plug in the PowerMate.
In Zadig: Options → List All Devices.
In the dropdown, select Griffin PowerMate (it shows 077D 0410).
Choose WinUSB as the replacement driver (the box to the right of the green arrow).
Click Replace Driver and wait. A completion dialog — or a harmless error — is normal.
> Reversible: This dedicates the knob to this app (it stops acting as a generic HID
> device, so the old Griffin software won't see it). To undo: Device Manager → the
> PowerMate → Driver → Roll Back Driver (or uninstall the device and replug).
> Why not libusb‑win32? This app uses the modern libusb‑1.0 backend, which works
> best with the WinUSB driver. libusb-win32 (libusb0) may also work but WinUSB is
> recommended.
Using the App
The app starts minimized to the system tray. Right‑click the tray icon for:
Settings — open the configuration window.
Pause / Resume — temporarily stop/restart responding to the knob.
Exit — quit.
Window controls (bottom bar)
Save / Reload — persist or re‑read config.json.
Minimize to Tray — hide the window but keep the app running in the background.
Quit — fully exit the application.
Start with Windows — checkbox that registers/unregisters the app in the
per‑user startup (HKCU …\Run) so it launches automatically at login.
Clicking the window's X also minimizes to the tray (it does not quit).
Knob Activity panel
The panel above the bottom bar shows a live, timestamped log of every event received
from the knob — the trigger, the focused app, and the action taken
(e.g. 12:01:03 rotate_right +2 [chrome.exe] -> scroll_down). Use it to confirm that
gestures like triple‑press and long‑press are firing and to debug bindings.
Clear empties the log.
On‑screen display (OSD)
A small translucent overlay appears in the bottom‑right corner, over the taskbar,
each time you use the knob. It shows two lines:
Line 1 — the gesture: Single Press, Double Press, Triple Press, Long Press,
Knob Left, Knob Right, Press + Knob Left/Right.
Line 2 — the action bound to that gesture in the active profile (e.g. Volume
Up, Play / Pause, or the key combo for a key/macro). Unbound gestures show —.
It is always‑on‑top, never steals keyboard focus, stays out of Alt‑Tab and the taskbar,
and auto‑hides ~1.2s after the last event with a brief fade (the timer resets while you
keep turning). It works even when the main window is minimized to the tray.
Configure it on the OSD tab: enable/disable, opacity, duration, background color
(hex or color picker), font family (any installed font), font color, and font size
(8–24pt). Preview flashes a sample using the current settings. Settings persist in
the osd block of config.json and apply live.
The Settings window
Profiles (left sidebar) — default plus one entry per app. Click to select.
+ Add Profile opens a picker listing your currently running apps; click one to
create a profile for it (e.g. chrome.exe).
- Delete Profile removes the selected profile (default cannot be deleted).
Bindings tab — a row for each of the 8 triggers. Pick an action and fill in its
parameters. The key action has a Record button: click it, press your shortcut, and
it captures the combo (e.g. ctrl+shift+n).
LED tab — choose the mode, brightness, pulse speed/waveform, flash timing, and
volume‑fade timing. Test LED applies the settings to the device live.
OSD tab — enable the on‑screen display and style it (opacity, duration, background
color, font family/color/size 8–24pt); Preview flashes a sample.
Save writes everything to config.json; Reload re‑reads it.
Triggers
Trigger
How to perform it
rotate_right
Turn clockwise
rotate_left
Turn counter‑clockwise
press
One click (fires after the multi‑click window elapses)
double_press
Two quick clicks
triple_press
Three quick clicks
long_press
Hold the button past the long‑press threshold (default 500 ms)
press_rotate_right
Hold the button and turn clockwise
press_rotate_left
Hold the button and turn counter‑clockwise
A single press intentionally waits for the multi‑click window (default 250 ms) so it can be
distinguished from a double/triple press. Press‑while‑rotating suppresses the click so it
doesn't also register as a press.
Actions
Action
Parameters
Description
volume_up
amount (0.0–1.0)
Raise system volume
volume_down
amount (0.0–1.0)
Lower system volume
mute
—
Toggle system mute
mic_mute_toggle
—
Toggle microphone mute
media_play_pause
—
Media play/pause key
media_next
—
Next track
media_prev
—
Previous track
scroll_up
amount (lines)
Scroll up
scroll_down
amount (lines)
Scroll down
key
keys (e.g. "ctrl+z")
Send a keyboard shortcut
macro
sequence (list, up to 3)
Send several key combos in order, e.g. ["ctrl+c","alt+tab","ctrl+v"]
launch
path
Open a file, app, or URL
run
command
Run a shell command
none
—
Do nothing (also used to inherit from default)
Volume and mic actions use pycaw (native Windows Core Audio) — no helper executables.
Keyboard and media actions use the keyboard library; shortcut syntax is
modifier+modifier+key, e.g. alt+left, ctrl+shift+escape, f5.
LED Modes
Mode
Behavior
off
LED off
solid
Constant brightness (set by brightness, 0.0–1.0)
pulse
Smooth software sine pulse; pulse_speed (0–510) sets the period
flash
On/off at flash_on_ms / flash_off_ms
volume
Brightness tracks the system volume, then fades after fade_delay/fade_speed
> On Windows the LED is driven entirely via interrupt‑OUT writes, so pulse and flash are
> implemented in software (the device's hardware‑pulse command uses a control transfer
> that is incompatible with simultaneous reads on WinUSB — see notes below).
Per‑App Profiles
Each profile maps triggers to actions. The default profile applies whenever the
focused app has no profile of its own. App profiles inherit from default: any trigger
you don't explicitly set falls back to the default binding.
The active app is detected from the foreground window's process name (e.g. chrome.exe,
code.exe, vlc.exe) via the Win32 API.
Example: make the knob scroll in Chrome but keep controlling volume everywhere else —
add a chrome.exe profile that overrides only rotate_left/rotate_right.
A ready-made spotify.exe profile ships in the defaults: it maps single-press to
play/pause (space), double/triple-press to next/previous track (ctrl+right /
ctrl+left), and inherits system-volume control on rotation. Because it sends Spotify's
own in-app shortcuts while Spotify is focused, play/pause toggles reliably (the global
media key can otherwise be routed to a different app by Windows).
Configuration File
Settings live in config.json next to the app (created on first run from defaults; a
template is provided as config.example.json). It is human‑editable and hot‑reloaded
within ~1 second of saving.
Window to detect double/triple clicks; a single press fires after this
long_press_ms
500
Hold time required to trigger long_press
rotate_sensitivity
1.0
Multiplier applied to each rotation delta before accumulation
rotate_acceleration
0.0
Reserved for future acceleration curves
Lower multi_click_ms for snappier single presses (at the cost of harder double/triple
detection); raise rotate_sensitivity to make a small turn do more.
Building the EXE
Requires pyinstaller (pip install pyinstaller) and the libusb package (in
requirements.txt, which bundles the native DLL).
locates and bundles libusb-1.0.dll from the libusb package,
collects CustomTkinter's theme/asset data files,
bundles assets/icon.ico,
adds the required hidden imports (comtypes, pycaw, pystray._win32, pywin32 modules).
At runtime the app finds the bundled DLL via sys._MEIPASS, so the EXE is fully
self‑contained — only the WinUSB driver (Zadig) is needed on the target machine.
Architecture
main.py App orchestration: worker thread, LED thread, GUI, tray, config wiring
device.py USB layer: libusb backend discovery, find/claim, interrupt read, LED write
gesture.py State machine: raw button/rotation -> high-level triggers (click timing)
led.py LED controller thread: solid / software pulse / flash / volume-fade
audio.py Native volume + mic control (pycaw), thread-safe across COM apartments
actions.py Action executors (volume, media, scroll, keys, launch, run)
app_monitor.py Foreground process detection for per-app profiles
config.py Load/save/validate config.json with live file-watch reload
tray.py System tray icon + menu (pystray)
ui/ CustomTkinter GUI (app_window, profile_view, action_editor,
led_panel, timing_panel)
Threading model
A worker thread owns the USB device: it reconnects on unplug, reads endpoint 0x81,
feeds packets to the gesture engine, resolves the active‑app profile, and dispatches
actions.
A LED thread continuously applies the chosen LED mode.
The GUI runs on the main thread; status updates from other threads are marshalled
with Tk.after().
The tray runs its own thread.
COM is initialized per‑thread (audio is called from both the worker and LED threads),
using thread‑local endpoint interfaces.
Reverse‑Engineering Notes (Windows / WinUSB)
These findings, discovered while validating against real hardware, are baked into the app:
The device must be on the WinUSB driver. With the default HID driver, libusb can
enumerate the device and even fake set_configuration/claim_interface, but every real
transfer fails with "Operation not supported on this platform." Zadig → WinUSB fixes it.
Do not call set_configuration() on Windows. WinUSB has already configured the
device; calling it again invalidates the endpoint pipe handles, after which interrupt
reads fail with "Entity not found." The app claims interface 0 and reads directly.
Set the LED via interrupt‑OUT (endpoint 0x02), not a vendor control transfer. A
vendor control transfer for brightness permanently breaks the interrupt‑IN read
pipe on WinUSB ("Entity not found" on all subsequent reads) until the device is
power‑cycled. The single‑byte interrupt‑OUT write — the method used by the original
hardware — sets brightness reliably and does not disturb reads. Consequently the
device's hardware pulse modes (also control transfers) are disabled on Windows, and
pulse/flash are done in software.
Rotation batches under slow polling. A fast spin polled slowly arrives as one large
signed delta, so the gesture engine accumulates deltas instead of counting packets.
pyusb needs a native backend. pyusb is bindings only; the libusb pip package
supplies libusb-1.0.dll, which device.py locates automatically (bundle dir, then the
installed package).
Security Considerations
A high-level assessment of the threat model:
No remote attack surface. The app opens no network sockets, servers, or ports. It
talks only to a local USB device and the local Windows audio/input APIs.
Runs with normal user privileges. No elevation is required or requested. The
Start with Windows entry is written to the per-user HKCU Run key (not system-wide
HKLM), and its command is built from the executable/script path — not from user input,
so it isn't injectable.
Code-execution actions are user-defined and local. The run (shell command),
launch (open file/app/URL), key, and macro actions execute what you put in your
own config.json. They run with your privileges — only bind commands you trust. The
threat model is "the user configures their own knob"; there is no path for a remote party
to inject actions.
run uses the shell (shell=True) so that shell features work; treat its value like a
line you'd type into a terminal yourself.
Input is bounded and parsed safely. USB reports are fixed-size 6-byte reads; the
config is parsed with json.load (no eval/pickle), and a malformed config falls back
to defaults rather than crashing.
Keyboard output only. The app uses the keyboard library to send keystrokes for
bindings; it does not install a global key logger/hook.
No high-severity issues were identified. If you distribute the EXE, dependencies are
sourced from PyPI and pinned in requirements.txt; rebuild from source if you require a
verified supply chain.
Testing
Three test suites run without hardware:
python _test_gesture.py # gesture state machine: all 8 triggers + regressions
python _test_features.py # macro action, startup module, debug panel, triple/long bindings
python _smoketest_gui.py # builds the GUI, exercises profiles/panels/save, tears down
The gesture tests cover single/double/triple/long press, both rotations, press‑while‑rotate,
and regressions for "no spurious press after press+rotate" and "no long‑press while
rotating." The GUI smoke test verifies the window builds, the default profile auto‑loads all
8 trigger editors, bindings round‑trip through save, the LED/Timing tabs populate, and
profile inheritance works.
Hardware was validated end‑to‑end: rotation → volume, press → mute, button detection, and
LED control all confirmed on a real PowerMate over WinUSB.
Troubleshooting
"PowerMate not found" (red status / tray)
Confirm the WinUSB driver is installed via Zadig (this is the most common cause).
Replug the device; check the status bar in Settings.
Inspect powermate.log next to the app.
Reads stop / LED works but knob does nothing
Almost always a stale driver state — unplug and replug to power‑cycle the device.
Ensure no other program is holding the device (only one process can claim WinUSB).
LED doesn't light
Make sure the LED mode isn't off and (for solid) brightness isn't 0.
Switch to flash briefly to confirm the device responds.
Keyboard shortcuts don't fire
The target app must have focus. Some apps capture keys differently.
Verify the keys syntax (e.g. ctrl+z, alt+tab, f5).
Config changes don't apply
The watcher reloads within ~1s; if your editor doesn't update the file's mtime, use
Reload in Settings or restart.