Skip to content

Instantly share code, notes, and snippets.

@figital
Created March 8, 2026 23:17
Show Gist options
  • Select an option

  • Save figital/b961a52f35168cdec2fd95ac0ef36e8f to your computer and use it in GitHub Desktop.

Select an option

Save figital/b961a52f35168cdec2fd95ac0ef36e8f to your computer and use it in GitHub Desktop.
powermate
"""
Griffin PowerMate Python 3 library for Windows
Ported from crash7/griffin-powermate (Python 2)
Requires: pip install pywinusb
"""
from pywinusb.hid import HidDevice, HidDeviceFilter
def find_griffin_powermate():
return GriffinPowermate.find_all()
class GriffinPowermate():
VENDOR = 0x077d
PRODUCT = 0x0410
MOVE_LEFT = -1
MOVE_RIGHT = 1
def __init__(self, raw_device):
self.__device = raw_device
self.__device.set_raw_data_handler(lambda raw_data: self.__internal_listener(raw_data))
self.__events = {}
@classmethod
def find_all(cls):
return [cls(device) for device in HidDeviceFilter(vendor_id=cls.VENDOR, product_id=cls.PRODUCT).get_devices()]
def __internal_listener(self, raw_data):
"""
[0, button_status, move, 0, bright, pulse_status, pulse_value]
move byte: 1-127 = clockwise (higher = faster), 129-255 = counter-clockwise (lower = faster)
"""
raw_move = raw_data[2]
if raw_move == 0:
delta = 0
elif raw_move < 128:
delta = raw_move # positive = clockwise
else:
delta = raw_move - 256 # negative = counter-clockwise
if 'move' in self.__events:
self.__events['move'](delta, raw_data[1])
if 'raw' in self.__events:
self.__events['raw'](raw_data)
def is_plugged(self):
return self.__device.is_plugged()
def open(self):
if not self.__device.is_opened():
self.__device.open()
def close(self):
if self.__device.is_opened():
self.__device.close()
def on_event(self, event, callback):
self.__events[event] = callback
def set_brightness(self, bright):
self.__device.send_feature_report([0, 0x41, 0x01, 0x01, 0x00, bright % 255, 0x00, 0x00, 0x00])
def set_led_pulsing_status(self, on=True):
self.__device.send_feature_report([0, 0x41, 0x01, 0x03, 0x00, 0x01 if on else 0x00, 0x00, 0x00, 0x00])
def set_led_pulsing_default(self):
self.__device.send_feature_report([0, 0x41, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00])
if __name__ == '__main__':
from time import sleep
from msvcrt import kbhit
position = 0
def move_listener(delta, button):
global position
position += delta
# LED on while button held
if button:
powermate.set_brightness(254)
else:
powermate.set_brightness(0)
direction = 'CW' if delta > 0 else 'CCW' if delta < 0 else '--'
print("Pos: {:<6d} Delta: {:<+4d} Speed: {:<3d} Dir: {} Button: {}".format(
position, delta, abs(delta), direction, button))
def raw_listener(data):
# handle button press/release even without rotation
if data[2] == 0:
if data[1]:
powermate.set_brightness(254)
else:
powermate.set_brightness(0)
devices = GriffinPowermate.find_all()
if len(devices) > 0:
print("Found Powermates")
powermate = devices[0]
try:
powermate.open()
powermate.set_led_pulsing_status(False)
powermate.set_brightness(0)
powermate.on_event('move', move_listener)
powermate.on_event('raw', raw_listener)
print("\nPos Delta Speed Dir Button")
print("----- ------ ----- --- ------")
print("Waiting for data... Press any keyboard key to stop.\n")
while not kbhit() and powermate.is_plugged():
sleep(0.5)
finally:
powermate.set_brightness(0)
powermate.close()
else:
print("No PowerMate found. Is it plugged in?")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment