1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
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
import os
import time
import displayio
import terminalio
import usb_cdc
import json
#from adafruit_display_shapes.rect import Rect
#from adafruit_display_text import label
from adafruit_macropad import MacroPad
from supervisor import runtime
from actions import Action
import skeleton
import menu
from sleep_mode import Power
# CONFIGURABLES ------------------------
MACRO_FOLDER = '/macros'
# INITIALIZATION -----------------------
# Do not reload the application when the files are changed
runtime.autoreload = False
macropad = MacroPad()
macropad.display.auto_refresh = False
macropad.pixels.auto_write = False
power = Power(macropad)
Action().set_macropad(macropad)
# 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.startswith('._'):
continue
if filename.endswith('.py'):
module_name = filename[:-3]
elif filename.endswith('.mpy'):
module_name = filename[:-4]
else:
continue
try:
module = __import__(MACRO_FOLDER + '/' + module_name)
apps.append(module.configuration)
except (SyntaxError, ImportError, AttributeError, KeyError, NameError,
IndexError, TypeError) as err:
print("ERROR in", filename)
import traceback
traceback.print_exception(err, err, err.__traceback__)
if not apps:
group[13].text = 'NO MACRO FILES FOUND'
macropad.display.refresh()
while True:
pass
app_index = 0
def run_application(app):
# Read encoder position. If it's changed, declare it as a button.
position = app.macropad.encoder
if position > app.position:
app.position = position
key_number = 12
pressed = True
elif position < app.position:
app.position = position
key_number = 13
pressed = True
else:
event = app.macropad.keys.events.get()
if not event:
app.tick()
power.tick()
return # No key events
key_number = event.key_number
pressed = event.pressed
power.execute_action(key_number, pressed or False)
return app.execute_action(key_number, pressed or False)
def switch_layout(configuration):
global app
app = skeleton.Handler(macropad, configuration)
power.set_configuration(configuration)
app.start()
def search_and_switch(layout):
for local_app in apps:
if local_app.title == layout and app is not local_app:
switch_layout(local_app)
break
def stack(layout):
for configuration in apps:
if configuration.title == layout and app is not configuration:
app.stack(configuration)
app.start()
break
def send_layout(_any):
if usb_cdc.data.connected:
usb_cdc.data.write(bytes("%s\n" % app.name, "utf-8"))
actions = {
"layout" : search_and_switch,
"stack" : stack,
"get_layout" : send_layout,
}
switch_layout(apps[app_index])
# MAIN LOOP ----------------------------
while True:
# First read from the serial
# If we have an application name from the command line, switch to this app.
if usb_cdc.data.connected:
while usb_cdc.data.in_waiting != 0:
line = usb_cdc.data.readline(usb_cdc.data.in_waiting).strip()
try:
commands = json.loads(line)
for command in commands:
for key, value in command.items():
actions[key](value)
continue
except ValueError:
print(line)
pass
# Handle encoder button. If state has changed, and if there's a
# corresponding macro, set up variables to act on this just like
# the keypad keys, as if it were a 13th key/macro.
app.macropad.encoder_switch_debounced.update()
encoder_switch = app.macropad.encoder_switch_debounced.pressed
# On the encoder button, switch to the next mode
if encoder_switch:
config = menu.build_application(apps)
menu_app = skeleton.Handler(macropad, config)
menu_app.start()
while True:
configuration = run_application(menu_app)
if configuration is None: continue
switch_layout(configuration)
break
if configuration.is_layout:
send_layout(None)
run_application(app)
continue
|