aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach White <skullydazed@gmail.com>2021-08-16 15:33:30 -0700
committerGitHub <noreply@github.com>2021-08-16 23:33:30 +0100
commit8d9bfdc25437bb401985ba93b47edae2126e7fac (patch)
tree2439e7adde0bafd6af9a403c92c1a89384c3f6ea
parentfac717c11cfa27780f2f9098383673784174141a (diff)
downloadqmk_firmware-8d9bfdc25437bb401985ba93b47edae2126e7fac.tar.gz
qmk_firmware-8d9bfdc25437bb401985ba93b47edae2126e7fac.zip
Add a lot more data to info.json (#13366)
* add some split data to info.json * add tags * add half of config_options.md to info.json * add support for designating master split * sort out split transport and primary * fix bad data in UNUSED_PINS * fixup custom transport * wip * allow for setting split right half keyboard matrix * add SPLIT_USB_DETECT * minor cleanup * fix an erroneous message * rework split.usb_detect * adding missing rgblight vars to info.json * add mouse_key to info.json * add all remaining options from docs/config_options.md * fix audio voices * qmk info: Change text output to use dotted notation * tweak layout output * resolve alias names * break out some functions to make flake8 happy * add a field for bootloader instructions * qmk generate-info-json: add a write-to-file argument Adds an argument that instructs qmk generate-info-json to write the output to a file instead of just to the terminal. * -arg_only, +action Because it was never my intention that one would have to specify a value for the argument that enables writing the file. * Bring qmk generate-info-json inline with other generate commands * pytest fixup * fix esca/getawayvan * fix data driven errors for bpiphany converters * features.force_nkro -> usb.force_nkro * split.primary->split.main * fix esca/getawayvan_f042 * fix the bpiphany converters for real * fix bpiphany/tiger_lily * Apply suggestions from code review Co-authored-by: Nick Brassel <nick@tzarc.org> * fix generate-api errors * fix matrix pin extraction for split boards * fix ploopyco/trackball_nano/rev1_001 Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com> Co-authored-by: Nick Brassel <nick@tzarc.org>
-rw-r--r--data/mappings/info_config.json56
-rw-r--r--data/mappings/info_rules.json14
-rw-r--r--data/schemas/keyboard.jsonschema220
-rw-r--r--keyboards/bpiphany/frosty_flake/config.h5
-rw-r--r--keyboards/bpiphany/tiger_lily/config.h5
-rw-r--r--keyboards/cest73/tkm/config.h2
-rw-r--r--keyboards/esca/getawayvan/config.h1
-rw-r--r--keyboards/esca/getawayvan_f042/config.h1
-rw-r--r--keyboards/handwired/tennie/config.h2
-rw-r--r--keyboards/mesa/mesa_tkl/config.h2
-rw-r--r--keyboards/ploopyco/trackball_nano/rev1_001/config.h2
-rwxr-xr-xlib/python/qmk/cli/generate/config_h.py140
-rwxr-xr-xlib/python/qmk/cli/generate/info_json.py40
-rwxr-xr-xlib/python/qmk/cli/generate/rules_mk.py11
-rwxr-xr-xlib/python/qmk/cli/info.py53
-rw-r--r--lib/python/qmk/info.py184
-rw-r--r--lib/python/qmk/json_schema.py15
17 files changed, 654 insertions, 99 deletions
diff --git a/data/mappings/info_config.json b/data/mappings/info_config.json
index b949b1332..18477f912 100644
--- a/data/mappings/info_config.json
+++ b/data/mappings/info_config.json
@@ -1,21 +1,46 @@
1# This file maps keys between `config.h` and `info.json`. It is used by QMK 1# This file maps keys between `config.h` and `info.json`. It is used by QMK
2# to correctly and consistently map back and forth between the two systems. 2# to correctly and consistently map back and forth between the two systems.
3{ 3{
4 # Format: 4 # Format:
5 # <config.h key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]} 5 # <config.h key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]}
6 # value_type: one of "array", "array.int", "int", "hex", "list", "mapping" 6 # value_type: one of "array", "array.int", "bool", "int", "hex", "list", "mapping"
7 # to_json: Default `true`. Set to `false` to exclude this mapping from info.json 7 # to_json: Default `true`. Set to `false` to exclude this mapping from info.json
8 # to_c: Default `true`. Set to `false` to exclude this mapping from config.h 8 # to_c: Default `true`. Set to `false` to exclude this mapping from config.h
9 # warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places 9 # warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places
10 "DEBOUNCE": {"info_key": "debounce", "value_type": "int"} 10 "AUDIO_VOICES": {"info_key": "audio.voices", "value_type": "bool"},
11 "BACKLIGHT_BREATHING": {"info_key": "backlight.breathing", "value_type": "bool"},
12 "BREATHING_PERIOD": {"info_key": "backlight.breathing_period", "value_type": "int"},
13 "BACKLIGHT_PIN": {"info_key": "backlight.pin"},
14 "COMBO_COUNT": {"info_key": "combo.count", "value_type": "int"},
15 "COMBO_TERM": {"info_key": "combo.term", "value_type": "int"},
16 "DEBOUNCE": {"info_key": "debounce", "value_type": "int"},
11 "DEVICE_VER": {"info_key": "usb.device_ver", "value_type": "hex"}, 17 "DEVICE_VER": {"info_key": "usb.device_ver", "value_type": "hex"},
12 "DESCRIPTION": {"info_key": "keyboard_folder", "to_json": false}, 18 "DESCRIPTION": {"info_key": "keyboard_folder", "to_json": false},
13 "DIODE_DIRECTION": {"info_key": "diode_direction"}, 19 "DIODE_DIRECTION": {"info_key": "diode_direction"},
20 "FORCE_NKRO": {"info_key": "usb.force_nkro", "value_type": "bool"},
21 "IGNORE_MOD_TAP_INTERRUPT": {"info_key": "tapping.ignore_mod_tap_interrupt", "value_type": "bool"},
22 "IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "tapping.ignore_mod_tap_interrupt_per_key", "value_type": "bool"},
14 "LAYOUTS": {"info_key": "layout_aliases", "value_type": "mapping"}, 23 "LAYOUTS": {"info_key": "layout_aliases", "value_type": "mapping"},
24 "LEADER_PER_KEY_TIMING": {"info_key": "leader_key.timing", "value_type": "bool"},
25 "LEADER_KEY_STRICT_KEY_PROCESSING": {"info_key": "leader_key.strict_processing", "value_type": "bool"},
26 "LEADER_TIMEOUT": {"info_key": "leader_key.timeout", "value_type": "int"},
15 "LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"}, 27 "LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"},
16 "LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"}, 28 "LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"},
17 "LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"}, 29 "LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"},
18 "MANUFACTURER": {"info_key": "manufacturer"}, 30 "MANUFACTURER": {"info_key": "manufacturer"},
31 "MATRIX_HAS_GHOST": {"info_key": "matrix_pins.ghost", "value_type": "bool"},
32 "MATRIX_IO_DELAY": {"info_key": "matrix_pins.io_delay", "value_type": "int"},
33 "MOUSEKEY_DELAY": {"info_key": "mousekey.delay", "value_type": "int"},
34 "MOUSEKEY_INTERVAL": {"info_key": "mousekey.interval", "value_type": "int"},
35 "MOUSEKEY_MAX_SPEED": {"info_key": "mousekey.max_speed", "value_type": "int"},
36 "MOUSEKEY_TIME_TO_MAX": {"info_key": "mousekey.time_to_max", "value_type": "int"},
37 "MOUSEKEY_WHEEL_DELAY": {"info_key": "mousekey.wheel_delay", "value_type": "int"},
38 "ONESHOT_TIMEOUT": {"info_key": "oneshot.timeout", "value_type": "int"},
39 "ONESHOT_TAP_TOGGLE": {"info_key": "oneshot.tap_toggle", "value_type": "int"},
40 "PERMISSIVE_HOLD": {"info_key": "tapping.permissive_hold", "value_type": "bool"},
41 "PERMISSIVE_HOLD_PER_KEY": {"info_key": "tapping.permissive_hold_per_key", "value_type": "bool"},
42 "RETRO_TAPPING": {"info_key": "tapping.retro", "value_type": "bool"},
43 "RETRO_TAPPING_PER_KEY": {"info_key": "tapping.retro_per_key", "value_type": "bool"},
19 "RGB_DI_PIN": {"info_key": "rgblight.pin"}, 44 "RGB_DI_PIN": {"info_key": "rgblight.pin"},
20 "RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"}, 45 "RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"},
21 "RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.int"}, 46 "RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.int"},
@@ -30,17 +55,40 @@
30 "RGBLIGHT_EFFECT_SNAKE": {"info_key": "rgblight.animations.snake", "value_type": "bool"}, 55 "RGBLIGHT_EFFECT_SNAKE": {"info_key": "rgblight.animations.snake", "value_type": "bool"},
31 "RGBLIGHT_EFFECT_STATIC_GRADIENT": {"info_key": "rgblight.animations.static_gradient", "value_type": "bool"}, 56 "RGBLIGHT_EFFECT_STATIC_GRADIENT": {"info_key": "rgblight.animations.static_gradient", "value_type": "bool"},
32 "RGBLIGHT_EFFECT_TWINKLE": {"info_key": "rgblight.animations.twinkle"}, 57 "RGBLIGHT_EFFECT_TWINKLE": {"info_key": "rgblight.animations.twinkle"},
58 "RGBLIGHT_LAYER_BLINK": {"info_key": "rgblight.layers.blink", "value_type": "bool"},
59 "RGBLIGHT_LAYERS": {"info_key": "rgblight.layers.enabled", "value_type": "bool"},
60 "RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF": {"info_key": "rgblight.layers.override_rgb", "value_type": "bool"},
33 "RGBLIGHT_LIMIT_VAL": {"info_key": "rgblight.max_brightness", "value_type": "int"}, 61 "RGBLIGHT_LIMIT_VAL": {"info_key": "rgblight.max_brightness", "value_type": "int"},
62 "RGBLIGHT_MAX_LAYERS": {"info_key": "rgblight.layers.max", "value_type": "int"},
34 "RGBLIGHT_HUE_STEP": {"info_key": "rgblight.hue_steps", "value_type": "int"}, 63 "RGBLIGHT_HUE_STEP": {"info_key": "rgblight.hue_steps", "value_type": "int"},
35 "RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_steps", "value_type": "int"}, 64 "RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_steps", "value_type": "int"},
36 "RGBLIGHT_VAL_STEP": {"info_key": "rgblight.brightness_steps", "value_type": "int"}, 65 "RGBLIGHT_VAL_STEP": {"info_key": "rgblight.brightness_steps", "value_type": "int"},
37 "RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"}, 66 "RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"},
38 "RGBLIGHT_SPLIT": {"info_key": "rgblight.split", "value_type": "bool"}, 67 "RGBLIGHT_SPLIT": {"info_key": "rgblight.split", "value_type": "bool"},
68 "RGBW": {"info_key": "rgblight.rgbw", "value_type": "bool"},
39 "PRODUCT": {"info_key": "keyboard_folder", "to_json": false}, 69 "PRODUCT": {"info_key": "keyboard_folder", "to_json": false},
40 "PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex"}, 70 "PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex"},
41 "VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex"}, 71 "VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex"},
42 "QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"}, 72 "QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"},
43 "QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"}, 73 "QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"},
74 "QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int"},
44 "QMK_LED": {"info_key": "qmk_lufa_bootloader.led"}, 75 "QMK_LED": {"info_key": "qmk_lufa_bootloader.led"},
45 "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"} 76 "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"},
77 "SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"},
78 "SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"},
79 "SPLIT_USB_DETECT": {"info_key": "split.usb_detect.enabled", "value_type": "bool"},
80 "SPLIT_USB_TIMEOUT": {"info_key": "split.usb_detect.timeout", "value_type": "int"},
81 "SPLIT_USB_TIMEOUT_POLL": {"info_key": "split.usb_detect.polling_interval", "value_type": "int"},
82 "SOFT_SERIAL_PIN": {"info_key": "split.soft_serial_pin"},
83 "SOFT_SERIAL_SPEED": {"info_key": "split.soft_serial_speed"},
84 "TAP_CODE_DELAY": {"info_key": "qmk.tap_keycode_delay", "value_type": "int"},
85 "TAP_HOLD_CAPS_DELAY": {"info_key": "qmk.tap_capslock_delay", "value_type": "int"},
86 "TAPPING_FORCE_HOLD": {"info_key": "tapping.force_hold", "value_type": "bool"},
87 "TAPPING_FORCE_HOLD_PER_KEY": {"info_key": "tapping.force_hold_per_key", "value_type": "bool"},
88 "TAPPING_TERM": {"info_key": "tapping.term", "value_type": "int"},
89 "TAPPING_TERM_PER_KEY": {"info_key": "tapping.term_per_key", "value_type": "bool"},
90 "TAPPING_TOGGLE": {"info_key": "tapping.toggle", "value_type": "int"},
91 "USB_MAX_POWER_CONSUMPTION": {"info_key": "usb.max_power", "value_type": "int"},
92 "USB_POLLING_INTERVAL_MS": {"info_key": "usb.polling_interval", "value_type": "int"},
93 "USB_SUSPEND_WAKEUP_DELAY": {"info_key": "usb.suspend_wakeup_delay", "value_type": "int"},
46} 94}
diff --git a/data/mappings/info_rules.json b/data/mappings/info_rules.json
index 97f772c4d..aea67e04c 100644
--- a/data/mappings/info_rules.json
+++ b/data/mappings/info_rules.json
@@ -1,15 +1,25 @@
1# This file maps keys between `rules.mk` and `info.json`. It is used by QMK 1# This file maps keys between `rules.mk` and `info.json`. It is used by QMK
2# to correctly and consistently map back and forth between the two systems. 2# to correctly and consistently map back and forth between the two systems.
3{ 3{
4 # Format: 4 # Format:
5 # <rules.mk key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]} 5 # <rules.mk key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]}
6 # value_type: one of "array", "array.int", "int", "list", "hex", "mapping" 6 # value_type: one of "array", "array.int", "bool", "int", "list", "hex", "mapping"
7 # to_json: Default `true`. Set to `false` to exclude this mapping from info.json 7 # to_json: Default `true`. Set to `false` to exclude this mapping from info.json
8 # to_c: Default `true`. Set to `false` to exclude this mapping from rules.mk 8 # to_c: Default `true`. Set to `false` to exclude this mapping from rules.mk
9 # warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places 9 # warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places
10 "BOARD": {"info_key": "board"}, 10 "BOARD": {"info_key": "board"},
11 "BOOTLOADER": {"info_key": "bootloader", "warn_duplicate": false}, 11 "BOOTLOADER": {"info_key": "bootloader", "warn_duplicate": false},
12 "BLUETOOTH": {"info_key": "bluetooth.driver"},
13 "FIRMWARE_FORMAT": {"info_key": "build.firmware_format"},
14 "KEYBOARD_SHARED_EP": {"info_key": "usb.shared_endpoint.keyboard", "value_type": "bool"},
15 "MOUSE_SHARED_EP": {"info_key": "usb.shared_endpoint.mouse", "value_type": "bool"},
12 "LAYOUTS": {"info_key": "community_layouts", "value_type": "list"}, 16 "LAYOUTS": {"info_key": "community_layouts", "value_type": "list"},
13 "LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"}, 17 "LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"},
18 "LTO_ENABLE": {"info_key": "build.lto", "value_type": "bool"},
14 "MCU": {"info_key": "processor", "warn_duplicate": false}, 19 "MCU": {"info_key": "processor", "warn_duplicate": false},
20 "MOUSEKEY_ENABLE": {"info_key": "mouse_key.enabled", "value_type": "bool"},
21 "NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"},
22 "SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"},
23 "SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "value_type": "str", "to_c": false},
24 "WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"}
15} 25}
diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema
index 3a3fa4b73..78c9a8d36 100644
--- a/data/schemas/keyboard.jsonschema
+++ b/data/schemas/keyboard.jsonschema
@@ -15,6 +15,40 @@
15 "type": "string", 15 "type": "string",
16 "enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK66F18", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F407", "STM32F411", "STM32F446", "STM32G431", "STM32G474", "STM32L412", "STM32L422", "STM32L433", "STM32L443", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"] 16 "enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK66F18", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F407", "STM32F411", "STM32F446", "STM32G431", "STM32G474", "STM32L412", "STM32L422", "STM32L433", "STM32L443", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"]
17 }, 17 },
18 "audio": {
19 "type": "object",
20 "additionalProperties": false,
21 "properties": {
22 "pins": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
23 "voices": {"type": "boolean"}
24 }
25 },
26 "backlight": {
27 "type": "object",
28 "additionalProperties": false,
29 "properties": {
30 "breathing": {"type": "boolean"},
31 "breathing_period": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
32 "levels": {
33 "type": "number",
34 "min": 1,
35 "max": 31,
36 "multipleOf": 1
37 },
38 "pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}
39 }
40 },
41 "bluetooth": {
42 "type": "object",
43 "additionalProperties": false,
44 "properties": {
45 "driver": {
46 "type": "string",
47 "enum": ["AdafruitBLE", "RN42"]
48 },
49 "lto": {"type": "boolean"},
50 }
51 },
18 "board": { 52 "board": {
19 "type": "string", 53 "type": "string",
20 "minLength": 2, 54 "minLength": 2,
@@ -22,13 +56,39 @@
22 }, 56 },
23 "bootloader": { 57 "bootloader": {
24 "type": "string", 58 "type": "string",
25 "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "unknown", "USBasp", "tinyuf2"] 59 "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "unknown", "USBasp", "tinyuf2"],
60 },
61 "bootloader_instructions": {
62 "type": "string",
63 "description": "Instructions for putting the keyboard into a mode that allows for firmware flashing."
64 },
65 "build": {
66 "type": "object",
67 "additionalProperties": false,
68 "properties": {
69 "debounce_type": {
70 "type": "string",
71 "enum": ["custom", "eager_pk", "eager_pr", "sym_defer_pk", "sym_eager_pk"]
72 },
73 "firmware_format": {
74 "type": "string",
75 "enum": ["bin", "hex", "uf2"]
76 },
77 "lto": {"type": "boolean"},
78 }
26 }, 79 },
27 "diode_direction": { 80 "diode_direction": {
28 "type": "string", 81 "type": "string",
29 "enum": ["COL2ROW", "ROW2COL"] 82 "enum": ["COL2ROW", "ROW2COL"]
30 }, 83 },
31 "debounce": {"$ref": "qmk.definitions.v1#/unsigned_int"}, 84 "debounce": {"$ref": "qmk.definitions.v1#/unsigned_int"},
85 "combo": {
86 "type": "object",
87 "properties": {
88 "count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
89 "term": {"$ref": "qmk.definitions.v1#/unsigned_int"}
90 }
91 },
32 "community_layouts": { 92 "community_layouts": {
33 "type": "array", 93 "type": "array",
34 "items": {"$ref": "qmk.definitions.v1#/filename"} 94 "items": {"$ref": "qmk.definitions.v1#/filename"}
@@ -90,16 +150,47 @@
90 } 150 }
91 } 151 }
92 }, 152 },
153 "leader_key": {
154 "type": "object",
155 "properties": {
156 "timing": {"type": "boolean"},
157 "strict_processing": {"type": "boolean"},
158 "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
159 }
160 },
93 "matrix_pins": { 161 "matrix_pins": {
94 "type": "object", 162 "type": "object",
95 "additionalProperties": false, 163 "additionalProperties": false,
96 "properties": { 164 "properties": {
165 "custom": {"type": "boolean"},
166 "custom_lite": {"type": "boolean"},
167 "ghost": {"type": "boolean"},
168 "io_delay": {"$ref": "qmk.definitions.v1#/unsigned_int"},
97 "direct": { 169 "direct": {
98 "type": "array", 170 "type": "array",
99 "items": {$ref": "qmk.definitions.v1#/mcu_pin_array"} 171 "items": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
100 }, 172 },
101 "cols": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}, 173 "cols": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
102 "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"} 174 "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
175 "unused": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
176 }
177 },
178 "mouse_key": {
179 "type": "object",
180 "properties": {
181 "enabled": {"type": "boolean"},
182 "delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
183 "interval": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
184 "max_speed": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
185 "time_to_max": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
186 "wheel_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
187 }
188 },
189 "oneshot": {
190 "type": "object",
191 "properties": {
192 "tap_toggle": {"$ref": "qmk.definitions.v1#/unsigned_int"},
193 "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
103 } 194 }
104 }, 195 },
105 "rgblight": { 196 "rgblight": {
@@ -114,9 +205,25 @@
114 }, 205 },
115 "brightness_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, 206 "brightness_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
116 "hue_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, 207 "hue_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
208 "layers": {
209 "type": "object",
210 "additionalProperties": false,
211 "properties": {
212 "blink": {"type": "boolean"},
213 "enabled": {"type": "boolean"},
214 "max": {
215 "type": "number",
216 "min": 1,
217 "max": 32,
218 "multipleOf": 1
219 },
220 "override_rgb": {"type": "boolean"}
221 }
222 },
117 "led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"}, 223 "led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
118 "max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, 224 "max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
119 "pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}, 225 "pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
226 "rgbw": {"type": "boolean"},
120 "saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, 227 "saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
121 "sleep": {"type": "boolean"}, 228 "sleep": {"type": "boolean"},
122 "split": {"type": "boolean"}, 229 "split": {"type": "boolean"},
@@ -128,13 +235,118 @@
128 } 235 }
129 } 236 }
130 }, 237 },
238 "split": {
239 "type": "object",
240 "additionalProperties": false,
241 "properties": {
242 "enabled": {"type": "boolean"},
243 "matrix_grid": {
244 "type": "array",
245 "items": {"$ref": "qmk.definitions.v1#/mcu_pin"}
246 },
247 "matrix_pins": {
248 "type": "object",
249 "additionalProperties": false,
250 "properties": {
251 "right": {
252 "type": "object",
253 "additionalProperties": false,
254 "properties": {
255 "direct": {
256 "type": "array",
257 "items": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
258 },
259 "cols": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
260 "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
261 "unused": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
262 }
263 }
264 }
265 },
266 "main": {
267 "type": "string",
268 "enum": ["eeprom", "left", "matrix_grid", "pin", "right"]
269 },
270 "soft_serial_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
271 "soft_serial_speed": {
272 "type": "number",
273 "min": 0,
274 "max": 5,
275 "multipleOf": 1
276 },
277 "transport": {
278 "type": "object",
279 "additionalProperties": false,
280 "properties": {
281 "protocol": {
282 "type": "string",
283 "enum": ["custom", "i2c", "serial", "serial_usart"]
284 },
285 "sync_matrix_state": {"type": "boolean"},
286 "sync_modifiers": {"type": "boolean"}
287 }
288 },
289 "usb_detect": {
290 "type": "object",
291 "additionalProperties": false,
292 "properties": {
293 "enabled": {"type": "boolean"},
294 "polling_interval": {"$ref": "qmk.definitions.v1#/unsigned_int"},
295 "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
296 }
297 }
298 }
299 },
300 "tags": {
301 "type": "array",
302 "items": {"type": "string"}
303 },
304 "tapping": {
305 "type": "object",
306 "properties": {
307 "force_hold": {"type": "boolean"},
308 "force_hold_per_key": {"type": "boolean"},
309 "ignore_mod_tap_interrupt": {"type": "boolean"},
310 "ignore_mod_tap_interrupt_per_key": {"type": "boolean"},
311 "permissive_hold": {"type": "boolean"},
312 "permissive_hold_per_key": {"type": "boolean"},
313 "retro": {"type": "boolean"},
314 "retro_per_key": {"type": "boolean"},
315 "term": {"$ref": "qmk.definitions.v1#/unsigned_int"},
316 "term_per_key": {"type": "boolean"},
317 "toggle": {"$ref": "qmk.definitions.v1#/unsigned_int"},
318 }
319 },
131 "usb": { 320 "usb": {
132 "type": "object", 321 "type": "object",
133 "additionalProperties": false, 322 "additionalProperties": false,
134 "properties": { 323 "properties": {
135 "device_ver": {"$ref": "qmk.definitions.v1#/hex_number_4d"}, 324 "device_ver": {"$ref": "qmk.definitions.v1#/hex_number_4d"},
325 "force_nkro": {"type": "boolean"},
136 "pid": {"$ref": "qmk.definitions.v1#/hex_number_4d"}, 326 "pid": {"$ref": "qmk.definitions.v1#/hex_number_4d"},
137 "vid": {"$ref": "qmk.definitions.v1#/hex_number_4d"} 327 "vid": {"$ref": "qmk.definitions.v1#/hex_number_4d"},
328 "max_power": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
329 "no_startup_check": {"type": "boolean"},
330 "polling_interval": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
331 "shared_endpoint": {
332 "type": "object",
333 "additionalProperties": false,
334 "properties": {
335 "keyboard": {"type": "boolean"},
336 "mouse": {"type": "boolean"}
337 }
338 },
339 "suspend_wakeup_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
340 "wait_for": {"type": "boolean"},
341 }
342 },
343 "qmk": {
344 "type": "object",
345 "additionalProperties": false,
346 "properties": {
347 "keys_per_scan": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
348 "tap_keycode_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
349 "tap_capslock_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
138 } 350 }
139 }, 351 },
140 "qmk_lufa_bootloader": { 352 "qmk_lufa_bootloader": {
diff --git a/keyboards/bpiphany/frosty_flake/config.h b/keyboards/bpiphany/frosty_flake/config.h
index 344db5f0d..ae19184a6 100644
--- a/keyboards/bpiphany/frosty_flake/config.h
+++ b/keyboards/bpiphany/frosty_flake/config.h
@@ -40,7 +40,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
40 40
41/* 41/*
42 * Keyboard Matrix Assignments 42 * Keyboard Matrix Assignments
43 *
44 * MATRIX_ROW_PINS and MATRIX_COL_PINS aren't actually used and are included
45 * for data driven compatibility.
43 */ 46 */
47#define MATRIX_COL_PINS { B0, B3, B2, B1, B6, B4, B5, C7 }
48#define MATRIX_ROW_PINS { NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN }
44#define UNUSED_PINS { C0, C1, C2, C3, C4, D2, D7 } 49#define UNUSED_PINS { C0, C1, C2, C3, C4, D2, D7 }
45 50
46/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */ 51/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
diff --git a/keyboards/bpiphany/tiger_lily/config.h b/keyboards/bpiphany/tiger_lily/config.h
index a86cffd16..d3e2464be 100644
--- a/keyboards/bpiphany/tiger_lily/config.h
+++ b/keyboards/bpiphany/tiger_lily/config.h
@@ -40,7 +40,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
40 40
41/* 41/*
42 * Keyboard Matrix Assignments 42 * Keyboard Matrix Assignments
43 *
44 * MATRIX_ROW_PINS and MATRIX_COL_PINS aren't actually used and are included
45 * for data driven compatibility.
43 */ 46 */
47#define MATRIX_ROW_PINS { C2, B3, B4, B2, B1, C7, B6, B5 }
48#define MATRIX_COL_PINS { NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN }
44#define UNUSED_PINS { B0, C4, D3 } 49#define UNUSED_PINS { B0, C4, D3 }
45 50
46#define LED_NUM_LOCK_PIN C5 51#define LED_NUM_LOCK_PIN C5
diff --git a/keyboards/cest73/tkm/config.h b/keyboards/cest73/tkm/config.h
index deef979b2..e925f3971 100644
--- a/keyboards/cest73/tkm/config.h
+++ b/keyboards/cest73/tkm/config.h
@@ -34,7 +34,7 @@
34#define MATRIX_ROW_PINS { B0, B1, B2, B3, B4, B5, B6, B7, C6, C7, D0 } 34#define MATRIX_ROW_PINS { B0, B1, B2, B3, B4, B5, B6, B7, C6, C7, D0 }
35/* column handy ruler: c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 */ 35/* column handy ruler: c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 */
36#define MATRIX_COL_PINS { D1, D2, D3, D4, D5, D6, D7, F0, F1, F4 } 36#define MATRIX_COL_PINS { D1, D2, D3, D4, D5, D6, D7, F0, F1, F4 }
37#define UNUSED_PINS { AF } 37#define UNUSED_PINS { }
38 38
39//NOTE: if D6 pin shows any issues in exploatation the LED on the Teensy is to be removed 39//NOTE: if D6 pin shows any issues in exploatation the LED on the Teensy is to be removed
40 40
diff --git a/keyboards/esca/getawayvan/config.h b/keyboards/esca/getawayvan/config.h
index e9fbc6b40..3b718c583 100644
--- a/keyboards/esca/getawayvan/config.h
+++ b/keyboards/esca/getawayvan/config.h
@@ -35,7 +35,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
35#define MATRIX_ROW_PINS { A9, A8, A3, A5 } 35#define MATRIX_ROW_PINS { A9, A8, A3, A5 }
36#define DIODE_DIRECTION COL2ROW 36#define DIODE_DIRECTION COL2ROW
37 37
38#define BACKLIGHT_PIN
39#define BACKLIGHT_LEVELS 6 38#define BACKLIGHT_LEVELS 6
40 39
41#define GRAVE_ESC_CTRL_OVERRIDE 40#define GRAVE_ESC_CTRL_OVERRIDE
diff --git a/keyboards/esca/getawayvan_f042/config.h b/keyboards/esca/getawayvan_f042/config.h
index f83ec7434..8921e9db4 100644
--- a/keyboards/esca/getawayvan_f042/config.h
+++ b/keyboards/esca/getawayvan_f042/config.h
@@ -34,7 +34,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
34#define MATRIX_ROW_PINS { A9, A8, A3, A5 } 34#define MATRIX_ROW_PINS { A9, A8, A3, A5 }
35#define DIODE_DIRECTION COL2ROW 35#define DIODE_DIRECTION COL2ROW
36 36
37#define BACKLIGHT_PIN
38#define BACKLIGHT_LEVELS 6 37#define BACKLIGHT_LEVELS 6
39 38
40#define GRAVE_ESC_CTRL_OVERRIDE 39#define GRAVE_ESC_CTRL_OVERRIDE
diff --git a/keyboards/handwired/tennie/config.h b/keyboards/handwired/tennie/config.h
index fee1258df..b1bddac45 100644
--- a/keyboards/handwired/tennie/config.h
+++ b/keyboards/handwired/tennie/config.h
@@ -42,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
42*/ 42*/
43#define MATRIX_ROW_PINS { C6, D4, D0} 43#define MATRIX_ROW_PINS { C6, D4, D0}
44#define MATRIX_COL_PINS { D7, E6, B4, B5 } 44#define MATRIX_COL_PINS { D7, E6, B4, B5 }
45#define UNUSED_PINS { B1, B2, B3, B6, F4, F5, F6, F7 D1} 45#define UNUSED_PINS { B1, B2, B3, B6, F4, F5, F6, F7, D1}
46 46
47/* COL2ROW, ROW2COL */ 47/* COL2ROW, ROW2COL */
48#define DIODE_DIRECTION COL2ROW 48#define DIODE_DIRECTION COL2ROW
diff --git a/keyboards/mesa/mesa_tkl/config.h b/keyboards/mesa/mesa_tkl/config.h
index f4c26e2a0..23da260bc 100644
--- a/keyboards/mesa/mesa_tkl/config.h
+++ b/keyboards/mesa/mesa_tkl/config.h
@@ -42,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
42 */ 42 */
43#define MATRIX_ROW_PINS { D2, D1, D0, B0, C6, C7 } 43#define MATRIX_ROW_PINS { D2, D1, D0, B0, C6, C7 }
44#define MATRIX_COL_PINS { D3, D5, D4, D6, D7, B4, B5, B6, F7, F6, F5, F4, F1, F0, B1, B2, B3 } 44#define MATRIX_COL_PINS { D3, D5, D4, D6, D7, B4, B5, B6, F7, F6, F5, F4, F1, F0, B1, B2, B3 }
45#define UNUSED_PINS { B7 E6 } 45#define UNUSED_PINS { B7, E6 }
46 46
47/* COL2ROW, ROW2COL*/ 47/* COL2ROW, ROW2COL*/
48#define DIODE_DIRECTION COL2ROW 48#define DIODE_DIRECTION COL2ROW
diff --git a/keyboards/ploopyco/trackball_nano/rev1_001/config.h b/keyboards/ploopyco/trackball_nano/rev1_001/config.h
index 63780ef04..6d265d723 100644
--- a/keyboards/ploopyco/trackball_nano/rev1_001/config.h
+++ b/keyboards/ploopyco/trackball_nano/rev1_001/config.h
@@ -29,7 +29,7 @@
29 * ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode) 29 * ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode)
30 * 30 *
31 */ 31 */
32#define DIRECT_PINS {} 32#define DIRECT_PINS {{NO_PIN}}
33 33
34// These pins are not broken out, and cannot be used normally. 34// These pins are not broken out, and cannot be used normally.
35// They are set as output and pulled high, by default 35// They are set as output and pulled high, by default
diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py
index 54cd5b96a..c0c148f1c 100755
--- a/lib/python/qmk/cli/generate/config_h.py
+++ b/lib/python/qmk/cli/generate/config_h.py
@@ -12,7 +12,7 @@ from qmk.keyboard import keyboard_completer, keyboard_folder
12from qmk.path import is_keyboard, normpath 12from qmk.path import is_keyboard, normpath
13 13
14 14
15def direct_pins(direct_pins): 15def direct_pins(direct_pins, postfix):
16 """Return the config.h lines that set the direct pins. 16 """Return the config.h lines that set the direct pins.
17 """ 17 """
18 rows = [] 18 rows = []
@@ -24,81 +24,60 @@ def direct_pins(direct_pins):
24 col_count = len(direct_pins[0]) 24 col_count = len(direct_pins[0])
25 row_count = len(direct_pins) 25 row_count = len(direct_pins)
26 26
27 return """ 27 return f"""
28#ifndef MATRIX_COLS 28#ifndef MATRIX_COLS{postfix}
29# define MATRIX_COLS %s 29# define MATRIX_COLS{postfix} {col_count}
30#endif // MATRIX_COLS 30#endif // MATRIX_COLS{postfix}
31 31
32#ifndef MATRIX_ROWS 32#ifndef MATRIX_ROWS{postfix}
33# define MATRIX_ROWS %s 33# define MATRIX_ROWS{postfix} {row_count}
34#endif // MATRIX_ROWS 34#endif // MATRIX_ROWS{postfix}
35 35
36#ifndef DIRECT_PINS 36#ifndef DIRECT_PINS{postfix}
37# define DIRECT_PINS {%s} 37# define DIRECT_PINS{postfix} {{ {", ".join(rows)} }}
38#endif // DIRECT_PINS 38#endif // DIRECT_PINS{postfix}
39""" % (col_count, row_count, ','.join(rows)) 39"""
40 40
41 41
42def pin_array(define, pins): 42def pin_array(define, pins, postfix):
43 """Return the config.h lines that set a pin array. 43 """Return the config.h lines that set a pin array.
44 """ 44 """
45 pin_num = len(pins) 45 pin_num = len(pins)
46 pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins])) 46 pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins]))
47 47
48 return f""" 48 return f"""
49#ifndef {define}S 49#ifndef {define}S{postfix}
50# define {define}S {pin_num} 50# define {define}S{postfix} {pin_num}
51#endif // {define}S 51#endif // {define}S{postfix}
52 52
53#ifndef {define}_PINS 53#ifndef {define}_PINS{postfix}
54# define {define}_PINS {{ {pin_array} }} 54# define {define}_PINS{postfix} {{ {pin_array} }}
55#endif // {define}_PINS 55#endif // {define}_PINS{postfix}
56""" 56"""
57 57
58 58
59def matrix_pins(matrix_pins): 59def matrix_pins(matrix_pins, postfix=''):
60 """Add the matrix config to the config.h. 60 """Add the matrix config to the config.h.
61 """ 61 """
62 pins = [] 62 pins = []
63 63
64 if 'direct' in matrix_pins: 64 if 'direct' in matrix_pins:
65 pins.append(direct_pins(matrix_pins['direct'])) 65 pins.append(direct_pins(matrix_pins['direct'], postfix))
66 66
67 if 'cols' in matrix_pins: 67 if 'cols' in matrix_pins:
68 pins.append(pin_array('MATRIX_COL', matrix_pins['cols'])) 68 pins.append(pin_array('MATRIX_COL', matrix_pins['cols'], postfix))
69 69
70 if 'rows' in matrix_pins: 70 if 'rows' in matrix_pins:
71 pins.append(pin_array('MATRIX_ROW', matrix_pins['rows'])) 71 pins.append(pin_array('MATRIX_ROW', matrix_pins['rows'], postfix))
72 72
73 return '\n'.join(pins) 73 return '\n'.join(pins)
74 74
75 75
76@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') 76def generate_config_items(kb_info_json, config_h_lines):
77@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") 77 """Iterate through the info_config map to generate basic config values.
78@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
79@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
80@automagic_keyboard
81@automagic_keymap
82def generate_config_h(cli):
83 """Generates the info_config.h file.
84 """ 78 """
85 # Determine our keyboard(s)
86 if not cli.config.generate_config_h.keyboard:
87 cli.log.error('Missing parameter: --keyboard')
88 cli.subcommands['info'].print_help()
89 return False
90
91 if not is_keyboard(cli.config.generate_config_h.keyboard):
92 cli.log.error('Invalid keyboard: "%s"', cli.config.generate_config_h.keyboard)
93 return False
94
95 # Build the info_config.h file.
96 kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard))
97 info_config_map = json_load(Path('data/mappings/info_config.json')) 79 info_config_map = json_load(Path('data/mappings/info_config.json'))
98 80
99 config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once']
100
101 # Iterate through the info_config map to generate basic things
102 for config_key, info_dict in info_config_map.items(): 81 for config_key, info_dict in info_config_map.items():
103 info_key = info_dict['info_key'] 82 info_key = info_dict['info_key']
104 key_type = info_dict.get('value_type', 'str') 83 key_type = info_dict.get('value_type', 'str')
@@ -135,9 +114,78 @@ def generate_config_h(cli):
135 config_h_lines.append(f'# define {config_key} {config_value}') 114 config_h_lines.append(f'# define {config_key} {config_value}')
136 config_h_lines.append(f'#endif // {config_key}') 115 config_h_lines.append(f'#endif // {config_key}')
137 116
117
118def generate_split_config(kb_info_json, config_h_lines):
119 """Generate the config.h lines for split boards."""
120 if 'primary' in kb_info_json['split']:
121 if kb_info_json['split']['primary'] in ('left', 'right'):
122 config_h_lines.append('')
123 config_h_lines.append('#ifndef MASTER_LEFT')
124 config_h_lines.append('# ifndef MASTER_RIGHT')
125 if kb_info_json['split']['primary'] == 'left':
126 config_h_lines.append('# define MASTER_LEFT')
127 elif kb_info_json['split']['primary'] == 'right':
128 config_h_lines.append('# define MASTER_RIGHT')
129 config_h_lines.append('# endif // MASTER_RIGHT')
130 config_h_lines.append('#endif // MASTER_LEFT')
131 elif kb_info_json['split']['primary'] == 'pin':
132 config_h_lines.append('')
133 config_h_lines.append('#ifndef SPLIT_HAND_PIN')
134 config_h_lines.append('# define SPLIT_HAND_PIN')
135 config_h_lines.append('#endif // SPLIT_HAND_PIN')
136 elif kb_info_json['split']['primary'] == 'matrix_grid':
137 config_h_lines.append('')
138 config_h_lines.append('#ifndef SPLIT_HAND_MATRIX_GRID')
139 config_h_lines.append('# define SPLIT_HAND_MATRIX_GRID {%s}' % (','.join(kb_info_json["split"]["matrix_grid"],)))
140 config_h_lines.append('#endif // SPLIT_HAND_MATRIX_GRID')
141 elif kb_info_json['split']['primary'] == 'eeprom':
142 config_h_lines.append('')
143 config_h_lines.append('#ifndef EE_HANDS')
144 config_h_lines.append('# define EE_HANDS')
145 config_h_lines.append('#endif // EE_HANDS')
146
147 if 'protocol' in kb_info_json['split'].get('transport', {}):
148 if kb_info_json['split']['transport']['protocol'] == 'i2c':
149 config_h_lines.append('')
150 config_h_lines.append('#ifndef USE_I2C')
151 config_h_lines.append('# define USE_I2C')
152 config_h_lines.append('#endif // USE_I2C')
153
154 if 'right' in kb_info_json['split'].get('matrix_pins', {}):
155 config_h_lines.append(matrix_pins(kb_info_json['split']['matrix_pins']['right'], '_RIGHT'))
156
157
158@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
159@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
160@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
161@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
162@automagic_keyboard
163@automagic_keymap
164def generate_config_h(cli):
165 """Generates the info_config.h file.
166 """
167 # Determine our keyboard(s)
168 if not cli.config.generate_config_h.keyboard:
169 cli.log.error('Missing parameter: --keyboard')
170 cli.subcommands['info'].print_help()
171 return False
172
173 if not is_keyboard(cli.config.generate_config_h.keyboard):
174 cli.log.error('Invalid keyboard: "%s"', cli.config.generate_config_h.keyboard)
175 return False
176
177 # Build the info_config.h file.
178 kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard))
179 config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once']
180
181 generate_config_items(kb_info_json, config_h_lines)
182
138 if 'matrix_pins' in kb_info_json: 183 if 'matrix_pins' in kb_info_json:
139 config_h_lines.append(matrix_pins(kb_info_json['matrix_pins'])) 184 config_h_lines.append(matrix_pins(kb_info_json['matrix_pins']))
140 185
186 if 'split' in kb_info_json:
187 generate_split_config(kb_info_json, config_h_lines)
188
141 # Show the results 189 # Show the results
142 config_h = '\n'.join(config_h_lines) 190 config_h = '\n'.join(config_h_lines)
143 191
diff --git a/lib/python/qmk/cli/generate/info_json.py b/lib/python/qmk/cli/generate/info_json.py
index 8931b68b6..284d1a851 100755
--- a/lib/python/qmk/cli/generate/info_json.py
+++ b/lib/python/qmk/cli/generate/info_json.py
@@ -4,15 +4,17 @@ Compile an info.json for a particular keyboard and pretty-print it.
4""" 4"""
5import json 5import json
6 6
7from jsonschema import Draft7Validator, validators 7from argcomplete.completers import FilesCompleter
8from jsonschema import Draft7Validator, RefResolver, validators
8from milc import cli 9from milc import cli
10from pathlib import Path
9 11
10from qmk.decorators import automagic_keyboard, automagic_keymap 12from qmk.decorators import automagic_keyboard, automagic_keymap
11from qmk.info import info_json 13from qmk.info import info_json
12from qmk.json_encoders import InfoJSONEncoder 14from qmk.json_encoders import InfoJSONEncoder
13from qmk.json_schema import load_jsonschema 15from qmk.json_schema import compile_schema_store
14from qmk.keyboard import keyboard_completer, keyboard_folder 16from qmk.keyboard import keyboard_completer, keyboard_folder
15from qmk.path import is_keyboard 17from qmk.path import is_keyboard, normpath
16 18
17 19
18def pruning_validator(validator_class): 20def pruning_validator(validator_class):
@@ -34,15 +36,19 @@ def pruning_validator(validator_class):
34def strip_info_json(kb_info_json): 36def strip_info_json(kb_info_json):
35 """Remove the API-only properties from the info.json. 37 """Remove the API-only properties from the info.json.
36 """ 38 """
39 schema_store = compile_schema_store()
37 pruning_draft_7_validator = pruning_validator(Draft7Validator) 40 pruning_draft_7_validator = pruning_validator(Draft7Validator)
38 schema = load_jsonschema('keyboard') 41 schema = schema_store['qmk.keyboard.v1']
39 validator = pruning_draft_7_validator(schema).validate 42 resolver = RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
43 validator = pruning_draft_7_validator(schema, resolver=resolver).validate
40 44
41 return validator(kb_info_json) 45 return validator(kb_info_json)
42 46
43 47
44@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.') 48@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
45@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.') 49@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
50@cli.argument('-o', '--output', arg_only=True, completer=FilesCompleter, help='Write the output the specified file, overwriting if necessary.')
51@cli.argument('-ow', '--overwrite', arg_only=True, action='store_true', help='Overwrite the existing info.json. (Overrides the location of --output)')
46@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True) 52@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)
47@automagic_keyboard 53@automagic_keyboard
48@automagic_keymap 54@automagic_keymap
@@ -59,9 +65,29 @@ def generate_info_json(cli):
59 cli.log.error('Invalid keyboard: "%s"', cli.config.generate_info_json.keyboard) 65 cli.log.error('Invalid keyboard: "%s"', cli.config.generate_info_json.keyboard)
60 return False 66 return False
61 67
68 if cli.args.overwrite:
69 output_path = (Path('keyboards') / cli.config.generate_info_json.keyboard / 'info.json').resolve()
70
71 if cli.args.output:
72 cli.log.warning('Overwriting user supplied --output with %s', output_path)
73
74 cli.args.output = output_path
75
62 # Build the info.json file 76 # Build the info.json file
63 kb_info_json = info_json(cli.config.generate_info_json.keyboard) 77 kb_info_json = info_json(cli.config.generate_info_json.keyboard)
64 strip_info_json(kb_info_json) 78 strip_info_json(kb_info_json)
79 info_json_text = json.dumps(kb_info_json, indent=4, cls=InfoJSONEncoder)
80
81 if cli.args.output:
82 # Write to a file
83 output_path = normpath(cli.args.output)
84
85 if output_path.exists():
86 cli.log.warning('Overwriting output file %s', output_path)
87
88 output_path.write_text(info_json_text + '\n')
89 cli.log.info('Wrote info.json to %s.', output_path)
65 90
66 # Display the results 91 else:
67 print(json.dumps(kb_info_json, indent=2, cls=InfoJSONEncoder)) 92 # Display the results
93 print(info_json_text)
diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py
index 41c94e16b..2712b81cb 100755
--- a/lib/python/qmk/cli/generate/rules_mk.py
+++ b/lib/python/qmk/cli/generate/rules_mk.py
@@ -76,6 +76,17 @@ def generate_rules_mk(cli):
76 enabled = 'yes' if enabled else 'no' 76 enabled = 'yes' if enabled else 'no'
77 rules_mk_lines.append(f'{feature}_ENABLE ?= {enabled}') 77 rules_mk_lines.append(f'{feature}_ENABLE ?= {enabled}')
78 78
79 # Set SPLIT_TRANSPORT, if needed
80 if kb_info_json.get('split', {}).get('transport', {}).get('protocol') == 'custom':
81 rules_mk_lines.append('SPLIT_TRANSPORT ?= custom')
82
83 # Set CUSTOM_MATRIX, if needed
84 if kb_info_json.get('matrix_pins', {}).get('custom'):
85 if kb_info_json.get('matrix_pins', {}).get('custom_lite'):
86 rules_mk_lines.append('CUSTOM_MATRIX ?= lite')
87 else:
88 rules_mk_lines.append('CUSTOM_MATRIX ?= yes')
89
79 # Show the results 90 # Show the results
80 rules_mk = '\n'.join(rules_mk_lines) + '\n' 91 rules_mk = '\n'.join(rules_mk_lines) + '\n'
81 92
diff --git a/lib/python/qmk/cli/info.py b/lib/python/qmk/cli/info.py
index 337b494a9..3131d4b53 100755
--- a/lib/python/qmk/cli/info.py
+++ b/lib/python/qmk/cli/info.py
@@ -24,19 +24,15 @@ def show_keymap(kb_info_json, title_caps=True):
24 keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap) 24 keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap)
25 25
26 if keymap_path and keymap_path.suffix == '.json': 26 if keymap_path and keymap_path.suffix == '.json':
27 if title_caps:
28 cli.echo('{fg_blue}Keymap "%s"{fg_reset}:', cli.config.info.keymap)
29 else:
30 cli.echo('{fg_blue}keymap_%s{fg_reset}:', cli.config.info.keymap)
31
32 keymap_data = json.load(keymap_path.open(encoding='utf-8')) 27 keymap_data = json.load(keymap_path.open(encoding='utf-8'))
33 layout_name = keymap_data['layout'] 28 layout_name = keymap_data['layout']
29 layout_name = kb_info_json.get('layout_aliases', {}).get(layout_name, layout_name) # Resolve alias names
34 30
35 for layer_num, layer in enumerate(keymap_data['layers']): 31 for layer_num, layer in enumerate(keymap_data['layers']):
36 if title_caps: 32 if title_caps:
37 cli.echo('{fg_cyan}Layer %s{fg_reset}:', layer_num) 33 cli.echo('{fg_cyan}Keymap %s Layer %s{fg_reset}:', cli.config.info.keymap, layer_num)
38 else: 34 else:
39 cli.echo('{fg_cyan}layer_%s{fg_reset}:', layer_num) 35 cli.echo('{fg_cyan}keymap.%s.layer.%s{fg_reset}:', cli.config.info.keymap, layer_num)
40 36
41 print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, layer)) 37 print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, layer))
42 38
@@ -45,7 +41,7 @@ def show_layouts(kb_info_json, title_caps=True):
45 """Render the layouts with info.json labels. 41 """Render the layouts with info.json labels.
46 """ 42 """
47 for layout_name, layout_art in render_layouts(kb_info_json, cli.config.info.ascii).items(): 43 for layout_name, layout_art in render_layouts(kb_info_json, cli.config.info.ascii).items():
48 title = layout_name.title() if title_caps else layout_name 44 title = f'Layout {layout_name.title()}' if title_caps else f'layouts.{layout_name}'
49 cli.echo('{fg_cyan}%s{fg_reset}:', title) 45 cli.echo('{fg_cyan}%s{fg_reset}:', title)
50 print(layout_art) # Avoid passing dirty data to cli.echo() 46 print(layout_art) # Avoid passing dirty data to cli.echo()
51 47
@@ -93,15 +89,6 @@ def print_friendly_output(kb_info_json):
93 aliases = [f'{key}={value}' for key, value in kb_info_json['layout_aliases'].items()] 89 aliases = [f'{key}={value}' for key, value in kb_info_json['layout_aliases'].items()]
94 cli.echo('{fg_blue}Layout aliases:{fg_reset} %s' % (', '.join(aliases),)) 90 cli.echo('{fg_blue}Layout aliases:{fg_reset} %s' % (', '.join(aliases),))
95 91
96 if cli.config.info.layouts:
97 show_layouts(kb_info_json, True)
98
99 if cli.config.info.matrix:
100 show_matrix(kb_info_json, True)
101
102 if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
103 show_keymap(kb_info_json, True)
104
105 92
106def print_text_output(kb_info_json): 93def print_text_output(kb_info_json):
107 """Print the info.json in a plain text format. 94 """Print the info.json in a plain text format.
@@ -122,6 +109,24 @@ def print_text_output(kb_info_json):
122 show_keymap(kb_info_json, False) 109 show_keymap(kb_info_json, False)
123 110
124 111
112def print_dotted_output(kb_info_json, prefix=''):
113 """Print the info.json in a plain text format with dot-joined keys.
114 """
115 for key in sorted(kb_info_json):
116 new_prefix = f'{prefix}.{key}' if prefix else key
117
118 if key in ['parse_errors', 'parse_warnings']:
119 continue
120 elif key == 'layouts' and prefix == '':
121 cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
122 elif isinstance(kb_info_json[key], dict):
123 print_dotted_output(kb_info_json[key], new_prefix)
124 elif isinstance(kb_info_json[key], list):
125 cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, sorted(kb_info_json[key]))))
126 else:
127 cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key])
128
129
125def print_parsed_rules_mk(keyboard_name): 130def print_parsed_rules_mk(keyboard_name):
126 rules = rules_mk(keyboard_name) 131 rules = rules_mk(keyboard_name)
127 for k in sorted(rules.keys()): 132 for k in sorted(rules.keys()):
@@ -162,10 +167,22 @@ def info(cli):
162 # Output in the requested format 167 # Output in the requested format
163 if cli.args.format == 'json': 168 if cli.args.format == 'json':
164 print(json.dumps(kb_info_json, cls=InfoJSONEncoder)) 169 print(json.dumps(kb_info_json, cls=InfoJSONEncoder))
170 return True
165 elif cli.args.format == 'text': 171 elif cli.args.format == 'text':
166 print_text_output(kb_info_json) 172 print_dotted_output(kb_info_json)
173 title_caps = False
167 elif cli.args.format == 'friendly': 174 elif cli.args.format == 'friendly':
168 print_friendly_output(kb_info_json) 175 print_friendly_output(kb_info_json)
176 title_caps = True
169 else: 177 else:
170 cli.log.error('Unknown format: %s', cli.args.format) 178 cli.log.error('Unknown format: %s', cli.args.format)
171 return False 179 return False
180
181 if cli.config.info.layouts:
182 show_layouts(kb_info_json, title_caps)
183
184 if cli.config.info.matrix:
185 show_matrix(kb_info_json, title_caps)
186
187 if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
188 show_keymap(kb_info_json, title_caps)
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py
index 858fbab33..b6f2ecf64 100644
--- a/lib/python/qmk/info.py
+++ b/lib/python/qmk/info.py
@@ -61,8 +61,8 @@ def info_json(keyboard):
61 61
62 # Merge in the data from info.json, config.h, and rules.mk 62 # Merge in the data from info.json, config.h, and rules.mk
63 info_data = merge_info_jsons(keyboard, info_data) 63 info_data = merge_info_jsons(keyboard, info_data)
64 info_data = _extract_config_h(info_data)
65 info_data = _extract_rules_mk(info_data) 64 info_data = _extract_rules_mk(info_data)
65 info_data = _extract_config_h(info_data)
66 66
67 # Ensure that we have matrix row and column counts 67 # Ensure that we have matrix row and column counts
68 info_data = _matrix_size(info_data) 68 info_data = _matrix_size(info_data)
@@ -161,10 +161,9 @@ def _extract_pins(pins):
161 return [_pin_name(pin) for pin in pins.split(',')] 161 return [_pin_name(pin) for pin in pins.split(',')]
162 162
163 163
164def _extract_direct_matrix(info_data, direct_pins): 164def _extract_direct_matrix(direct_pins):
165 """ 165 """
166 """ 166 """
167 info_data['matrix_pins'] = {}
168 direct_pin_array = [] 167 direct_pin_array = []
169 168
170 while direct_pins[-1] != '}': 169 while direct_pins[-1] != '}':
@@ -188,12 +187,157 @@ def _extract_direct_matrix(info_data, direct_pins):
188 return direct_pin_array 187 return direct_pin_array
189 188
190 189
190def _extract_audio(info_data, config_c):
191 """Populate data about the audio configuration
192 """
193 audio_pins = []
194
195 for pin in 'B5', 'B6', 'B7', 'C4', 'C5', 'C6':
196 if config_c.get(f'{pin}_AUDIO'):
197 audio_pins.append(pin)
198
199 if audio_pins:
200 info_data['audio'] = {'pins': audio_pins}
201
202
203def _extract_split_main(info_data, config_c):
204 """Populate data about the split configuration
205 """
206 # Figure out how the main half is determined
207 if config_c.get('SPLIT_HAND_PIN') is True:
208 if 'split' not in info_data:
209 info_data['split'] = {}
210
211 if 'main' in info_data['split']:
212 _log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_PIN) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
213
214 info_data['split']['main'] = 'pin'
215
216 if config_c.get('SPLIT_HAND_MATRIX_GRID'):
217 if 'split' not in info_data:
218 info_data['split'] = {}
219
220 if 'main' in info_data['split']:
221 _log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_MATRIX_GRID) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
222
223 info_data['split']['main'] = 'matrix_grid'
224 info_data['split']['matrix_grid'] = _extract_pins(config_c['SPLIT_HAND_MATRIX_GRID'])
225
226 if config_c.get('EE_HANDS') is True:
227 if 'split' not in info_data:
228 info_data['split'] = {}
229
230 if 'main' in info_data['split']:
231 _log_warning(info_data, 'Split main hand is specified in both config.h (EE_HANDS) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
232
233 info_data['split']['main'] = 'eeprom'
234
235 if config_c.get('MASTER_RIGHT') is True:
236 if 'split' not in info_data:
237 info_data['split'] = {}
238
239 if 'main' in info_data['split']:
240 _log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_RIGHT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
241
242 info_data['split']['main'] = 'right'
243
244 if config_c.get('MASTER_LEFT') is True:
245 if 'split' not in info_data:
246 info_data['split'] = {}
247
248 if 'main' in info_data['split']:
249 _log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_LEFT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
250
251 info_data['split']['main'] = 'left'
252
253
254def _extract_split_transport(info_data, config_c):
255 # Figure out the transport method
256 if config_c.get('USE_I2C') is True:
257 if 'split' not in info_data:
258 info_data['split'] = {}
259
260 if 'transport' not in info_data['split']:
261 info_data['split']['transport'] = {}
262
263 if 'protocol' in info_data['split']['transport']:
264 _log_warning(info_data, 'Split transport is specified in both config.h (USE_I2C) and info.json (split.transport.protocol) (Value: %s), the config.h value wins.' % info_data['split']['transport'])
265
266 info_data['split']['transport']['protocol'] = 'i2c'
267
268 elif 'protocol' not in info_data.get('split', {}).get('transport', {}):
269 if 'split' not in info_data:
270 info_data['split'] = {}
271
272 if 'transport' not in info_data['split']:
273 info_data['split']['transport'] = {}
274
275 info_data['split']['transport']['protocol'] = 'serial'
276
277
278def _extract_split_right_pins(info_data, config_c):
279 # Figure out the right half matrix pins
280 row_pins = config_c.get('MATRIX_ROW_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()
281 col_pins = config_c.get('MATRIX_COL_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()
282 unused_pin_text = config_c.get('UNUSED_PINS_RIGHT')
283 unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None
284 direct_pins = config_c.get('DIRECT_PINS_RIGHT', '').replace(' ', '')[1:-1]
285
286 if row_pins and col_pins:
287 if info_data.get('split', {}).get('matrix_pins', {}).get('right') in info_data:
288 _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
289
290 if 'split' not in info_data:
291 info_data['split'] = {}
292
293 if 'matrix_pins' not in info_data['split']:
294 info_data['split']['matrix_pins'] = {}
295
296 if 'right' not in info_data['split']['matrix_pins']:
297 info_data['split']['matrix_pins']['right'] = {}
298
299 info_data['split']['matrix_pins']['right'] = {
300 'cols': _extract_pins(col_pins),
301 'rows': _extract_pins(row_pins),
302 }
303
304 if direct_pins:
305 if info_data.get('split', {}).get('matrix_pins', {}).get('right', {}):
306 _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
307
308 if 'split' not in info_data:
309 info_data['split'] = {}
310
311 if 'matrix_pins' not in info_data['split']:
312 info_data['split']['matrix_pins'] = {}
313
314 if 'right' not in info_data['split']['matrix_pins']:
315 info_data['split']['matrix_pins']['right'] = {}
316
317 info_data['split']['matrix_pins']['right']['direct'] = _extract_direct_matrix(direct_pins)
318
319 if unused_pins:
320 if 'split' not in info_data:
321 info_data['split'] = {}
322
323 if 'matrix_pins' not in info_data['split']:
324 info_data['split']['matrix_pins'] = {}
325
326 if 'right' not in info_data['split']['matrix_pins']:
327 info_data['split']['matrix_pins']['right'] = {}
328
329 info_data['split']['matrix_pins']['right']['unused'] = _extract_pins(unused_pins)
330
331
191def _extract_matrix_info(info_data, config_c): 332def _extract_matrix_info(info_data, config_c):
192 """Populate the matrix information. 333 """Populate the matrix information.
193 """ 334 """
194 row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip() 335 row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip()
195 col_pins = config_c.get('MATRIX_COL_PINS', '').replace('{', '').replace('}', '').strip() 336 col_pins = config_c.get('MATRIX_COL_PINS', '').replace('{', '').replace('}', '').strip()
337 unused_pin_text = config_c.get('UNUSED_PINS')
338 unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None
196 direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1] 339 direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1]
340 info_snippet = {}
197 341
198 if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c: 342 if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c:
199 if 'matrix_size' in info_data: 343 if 'matrix_size' in info_data:
@@ -205,19 +349,35 @@ def _extract_matrix_info(info_data, config_c):
205 } 349 }
206 350
207 if row_pins and col_pins: 351 if row_pins and col_pins:
208 if 'matrix_pins' in info_data: 352 if 'matrix_pins' in info_data and 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:
209 _log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.') 353 _log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.')
210 354
211 info_data['matrix_pins'] = { 355 info_snippet['cols'] = _extract_pins(col_pins)
212 'cols': _extract_pins(col_pins), 356 info_snippet['rows'] = _extract_pins(row_pins)
213 'rows': _extract_pins(row_pins),
214 }
215 357
216 if direct_pins: 358 if direct_pins:
217 if 'matrix_pins' in info_data: 359 if 'matrix_pins' in info_data and 'direct' in info_data['matrix_pins']:
218 _log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.') 360 _log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.')
219 361
220 info_data['matrix_pins']['direct'] = _extract_direct_matrix(info_data, direct_pins) 362 info_snippet['direct'] = _extract_direct_matrix(direct_pins)
363
364 if unused_pins:
365 if 'matrix_pins' not in info_data:
366 info_data['matrix_pins'] = {}
367
368 info_snippet['unused'] = _extract_pins(unused_pins)
369
370 if config_c.get('CUSTOM_MATRIX', 'no') != 'no':
371 if 'matrix_pins' in info_data and 'custom' in info_data['matrix_pins']:
372 _log_warning(info_data, 'Custom Matrix is specified in both info.json and config.h, the config.h values win.')
373
374 info_snippet['custom'] = True
375
376 if config_c['CUSTOM_MATRIX'] == 'lite':
377 info_snippet['custom_lite'] = True
378
379 if info_snippet:
380 info_data['matrix_pins'] = info_snippet
221 381
222 return info_data 382 return info_data
223 383
@@ -275,6 +435,10 @@ def _extract_config_h(info_data):
275 435
276 # Pull data that easily can't be mapped in json 436 # Pull data that easily can't be mapped in json
277 _extract_matrix_info(info_data, config_c) 437 _extract_matrix_info(info_data, config_c)
438 _extract_audio(info_data, config_c)
439 _extract_split_main(info_data, config_c)
440 _extract_split_transport(info_data, config_c)
441 _extract_split_right_pins(info_data, config_c)
278 442
279 return info_data 443 return info_data
280 444
diff --git a/lib/python/qmk/json_schema.py b/lib/python/qmk/json_schema.py
index cbc5bff51..ffc7c6bcd 100644
--- a/lib/python/qmk/json_schema.py
+++ b/lib/python/qmk/json_schema.py
@@ -2,6 +2,7 @@
2""" 2"""
3import json 3import json
4from collections.abc import Mapping 4from collections.abc import Mapping
5from functools import lru_cache
5from pathlib import Path 6from pathlib import Path
6 7
7import hjson 8import hjson
@@ -25,6 +26,7 @@ def json_load(json_file):
25 exit(1) 26 exit(1)
26 27
27 28
29@lru_cache(maxsize=0)
28def load_jsonschema(schema_name): 30def load_jsonschema(schema_name):
29 """Read a jsonschema file from disk. 31 """Read a jsonschema file from disk.
30 """ 32 """
@@ -39,8 +41,9 @@ def load_jsonschema(schema_name):
39 return json_load(schema_path) 41 return json_load(schema_path)
40 42
41 43
42def create_validator(schema): 44@lru_cache(maxsize=0)
43 """Creates a validator for the given schema id. 45def compile_schema_store():
46 """Compile all our schemas into a schema store.
44 """ 47 """
45 schema_store = {} 48 schema_store = {}
46 49
@@ -51,6 +54,14 @@ def create_validator(schema):
51 continue 54 continue
52 schema_store[schema_data['$id']] = schema_data 55 schema_store[schema_data['$id']] = schema_data
53 56
57 return schema_store
58
59
60@lru_cache(maxsize=0)
61def create_validator(schema):
62 """Creates a validator for the given schema id.
63 """
64 schema_store = compile_schema_store()
54 resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store) 65 resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
55 66
56 return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate 67 return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate