Skip to content

Add Macropad_Hotkeys project #1593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions Macropad_Hotkeys/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""
A fairly straightforward macro/hotkey program for Adafruit MACROPAD.
Macro key setups are stored in the /macros folder (configurable below),
load up just the ones you're likely to use. Plug into computer's USB port,
use dial to select an application macro set, press MACROPAD keys to send
key sequences.
"""

# pylint: disable=import-error, unused-import, too-few-public-methods, eval-used

import os
import time
import board
import digitalio
import displayio
import neopixel
import rotaryio
import terminalio
import usb_hid
from adafruit_display_shapes.rect import Rect
from adafruit_display_text import label
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS


# CONFIGURABLES ------------------------

MACRO_FOLDER = '/macros'


# CLASSES AND FUNCTIONS ----------------

class Key:
""" Class representing the physical hardware of each MACROPAD key. """
DEBOUNCE_TIME = 1 / 50

def __init__(self, keyname):
self.pin = digitalio.DigitalInOut(keyname)
self.pin.direction = digitalio.Direction.INPUT
self.pin.pull = digitalio.Pull.UP
self.last_value = self.pin.value # Initial state
self.last_time = time.monotonic()

def debounce(self):
""" Read a key's current state (hardware pin value), filtering out
any "bounce" noise. This function needs to be called frequently,
once for each key on pad, plus encoder switch. """
value = self.pin.value
if value != self.last_value:
now = time.monotonic()
elapsed = now - self.last_time
if elapsed >= self.DEBOUNCE_TIME:
self.last_value = value
self.last_time = now
return value
return None

class App:
""" Class representing a host-side application, for which we have a set
of macro sequences. """
def __init__(self, appdata):
self.name = appdata['name']
self.macros = appdata['macros']

def switch(self):
""" Activate application settings; update OLED labels and LED
colors. """
GROUP[13].text = self.name # Application name
for i in range(12):
if i < len(self.macros): # Key in use, set label + LED color
PIXELS[i] = self.macros[i][0]
GROUP[i].text = self.macros[i][1]
else: # Key not in use, no label or LED
PIXELS[i] = 0
GROUP[i].text = ''
PIXELS.show()
DISPLAY.refresh()


# INITIALIZATION -----------------------

DISPLAY = board.DISPLAY
DISPLAY.auto_refresh = False
ENCODER = rotaryio.IncrementalEncoder(board.ENCODER_B, board.ENCODER_A)
PIXELS = neopixel.NeoPixel(board.NEOPIXEL, 12, auto_write=False)
KEYBOARD = Keyboard(usb_hid.devices)
LAYOUT = KeyboardLayoutUS(KEYBOARD)

GROUP = displayio.Group(max_size=14)
for KEY_INDEX in range(12):
x = KEY_INDEX % 3
y = KEY_INDEX // 3
GROUP.append(label.Label(terminalio.FONT, text='', color=0xFFFFFF,
anchored_position=((DISPLAY.width - 1) * x / 2,
DISPLAY.height - 1 -
(3 - y) * 12),
anchor_point=(x / 2, 1.0), max_glyphs=15))
GROUP.append(Rect(0, 0, DISPLAY.width, 12, fill=0xFFFFFF))
GROUP.append(label.Label(terminalio.FONT, text='', color=0x000000,
anchored_position=(DISPLAY.width//2, -2),
anchor_point=(0.5, 0.0), max_glyphs=30))
DISPLAY.show(GROUP)

KEYS = []
for pin in (board.KEY1, board.KEY2, board.KEY3, board.KEY4, board.KEY5,
board.KEY6, board.KEY7, board.KEY8, board.KEY9, board.KEY10,
board.KEY11, board.KEY12, board.ENCODER_SWITCH):
KEYS.append(Key(pin))

# Load all the macro key setups from .py files in MACRO_FOLDER
APPS = []
FILES = os.listdir(MACRO_FOLDER)
FILES.sort()
for FILENAME in FILES:
if FILENAME.endswith('.py'):
module = __import__(MACRO_FOLDER + '/' + FILENAME[:-3])
APPS.append(App(module.app))

if not APPS:
print('No valid macro files found')
while True:
pass

LAST_POSITION = None
APP_INDEX = 0
APPS[APP_INDEX].switch()


# MAIN LOOP ----------------------------

while True:
POSITION = ENCODER.position
if POSITION != LAST_POSITION:
APP_INDEX = POSITION % len(APPS)
APPS[APP_INDEX].switch()
LAST_POSITION = POSITION

for KEY_INDEX, KEY in enumerate(KEYS[0: len(APPS[APP_INDEX].macros)]):
action = KEY.debounce()
if action is not None:
sequence = APPS[APP_INDEX].macros[KEY_INDEX][2]
if action is False: # Macro key pressed
if KEY_INDEX < 12:
PIXELS[KEY_INDEX] = 0xFFFFFF
PIXELS.show()
for item in sequence:
if isinstance(item, int):
if item >= 0:
KEYBOARD.press(item)
else:
KEYBOARD.release(item)
else:
LAYOUT.write(item)
elif action is True: # Macro key released
# Release any still-pressed modifier keys
for item in sequence:
if isinstance(item, int) and item >= 0:
KEYBOARD.release(item)
if KEY_INDEX < 12:
PIXELS[KEY_INDEX] = APPS[APP_INDEX].macros[KEY_INDEX][0]
PIXELS.show()
27 changes: 27 additions & 0 deletions Macropad_Hotkeys/macros/mac-adobe-illustrator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values

app = { # REQUIRED dict, must be named 'app'
'name' : 'Illustrator', # Application name
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x004000, 'Undo', [Keycode.COMMAND, 'z']),
(0x004000, 'Redo', [Keycode.COMMAND, 'Z']),
(0x303000, 'Pen', 'p'), # Path-drawing tool
# 2nd row ----------

(0x101010, 'Select', 'v'), # Select (path)
(0x400000, 'Reflect', 'o'), # Reflect selection
(0x303000, 'Rect', 'm'), # Draw rectangle
# 3rd row ----------
(0x101010, 'Direct', 'a'), # Direct (point) selection
(0x400000, 'Rotate', 'r'), # Rotate selection
(0x303000, 'Oval', 'l'), # Draw ellipse
# 4th row ----------
(0x101010, 'Eyedrop', 'i'), # Cycle eyedropper/measure modes
(0x400000, 'Scale', 's'), # Scale selection
(0x303000, 'Text', 't'), # Type tool
# Encoder button ---
(0x000000, '', [Keycode.COMMAND, Keycode.OPTION, 'S']) # Save for web
]
}
26 changes: 26 additions & 0 deletions Macropad_Hotkeys/macros/mac-adobe-photoshop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values

app = { # REQUIRED dict, must be named 'app'
'name' : 'Photoshop', # Application name
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x004000, 'Undo', [Keycode.COMMAND, 'z']),
(0x004000, 'Redo', [Keycode.COMMAND, 'Z']),
(0x000040, 'Brush', 'B'), # Cycle brush modes
# 2nd row ----------
(0x101010, 'B&W', 'd'), # Default colors
(0x101010, 'Marquee', 'M'), # Cycle rect/ellipse marquee (select)
(0x000040, 'Eraser', 'E'), # Cycle eraser modes
# 3rd row ----------
(0x101010, 'Swap', 'x'), # Swap foreground/background colors
(0x101010, 'Move', 'v'), # Move layer
(0x000040, 'Fill', 'G'), # Cycle fill/gradient modes
# 4th row ----------
(0x101010, 'Eyedrop', 'I'), # Cycle eyedropper/measure modes
(0x101010, 'Wand', 'W'), # Cycle "magic wand" (selection) modes
(0x000040, 'Heal', 'J'), # Cycle "healing" modes
# Encoder button ---
(0x000000, '', [Keycode.COMMAND, Keycode.OPTION, 'S']) # Save for web
]
}
29 changes: 29 additions & 0 deletions Macropad_Hotkeys/macros/mac-safari.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values

app = { # REQUIRED dict, must be named 'app'
'name' : 'Safari', # Application name
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x004000, '< Back', [Keycode.COMMAND, '[']),
(0x004000, 'Fwd >', [Keycode.COMMAND, ']']),
(0x400000, 'Up', [Keycode.SHIFT, ' ']), # Scroll up
# 2nd row ----------
(0x202000, '< Tab', [Keycode.CONTROL, Keycode.SHIFT, Keycode.TAB]),
(0x202000, 'Tab >', [Keycode.CONTROL, Keycode.TAB]),
(0x400000, 'Down', ' '), # Scroll down
# 3rd row ----------
(0x000040, 'Reload', [Keycode.COMMAND, 'r']),
(0x000040, 'Home', [Keycode.COMMAND, 'H']),
(0x000040, 'Private', [Keycode.COMMAND, 'N']),
# 4th row ----------
(0x000000, 'Ada', [Keycode.COMMAND, 'n', -Keycode.COMMAND,
'www.adafruit.com\n']), # Adafruit in new window
(0x800000, 'Digi', [Keycode.COMMAND, 'n', -Keycode.COMMAND,
'www.digikey.com\n']), # Digi-Key in new window
(0x101010, 'Hacks', [Keycode.COMMAND, 'n', -Keycode.COMMAND,
'www.hackaday.com\n']), # Hack-a-Day in new win
# Encoder button ---
(0x000000, '', [Keycode.COMMAND, 'w']) # Close window/tab
]
}