aboutsummaryrefslogtreecommitdiff
path: root/lib/python/qmk
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python/qmk')
-rw-r--r--lib/python/qmk/__init__.py0
-rw-r--r--lib/python/qmk/cli/compile/__init__.py0
-rwxr-xr-xlib/python/qmk/cli/compile/json.py44
-rwxr-xr-xlib/python/qmk/cli/doctor.py47
-rwxr-xr-xlib/python/qmk/cli/hello.py13
-rw-r--r--lib/python/qmk/cli/json/__init__.py0
-rwxr-xr-xlib/python/qmk/cli/json/keymap.py54
-rw-r--r--lib/python/qmk/errors.py6
-rw-r--r--lib/python/qmk/keymap.py100
-rw-r--r--lib/python/qmk/path.py32
10 files changed, 296 insertions, 0 deletions
diff --git a/lib/python/qmk/__init__.py b/lib/python/qmk/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/lib/python/qmk/__init__.py
diff --git a/lib/python/qmk/cli/compile/__init__.py b/lib/python/qmk/cli/compile/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/lib/python/qmk/cli/compile/__init__.py
diff --git a/lib/python/qmk/cli/compile/json.py b/lib/python/qmk/cli/compile/json.py
new file mode 100755
index 000000000..89c16b206
--- /dev/null
+++ b/lib/python/qmk/cli/compile/json.py
@@ -0,0 +1,44 @@
1"""Create a keymap directory from a configurator export.
2"""
3import json
4import os
5import sys
6import subprocess
7
8from milc import cli
9
10import qmk.keymap
11import qmk.path
12
13
14@cli.argument('filename', help='Configurator JSON export')
15@cli.entrypoint('Compile a QMK Configurator export.')
16def main(cli):
17 """Compile a QMK Configurator export.
18
19 This command creates a new keymap from a configurator export, overwriting an existing keymap if one exists.
20
21 FIXME(skullydazed): add code to check and warn if the keymap already exists
22 """
23 # Error checking
24 if cli.args.filename == ('-'):
25 cli.log.error('Reading from STDIN is not (yet) supported.')
26 exit(1)
27 if not os.path.exists(qmk.path.normpath(cli.args.filename)):
28 cli.log.error('JSON file does not exist!')
29 exit(1)
30
31 # Parse the configurator json
32 with open(qmk.path.normpath(cli.args.filename), 'r') as fd:
33 user_keymap = json.load(fd)
34
35 # Generate the keymap
36 keymap_path = qmk.path.keymap(user_keymap['keyboard'])
37 cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path)
38 qmk.keymap.write(user_keymap['keyboard'], user_keymap['keymap'], user_keymap['layout'], user_keymap['layers'])
39 cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
40
41 # Compile the keymap
42 command = ['make', ':'.join((user_keymap['keyboard'], user_keymap['keymap']))]
43 cli.log.info('Compiling keymap with {fg_cyan}%s\n\n', ' '.join(command))
44 subprocess.run(command)
diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py
new file mode 100755
index 000000000..9ce765a4b
--- /dev/null
+++ b/lib/python/qmk/cli/doctor.py
@@ -0,0 +1,47 @@
1"""QMK Python Doctor
2
3Check up for QMK environment.
4"""
5import shutil
6import platform
7import os
8
9from milc import cli
10
11
12@cli.entrypoint('Basic QMK environment checks')
13def main(cli):
14 """Basic QMK environment checks.
15
16 This is currently very simple, it just checks that all the expected binaries are on your system.
17
18 TODO(unclaimed):
19 * [ ] Run the binaries to make sure they work
20 * [ ] Compile a trivial program with each compiler
21 * [ ] Check for udev entries on linux
22 """
23
24 binaries = ['dfu-programmer', 'avrdude', 'dfu-util', 'avr-gcc', 'arm-none-eabi-gcc']
25
26 cli.log.info('QMK Doctor is Checking your environment')
27
28 ok = True
29 for binary in binaries:
30 res = shutil.which(binary)
31 if res is None:
32 cli.log.error('{fg_red}QMK can\'t find ' + binary + ' in your path')
33 ok = False
34
35 OS = platform.system()
36 if OS == "Darwin":
37 cli.log.info("Detected {fg_cyan}macOS")
38 elif OS == "Linux":
39 cli.log.info("Detected {fg_cyan}linux")
40 test = 'systemctl list-unit-files | grep enabled | grep -i ModemManager'
41 if os.system(test) == 0:
42 cli.log.warn("{bg_yellow}Detected modem manager. Please disable it if you are using Pro Micros")
43 else:
44 cli.log.info("Assuming {fg_cyan}Windows")
45
46 if ok:
47 cli.log.info('{fg_green}QMK is ready to go')
diff --git a/lib/python/qmk/cli/hello.py b/lib/python/qmk/cli/hello.py
new file mode 100755
index 000000000..bc0cb6de1
--- /dev/null
+++ b/lib/python/qmk/cli/hello.py
@@ -0,0 +1,13 @@
1"""QMK Python Hello World
2
3This is an example QMK CLI script.
4"""
5from milc import cli
6
7
8@cli.argument('-n', '--name', default='World', help='Name to greet.')
9@cli.entrypoint('QMK Hello World.')
10def main(cli):
11 """Log a friendly greeting.
12 """
13 cli.log.info('Hello, %s!', cli.config.general.name)
diff --git a/lib/python/qmk/cli/json/__init__.py b/lib/python/qmk/cli/json/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/lib/python/qmk/cli/json/__init__.py
diff --git a/lib/python/qmk/cli/json/keymap.py b/lib/python/qmk/cli/json/keymap.py
new file mode 100755
index 000000000..35fc8f9c0
--- /dev/null
+++ b/lib/python/qmk/cli/json/keymap.py
@@ -0,0 +1,54 @@
1"""Generate a keymap.c from a configurator export.
2"""
3import json
4import os
5import sys
6
7from milc import cli
8
9import qmk.keymap
10
11
12@cli.argument('-o', '--output', help='File to write to')
13@cli.argument('filename', help='Configurator JSON file')
14@cli.entrypoint('Create a keymap.c from a QMK Configurator export.')
15def main(cli):
16 """Generate a keymap.c from a configurator export.
17
18 This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided.
19 """
20 # Error checking
21 if cli.args.filename == ('-'):
22 cli.log.error('Reading from STDIN is not (yet) supported.')
23 cli.print_usage()
24 exit(1)
25 if not os.path.exists(qmk.path.normpath(cli.args.filename)):
26 cli.log.error('JSON file does not exist!')
27 cli.print_usage()
28 exit(1)
29
30 # Environment processing
31 if cli.args.output == ('-'):
32 cli.args.output = None
33
34 # Parse the configurator json
35 with open(qmk.path.normpath(cli.args.filename), 'r') as fd:
36 user_keymap = json.load(fd)
37
38 # Generate the keymap
39 keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
40
41 if cli.args.output:
42 output_dir = os.path.dirname(cli.args.output)
43
44 if not os.path.exists(output_dir):
45 os.makedirs(output_dir)
46
47 output_file = qmk.path.normpath(cli.args.output)
48 with open(output_file, 'w') as keymap_fd:
49 keymap_fd.write(keymap_c)
50
51 cli.log.info('Wrote keymap to %s.', cli.args.output)
52
53 else:
54 print(keymap_c)
diff --git a/lib/python/qmk/errors.py b/lib/python/qmk/errors.py
new file mode 100644
index 000000000..f9bf5b9af
--- /dev/null
+++ b/lib/python/qmk/errors.py
@@ -0,0 +1,6 @@
1class NoSuchKeyboardError(Exception):
2 """Raised when we can't find a keyboard/keymap directory.
3 """
4
5 def __init__(self, message):
6 self.message = message
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
new file mode 100644
index 000000000..6eccab788
--- /dev/null
+++ b/lib/python/qmk/keymap.py
@@ -0,0 +1,100 @@
1"""Functions that help you work with QMK keymaps.
2"""
3import json
4import logging
5import os
6from traceback import format_exc
7
8import qmk.path
9from qmk.errors import NoSuchKeyboardError
10
11# The `keymap.c` template to use when a keyboard doesn't have its own
12DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
13
14/* THIS FILE WAS GENERATED!
15 *
16 * This file was generated by qmk-compile-json. You may or may not want to
17 * edit it directly.
18 */
19
20const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
21__KEYMAP_GOES_HERE__
22};
23"""
24
25
26def template(keyboard):
27 """Returns the `keymap.c` template for a keyboard.
28
29 If a template exists in `keyboards/<keyboard>/templates/keymap.c` that
30 text will be used instead of `DEFAULT_KEYMAP_C`.
31
32 Args:
33 keyboard
34 The keyboard to return a template for.
35 """
36 template_name = 'keyboards/%s/templates/keymap.c' % keyboard
37
38 if os.path.exists(template_name):
39 with open(template_name, 'r') as fd:
40 return fd.read()
41
42 return DEFAULT_KEYMAP_C
43
44
45def generate(keyboard, layout, layers):
46 """Returns a keymap.c for the specified keyboard, layout, and layers.
47
48 Args:
49 keyboard
50 The name of the keyboard
51
52 layout
53 The LAYOUT macro this keymap uses.
54
55 layers
56 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
57 """
58 layer_txt = []
59 for layer_num, layer in enumerate(layers):
60 if layer_num != 0:
61 layer_txt[-1] = layer_txt[-1] + ','
62 layer_keys = ', '.join(layer)
63 layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
64
65 keymap = '\n'.join(layer_txt)
66 keymap_c = template(keyboard, keymap)
67
68 return keymap_c.replace('__KEYMAP_GOES_HERE__', keymap)
69
70
71def write(keyboard, keymap, layout, layers):
72 """Generate the `keymap.c` and write it to disk.
73
74 Returns the filename written to.
75
76 Args:
77 keyboard
78 The name of the keyboard
79
80 keymap
81 The name of the keymap
82
83 layout
84 The LAYOUT macro this keymap uses.
85
86 layers
87 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
88 """
89 keymap_c = generate(keyboard, layout, layers)
90 keymap_path = qmk.path.keymap(keyboard)
91 keymap_dir = os.path.join(keymap_path, keymap)
92 keymap_file = os.path.join(keymap_dir, 'keymap.c')
93
94 if not os.path.exists(keymap_dir):
95 os.makedirs(keymap_dir)
96
97 with open(keymap_file, 'w') as keymap_fd:
98 keymap_fd.write(keymap_c)
99
100 return keymap_file
diff --git a/lib/python/qmk/path.py b/lib/python/qmk/path.py
new file mode 100644
index 000000000..f2a8346a5
--- /dev/null
+++ b/lib/python/qmk/path.py
@@ -0,0 +1,32 @@
1"""Functions that help us work with files and folders.
2"""
3import os
4
5
6def keymap(keyboard):
7 """Locate the correct directory for storing a keymap.
8
9 Args:
10 keyboard
11 The name of the keyboard. Example: clueboard/66/rev3
12 """
13 for directory in ['.', '..', '../..', '../../..', '../../../..', '../../../../..']:
14 basepath = os.path.normpath(os.path.join('keyboards', keyboard, directory, 'keymaps'))
15
16 if os.path.exists(basepath):
17 return basepath
18
19 logging.error('Could not find keymaps directory!')
20 raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard)
21
22
23def normpath(path):
24 """Returns the fully resolved absolute path to a file.
25
26 This function will return the absolute path to a file as seen from the
27 directory the script was called from.
28 """
29 if path and path[0] == '/':
30 return os.path.normpath(path)
31
32 return os.path.normpath(os.path.join(os.environ['ORIG_CWD'], path))