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>
master
Zach White 2021-08-16 15:33:30 -07:00 committed by GitHub
parent fac717c11c
commit 8d9bfdc254
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 654 additions and 99 deletions

View File

@ -3,19 +3,44 @@
{ {
# Format: # Format:
# <config.h key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]} # <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_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 # 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 # 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"}, "DEVICE_VER": {"info_key": "usb.device_ver", "value_type": "hex"},
"DESCRIPTION": {"info_key": "keyboard_folder", "to_json": false}, "DESCRIPTION": {"info_key": "keyboard_folder", "to_json": false},
"DIODE_DIRECTION": {"info_key": "diode_direction"}, "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"}, "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_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"},
"LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"}, "LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"},
"LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"}, "LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"},
"MANUFACTURER": {"info_key": "manufacturer"}, "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"}, "RGB_DI_PIN": {"info_key": "rgblight.pin"},
"RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"}, "RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"},
"RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.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_SNAKE": {"info_key": "rgblight.animations.snake", "value_type": "bool"},
"RGBLIGHT_EFFECT_STATIC_GRADIENT": {"info_key": "rgblight.animations.static_gradient", "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_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_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_HUE_STEP": {"info_key": "rgblight.hue_steps", "value_type": "int"},
"RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_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_VAL_STEP": {"info_key": "rgblight.brightness_steps", "value_type": "int"},
"RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"}, "RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"},
"RGBLIGHT_SPLIT": {"info_key": "rgblight.split", "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": {"info_key": "keyboard_folder", "to_json": false},
"PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex"}, "PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex"},
"VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex"}, "VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex"},
"QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"}, "QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"},
"QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"}, "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_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"},
} }

View File

@ -3,13 +3,23 @@
{ {
# Format: # Format:
# <rules.mk key>: {"info_key": <info.json key>, ["value_type": <value_type>], ["to_json": <true/false>], ["to_c": <true/false>]} # <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_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 # 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 # warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places
"BOARD": {"info_key": "board"}, "BOARD": {"info_key": "board"},
"BOOTLOADER": {"info_key": "bootloader", "warn_duplicate": false}, "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"}, "LAYOUTS": {"info_key": "community_layouts", "value_type": "list"},
"LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"}, "LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"},
"LTO_ENABLE": {"info_key": "build.lto", "value_type": "bool"},
"MCU": {"info_key": "processor", "warn_duplicate": false}, "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"}
} }

View File

@ -15,6 +15,40 @@
"type": "string", "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"] "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": { "board": {
"type": "string", "type": "string",
"minLength": 2, "minLength": 2,
@ -22,13 +56,39 @@
}, },
"bootloader": { "bootloader": {
"type": "string", "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": { "diode_direction": {
"type": "string", "type": "string",
"enum": ["COL2ROW", "ROW2COL"] "enum": ["COL2ROW", "ROW2COL"]
}, },
"debounce": {"$ref": "qmk.definitions.v1#/unsigned_int"}, "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": { "community_layouts": {
"type": "array", "type": "array",
"items": {"$ref": "qmk.definitions.v1#/filename"} "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": { "matrix_pins": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"custom": {"type": "boolean"},
"custom_lite": {"type": "boolean"},
"ghost": {"type": "boolean"},
"io_delay": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"direct": { "direct": {
"type": "array", "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"}, "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": { "rgblight": {
@ -114,9 +205,25 @@
}, },
"brightness_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, "brightness_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"hue_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"}, "led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, "max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
"pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}, "pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
"rgbw": {"type": "boolean"},
"saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, "saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"sleep": {"type": "boolean"}, "sleep": {"type": "boolean"},
"split": {"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": { "usb": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"device_ver": {"$ref": "qmk.definitions.v1#/hex_number_4d"}, "device_ver": {"$ref": "qmk.definitions.v1#/hex_number_4d"},
"force_nkro": {"type": "boolean"},
"pid": {"$ref": "qmk.definitions.v1#/hex_number_4d"}, "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": { "qmk_lufa_bootloader": {

View File

@ -40,7 +40,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* /*
* Keyboard Matrix Assignments * 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 } #define UNUSED_PINS { C0, C1, C2, C3, C4, D2, D7 }
/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */ /* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */

View File

@ -40,7 +40,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* /*
* Keyboard Matrix Assignments * 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 UNUSED_PINS { B0, C4, D3 }
#define LED_NUM_LOCK_PIN C5 #define LED_NUM_LOCK_PIN C5

View File

@ -34,7 +34,7 @@
#define MATRIX_ROW_PINS { B0, B1, B2, B3, B4, B5, B6, B7, C6, C7, D0 } #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 */ /* 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 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 //NOTE: if D6 pin shows any issues in exploatation the LED on the Teensy is to be removed

View File

@ -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 MATRIX_ROW_PINS { A9, A8, A3, A5 }
#define DIODE_DIRECTION COL2ROW #define DIODE_DIRECTION COL2ROW
#define BACKLIGHT_PIN
#define BACKLIGHT_LEVELS 6 #define BACKLIGHT_LEVELS 6
#define GRAVE_ESC_CTRL_OVERRIDE #define GRAVE_ESC_CTRL_OVERRIDE

View File

@ -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 MATRIX_ROW_PINS { A9, A8, A3, A5 }
#define DIODE_DIRECTION COL2ROW #define DIODE_DIRECTION COL2ROW
#define BACKLIGHT_PIN
#define BACKLIGHT_LEVELS 6 #define BACKLIGHT_LEVELS 6
#define GRAVE_ESC_CTRL_OVERRIDE #define GRAVE_ESC_CTRL_OVERRIDE

View File

@ -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_ROW_PINS { C6, D4, D0}
#define MATRIX_COL_PINS { D7, E6, B4, B5 } #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 */ /* COL2ROW, ROW2COL */
#define DIODE_DIRECTION COL2ROW #define DIODE_DIRECTION COL2ROW

View File

@ -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_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 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*/ /* COL2ROW, ROW2COL*/
#define DIODE_DIRECTION COL2ROW #define DIODE_DIRECTION COL2ROW

View File

@ -29,7 +29,7 @@
* ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode) * 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. // These pins are not broken out, and cannot be used normally.
// They are set as output and pulled high, by default // They are set as output and pulled high, by default

View File

@ -12,7 +12,7 @@ from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.path import is_keyboard, normpath 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. """Return the config.h lines that set the direct pins.
""" """
rows = [] rows = []
@ -24,81 +24,60 @@ def direct_pins(direct_pins):
col_count = len(direct_pins[0]) col_count = len(direct_pins[0])
row_count = len(direct_pins) row_count = len(direct_pins)
return """ return f"""
#ifndef MATRIX_COLS #ifndef MATRIX_COLS{postfix}
# define MATRIX_COLS %s # define MATRIX_COLS{postfix} {col_count}
#endif // MATRIX_COLS #endif // MATRIX_COLS{postfix}
#ifndef MATRIX_ROWS #ifndef MATRIX_ROWS{postfix}
# define MATRIX_ROWS %s # define MATRIX_ROWS{postfix} {row_count}
#endif // MATRIX_ROWS #endif // MATRIX_ROWS{postfix}
#ifndef DIRECT_PINS #ifndef DIRECT_PINS{postfix}
# define DIRECT_PINS {%s} # define DIRECT_PINS{postfix} {{ {", ".join(rows)} }}
#endif // DIRECT_PINS #endif // DIRECT_PINS{postfix}
""" % (col_count, row_count, ','.join(rows)) """
def pin_array(define, pins): def pin_array(define, pins, postfix):
"""Return the config.h lines that set a pin array. """Return the config.h lines that set a pin array.
""" """
pin_num = len(pins) pin_num = len(pins)
pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins])) pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins]))
return f""" return f"""
#ifndef {define}S #ifndef {define}S{postfix}
# define {define}S {pin_num} # define {define}S{postfix} {pin_num}
#endif // {define}S #endif // {define}S{postfix}
#ifndef {define}_PINS #ifndef {define}_PINS{postfix}
# define {define}_PINS {{ {pin_array} }} # define {define}_PINS{postfix} {{ {pin_array} }}
#endif // {define}_PINS #endif // {define}_PINS{postfix}
""" """
def matrix_pins(matrix_pins): def matrix_pins(matrix_pins, postfix=''):
"""Add the matrix config to the config.h. """Add the matrix config to the config.h.
""" """
pins = [] pins = []
if 'direct' in matrix_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: 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: 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) return '\n'.join(pins)
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') def generate_config_items(kb_info_json, config_h_lines):
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") """Iterate through the info_config map to generate basic config values.
@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))
info_config_map = json_load(Path('data/mappings/info_config.json')) 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(): for config_key, info_dict in info_config_map.items():
info_key = info_dict['info_key'] info_key = info_dict['info_key']
key_type = info_dict.get('value_type', 'str') 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'# define {config_key} {config_value}')
config_h_lines.append(f'#endif // {config_key}') 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: if 'matrix_pins' in kb_info_json:
config_h_lines.append(matrix_pins(kb_info_json['matrix_pins'])) 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 # Show the results
config_h = '\n'.join(config_h_lines) config_h = '\n'.join(config_h_lines)

View File

@ -4,15 +4,17 @@ Compile an info.json for a particular keyboard and pretty-print it.
""" """
import json import json
from jsonschema import Draft7Validator, validators from argcomplete.completers import FilesCompleter
from jsonschema import Draft7Validator, RefResolver, validators
from milc import cli from milc import cli
from pathlib import Path
from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json from qmk.info import info_json
from qmk.json_encoders import InfoJSONEncoder 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.keyboard import keyboard_completer, keyboard_folder
from qmk.path import is_keyboard from qmk.path import is_keyboard, normpath
def pruning_validator(validator_class): def pruning_validator(validator_class):
@ -34,15 +36,19 @@ def pruning_validator(validator_class):
def strip_info_json(kb_info_json): def strip_info_json(kb_info_json):
"""Remove the API-only properties from the info.json. """Remove the API-only properties from the info.json.
""" """
schema_store = compile_schema_store()
pruning_draft_7_validator = pruning_validator(Draft7Validator) pruning_draft_7_validator = pruning_validator(Draft7Validator)
schema = load_jsonschema('keyboard') schema = schema_store['qmk.keyboard.v1']
validator = pruning_draft_7_validator(schema).validate 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) return validator(kb_info_json)
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.') @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('-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) @cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)
@automagic_keyboard @automagic_keyboard
@automagic_keymap @automagic_keymap
@ -59,9 +65,29 @@ def generate_info_json(cli):
cli.log.error('Invalid keyboard: "%s"', cli.config.generate_info_json.keyboard) cli.log.error('Invalid keyboard: "%s"', cli.config.generate_info_json.keyboard)
return False 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 # Build the info.json file
kb_info_json = info_json(cli.config.generate_info_json.keyboard) kb_info_json = info_json(cli.config.generate_info_json.keyboard)
strip_info_json(kb_info_json) strip_info_json(kb_info_json)
info_json_text = json.dumps(kb_info_json, indent=4, cls=InfoJSONEncoder)
# Display the results if cli.args.output:
print(json.dumps(kb_info_json, indent=2, cls=InfoJSONEncoder)) # 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)
else:
# Display the results
print(info_json_text)

View File

@ -76,6 +76,17 @@ def generate_rules_mk(cli):
enabled = 'yes' if enabled else 'no' enabled = 'yes' if enabled else 'no'
rules_mk_lines.append(f'{feature}_ENABLE ?= {enabled}') 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 # Show the results
rules_mk = '\n'.join(rules_mk_lines) + '\n' rules_mk = '\n'.join(rules_mk_lines) + '\n'

View File

@ -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) keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap)
if keymap_path and keymap_path.suffix == '.json': 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')) keymap_data = json.load(keymap_path.open(encoding='utf-8'))
layout_name = keymap_data['layout'] 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']): for layer_num, layer in enumerate(keymap_data['layers']):
if title_caps: 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: 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)) 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. """Render the layouts with info.json labels.
""" """
for layout_name, layout_art in render_layouts(kb_info_json, cli.config.info.ascii).items(): 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) cli.echo('{fg_cyan}%s{fg_reset}:', title)
print(layout_art) # Avoid passing dirty data to cli.echo() 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()] 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),)) 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): def print_text_output(kb_info_json):
"""Print the info.json in a plain text format. """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) 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): def print_parsed_rules_mk(keyboard_name):
rules = rules_mk(keyboard_name) rules = rules_mk(keyboard_name)
for k in sorted(rules.keys()): for k in sorted(rules.keys()):
@ -162,10 +167,22 @@ def info(cli):
# Output in the requested format # Output in the requested format
if cli.args.format == 'json': if cli.args.format == 'json':
print(json.dumps(kb_info_json, cls=InfoJSONEncoder)) print(json.dumps(kb_info_json, cls=InfoJSONEncoder))
return True
elif cli.args.format == 'text': 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': elif cli.args.format == 'friendly':
print_friendly_output(kb_info_json) print_friendly_output(kb_info_json)
title_caps = True
else: else:
cli.log.error('Unknown format: %s', cli.args.format) cli.log.error('Unknown format: %s', cli.args.format)
return False 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)

View File

@ -61,8 +61,8 @@ def info_json(keyboard):
# Merge in the data from info.json, config.h, and rules.mk # Merge in the data from info.json, config.h, and rules.mk
info_data = merge_info_jsons(keyboard, info_data) 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_rules_mk(info_data)
info_data = _extract_config_h(info_data)
# Ensure that we have matrix row and column counts # Ensure that we have matrix row and column counts
info_data = _matrix_size(info_data) info_data = _matrix_size(info_data)
@ -161,10 +161,9 @@ def _extract_pins(pins):
return [_pin_name(pin) for pin in pins.split(',')] 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 = [] direct_pin_array = []
while direct_pins[-1] != '}': while direct_pins[-1] != '}':
@ -188,12 +187,157 @@ def _extract_direct_matrix(info_data, direct_pins):
return direct_pin_array 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): def _extract_matrix_info(info_data, config_c):
"""Populate the matrix information. """Populate the matrix information.
""" """
row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip() row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip()
col_pins = config_c.get('MATRIX_COL_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] 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_ROWS' in config_c and 'MATRIX_COLS' in config_c:
if 'matrix_size' in info_data: 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 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.') _log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.')
info_data['matrix_pins'] = { info_snippet['cols'] = _extract_pins(col_pins)
'cols': _extract_pins(col_pins), info_snippet['rows'] = _extract_pins(row_pins)
'rows': _extract_pins(row_pins),
}
if direct_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.') _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 return info_data
@ -275,6 +435,10 @@ def _extract_config_h(info_data):
# Pull data that easily can't be mapped in json # Pull data that easily can't be mapped in json
_extract_matrix_info(info_data, config_c) _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 return info_data

View File

@ -2,6 +2,7 @@
""" """
import json import json
from collections.abc import Mapping from collections.abc import Mapping
from functools import lru_cache
from pathlib import Path from pathlib import Path
import hjson import hjson
@ -25,6 +26,7 @@ def json_load(json_file):
exit(1) exit(1)
@lru_cache(maxsize=0)
def load_jsonschema(schema_name): def load_jsonschema(schema_name):
"""Read a jsonschema file from disk. """Read a jsonschema file from disk.
""" """
@ -39,8 +41,9 @@ def load_jsonschema(schema_name):
return json_load(schema_path) return json_load(schema_path)
def create_validator(schema): @lru_cache(maxsize=0)
"""Creates a validator for the given schema id. def compile_schema_store():
"""Compile all our schemas into a schema store.
""" """
schema_store = {} schema_store = {}
@ -51,6 +54,14 @@ def create_validator(schema):
continue continue
schema_store[schema_data['$id']] = schema_data 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) resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)
return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate