Files
qmk_firmware/lib/python/qmk/cli/kle2json.py
2021-01-08 17:21:55 -08:00

170 lines
5.4 KiB
Python
Executable File

"""Convert raw KLE to JSON
"""
import json
import os
from pathlib import Path
import requests
from milc import cli
from kle2xy import KLE2xy
import qmk.path
from qmk.converter import kle2qmk
from qmk.info import info_json
from qmk.info_json_encoder import InfoJSONEncoder
def fetch_json(url):
"""Gets the JSON from a url.
"""
response = fetch_url(url)
if response.status_code == 200:
return response.json()
print(f'ERROR: {url} returned {response.status_code}: {response.text}')
return {}
def fetch_url(url):
"""Fetch a URL.
"""
response = requests.get(url, timeout=30)
response.encoding = 'utf-8-sig'
return response
def fetch_gist(id):
"""Retrieve a gist from gist.github.com
"""
url = f'https://api.github.com/gists/{id}'
gist = fetch_json(url)
for data in gist['files'].values():
if data['filename'].endswith('kbd.json'):
if data.get('truncated'):
return fetch_url(data['raw_url']).text
else:
return data['content']
return None
def fetch_kle(id):
"""Fetch the kle data from a gist ID.
"""
gist = fetch_gist(id)
return gist[1:-1]
@cli.argument('kle', arg_only=True, help='A file or KLE id to convert')
@cli.argument('-l', '--layout', arg_only=True, default='LAYOUT', help='The LAYOUT name this KLE represents')
@cli.argument('-kb', '--keyboard', arg_only=True, required=True, help='The folder name for the keyboard')
@cli.argument('-km', '--keymap', arg_only=True, default='default', help='The name of the keymap to write (Default: default)')
@cli.subcommand('Use a KLE layout to build info.json and a keymap', hidden=False if cli.config.user.developer else True)
def kle2json(cli):
"""Convert a KLE layout to QMK's layout format.
"""
file_path = Path(os.environ['ORIG_CWD'], cli.args.kle)
# Find our KLE text
if cli.args.kle.startswith('http') and '#' in cli.args.kle:
kle_path = cli.args.kle.split('#', 1)[1]
if 'gists' not in kle_path:
cli.log.error('Invalid KLE url: {fg_cyan}%s', cli.args.kle)
return False
else:
raw_code = fetch_kle(kle_path.split('/')[-1])
elif file_path.exists():
raw_code = file_path.open().read()
else:
raw_code = fetch_kle(cli.args.kle)
if not raw_code:
cli.log.error('File {fg_cyan}%s{style_reset_all} was not found.', file_path)
return False
# Make sure the user supplied a keyboard
if not cli.args.keyboard:
cli.log.error('You must pass --keyboard or be in a keyboard directory!')
cli.print_usage()
return False
# Check for an existing info.json
if qmk.path.is_keyboard(cli.args.keyboard):
kb_info_json = info_json(cli.args.keyboard)
else:
kb_info_json = {
"keyboard_name": cli.args.keyboard,
"maintainer": "",
"features": {
"console": True,
"extrakey": True,
"mousekey": True,
"nkro": True
},
"matrix_pins": {
"cols": [],
"rows": [],
},
"usb": {
"device_ver": "0x0001",
"pid": '0x0000',
"vid": '0x03A8',
},
"layouts": {},
}
# Build and merge in the new layout
try:
# Convert KLE raw to x/y coordinates (using kle2xy package from skullydazed)
kle = KLE2xy(raw_code)
except Exception as e:
cli.log.error('Could not parse KLE raw data: %s', raw_code)
cli.log.exception(e)
return False
if 'layouts' not in kb_info_json:
kb_info_json['layouts'] = {}
if cli.args.layout not in kb_info_json['layouts']:
kb_info_json['layouts'][cli.args.layout] = {}
kb_info_json['layouts'][cli.args.layout]['layout'] = kle2qmk(kle)
# Write our info.json
keyboard_dir = qmk.path.keyboard(cli.args.keyboard)
keyboard_dir.mkdir(exist_ok=True, parents=True)
info_json_file = keyboard_dir / 'info.json'
json.dump(kb_info_json, info_json_file.open('w', newline='\n'), indent=4, separators=(', ', ': '), sort_keys=False, cls=InfoJSONEncoder)
cli.log.info('Wrote file {fg_cyan}%s', info_json_file)
# Generate and write a keymap
keymap_path = keyboard_dir / 'keymaps' / cli.args.keymap
keymap_file = keymap_path / 'keymap.json'
if keymap_path.exists():
cli.log.warning('{fg_cyan}%s{fg_reset} already exists, not generating a keymap.', keymap_path)
else:
keymap = [key.get('label', 'KC_NO') for key in kb_info_json['layouts'][cli.args.layout]['layout']]
keymap_json = {
'version': 1,
'documentation': "This file is a QMK Keymap. You can compile it with `qmk compile` or import it at <https://config.qmk.fm>. It can also be used directly with QMK's source code.",
'author': '',
'keyboard': kb_info_json['keyboard_name'],
'keymap': cli.args.keymap,
'layout': cli.args.layout,
'layers': [
keymap,
['KC_TRNS' for key in keymap],
],
}
keymap_path.mkdir(exist_ok=True, parents=True)
json.dump(keymap_json, keymap_file.open('w', newline='\n'), indent=4, separators=(', ', ': '), sort_keys=False)
cli.log.info('Wrote file %s', keymap_file)