summaryrefslogtreecommitdiff
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)
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 b949b13320..18477f9129 100644
--- a/data/mappings/info_config.json
+++ b/data/mappings/info_config.json
@@ -1,21 +1,46 @@
-# This file maps keys between `config.h` and `info.json`. It is used by QMK
+# This file maps keys between `config.h` and `info.json`. It is used by QMK
# to correctly and consistently map back and forth between the two systems.
{
# Format:
# <config.h key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]}
- # value_type: one of "array", "array.int", "int", "hex", "list", "mapping"
+ # value_type: one of "array", "array.int", "bool", "int", "hex", "list", "mapping"
# to_json: Default `true`. Set to `false` to exclude this mapping from info.json
# to_c: Default `true`. Set to `false` to exclude this mapping from config.h
# warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places
- "DEBOUNCE": {"info_key": "debounce", "value_type": "int"}
+ "AUDIO_VOICES": {"info_key": "audio.voices", "value_type": "bool"},
+ "BACKLIGHT_BREATHING": {"info_key": "backlight.breathing", "value_type": "bool"},
+ "BREATHING_PERIOD": {"info_key": "backlight.breathing_period", "value_type": "int"},
+ "BACKLIGHT_PIN": {"info_key": "backlight.pin"},
+ "COMBO_COUNT": {"info_key": "combo.count", "value_type": "int"},
+ "COMBO_TERM": {"info_key": "combo.term", "value_type": "int"},
+ "DEBOUNCE": {"info_key": "debounce", "value_type": "int"},
"DEVICE_VER": {"info_key": "usb.device_ver", "value_type": "hex"},
"DESCRIPTION": {"info_key": "keyboard_folder", "to_json": false},
"DIODE_DIRECTION": {"info_key": "diode_direction"},
+ "FORCE_NKRO": {"info_key": "usb.force_nkro", "value_type": "bool"},
+ "IGNORE_MOD_TAP_INTERRUPT": {"info_key": "tapping.ignore_mod_tap_interrupt", "value_type": "bool"},
+ "IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "tapping.ignore_mod_tap_interrupt_per_key", "value_type": "bool"},
"LAYOUTS": {"info_key": "layout_aliases", "value_type": "mapping"},
+ "LEADER_PER_KEY_TIMING": {"info_key": "leader_key.timing", "value_type": "bool"},
+ "LEADER_KEY_STRICT_KEY_PROCESSING": {"info_key": "leader_key.strict_processing", "value_type": "bool"},
+ "LEADER_TIMEOUT": {"info_key": "leader_key.timeout", "value_type": "int"},
"LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"},
"LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"},
"LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"},
"MANUFACTURER": {"info_key": "manufacturer"},
+ "MATRIX_HAS_GHOST": {"info_key": "matrix_pins.ghost", "value_type": "bool"},
+ "MATRIX_IO_DELAY": {"info_key": "matrix_pins.io_delay", "value_type": "int"},
+ "MOUSEKEY_DELAY": {"info_key": "mousekey.delay", "value_type": "int"},
+ "MOUSEKEY_INTERVAL": {"info_key": "mousekey.interval", "value_type": "int"},
+ "MOUSEKEY_MAX_SPEED": {"info_key": "mousekey.max_speed", "value_type": "int"},
+ "MOUSEKEY_TIME_TO_MAX": {"info_key": "mousekey.time_to_max", "value_type": "int"},
+ "MOUSEKEY_WHEEL_DELAY": {"info_key": "mousekey.wheel_delay", "value_type": "int"},
+ "ONESHOT_TIMEOUT": {"info_key": "oneshot.timeout", "value_type": "int"},
+ "ONESHOT_TAP_TOGGLE": {"info_key": "oneshot.tap_toggle", "value_type": "int"},
+ "PERMISSIVE_HOLD": {"info_key": "tapping.permissive_hold", "value_type": "bool"},
+ "PERMISSIVE_HOLD_PER_KEY": {"info_key": "tapping.permissive_hold_per_key", "value_type": "bool"},
+ "RETRO_TAPPING": {"info_key": "tapping.retro", "value_type": "bool"},
+ "RETRO_TAPPING_PER_KEY": {"info_key": "tapping.retro_per_key", "value_type": "bool"},
"RGB_DI_PIN": {"info_key": "rgblight.pin"},
"RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"},
"RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.int"},
@@ -30,17 +55,40 @@
"RGBLIGHT_EFFECT_SNAKE": {"info_key": "rgblight.animations.snake", "value_type": "bool"},
"RGBLIGHT_EFFECT_STATIC_GRADIENT": {"info_key": "rgblight.animations.static_gradient", "value_type": "bool"},
"RGBLIGHT_EFFECT_TWINKLE": {"info_key": "rgblight.animations.twinkle"},
+ "RGBLIGHT_LAYER_BLINK": {"info_key": "rgblight.layers.blink", "value_type": "bool"},
+ "RGBLIGHT_LAYERS": {"info_key": "rgblight.layers.enabled", "value_type": "bool"},
+ "RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF": {"info_key": "rgblight.layers.override_rgb", "value_type": "bool"},
"RGBLIGHT_LIMIT_VAL": {"info_key": "rgblight.max_brightness", "value_type": "int"},
+ "RGBLIGHT_MAX_LAYERS": {"info_key": "rgblight.layers.max", "value_type": "int"},
"RGBLIGHT_HUE_STEP": {"info_key": "rgblight.hue_steps", "value_type": "int"},
"RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_steps", "value_type": "int"},
"RGBLIGHT_VAL_STEP": {"info_key": "rgblight.brightness_steps", "value_type": "int"},
"RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"},
"RGBLIGHT_SPLIT": {"info_key": "rgblight.split", "value_type": "bool"},
+ "RGBW": {"info_key": "rgblight.rgbw", "value_type": "bool"},
"PRODUCT": {"info_key": "keyboard_folder", "to_json": false},
"PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex"},
"VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex"},
"QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"},
"QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"},
+ "QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int"},
"QMK_LED": {"info_key": "qmk_lufa_bootloader.led"},
- "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"}
+ "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"},
+ "SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"},
+ "SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"},
+ "SPLIT_USB_DETECT": {"info_key": "split.usb_detect.enabled", "value_type": "bool"},
+ "SPLIT_USB_TIMEOUT": {"info_key": "split.usb_detect.timeout", "value_type": "int"},
+ "SPLIT_USB_TIMEOUT_POLL": {"info_key": "split.usb_detect.polling_interval", "value_type": "int"},
+ "SOFT_SERIAL_PIN": {"info_key": "split.soft_serial_pin"},
+ "SOFT_SERIAL_SPEED": {"info_key": "split.soft_serial_speed"},
+ "TAP_CODE_DELAY": {"info_key": "qmk.tap_keycode_delay", "value_type": "int"},
+ "TAP_HOLD_CAPS_DELAY": {"info_key": "qmk.tap_capslock_delay", "value_type": "int"},
+ "TAPPING_FORCE_HOLD": {"info_key": "tapping.force_hold", "value_type": "bool"},
+ "TAPPING_FORCE_HOLD_PER_KEY": {"info_key": "tapping.force_hold_per_key", "value_type": "bool"},
+ "TAPPING_TERM": {"info_key": "tapping.term", "value_type": "int"},
+ "TAPPING_TERM_PER_KEY": {"info_key": "tapping.term_per_key", "value_type": "bool"},
+ "TAPPING_TOGGLE": {"info_key": "tapping.toggle", "value_type": "int"},
+ "USB_MAX_POWER_CONSUMPTION": {"info_key": "usb.max_power", "value_type": "int"},
+ "USB_POLLING_INTERVAL_MS": {"info_key": "usb.polling_interval", "value_type": "int"},
+ "USB_SUSPEND_WAKEUP_DELAY": {"info_key": "usb.suspend_wakeup_delay", "value_type": "int"},
}
diff --git a/data/mappings/info_rules.json b/data/mappings/info_rules.json
index 97f772c4d5..aea67e04c8 100644
--- a/data/mappings/info_rules.json
+++ b/data/mappings/info_rules.json
@@ -1,15 +1,25 @@
-# This file maps keys between `rules.mk` and `info.json`. It is used by QMK
+# This file maps keys between `rules.mk` and `info.json`. It is used by QMK
# to correctly and consistently map back and forth between the two systems.
{
# Format:
# <rules.mk key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]}
- # value_type: one of "array", "array.int", "int", "list", "hex", "mapping"
+ # value_type: one of "array", "array.int", "bool", "int", "list", "hex", "mapping"
# to_json: Default `true`. Set to `false` to exclude this mapping from info.json
# to_c: Default `true`. Set to `false` to exclude this mapping from rules.mk
# warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places
"BOARD": {"info_key": "board"},
"BOOTLOADER": {"info_key": "bootloader", "warn_duplicate": false},
+ "BLUETOOTH": {"info_key": "bluetooth.driver"},
+ "FIRMWARE_FORMAT": {"info_key": "build.firmware_format"},
+ "KEYBOARD_SHARED_EP": {"info_key": "usb.shared_endpoint.keyboard", "value_type": "bool"},
+ "MOUSE_SHARED_EP": {"info_key": "usb.shared_endpoint.mouse", "value_type": "bool"},
"LAYOUTS": {"info_key": "community_layouts", "value_type": "list"},
"LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"},
+ "LTO_ENABLE": {"info_key": "build.lto", "value_type": "bool"},
"MCU": {"info_key": "processor", "warn_duplicate": false},
+ "MOUSEKEY_ENABLE": {"info_key": "mouse_key.enabled", "value_type": "bool"},
+ "NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"},
+ "SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"},
+ "SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "value_type": "str", "to_c": false},
+ "WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"}
}
diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema
index 3a3fa4b738..78c9a8d36b 100644
--- a/data/schemas/keyboard.jsonschema
+++ b/data/schemas/keyboard.jsonschema
@@ -15,6 +15,40 @@
"type": "string",
"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"]
},
+ "audio": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "pins": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
+ "voices": {"type": "boolean"}
+ }
+ },
+ "backlight": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "breathing": {"type": "boolean"},
+ "breathing_period": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
+ "levels": {
+ "type": "number",
+ "min": 1,
+ "max": 31,
+ "multipleOf": 1
+ },
+ "pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}
+ }
+ },
+ "bluetooth": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "driver": {
+ "type": "string",
+ "enum": ["AdafruitBLE", "RN42"]
+ },
+ "lto": {"type": "boolean"},
+ }
+ },
"board": {
"type": "string",
"minLength": 2,
@@ -22,13 +56,39 @@
},
"bootloader": {
"type": "string",
- "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "unknown", "USBasp", "tinyuf2"]
+ "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "unknown", "USBasp", "tinyuf2"],
+ },
+ "bootloader_instructions": {
+ "type": "string",
+ "description": "Instructions for putting the keyboard into a mode that allows for firmware flashing."
+ },
+ "build": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "debounce_type": {
+ "type": "string",
+ "enum": ["custom", "eager_pk", "eager_pr", "sym_defer_pk", "sym_eager_pk"]
+ },
+ "firmware_format": {
+ "type": "string",
+ "enum": ["bin", "hex", "uf2"]
+ },
+ "lto": {"type": "boolean"},
+ }
},
"diode_direction": {
"type": "string",
"enum": ["COL2ROW", "ROW2COL"]
},
"debounce": {"$ref": "qmk.definitions.v1#/unsigned_int"},
+ "combo": {
+ "type": "object",
+ "properties": {
+ "count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
+ "term": {"$ref": "qmk.definitions.v1#/unsigned_int"}
+ }
+ },
"community_layouts": {
"type": "array",
"items": {"$ref": "qmk.definitions.v1#/filename"}
@@ -90,16 +150,47 @@
}
}
},
+ "leader_key": {
+ "type": "object",
+ "properties": {
+ "timing": {"type": "boolean"},
+ "strict_processing": {"type": "boolean"},
+ "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
+ }
+ },
"matrix_pins": {
"type": "object",
"additionalProperties": false,
"properties": {
+ "custom": {"type": "boolean"},
+ "custom_lite": {"type": "boolean"},
+ "ghost": {"type": "boolean"},
+ "io_delay": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"direct": {
"type": "array",
- "items": {$ref": "qmk.definitions.v1#/mcu_pin_array"}
+ "items": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
},
"cols": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
- "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
+ "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
+ "unused": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
+ }
+ },
+ "mouse_key": {
+ "type": "object",
+ "properties": {
+ "enabled": {"type": "boolean"},
+ "delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
+ "interval": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
+ "max_speed": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
+ "time_to_max": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
+ "wheel_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}
+ }
+ },
+ "oneshot": {
+ "type": "object",
+ "properties": {
+ "tap_toggle": {"$ref": "qmk.definitions.v1#/unsigned_int"},
+ "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
}
},
"rgblight": {
@@ -114,9 +205,25 @@
},
"brightness_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"hue_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
+ "layers": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "blink": {"type": "boolean"},
+ "enabled": {"type": "boolean"},
+ "max": {
+ "type": "number",
+ "min": 1,
+ "max": 32,
+ "multipleOf": 1
+ },
+ "override_rgb": {"type": "boolean"}
+ }
+ },
"led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
"pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
+ "rgbw": {"type": "boolean"},
"saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"sleep": {"type": "boolean"},
"split": {"type": "boolean"},
@@ -128,13 +235,118 @@
}
}
},
+ "split": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "enabled": {"type": "boolean"},
+ "matrix_grid": {
+ "type": "array",
+ "items": {"$ref": "qmk.definitions.v1#/mcu_pin"}
+ },
+ "matrix_pins": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "right": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "direct": {
+ "type": "array",
+ "items": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
+ },
+ "cols": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
+ "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
+ "unused": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
+ }
+ }
+ }
+ },
+ "main": {
+ "type": "string",
+ "enum": ["eeprom", "left", "matrix_grid", "pin", "right"]
+ },
+ "soft_serial_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
+ "soft_serial_speed": {
+ "type": "number",
+ "min": 0,
+ "max": 5,
+ "multipleOf": 1
+ },
+ "transport": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "protocol": {
+ "type": "string",
+ "enum": ["custom", "i2c", "serial", "serial_usart"]
+ },
+ "sync_matrix_state": {"type": "boolean"},
+ "sync_modifiers": {"type": "boolean"}
+ }
+ },
+ "usb_detect": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "enabled": {"type": "boolean"},
+ "polling_interval": {"$ref": "qmk.definitions.v1#/unsigned_int"},
+ "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
+ }
+ }
+ }
+ },
+ "tags": {
+ "type": "array",
+ "items": {"type": "string"}
+ },
+ "tapping": {
+ "type": "object",
+ "properties": {
+ "force_hold": {"type": "boolean"},
+ "force_hold_per_key": {"type": "boolean"},
+ "ignore_mod_tap_interrupt": {"type": "boolean"},
+ "ignore_mod_tap_interrupt_per_key": {"type": "boolean"},
+ "permissive_hold": {"type": "boolean"},
+ "permissive_hold_per_key": {"type": "boolean"},
+ "retro": {"type": "boolean"},
+ "retro_per_key": {"type": "boolean"},
+ "term": {"$ref": "qmk.definitions.v1#/unsigned_int"},
+ "term_per_key": {"type": "boolean"},
+ "toggle": {"$ref": "qmk.definitions.v1#/unsigned_int"},
+ }
+ },
"usb": {
"type": "object",
"additionalProperties": false,
"properties": {
"device_ver": {"$ref": "qmk.definitions.v1#/hex_number_4d"},
+ "force_nkro": {"type": "boolean"},
"pid": {"$ref": "qmk.definitions.v1#/hex_number_4d"},
- "vid": {"$ref": "qmk.definitions.v1#/hex_number_4d"}
+ "vid": {"$ref": "qmk.definitions.v1#/hex_number_4d"},
+ "max_power": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
+ "no_startup_check": {"type": "boolean"},
+ "polling_interval": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
+ "shared_endpoint": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "keyboard": {"type": "boolean"},
+ "mouse": {"type": "boolean"}
+ }
+ },
+ "suspend_wakeup_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
+ "wait_for": {"type": "boolean"},
+ }
+ },
+ "qmk": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "keys_per_scan": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
+ "tap_keycode_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
+ "tap_capslock_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
}
},
"qmk_lufa_bootloader": {
diff --git a/keyboards/bpiphany/frosty_flake/config.h b/keyboards/bpiphany/frosty_flake/config.h
index 344db5f0d9..ae19184a6a 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/>.
/*
* Keyboard Matrix Assignments
+ *
+ * MATRIX_ROW_PINS and MATRIX_COL_PINS aren't actually used and are included
+ * for data driven compatibility.
*/
+#define MATRIX_COL_PINS { B0, B3, B2, B1, B6, B4, B5, C7 }
+#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 }
#define UNUSED_PINS { C0, C1, C2, C3, C4, D2, D7 }
/* 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 a86cffd16b..d3e2464bee 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/>.
/*
* Keyboard Matrix Assignments
+ *
+ * MATRIX_ROW_PINS and MATRIX_COL_PINS aren't actually used and are included
+ * for data driven compatibility.
*/
+#define MATRIX_ROW_PINS { C2, B3, B4, B2, B1, C7, B6, B5 }
+#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 }
#define UNUSED_PINS { B0, C4, D3 }
#define LED_NUM_LOCK_PIN C5
diff --git a/keyboards/cest73/tkm/config.h b/keyboards/cest73/tkm/config.h
index deef979b2c..e925f39715 100644
--- a/keyboards/cest73/tkm/config.h
+++ b/keyboards/cest73/tkm/config.h
@@ -34,7 +34,7 @@
#define MATRIX_ROW_PINS { B0, B1, B2, B3, B4, B5, B6, B7, C6, C7, D0 }
/* column handy ruler: c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 */
#define MATRIX_COL_PINS { D1, D2, D3, D4, D5, D6, D7, F0, F1, F4 }
-#define UNUSED_PINS { AF }
+#define UNUSED_PINS { }
//NOTE: if D6 pin shows any issues in exploatation the LED on the Teensy is to be removed
diff --git a/keyboards/esca/getawayvan/config.h b/keyboards/esca/getawayvan/config.h
index e9fbc6b404..3b718c5835 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/>.
#define MATRIX_ROW_PINS { A9, A8, A3, A5 }
#define DIODE_DIRECTION COL2ROW
-#define BACKLIGHT_PIN
#define BACKLIGHT_LEVELS 6
#define GRAVE_ESC_CTRL_OVERRIDE
diff --git a/keyboards/esca/getawayvan_f042/config.h b/keyboards/esca/getawayvan_f042/config.h
index f83ec74341..8921e9db4d 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/>.
#define MATRIX_ROW_PINS { A9, A8, A3, A5 }
#define DIODE_DIRECTION COL2ROW
-#define BACKLIGHT_PIN
#define BACKLIGHT_LEVELS 6
#define GRAVE_ESC_CTRL_OVERRIDE
diff --git a/keyboards/handwired/tennie/config.h b/keyboards/handwired/tennie/config.h
index fee1258dfd..b1bddac45b 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/>.
*/
#define MATRIX_ROW_PINS { C6, D4, D0}
#define MATRIX_COL_PINS { D7, E6, B4, B5 }
-#define UNUSED_PINS { B1, B2, B3, B6, F4, F5, F6, F7 D1}
+#define UNUSED_PINS { B1, B2, B3, B6, F4, F5, F6, F7, D1}
/* COL2ROW, ROW2COL */
#define DIODE_DIRECTION COL2ROW
diff --git a/keyboards/mesa/mesa_tkl/config.h b/keyboards/mesa/mesa_tkl/config.h
index f4c26e2a0f..23da260bcf 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/>.
*/
#define MATRIX_ROW_PINS { D2, D1, D0, B0, C6, C7 }
#define MATRIX_COL_PINS { D3, D5, D4, D6, D7, B4, B5, B6, F7, F6, F5, F4, F1, F0, B1, B2, B3 }
-#define UNUSED_PINS { B7 E6 }
+#define UNUSED_PINS { B7, E6 }
/* COL2ROW, ROW2COL*/
#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 63780ef048..6d265d7233 100644
--- a/keyboards/ploopyco/trackball_nano/rev1_001/config.h
+++ b/keyboards/ploopyco/trackball_nano/rev1_001/config.h
@@ -29,7 +29,7 @@
* ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode)
*
*/
-#define DIRECT_PINS {}
+#define DIRECT_PINS {{NO_PIN}}
// These pins are not broken out, and cannot be used normally.
// 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 54cd5b96a8..c0c148f1c0 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
from qmk.path import is_keyboard, normpath
-def direct_pins(direct_pins):
+def direct_pins(direct_pins, postfix):
"""Return the config.h lines that set the direct pins.
"""
rows = []
@@ -24,81 +24,60 @@ def direct_pins(direct_pins):
col_count = len(direct_pins[0])
row_count = len(direct_pins)
- return """
-#ifndef MATRIX_COLS
-# define MATRIX_COLS %s
-#endif // MATRIX_COLS
+ return f"""
+#ifndef MATRIX_COLS{postfix}
+# define MATRIX_COLS{postfix} {col_count}
+#endif // MATRIX_COLS{postfix}
-#ifndef MATRIX_ROWS
-# define MATRIX_ROWS %s
-#endif // MATRIX_ROWS
+#ifndef MATRIX_ROWS{postfix}
+# define MATRIX_ROWS{postfix} {row_count}
+#endif // MATRIX_ROWS{postfix}
-#ifndef DIRECT_PINS
-# define DIRECT_PINS {%s}
-#endif // DIRECT_PINS
-""" % (col_count, row_count, ','.join(rows))
+#ifndef DIRECT_PINS{postfix}
+# define DIRECT_PINS{postfix} {{ {", ".join(rows)} }}
+#endif // DIRECT_PINS{postfix}
+"""
-def pin_array(define, pins):
+def pin_array(define, pins, postfix):
"""Return the config.h lines that set a pin array.
"""
pin_num = len(pins)
pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins]))
return f"""
-#ifndef {define}S
-# define {define}S {pin_num}
-#endif // {define}S
+#ifndef {define}S{postfix}
+# define {define}S{postfix} {pin_num}
+#endif // {define}S{postfix}
-#ifndef {define}_PINS
-# define {define}_PINS {{ {pin_array} }}
-#endif // {define}_PINS
+#ifndef {define}_PINS{postfix}
+# define {define}_PINS{postfix} {{ {pin_array} }}
+#endif // {define}_PINS{postfix}
"""
-def matrix_pins(matrix_pins):
+def matrix_pins(matrix_pins, postfix=''):
"""Add the matrix config to the config.h.
"""
pins = []
if 'direct' in matrix_pins:
- pins.append(direct_pins(matrix_pins['direct']))
+ pins.append(direct_pins(matrix_pins['direct'], postfix))
if 'cols' in matrix_pins:
- pins.append(pin_array('MATRIX_COL', matrix_pins['cols']))
+ pins.append(pin_array('MATRIX_COL', matrix_pins['cols'], postfix))
if 'rows' in matrix_pins:
- pins.append(pin_array('MATRIX_ROW', matrix_pins['rows']))
+ pins.append(pin_array('MATRIX_ROW', matrix_pins['rows'], postfix))
return '\n'.join(pins)
-@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
-@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
-@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
-@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
-@automagic_keyboard
-@automagic_keymap
-def generate_config_h(cli):
- """Generates the info_config.h file.
+def generate_config_items(kb_info_json, config_h_lines):
+ """Iterate through the info_config map to generate basic config values.
"""
- # Determine our keyboard(s)
- if not cli.config.generate_config_h.keyboard:
- cli.log.error('Missing parameter: --keyboard')
- cli.subcommands['info'].print_help()
- return False
-
- if not is_keyboard(cli.config.generate_config_h.keyboard):
- cli.log.error('Invalid keyboard: "%s"', cli.config.generate_config_h.keyboard)
- return False
-
- # Build the info_config.h file.
- kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard))
info_config_map = json_load(Path('data/mappings/info_config.json'))
- config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once']
-
- # Iterate through the info_config map to generate basic things
for config_key, info_dict in info_config_map.items():
info_key = info_dict['info_key']
key_type = info_dict.get('value_type', 'str')
@@ -135,9 +114,78 @@ def generate_config_h(cli):
config_h_lines.append(f'# define {config_key} {config_value}')
config_h_lines.append(f'#endif // {config_key}')
+
+def generate_split_config(kb_info_json, config_h_lines):
+ """Generate the config.h lines for split boards."""
+ if 'primary' in kb_info_json['split']:
+ if kb_info_json['split']['primary'] in ('left', 'right'):
+ config_h_lines.append('')
+ config_h_lines.append('#ifndef MASTER_LEFT')
+ config_h_lines.append('# ifndef MASTER_RIGHT')
+ if kb_info_json['split']['primary'] == 'left':
+ config_h_lines.append('# define MASTER_LEFT')
+ elif kb_info_json['split']['primary'] == 'right':
+ config_h_lines.append('# define MASTER_RIGHT')
+ config_h_lines.append('# endif // MASTER_RIGHT')
+ config_h_lines.append('#endif // MASTER_LEFT')
+ elif kb_info_json['split']['primary'] == 'pin':
+ config_h_lines.append('')
+ config_h_lines.append('#ifndef SPLIT_HAND_PIN')
+ config_h_lines.append('# define SPLIT_HAND_PIN')
+ config_h_lines.append('#endif // SPLIT_HAND_PIN')
+ elif kb_info_json['split']['primary'] == 'matrix_grid':
+ config_h_lines.append('')
+ config_h_lines.append('#ifndef SPLIT_HAND_MATRIX_GRID')
+ config_h_lines.append('# define SPLIT_HAND_MATRIX_GRID {%s}' % (','.join(kb_info_json["split"]["matrix_grid"],)))
+ config_h_lines.append('#endif // SPLIT_HAND_MATRIX_GRID')
+ elif kb_info_json['split']['primary'] == 'eeprom':
+ config_h_lines.append('')
+ config_h_lines.append('#ifndef EE_HANDS')
+ config_h_lines.append('# define EE_HANDS')
+ config_h_lines.append('#endif // EE_HANDS')
+
+ if 'protocol' in kb_info_json['split'].get('transport', {}):
+ if kb_info_json['split']['transport']['protocol'] == 'i2c':
+ config_h_lines.append('')
+ config_h_lines.append('#ifndef USE_I2C')
+ config_h_lines.append('# define USE_I2C')
+ config_h_lines.append('#endif // USE_I2C')
+
+ if 'right' in kb_info_json['split'].get('matrix_pins', {}):
+ config_h_lines.append(matrix_pins(kb_info_json['split']['matrix_pins']['right'], '_RIGHT'))
+
+
+@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
+@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
+@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
+@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
+@automagic_keyboard
+@automagic_keymap
+def generate_config_h(cli):
+ """Generates the info_config.h file.
+ """
+ # Determine our keyboard(s)
+ if not cli.config.generate_config_h.keyboard:
+ cli.log.error('Missing parameter: --keyboard')
+ cli.subcommands['info'].print_help()
+ return False
+
+ if not is_keyboard(cli.config.generate_config_h.keyboard):
+ cli.log.error('Invalid keyboard: "%s"', cli.config.generate_config_h.keyboard)
+ return False
+
+ # Build the info_config.h file.
+ kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard))
+ config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once']
+
+ generate_config_items(kb_info_json, config_h_lines)
+
if 'matrix_pins' in kb_info_json:
config_h_lines.append(matrix_pins(kb_info_json['matrix_pins']))
+ if 'split' in kb_info_json:
+ generate_split_config(kb_info_json, config_h_lines)
+
# Show the results
config_h = '\n'.join(config_h_lines)
diff --git a/lib/python/qmk/cli/generate/info_json.py b/lib/python/qmk/cli/generate/info_json.py
index 8931b68b6f..284d1a8510 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.
"""
import json
-from jsonschema import Draft7Validator, validators
+from argcomplete.completers import FilesCompleter
+from jsonschema import Draft7Validator, RefResolver, validators
from milc import cli
+from pathlib import Path
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json
from qmk.json_encoders import InfoJSONEncoder
-from qmk.json_schema import load_jsonschema
+from qmk.json_schema import compile_schema_store
from qmk.keyboard import keyboard_completer, keyboard_folder
-from qmk.path import is_keyboard
+from qmk.path import is_keyboard, normpath
def pruning_validator(validator_class):
@@ -34,15 +36,19 @@ def pruning_validator(validator_class):
def strip_info_json(kb_info_json):
"""Remove the API-only properties from the info.json.
"""
+ schema_store = compile_schema_store()
pruning_draft_7_validator = pruning_validator(Draft7Validator)
- schema = load_jsonschema('keyboard')
- validator = pruning_draft_7_validator(schema).validate
+ schema = schema_store['qmk.keyboard.v1']
+ resolver = RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
+ validator = pruning_draft_7_validator(schema, resolver=resolver).validate
return validator(kb_info_json)
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
+@cli.argument('-o', '--output', arg_only=True, completer=FilesCompleter, help='Write the output the specified file, overwriting if necessary.')
+@cli.argument('-ow', '--overwrite', arg_only=True, action='store_true', help='Overwrite the existing info.json. (Overrides the location of --output)')
@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)
@automagic_keyboard
@automagic_keymap
@@ -59,9 +65,29 @@ def generate_info_json(cli):
cli.log.error('Invalid keyboard: "%s"', cli.config.generate_info_json.keyboard)
return False
+ if cli.args.overwrite:
+ output_path = (Path('keyboards') / cli.config.generate_info_json.keyboard / 'info.json').resolve()
+
+ if cli.args.output:
+ cli.log.warning('Overwriting user supplied --output with %s', output_path)
+
+ cli.args.output = output_path
+
# Build the info.json file
kb_info_json = info_json(cli.config.generate_info_json.keyboard)
strip_info_json(kb_info_json)
+ info_json_text = json.dumps(kb_info_json, indent=4, cls=InfoJSONEncoder)
+
+ if cli.args.output:
+ # Write to a file
+ output_path = normpath(cli.args.output)
+
+ if output_path.exists():
+ cli.log.warning('Overwriting output file %s', output_path)
+
+ output_path.write_text(info_json_text + '\n')
+ cli.log.info('Wrote info.json to %s.', output_path)
- # Display the results
- print(json.dumps(kb_info_json, indent=2, cls=InfoJSONEncoder))
+ else:
+ # Display the results
+ 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 41c94e16b5..2712b81cb5 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):
enabled = 'yes' if enabled else 'no'
rules_mk_lines.append(f'{feature}_ENABLE ?= {enabled}')
+ # Set SPLIT_TRANSPORT, if needed
+ if kb_info_json.get('split', {}).get('transport', {}).get('protocol') == 'custom':
+ rules_mk_lines.append('SPLIT_TRANSPORT ?= custom')
+
+ # Set CUSTOM_MATRIX, if needed
+ if kb_info_json.get('matrix_pins', {}).get('custom'):
+ if kb_info_json.get('matrix_pins', {}).get('custom_lite'):
+ rules_mk_lines.append('CUSTOM_MATRIX ?= lite')
+ else:
+ rules_mk_lines.append('CUSTOM_MATRIX ?= yes')
+
# Show the results
rules_mk = '\n'.join(rules_mk_lines) + '\n'
diff --git a/lib/python/qmk/cli/info.py b/lib/python/qmk/cli/info.py
index 337b494a99..3131d4b53f 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):
keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap)
if keymap_path and keymap_path.suffix == '.json':
- if title_caps:
- cli.echo('{fg_blue}Keymap "%s"{fg_reset}:', cli.config.info.keymap)
- else:
- cli.echo('{fg_blue}keymap_%s{fg_reset}:', cli.config.info.keymap)
-
keymap_data = json.load(keymap_path.open(encoding='utf-8'))
layout_name = keymap_data['layout']
+ layout_name = kb_info_json.get('layout_aliases', {}).get(layout_name, layout_name) # Resolve alias names
for layer_num, layer in enumerate(keymap_data['layers']):
if title_caps:
- cli.echo('{fg_cyan}Layer %s{fg_reset}:', layer_num)
+ cli.echo('{fg_cyan}Keymap %s Layer %s{fg_reset}:', cli.config.info.keymap, layer_num)
else:
- cli.echo('{fg_cyan}layer_%s{fg_reset}:', layer_num)
+ cli.echo('{fg_cyan}keymap.%s.layer.%s{fg_reset}:', cli.config.info.keymap, layer_num)
print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, layer))
@@ -45,7 +41,7 @@ def show_layouts(kb_info_json, title_caps=True):
"""Render the layouts with info.json labels.
"""
for layout_name, layout_art in render_layouts(kb_info_json, cli.config.info.ascii).items():
- title = layout_name.title() if title_caps else layout_name
+ title = f'Layout {layout_name.title()}' if title_caps else f'layouts.{layout_name}'
cli.echo('{fg_cyan}%s{fg_reset}:', title)
print(layout_art) # Avoid passing dirty data to cli.echo()
@@ -93,15 +89,6 @@ def print_friendly_output(kb_info_json):
aliases = [f'{key}={value}' for key, value in kb_info_json['layout_aliases'].items()]
cli.echo('{fg_blue}Layout aliases:{fg_reset} %s' % (', '.join(aliases),))
- if cli.config.info.layouts:
- show_layouts(kb_info_json, True)
-
- if cli.config.info.matrix:
- show_matrix(kb_info_json, True)
-
- if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
- show_keymap(kb_info_json, True)
-
def print_text_output(kb_info_json):
"""Print the info.json in a plain text format.
@@ -122,6 +109,24 @@ def print_text_output(kb_info_json):
show_keymap(kb_info_json, False)
+def print_dotted_output(kb_info_json, prefix=''):
+ """Print the info.json in a plain text format with dot-joined keys.
+ """
+ for key in sorted(kb_info_json):
+ new_prefix = f'{prefix}.{key}' if prefix else key
+
+ if key in ['parse_errors', 'parse_warnings']:
+ continue
+ elif key == 'layouts' and prefix == '':
+ cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
+ elif isinstance(kb_info_json[key], dict):
+ print_dotted_output(kb_info_json[key], new_prefix)
+ elif isinstance(kb_info_json[key], list):
+ cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, sorted(kb_info_json[key]))))
+ else:
+ cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key])
+
+
def print_parsed_rules_mk(keyboard_name):
rules = rules_mk(keyboard_name)
for k in sorted(rules.keys()):
@@ -162,10 +167,22 @@ def info(cli):
# Output in the requested format
if cli.args.format == 'json':
print(json.dumps(kb_info_json, cls=InfoJSONEncoder))
+ return True
elif cli.args.format == 'text':
- print_text_output(kb_info_json)
+ print_dotted_output(kb_info_json)
+ title_caps = False
elif cli.args.format == 'friendly':
print_friendly_output(kb_info_json)
+ title_caps = True
else:
cli.log.error('Unknown format: %s', cli.args.format)
return False
+
+ if cli.config.info.layouts:
+ show_layouts(kb_info_json, title_caps)
+
+ if cli.config.info.matrix:
+ show_matrix(kb_info_json, title_caps)
+
+ if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
+ show_keymap(kb_info_json, title_caps)
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py
index 858fbab335..b6f2ecf644 100644
--- a/lib/python/qmk/info.py
+++ b/lib/python/qmk/info.py
@@ -61,8 +61,8 @@ def info_json(keyboard):
# Merge in the data from info.json, config.h, and rules.mk
info_data = merge_info_jsons(keyboard, info_data)
- info_data = _extract_config_h(info_data)
info_data = _extract_rules_mk(info_data)
+ info_data = _extract_config_h(info_data)
# Ensure that we have matrix row and column counts
info_data = _matrix_size(info_data)
@@ -161,10 +161,9 @@ def _extract_pins(pins):
return [_pin_name(pin) for pin in pins.split(',')]
-def _extract_direct_matrix(info_data, direct_pins):
+def _extract_direct_matrix(direct_pins):
"""
"""
- info_data['matrix_pins'] = {}
direct_pin_array = []
while direct_pins[-1] != '}':
@@ -188,12 +187,157 @@ def _extract_direct_matrix(info_data, direct_pins):
return direct_pin_array
+def _extract_audio(info_data, config_c):
+ """Populate data about the audio configuration
+ """
+ audio_pins = []
+
+ for pin in 'B5', 'B6', 'B7', 'C4', 'C5', 'C6':
+ if config_c.get(f'{pin}_AUDIO'):
+ audio_pins.append(pin)
+
+ if audio_pins:
+ info_data['audio'] = {'pins': audio_pins}
+
+
+def _extract_split_main(info_data, config_c):
+ """Populate data about the split configuration
+ """
+ # Figure out how the main half is determined
+ if config_c.get('SPLIT_HAND_PIN') is True:
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'main' in info_data['split']:
+ _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'])
+
+ info_data['split']['main'] = 'pin'
+
+ if config_c.get('SPLIT_HAND_MATRIX_GRID'):
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'main' in info_data['split']:
+ _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'])
+
+ info_data['split']['main'] = 'matrix_grid'
+ info_data['split']['matrix_grid'] = _extract_pins(config_c['SPLIT_HAND_MATRIX_GRID'])
+
+ if config_c.get('EE_HANDS') is True:
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'main' in info_data['split']:
+ _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'])
+
+ info_data['split']['main'] = 'eeprom'
+
+ if config_c.get('MASTER_RIGHT') is True:
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'main' in info_data['split']:
+ _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'])
+
+ info_data['split']['main'] = 'right'
+
+ if config_c.get('MASTER_LEFT') is True:
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'main' in info_data['split']:
+ _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'])
+
+ info_data['split']['main'] = 'left'
+
+
+def _extract_split_transport(info_data, config_c):
+ # Figure out the transport method
+ if config_c.get('USE_I2C') is True:
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'transport' not in info_data['split']:
+ info_data['split']['transport'] = {}
+
+ if 'protocol' in info_data['split']['transport']:
+ _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'])
+
+ info_data['split']['transport']['protocol'] = 'i2c'
+
+ elif 'protocol' not in info_data.get('split', {}).get('transport', {}):
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'transport' not in info_data['split']:
+ info_data['split']['transport'] = {}
+
+ info_data['split']['transport']['protocol'] = 'serial'
+
+
+def _extract_split_right_pins(info_data, config_c):
+ # Figure out the right half matrix pins
+ row_pins = config_c.get('MATRIX_ROW_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()
+ col_pins = config_c.get('MATRIX_COL_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()
+ unused_pin_text = config_c.get('UNUSED_PINS_RIGHT')
+ unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None
+ direct_pins = config_c.get('DIRECT_PINS_RIGHT', '').replace(' ', '')[1:-1]
+
+ if row_pins and col_pins:
+ if info_data.get('split', {}).get('matrix_pins', {}).get('right') in info_data:
+ _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
+
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'matrix_pins' not in info_data['split']:
+ info_data['split']['matrix_pins'] = {}
+
+ if 'right' not in info_data['split']['matrix_pins']:
+ info_data['split']['matrix_pins']['right'] = {}
+
+ info_data['split']['matrix_pins']['right'] = {
+ 'cols': _extract_pins(col_pins),
+ 'rows': _extract_pins(row_pins),
+ }
+
+ if direct_pins:
+ if info_data.get('split', {}).get('matrix_pins', {}).get('right', {}):
+ _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
+
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'matrix_pins' not in info_data['split']:
+ info_data['split']['matrix_pins'] = {}
+
+ if 'right' not in info_data['split']['matrix_pins']:
+ info_data['split']['matrix_pins']['right'] = {}
+
+ info_data['split']['matrix_pins']['right']['direct'] = _extract_direct_matrix(direct_pins)
+
+ if unused_pins:
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'matrix_pins' not in info_data['split']:
+ info_data['split']['matrix_pins'] = {}
+
+ if 'right' not in info_data['split']['matrix_pins']:
+ info_data['split']['matrix_pins']['right'] = {}
+
+ info_data['split']['matrix_pins']['right']['unused'] = _extract_pins(unused_pins)
+
+
def _extract_matrix_info(info_data, config_c):
"""Populate the matrix information.
"""
row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip()
col_pins = config_c.get('MATRIX_COL_PINS', '').replace('{', '').replace('}', '').strip()
+ unused_pin_text = config_c.get('UNUSED_PINS')
+ unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None
direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1]
+ info_snippet = {}
if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c:
if 'matrix_size' in info_data:
@@ -205,19 +349,35 @@ def _extract_matrix_info(info_data, config_c):
}
if row_pins and col_pins:
- if 'matrix_pins' in info_data:
+ if 'matrix_pins' in info_data and 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:
_log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.')
- info_data['matrix_pins'] = {
- 'cols': _extract_pins(col_pins),
- 'rows': _extract_pins(row_pins),
- }
+ info_snippet['cols'] = _extract_pins(col_pins)
+ info_snippet['rows'] = _extract_pins(row_pins)
if direct_pins:
- if 'matrix_pins' in info_data:
+ if 'matrix_pins' in info_data and 'direct' in info_data['matrix_pins']:
_log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.')
- info_data['matrix_pins']['direct'] = _extract_direct_matrix(info_data, direct_pins)
+ info_snippet['direct'] = _extract_direct_matrix(direct_pins)
+
+ if unused_pins:
+ if 'matrix_pins' not in info_data:
+ info_data['matrix_pins'] = {}
+
+ info_snippet['unused'] = _extract_pins(unused_pins)
+
+ if config_c.get('CUSTOM_MATRIX', 'no') != 'no':
+ if 'matrix_pins' in info_data and 'custom' in info_data['matrix_pins']:
+ _log_warning(info_data, 'Custom Matrix is specified in both info.json and config.h, the config.h values win.')
+
+ info_snippet['custom'] = True
+
+ if config_c['CUSTOM_MATRIX'] == 'lite':
+ info_snippet['custom_lite'] = True
+
+ if info_snippet:
+ info_data['matrix_pins'] = info_snippet
return info_data
@@ -275,6 +435,10 @@ def _extract_config_h(info_data):
# Pull data that easily can't be mapped in json
_extract_matrix_info(info_data, config_c)
+ _extract_audio(info_data, config_c)
+ _extract_split_main(info_data, config_c)
+ _extract_split_transport(info_data, config_c)
+ _extract_split_right_pins(info_data, config_c)
return info_data
diff --git a/lib/python/qmk/json_schema.py b/lib/python/qmk/json_schema.py
index cbc5bff518..ffc7c6bcd1 100644
--- a/lib/python/qmk/json_schema.py
+++ b/lib/python/qmk/json_schema.py
@@ -2,6 +2,7 @@
"""
import json
from collections.abc import Mapping
+from functools import lru_cache
from pathlib import Path
import hjson
@@ -25,6 +26,7 @@ def json_load(json_file):
exit(1)
+@lru_cache(maxsize=0)
def load_jsonschema(schema_name):
"""Read a jsonschema file from disk.
"""
@@ -39,8 +41,9 @@ def load_jsonschema(schema_name):
return json_load(schema_path)
-def create_validator(schema):
- """Creates a validator for the given schema id.
+@lru_cache(maxsize=0)
+def compile_schema_store():
+ """Compile all our schemas into a schema store.
"""
schema_store = {}
@@ -51,6 +54,14 @@ def create_validator(schema):
continue
schema_store[schema_data['$id']] = schema_data
+ return schema_store
+
+
+@lru_cache(maxsize=0)
+def create_validator(schema):
+ """Creates a validator for the given schema id.
+ """
+ schema_store = compile_schema_store()
resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate