"""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 . 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)