Hotkeys: add Consumer Control, mouse, audio feedback, clean up some macro formatting

This commit is contained in:
Phillip Burgess 2021-09-03 11:18:32 -07:00
parent f1636ad9c9
commit 224738889c
10 changed files with 208 additions and 54 deletions

View file

@ -1,9 +1,8 @@
"""
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.
A macro/hotkey program for Adafruit MACROPAD. Macro 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 and other USB protocols.
"""
# pylint: disable=import-error, unused-import, too-few-public-methods
@ -44,6 +43,9 @@ class App:
macropad.pixels[i] = 0
group[i].text = ''
macropad.keyboard.release_all()
macropad.consumer_control.release()
macropad.mouse.release_all()
macropad.stop_tone()
macropad.pixels.show()
macropad.display.refresh()
@ -129,15 +131,13 @@ while True:
sequence = apps[app_index].macros[key_number][2]
if pressed:
# the sequence is arbitrary-length
# each item in the sequence is either
# an integer (e.g., Keycode.KEYPAD_MINUS),
# a floating point value (e.g., 0.20)
# or a string.
# Positive Integers ==> key pressed
# Negative Integers ==> key released
# Float ==> sleep in seconds
# String ==> each key in string pressed & released
# 'sequence' is an arbitrary-length list, each item is one of:
# Positive integer (e.g. Keycode.KEYPAD_MINUS): key pressed
# Negative integer: (absolute value) key released
# Float (e.g. 0.25): delay in seconds
# String (e.g. "Foo"): corresponding keys pressed & released
# List []: one or more Consumer Control codes (can also do float delay)
# Dict {}: mouse buttons/motion (might extend in future)
if key_number < 12: # No pixel for encoder button
macropad.pixels[key_number] = 0xFFFFFF
macropad.pixels.show()
@ -149,13 +149,49 @@ while True:
macropad.keyboard.release(-item)
elif isinstance(item, float):
time.sleep(item)
else:
elif isinstance(item, str):
macropad.keyboard_layout.write(item)
elif isinstance(item, list):
for code in item:
if isinstance(code, int):
macropad.consumer_control.release()
macropad.consumer_control.press(code)
if isinstance(code, float):
time.sleep(code)
elif isinstance(item, dict):
if 'buttons' in item:
if item['buttons'] >= 0:
macropad.mouse.press(item['buttons'])
else:
macropad.mouse.release(-item['buttons'])
macropad.mouse.move(item['x'] if 'x' in item else 0,
item['y'] if 'y' in item else 0,
item['wheel'] if 'wheel' in item else 0)
if 'tone' in item:
if item['tone'] > 0:
macropad.stop_tone()
macropad.start_tone(item['tone'])
else:
macropad.stop_tone()
elif 'play' in item:
macropad.play_file(item['play'])
else:
# Release any still-pressed keys
# Release any still-pressed keys, consumer codes, mouse buttons
# Keys and mouse buttons are individually released this way (rather
# than release_all()) because pad supports multi-key rollover, e.g.
# could have a meta key or right-mouse held down by one macro and
# press/release keys/buttons with others. Navigate popups, etc.
for item in sequence:
if isinstance(item, int) and item >= 0:
macropad.keyboard.release(item)
if isinstance(item, int):
if item >= 0:
macropad.keyboard.release(item)
elif isinstance(item, dict):
if 'buttons' in item:
if item['buttons'] >= 0:
macropad.mouse.release(item['buttons'])
elif 'tone' in item:
macropad.stop_tone()
macropad.consumer_control.release()
if key_number < 12: # No pixel for encoder button
macropad.pixels[key_number] = apps[app_index].macros[key_number][0]
macropad.pixels.show()

View file

@ -2,9 +2,9 @@
from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values
app = { # REQUIRED dict, must be named 'app'
app = { # REQUIRED dict, must be named 'app'
'name' : 'Linux Firefox', # Application name
'macros' : [ # List of button macros...
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x004000, '< Back', [Keycode.CONTROL, '[']),
@ -20,10 +20,10 @@ app = { # REQUIRED dict, must be named 'app'
(0x000040, 'Private', [Keycode.CONTROL, Keycode.SHIFT, 'p']),
# 4th row ----------
(0x101010, 'Ada', [Keycode.CONTROL, 't', -Keycode.CONTROL,
'www.adafruit.com\n']), # adafruit.com in a new tab
(0x000040, 'Dev Mode', [Keycode.F12]), # dev mode
'www.adafruit.com\n']), # adafruit.com in a new tab
(0x000040, 'Dev Mode', [Keycode.F12]), # dev mode
(0x101010, 'Digi', [Keycode.CONTROL, 't', -Keycode.CONTROL,
'digikey.com\n']), # digikey in a new tab
'digikey.com\n']), # digikey in a new tab
# Encoder button ---
(0x000000, '', [Keycode.CONTROL, 'w']) # Close window/tab
]

View file

@ -20,8 +20,8 @@ app = { # REQUIRED dict, must be named 'app'
(0x004000, 'Nums', [Keycode.SHIFT, Keycode.COMMAND, 'o']),
(0x004000, 'Check', [Keycode.SHIFT, Keycode.COMMAND, 't']),
# 4th row ----------
(0x004000, 'Date', [Keycode.SHIFT, Keycode.COMMAND, 'D' ]),
(0x004000, 'Time', [Keycode.OPTION, Keycode.SHIFT, Keycode.COMMAND, 'D' ]),
(0x004000, 'Date', [Keycode.SHIFT, Keycode.COMMAND, 'D']),
(0x004000, 'Time', [Keycode.OPTION, Keycode.SHIFT, Keycode.COMMAND, 'D']),
(0x004000, 'Divider', [Keycode.SHIFT, Keycode.COMMAND, 'H']),
# Encoder button ---
(0x000000, '', [Keycode.COMMAND, 'w']) # Close window/tab

View file

@ -0,0 +1,40 @@
# MACROPAD Hotkeys example: Consumer Control codes (media keys)
# The syntax for Consumer Control macros is a little peculiar, in order to
# maintain backward compatibility with the original keycode-only macro files.
# The third item for each macro is a list in brackets, and each value within
# is normally an integer (Keycode), float (delay) or string (typed literally).
# Consumer Control codes are distinguished by enclosing them in a list within
# the list, which is why you'll see double brackets [[ ]] below.
# Like Keycodes, Consumer Control codes can be positive (press) or negative
# (release), and float values can be inserted for pauses.
# To reference Consumer Control codes, import ConsumerControlCode like so...
from adafruit_hid.consumer_control_code import ConsumerControlCode
# You can still import Keycode as well if a macro file mixes types!
# See other macro files for typical Keycode examples.
app = { # REQUIRED dict, must be named 'app'
'name' : 'Media', # Application name
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x000000, '', []),
(0x000020, 'Vol+', [[ConsumerControlCode.VOLUME_INCREMENT]]),
(0x202020, 'Bright+', [[ConsumerControlCode.BRIGHTNESS_INCREMENT]]),
# 2nd row ----------
(0x000000, '', []),
(0x000020, 'Vol-', [[ConsumerControlCode.VOLUME_DECREMENT]]),
(0x202020, 'Bright-', [[ConsumerControlCode.BRIGHTNESS_DECREMENT]]),
# 3rd row ----------
(0x000000, '', []),
(0x200000, 'Mute', [[ConsumerControlCode.MUTE]]),
(0x000000, '', []),
# 4th row ----------
(0x202000, '<<', [[ConsumerControlCode.SCAN_PREVIOUS_TRACK]]),
(0x002000, 'Play/Pause', [[ConsumerControlCode.PLAY_PAUSE]]),
(0x202000, '>>', [[ConsumerControlCode.SCAN_NEXT_TRACK]]),
# Encoder button ---
(0x000000, '', [])
]
}

View file

@ -8,16 +8,16 @@ from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values
# NOTE: There appears to be some delay when bringing up the command screen.
DELAY_AFTER_SLASH = 0.80 # required so minecraft has time to bring up command screen
DELAY_AFTER_SLASH = 0.80 # required so minecraft has time to bring up command screen
DELAY_BEFORE_RETURN = 0.10
# NOTE: On PC, characters are sometimes lost due to lag. No simple fix for
# lost keystrokes is known. However, the commands do work most of the time.
app = { # REQUIRED dict, must be named 'app'
'name' : 'Minecraft (/msg)', # Application name
'macros' : [ # List of button macros...
app = { # REQUIRED dict, must be named 'app'
'name' : 'Minecraft (/msg)', # Application name
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x000020, 'list', [
@ -33,13 +33,13 @@ app = { # REQUIRED dict, must be named 'app'
'list',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
# 2nd row ----------
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
# 3rd row ----------
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
# 4th row ----------
(0x101010, 'bed', [
'/', DELAY_AFTER_SLASH,

View file

@ -8,32 +8,32 @@ from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values
# See https://minecraft.fandom.com/wiki/Effect
DELAY_AFTER_SLASH = 0.80 # required so minecraft has time to bring up command screen
DELAY_AFTER_SLASH = 0.80 # required so minecraft has time to bring up command screen
DELAY_BEFORE_RETURN = 0.10 # give minecraft time to show all the keys pressed...
app = { # REQUIRED dict, must be named 'app'
'name' : 'Minecraft PE (effect)', # Application name
app = { # REQUIRED dict, must be named 'app'
'name' : 'Minecraft PE (effect)', # Application name
#
# /effect <player: target> <effect: Effect>
# [seconds: int] [amplifier: int] [hideParticles: Boolean]
#
'macros' : [ # List of button macros...
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x002000, 'speed', [
(0x002000, 'speed', [
'/', DELAY_AFTER_SLASH,
'effect @s speed 999999999 1 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
(0x002000, 'str', [
(0x002000, 'str', [
'/', DELAY_AFTER_SLASH,
'effect @s strength 999999999 1 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
(0x002000, 'haste', [
(0x002000, 'haste', [
'/', DELAY_AFTER_SLASH,
'effect @s haste 999999999 1 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
# 2nd row ----------
(0x002000, 'jump', [
(0x002000, 'jump', [
'/', DELAY_AFTER_SLASH,
'effect @s jump_boost 999999999 1 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
@ -41,7 +41,7 @@ app = { # REQUIRED dict, must be named 'app'
'/', DELAY_AFTER_SLASH,
'effect @s water_breathing 999999999 0 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
(0x202020, 'darkv', [
(0x202020, 'darkv', [
'/', DELAY_AFTER_SLASH,
'effect @s night_vision 999999999 0 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
@ -50,7 +50,7 @@ app = { # REQUIRED dict, must be named 'app'
'/', DELAY_AFTER_SLASH,
'effect @s health_boost 999999999 4 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
(0x300000, 'regen', [
(0x300000, 'regen', [
'/', DELAY_AFTER_SLASH,
'effect @s regeneration 999999999 4 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
@ -63,7 +63,7 @@ app = { # REQUIRED dict, must be named 'app'
'/', DELAY_AFTER_SLASH,
'effect @s resistance 999999999 3 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),
(0x101010, 'invis', [
(0x101010, 'invis', [
'/', DELAY_AFTER_SLASH,
'effect @s invisibility 999999999 0 true',
DELAY_BEFORE_RETURN, Keycode.RETURN, -Keycode.RETURN]),

View file

@ -3,11 +3,9 @@
# Note: Must enable "full keyboad gameplay" to equip armor automatically.
# This is found under "settings", then "keyboard and mouse".
# NOTE: There is a line length limit (? ~100 char ?). Exceeding that limit appears
# to result in silent failure. Therefore, the key sequences are split
# across multiple lines.
# NOTE: There is a line length limit (? ~100 char ?). Exceeding that limit
# appears to result in silent failure. Therefore, the key sequences are
# split across multiple lines.
from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values
@ -21,7 +19,7 @@ from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values
# macro files before attempting to adjust settings in this one.
DELAY_AFTER_COMMAND = 0.75
DELAY_AFTER_SLASH = 0.80 # required so minecraft has time to bring up command screen
DELAY_AFTER_SLASH = 0.80 # required so minecraft has time to bring up command screen
DELAY_BEFORE_RETURN = 0.10 # give minecraft time to show all the keys pressed...

View file

@ -0,0 +1,41 @@
# MACROPAD Hotkeys example: Mouse control
# The syntax for Mouse macros is highly peculiar, in order to maintain
# backward compatibility with the original keycode-only macro files.
# The third item for each macro is a list in brackets, and each value within
# is normally an integer (Keycode), float (delay) or string (typed literally).
# Consumer Control codes were added as list-within-list, and then mouse
# further complicates this by adding dicts-within-list. Each mouse-related
# dict can have any mix of keys 'buttons' w/integer mask of button values
# (positive to press, negative to release), 'x' w/horizontal motion,
# 'y' w/vertical and 'wheel' with scrollwheel motion.
# To reference Mouse constants, import Mouse like so...
from adafruit_hid.mouse import Mouse
# You can still import Keycode and/or ConsumerControl as well if a macro file
# mixes types! See other macro files for typical Keycode examples.
app = { # REQUIRED dict, must be named 'app'
'name' : 'Mouse', # Application name
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x200000, 'L', [{'buttons':Mouse.LEFT_BUTTON}]),
(0x202000, 'M', [{'buttons':Mouse.MIDDLE_BUTTON}]),
(0x002000, 'R', [{'buttons':Mouse.RIGHT_BUTTON}]),
# 2nd row ----------
(0x000000, '', []),
(0x202020, 'Up', [{'y':-10}]),
(0x000000, '', []),
# 3rd row ----------
(0x202020, 'Left', [{'x':-10}]),
(0x000000, '', []),
(0x202020, 'Right', [{'x':10}]),
# 4th row ----------
(0x000000, '', []),
(0x202020, 'Down', [{'y':10}]),
(0x000000, '', []),
# Encoder button ---
(0x000000, '', [])
]
}

View file

@ -2,9 +2,9 @@
from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values
app = { # REQUIRED dict, must be named 'app'
app = { # REQUIRED dict, must be named 'app'
'name' : 'Numpad', # Application name
'macros' : [ # List of button macros...
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x202000, '7', ['7']),

View file

@ -0,0 +1,39 @@
# MACROPAD Hotkeys example: Tones
# The syntax for Tones in macros is highly peculiar, in order to maintain
# backward compatibility with the original keycode-only macro files.
# The third item for each macro is a list in brackets, and each value within
# is normally an integer (Keycode), float (delay) or string (typed literally).
# Consumer Control codes were added as list-within-list, and then mouse and
# tone further complicate this by adding dicts-within-list. Each tone-related
# item is the key 'tone' with either an integer frequency value, or 0 to stop
# the tone mid-macro (tone is also stopped when key is released).
# Helpful: https://en.wikipedia.org/wiki/Piano_key_frequencies
# This example ONLY shows tones (and delays), but really they can be mixed
# with other elements (keys, codes, mouse) to provide auditory feedback.
app = { # REQUIRED dict, must be named 'app'
'name' : 'Tones', # Application name
'macros' : [ # List of button macros...
# COLOR LABEL KEY SEQUENCE
# 1st row ----------
(0x200000, 'C3', [{'tone':131}]),
(0x202000, 'C4', [{'tone':262}]),
(0x002000, 'C5', [{'tone':523}]),
# 2nd row ----------
(0x000020, 'Rising', [{'tone':131}, 0.2, {'tone':262}, 0.2, {'tone':523}]),
(0x000000, '', []),
(0x000020, 'Falling', [{'tone':523}, 0.2, {'tone':262}, 0.2, {'tone':131}]),
# 3rd row ----------
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
# 4th row ----------
(0x000000, '', []),
(0x000000, '', []),
(0x000000, '', []),
# Encoder button ---
(0x000000, '', [])
]
}