qmk-firmware/lib/python/qmk/cli/info.py
Zach White 8d9bfdc254
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>
2021-08-16 23:33:30 +01:00

189 lines
7.6 KiB
Python
Executable File

"""Keyboard information script.
Compile an info.json for a particular keyboard and pretty-print it.
"""
import sys
import json
from milc import cli
from qmk.json_encoders import InfoJSONEncoder
from qmk.constants import COL_LETTERS, ROW_LETTERS
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.keyboard import keyboard_completer, keyboard_folder, render_layouts, render_layout, rules_mk
from qmk.keymap import locate_keymap
from qmk.info import info_json
from qmk.path import is_keyboard
UNICODE_SUPPORT = sys.stdout.encoding.lower().startswith('utf')
def show_keymap(kb_info_json, title_caps=True):
"""Render the keymap in ascii art.
"""
keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap)
if keymap_path and keymap_path.suffix == '.json':
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}Keymap %s Layer %s{fg_reset}:', cli.config.info.keymap, layer_num)
else:
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))
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 = 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()
def show_matrix(kb_info_json, title_caps=True):
"""Render the layout with matrix labels in ascii art.
"""
for layout_name, layout in kb_info_json['layouts'].items():
# Build our label list
labels = []
for key in layout['layout']:
if 'matrix' in key:
row = ROW_LETTERS[key['matrix'][0]]
col = COL_LETTERS[key['matrix'][1]]
labels.append(row + col)
else:
labels.append('')
# Print the header
if title_caps:
cli.echo('{fg_blue}Matrix for "%s"{fg_reset}:', layout_name)
else:
cli.echo('{fg_blue}matrix_%s{fg_reset}:', layout_name)
print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, labels))
def print_friendly_output(kb_info_json):
"""Print the info.json in a friendly text format.
"""
cli.echo('{fg_blue}Keyboard Name{fg_reset}: %s', kb_info_json.get('keyboard_name', 'Unknown'))
cli.echo('{fg_blue}Manufacturer{fg_reset}: %s', kb_info_json.get('manufacturer', 'Unknown'))
if 'url' in kb_info_json:
cli.echo('{fg_blue}Website{fg_reset}: %s', kb_info_json.get('url', ''))
if kb_info_json.get('maintainer', 'qmk') == 'qmk':
cli.echo('{fg_blue}Maintainer{fg_reset}: QMK Community')
else:
cli.echo('{fg_blue}Maintainer{fg_reset}: %s', kb_info_json['maintainer'])
cli.echo('{fg_blue}Keyboard Folder{fg_reset}: %s', kb_info_json.get('keyboard_folder', 'Unknown'))
cli.echo('{fg_blue}Layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
cli.echo('{fg_blue}Processor{fg_reset}: %s', kb_info_json.get('processor', 'Unknown'))
cli.echo('{fg_blue}Bootloader{fg_reset}: %s', kb_info_json.get('bootloader', 'Unknown'))
if 'layout_aliases' in 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),))
def print_text_output(kb_info_json):
"""Print the info.json in a plain text format.
"""
for key in sorted(kb_info_json):
if key == 'layouts':
cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
else:
cli.echo('{fg_blue}%s{fg_reset}: %s', key, kb_info_json[key])
if cli.config.info.layouts:
show_layouts(kb_info_json, False)
if cli.config.info.matrix:
show_matrix(kb_info_json, False)
if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
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()):
print('%s = %s' % (k, rules[k]))
return
@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('-l', '--layouts', action='store_true', help='Render the layouts.')
@cli.argument('-m', '--matrix', action='store_true', help='Render the layouts with matrix information.')
@cli.argument('-f', '--format', default='friendly', arg_only=True, help='Format to display the data in (friendly, text, json) (Default: friendly).')
@cli.argument('--ascii', action='store_true', default=not UNICODE_SUPPORT, help='Render layout box drawings in ASCII only.')
@cli.argument('-r', '--rules-mk', action='store_true', help='Render the parsed values of the keyboard\'s rules.mk file.')
@cli.subcommand('Keyboard information.')
@automagic_keyboard
@automagic_keymap
def info(cli):
"""Compile an info.json for a particular keyboard and pretty-print it.
"""
# Determine our keyboard(s)
if not cli.config.info.keyboard:
cli.log.error('Missing parameter: --keyboard')
cli.subcommands['info'].print_help()
return False
if not is_keyboard(cli.config.info.keyboard):
cli.log.error('Invalid keyboard: "%s"', cli.config.info.keyboard)
return False
if bool(cli.args.rules_mk):
print_parsed_rules_mk(cli.config.info.keyboard)
return False
# Build the info.json file
kb_info_json = info_json(cli.config.info.keyboard)
# 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_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)