2020-10-25 21:48:44 +00:00
""" This script automates the generation of the QMK API data.
"""
from pathlib import Path
2022-02-27 11:28:51 +00:00
import shutil
2022-05-06 22:11:16 +00:00
import hjson
2020-10-25 21:48:44 +00:00
import json
from milc import cli
from qmk . datetime import current_datetime
from qmk . info import info_json
2021-03-25 11:38:10 +00:00
from qmk . json_encoders import InfoJSONEncoder
2021-03-24 16:26:38 +00:00
from qmk . json_schema import json_load
2021-05-25 02:38:27 +00:00
from qmk . keyboard import find_readme , list_keyboards
2022-05-06 22:11:16 +00:00
from qmk . xap . common import get_xap_definition_files , update_xap_definitions
2020-10-25 21:48:44 +00:00
2022-05-06 22:11:16 +00:00
DATA_PATH = Path ( ' data ' )
TEMPLATE_PATH = DATA_PATH / ' templates/api/ '
2022-02-27 11:28:51 +00:00
BUILD_API_PATH = Path ( ' .build/api_data/ ' )
2020-10-25 21:48:44 +00:00
2022-05-06 22:11:16 +00:00
def _filtered_keyboard_list ( ) :
""" Perform basic filtering of list_keyboards
"""
keyboard_list = list_keyboards ( )
if cli . args . filter :
kb_list = [ ]
for keyboard_name in keyboard_list :
if any ( i in keyboard_name for i in cli . args . filter ) :
kb_list . append ( keyboard_name )
keyboard_list = kb_list
return keyboard_list
def _resolve_xap_specs ( output_folder ) :
""" To make it easier for consumers, replace specs with pre-merged versions
"""
overall = None
for file in get_xap_definition_files ( ) :
overall = update_xap_definitions ( overall , hjson . load ( file . open ( encoding = ' utf-8 ' ) ) )
# Inject dummy bits for unspecified response flags
for n in range ( 0 , 8 ) :
if str ( n ) not in overall [ ' response_flags ' ] [ ' bits ' ] :
overall [ ' response_flags ' ] [ ' bits ' ] [ str ( n ) ] = { ' name ' : ' ' , ' description ' : ' ' , ' define ' : ' - ' }
hjson . dump ( overall , ( output_folder / file . name ) . open ( mode = ' w ' , encoding = ' utf-8 ' ) )
2021-05-10 18:18:44 +00:00
@cli.argument ( ' -n ' , ' --dry-run ' , arg_only = True , action = ' store_true ' , help = " Don ' t write the data to disk. " )
2022-02-27 11:28:51 +00:00
@cli.argument ( ' -f ' , ' --filter ' , arg_only = True , action = ' append ' , default = [ ] , help = " Filter the list of keyboards based on partial name matches the supplied value. May be passed multiple times. " )
2022-05-06 22:11:16 +00:00
@cli.subcommand ( ' Generate QMK API data ' , hidden = False if cli . config . user . developer else True )
2020-10-25 21:48:44 +00:00
def generate_api ( cli ) :
""" Generates the QMK API data.
"""
2022-02-27 11:28:51 +00:00
v1_dir = BUILD_API_PATH / ' v1 '
2021-03-25 03:33:25 +00:00
keyboard_all_file = v1_dir / ' keyboards.json ' # A massive JSON containing everything
keyboard_list_file = v1_dir / ' keyboard_list.json ' # A simple list of keyboard targets
keyboard_aliases_file = v1_dir / ' keyboard_aliases.json ' # A list of historical keyboard names and their new name
2021-03-24 16:26:38 +00:00
keyboard_metadata_file = v1_dir / ' keyboard_metadata.json ' # All the data configurator/via needs for initialization
2021-03-25 03:33:25 +00:00
usb_file = v1_dir / ' usb.json ' # A mapping of USB VID/PID -> keyboard target
2020-10-25 21:48:44 +00:00
2022-05-06 22:11:16 +00:00
if BUILD_API_PATH . exists ( ) :
shutil . rmtree ( BUILD_API_PATH )
shutil . copytree ( TEMPLATE_PATH , BUILD_API_PATH )
shutil . copytree ( DATA_PATH , v1_dir )
2022-02-27 11:28:51 +00:00
# Filter down when required
2022-05-06 22:11:16 +00:00
keyboard_list = _filtered_keyboard_list ( )
2020-10-25 21:48:44 +00:00
2021-03-24 16:26:38 +00:00
kb_all = { }
usb_list = { }
2020-10-25 21:48:44 +00:00
# Generate and write keyboard specific JSON files
2022-02-27 11:28:51 +00:00
for keyboard_name in keyboard_list :
2021-03-24 16:26:38 +00:00
kb_all [ keyboard_name ] = info_json ( keyboard_name )
2020-10-25 21:48:44 +00:00
keyboard_dir = v1_dir / ' keyboards ' / keyboard_name
keyboard_info = keyboard_dir / ' info.json '
keyboard_readme = keyboard_dir / ' readme.md '
2021-05-25 02:38:27 +00:00
keyboard_readme_src = find_readme ( keyboard_name )
2020-10-25 21:48:44 +00:00
keyboard_dir . mkdir ( parents = True , exist_ok = True )
2021-05-10 18:18:44 +00:00
keyboard_json = json . dumps ( { ' last_updated ' : current_datetime ( ) , ' keyboards ' : { keyboard_name : kb_all [ keyboard_name ] } } )
if not cli . args . dry_run :
keyboard_info . write_text ( keyboard_json )
cli . log . debug ( ' Wrote file %s ' , keyboard_info )
2020-10-25 21:48:44 +00:00
2021-05-25 02:38:27 +00:00
if keyboard_readme_src :
2022-02-27 11:28:51 +00:00
shutil . copyfile ( keyboard_readme_src , keyboard_readme )
2021-05-10 18:18:44 +00:00
cli . log . debug ( ' Copied %s -> %s ' , keyboard_readme_src , keyboard_readme )
2020-10-25 21:48:44 +00:00
2021-03-24 16:26:38 +00:00
if ' usb ' in kb_all [ keyboard_name ] :
usb = kb_all [ keyboard_name ] [ ' usb ' ]
2020-10-25 21:48:44 +00:00
2021-03-24 16:26:38 +00:00
if ' vid ' in usb and usb [ ' vid ' ] not in usb_list :
usb_list [ usb [ ' vid ' ] ] = { }
2020-10-25 21:48:44 +00:00
2021-03-24 16:26:38 +00:00
if ' pid ' in usb and usb [ ' pid ' ] not in usb_list [ usb [ ' vid ' ] ] :
usb_list [ usb [ ' vid ' ] ] [ usb [ ' pid ' ] ] = { }
2020-10-25 21:48:44 +00:00
2020-12-30 18:27:37 +00:00
if ' vid ' in usb and ' pid ' in usb :
2021-03-24 16:26:38 +00:00
usb_list [ usb [ ' vid ' ] ] [ usb [ ' pid ' ] ] [ keyboard_name ] = usb
2020-10-25 21:48:44 +00:00
2021-05-10 18:18:44 +00:00
# Generate data for the global files
2021-03-24 16:26:38 +00:00
keyboard_list = sorted ( kb_all )
keyboard_aliases = json_load ( Path ( ' data/mappings/keyboard_aliases.json ' ) )
keyboard_metadata = {
' last_updated ' : current_datetime ( ) ,
' keyboards ' : keyboard_list ,
' keyboard_aliases ' : keyboard_aliases ,
2021-03-25 03:33:25 +00:00
' usb ' : usb_list ,
2021-03-24 16:26:38 +00:00
}
2021-05-10 18:18:44 +00:00
2022-05-06 22:11:16 +00:00
# Feature specific handling
_resolve_xap_specs ( v1_dir / ' xap ' )
2021-05-10 18:18:44 +00:00
# Write the global JSON files
keyboard_all_json = json . dumps ( { ' last_updated ' : current_datetime ( ) , ' keyboards ' : kb_all } , cls = InfoJSONEncoder )
usb_json = json . dumps ( { ' last_updated ' : current_datetime ( ) , ' usb ' : usb_list } , cls = InfoJSONEncoder )
keyboard_list_json = json . dumps ( { ' last_updated ' : current_datetime ( ) , ' keyboards ' : keyboard_list } , cls = InfoJSONEncoder )
keyboard_aliases_json = json . dumps ( { ' last_updated ' : current_datetime ( ) , ' keyboard_aliases ' : keyboard_aliases } , cls = InfoJSONEncoder )
keyboard_metadata_json = json . dumps ( keyboard_metadata , cls = InfoJSONEncoder )
if not cli . args . dry_run :
keyboard_all_file . write_text ( keyboard_all_json )
usb_file . write_text ( usb_json )
keyboard_list_file . write_text ( keyboard_list_json )
keyboard_aliases_file . write_text ( keyboard_aliases_json )
keyboard_metadata_file . write_text ( keyboard_metadata_json )