qmk_firmware/cli_development.html

42 lines
117 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>QMK CLI Development | QMK Firmware</title>
<meta name="description" content="Documentation for QMK Firmware">
<meta name="generator" content="VitePress v1.1.3">
<link rel="preload stylesheet" href="/assets/style.No8zk9aC.css" as="style">
<script type="module" src="/assets/app.lJkKpnnX.js"></script>
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="modulepreload" href="/assets/chunks/theme.Onz3p0tR.js">
<link rel="modulepreload" href="/assets/chunks/framework.B9AX-CPi.js">
<link rel="modulepreload" href="/assets/cli_development.md.C1RMbrqW.lean.js">
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
</head>
<body>
<div id="app"><div class="Layout" data-v-5d98c3a5><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0f60ec36></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0f60ec36> Skip to content </a><!--]--><!----><header class="VPNav" data-v-5d98c3a5 data-v-ae24b3ad><div class="VPNavBar has-sidebar top" data-v-ae24b3ad data-v-ccf7ddec><div class="wrapper" data-v-ccf7ddec><div class="container" data-v-ccf7ddec><div class="title" data-v-ccf7ddec><div class="VPNavBarTitle has-sidebar" data-v-ccf7ddec data-v-ab179fa1><a class="title" href="/" data-v-ab179fa1><!--[--><!--]--><!--[--><!--[--><!--[--><img class="VPImage dark logo" src="/qmk-logo-dark.svg" alt data-v-8426fc1a><!--]--><!--[--><img class="VPImage light logo" src="/qmk-logo-light.svg" alt data-v-8426fc1a><!--]--><!--]--><!--]--><span data-v-ab179fa1>QMK Firmware</span><!--[--><!--]--></a></div></div><div class="content" data-v-ccf7ddec><div class="content-body" data-v-ccf7ddec><!--[--><!--]--><div class="VPNavBarSearch search" data-v-ccf7ddec><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-ccf7ddec data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-7f418b0f data-v-9c663999><!--[--><span data-v-9c663999>Home</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-ccf7ddec data-v-e6aabb21><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="Switch to dark theme" aria-checked="false" data-v-e6aabb21 data-v-d1f28634 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-d1f28634></span><span class="vpi-moon moon" data-v-d1f28634></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-ccf7ddec data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://reddit.com/r/olkb" aria-label target="_blank" rel="noopener" data-v-7bc22406 data-v-eee4e7cb><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 29 3 C 28.0625 3 27.164063 3.382813 26.5 4 C 25.835938 4.617188 25.363281 5.433594 25 6.40625 C 24.355469 8.140625 24.085938 10.394531 24.03125 13.03125 C 19.234375 13.179688 14.820313 14.421875 11.28125 16.46875 C 10.214844 15.46875 8.855469 14.96875 7.5 14.96875 C 6.089844 14.96875 4.675781 15.511719 3.59375 16.59375 C 1.425781 18.761719 1.425781 22.238281 3.59375 24.40625 L 3.84375 24.65625 C 3.3125 26.035156 3 27.488281 3 29 C 3 33.527344 5.566406 37.585938 9.5625 40.4375 C 13.558594 43.289063 19.007813 45 25 45 C 30.992188 45 36.441406 43.289063 40.4375 40.4375 C 44.433594 37.585938 47 33.527344 47 29 C 47 27.488281 46.6875 26.035156 46.15625 24.65625 L 46.40625 24.40625 C 48.574219 22.238281 48.574219 18.761719 46.40625 16.59375 C 45.324219 15.511719 43.910156 14.96875 42.5 14.96875 C 41.144531 14.96875 39.785156 15.46875 38.71875 16.46875 C 35.195313 14.433594 30.800781 13.191406 26.03125 13.03125 C 26.09375 10.546875 26.363281 8.46875 26.875 7.09375 C 27.164063 6.316406 27.527344 5.757813 27.875 5.4375 C 28.222656 5.117188 28.539063 5 29 5 C 29.460938 5 29.683594 5.125 30.03125 5.40625 C 30.378906 5.6875 30.785156 6.148438 31.3125 6.6875 C 32.253906 7.652344 33.695313 8.714844 36.09375 8.9375 C 36.539063 11.238281 38.574219 13 41 13 C 43.75 13 46 10.75 46 8 C 46 5.25 43.75 3 41 3 C 38.605469 3 36.574219 4.710938 36.09375 6.96875 C 34.3125 6.796875 33.527344 6.109375 32.75 5.3125 C 32.300781 4.851563 31.886719 4
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">This is an example QMK CLI script.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;&quot;&quot;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> milc </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> cli</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">@cli.argument</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;-n&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;--name&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">default</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;World&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">help</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;Name to greet.&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">@cli.subcommand</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;QMK Hello World.&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> hello</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(cli):</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;&quot;&quot;Log a friendly greeting.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;&quot;&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> cli.log.info(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;Hello, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">%s</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">!&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, cli.config.hello.name)</span></span></code></pre></div><p>First we import the <code>cli</code> object from <code>milc</code>. This is how we interact with the user and control the script&#39;s behavior. We use <code>@cli.argument()</code> to define a command line flag, <code>--name</code>. This also creates a configuration variable named <code>hello.name</code> (and the corresponding <code>user.name</code>) which the user can set so they don&#39;t have to specify the argument. The <code>cli.subcommand()</code> decorator designates this function as a subcommand. The name of the subcommand will be taken from the name of the function.</p><p>Once inside our function we find a typical &quot;Hello, World!&quot; program. We use <code>cli.log</code> to access the underlying <a href="https://docs.python.org/3.7/library/logging.html#logger-objects" target="_blank" rel="noreferrer">Logger Object</a>, whose behavior is user controllable. We also access the value for name supplied by the user as <code>cli.config.hello.name</code>. The value for <code>cli.config.hello.name</code> will be determined by looking at the <code>--name</code> argument supplied by the user, if not provided it will use the value in the <code>qmk.ini</code> config file, and if neither of those is provided it will fall back to the default supplied in the <code>cli.argument()</code> decorator.</p><h1 id="user-interaction" tabindex="-1">User Interaction <a class="header-anchor" href="#user-interaction" aria-label="Permalink to &quot;User Interaction&quot;"></a></h1><p>MILC and the QMK CLI have several nice tools for interacting with the user. Using these standard tools will allow you to colorize your text for easier interactions, and allow the user to control when and how that information is displayed and stored.</p><h2 id="printing-text" tabindex="-1">Printing Text <a class="header-anchor" href="#printing-text" aria-label="Permalink to &quot;Printing Text&quot;"></a></h2><p>There are two main methods for outputting text in a subcommand- <code>cli.log</code> and <code>cli.echo()</code>. They operate in similar ways but you should prefer to use <code>cli.log.info()</code> for most general purpose printing.</p><p>You can use special tokens to colorize your text, to make it easier to understand the output of your program. See <a href="#colorizing-text">Colorizing Text</a> below.</p><p>Both of these methods support built-in string formatting using python&#39;s <a href="https://docs.python.org/3.7/library/stdtypes.html#old-string-formatting" target="_blank" rel="noreferrer">printf style string format operations</a>. You can use tokens such as <code>%s</code> and <code>%d</code> within your text strings then pass the values as arguments. See our Hello, World program above for an example.</p><p>You should never use the format operator (<code>%</code>) directly, always pass values as arguments.</p><h3 id="logging-cli-log" tabindex="-1">Logging (<code>cli.log</code>) <a class="header-anchor" href="#logging-cli-log" aria-label="Permalink to &quot;Logging (`cli.log`)&quot;"></a></h3><p>The <code>cli.log</code> object gives you access to a <a href="https://docs.python.org/3.7/library/logging.html#logger-objects" target="_blank" rel="noreferrer">Logger Object</a>. We have configured our log output to show the user a nice emoji for each log level (or the log level name if their terminal does not support unicode.) This way the user can tell at a glance which messages are most important when something goes wrong.</p><p>The default log level is <code>INFO</code>. If the user runs <code>qmk -v &lt;subcommand&gt;</code> the default log level will be set to <code>DEBUG</code>.</p><table><thead><tr><th>Fun
<span class="line"><span> for key in cli.config[section]:</span></span>
<span class="line"><span> cli.log.info(&#39;%s.%s: %s&#39;, section, key, cli.config[section][key])</span></span></code></pre></div><h2 id="setting-configuration-values" tabindex="-1">Setting Configuration Values <a class="header-anchor" href="#setting-configuration-values" aria-label="Permalink to &quot;Setting Configuration Values&quot;"></a></h2><p>You can set configuration values in the usual ways.</p><p>Dictionary style:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>cli.config[&#39;&lt;section&gt;&#39;][&#39;&lt;key&gt;&#39;] = &lt;value&gt;</span></span></code></pre></div><p>Attribute style:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>cli.config.&lt;section&gt;.&lt;key&gt; = &lt;value&gt;</span></span></code></pre></div><h2 id="deleting-configuration-values" tabindex="-1">Deleting Configuration Values <a class="header-anchor" href="#deleting-configuration-values" aria-label="Permalink to &quot;Deleting Configuration Values&quot;"></a></h2><p>You can delete configuration values in the usual ways.</p><p>Dictionary style:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>del(cli.config[&#39;&lt;section&gt;&#39;][&#39;&lt;key&gt;&#39;])</span></span></code></pre></div><p>Attribute style:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>del(cli.config.&lt;section&gt;.&lt;key&gt;)</span></span></code></pre></div><h2 id="writing-the-configuration-file" tabindex="-1">Writing The Configuration File <a class="header-anchor" href="#writing-the-configuration-file" aria-label="Permalink to &quot;Writing The Configuration File&quot;"></a></h2><p>The configuration is not written out when it is changed. Most commands do not need to do this. We prefer to have the user change their configuration deliberately using <code>qmk config</code>.</p><p>You can use <code>cli.save_config()</code> to write out the configuration.</p><h2 id="excluding-arguments-from-configuration" tabindex="-1">Excluding Arguments From Configuration <a class="header-anchor" href="#excluding-arguments-from-configuration" aria-label="Permalink to &quot;Excluding Arguments From Configuration&quot;"></a></h2><p>Some arguments should not be propagated to the configuration file. These can be excluded by adding <code>arg_only=True</code> when creating the argument.</p><p>Example:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>@cli.argument(&#39;-o&#39;, &#39;--output&#39;, arg_only=True, help=&#39;File to write to&#39;)</span></span>
<span class="line"><span>@cli.argument(&#39;filename&#39;, arg_only=True, help=&#39;Configurator JSON file&#39;)</span></span>
<span class="line"><span>@cli.subcommand(&#39;Create a keymap.c from a QMK Configurator export.&#39;)</span></span>
<span class="line"><span>def json_keymap(cli):</span></span>
<span class="line"><span> pass</span></span></code></pre></div><p>You will only be able to access these arguments using <code>cli.args</code>. For example:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>cli.log.info(&#39;Reading from %s and writing to %s&#39;, cli.args.filename, cli.args.output)</span></span></code></pre></div><h1 id="testing-and-linting-and-formatting-oh-my" tabindex="-1">Testing, and Linting, and Formatting (oh my!) <a class="header-anchor" href="#testing-and-linting-and-formatting-oh-my" aria-label="Permalink to &quot;Testing, and Linting, and Formatting (oh my!)&quot;"></a></h1><p>We use nose2, flake8, and yapf to test, lint, and format code. You can use the <code>pytest</code> and <code>format-python</code> subcommands to run these tests:</p><h3 id="testing-and-linting" tabindex="-1">Testing and Linting <a class="header-anchor" href="#testing-and-linting" aria-label="Permalink to &quot;Testing and Linting&quot;"></a></h3><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>qmk pytest</span></span></code></pre></div><h3 id="formatting" tabindex="-1">Formatting <a class="header-anchor" href="#formatting" aria-label="Permalink to &quot;Formatting&quot;"></a></h3><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>qmk format-python</span></span></code></pre></div><h2 id="formatting-details" tabindex="-1">Formatting Details <a class="header-anchor" href="#formatting-details" aria-label="Permalink to &quot;Formatting Details&quot;"></a></h2><p>We use <a href="https://github.com/google/yapf" target="_blank" rel="noreferrer">yapf</a> to automatically format code. Our configuration is in the <code>[yapf]</code> section of <code>setup.cfg</code>.</p><div class="tip custom-block"><p class="custom-block-title">TIP</p><p>Many editors can use yapf as a plugin to automatically format code as you type.</p></div><h2 id="testing-details" tabindex="-1">Testing Details <a class="header-anchor" href="#testing-details" aria-label="Permalink to &quot;Testing Details&quot;"></a></h2><p>Our tests can be found in <code>lib/python/qmk/tests/</code>. You will find both unit and integration tests in this directory. We hope you will write both unit and integration tests for your code, but if you do not please favor integration tests.</p><p>If your PR does not include a comprehensive set of tests please add comments like this to your code so that other people know where they can help:</p><div class="language-python vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">TODO</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">(unassigned/&lt;your_github_username&gt;): Write &lt;unit|integration&gt; tests</span></span></code></pre></div><p>We use <a href="https://nose2.readthedocs.io/en/latest/getting_started.html" target="_blank" rel="noreferrer">nose2</a> to run our tests. You can refer to the nose2 documentation for more details on what you can do in your test functions.</p><h2 id="linting-details" tabindex="-1">Linting Details <a class="header-anchor" href="#linting-details" aria-label="Permalink to &quot;Linting Details&quot;"></a></h2><p>We use flake8 to lint our code. Your code should pass flake8 before you open a PR. This will be checked when you run <code>qmk pytest</code> and by CI when you submit a PR.</p></div></div></main><footer class="VPDocFooter" data-v-
<script>window.__VP_HASH_MAP__=JSON.parse("{\"changelog_20190830.md\":\"6XixyP8L\",\"changelog_20200229.md\":\"BmeRJene\",\"changelog_20200530.md\":\"q1Nx4Bsf\",\"changelog_20200829.md\":\"DPwMxUVb\",\"changelog_20201128.md\":\"cyEKi6md\",\"changelog_20210227.md\":\"D-qZNAtE\",\"changelog_20210529.md\":\"CMCDBsST\",\"changelog_20210828.md\":\"Bz_Cqhkz\",\"changelog_20211127.md\":\"CKqQnO6y\",\"changelog_20220226.md\":\"CyVoLONs\",\"changelog_20220528.md\":\"CihUA7fi\",\"changelog_20220827.md\":\"Df1nzx8O\",\"changelog_20221126.md\":\"Coqj0YNT\",\"changelog_20230226.md\":\"kWJusvXj\",\"changelog_20230528.md\":\"BQOXA7rR\",\"changelog_20230827.md\":\"CysqTdAP\",\"changelog_20231126.md\":\"D6JlaCSH\",\"changelog_20240225.md\":\"DmbHo-aA\",\"changelog_20240526.md\":\"ChevS3SM\",\"changelog_20240825.md\":\"Bim1xI09\",\"capabilities.md\":\"C8eGasTO\",\"capabilities_inc.md\":\"Pt72L4b9\",\"api_development_environment.md\":\"Co1Lkuhl\",\"api_development_overview.md\":\"iUK6GGbG\",\"api_docs.md\":\"DrPevC-R\",\"api_overview.md\":\"B3sDKDJg\",\"arm_debugging.md\":\"BiRXU6nl\",\"breaking_changes.md\":\"BIpM1JDF\",\"breaking_changes_history.md\":\"B_yw4mvF\",\"breaking_changes_instructions.md\":\"DUu8Tdu8\",\"chibios_upgrade_instructions.md\":\"BkS-xCQh\",\"cli.md\":\"Ct8-gh67\",\"cli_commands.md\":\"CijNW8-t\",\"cli_configuration.md\":\"DT4O5Qrb\",\"cli_development.md\":\"C1RMbrqW\",\"cli_tab_complete.md\":\"CT9NBEM3\",\"coding_conventions_c.md\":\"Bi7EH0Zk\",\"coding_conventions_python.md\":\"C45woQX8\",\"compatible_microcontrollers.md\":\"C3RSssel\",\"config_options.md\":\"BroSRrlf\",\"configurator_architecture.md\":\"ClRUnOlV\",\"configurator_default_keymaps.md\":\"Btb4Y_-z\",\"configurator_step_by_step.md\":\"Bhhrr-KZ\",\"configurator_troubleshooting.md\":\"CyTH_MO_\",\"contributing.md\":\"Z2KawZJM\",\"custom_matrix.md\":\"C5Wxxs18\",\"custom_quantum_functions.md\":\"CchHkQ2j\",\"data_driven_config.md\":\"CemhrDnh\",\"documentation_best_practices.md\":\"D_2t5SCy\",\"documentation_templates.md\":\"DNSTMFQT\",\"driver_installation_zadig.md\":\"CzMMyItw\",\"drivers_adc.md\":\"CdK8AtnR\",\"drivers_apa102.md\":\"D1i8N4Yl\",\"drivers_audio.md\":\"FGaarcoq\",\"drivers_aw20216s.md\":\"DFO5qaXQ\",\"drivers_eeprom.md\":\"DjbSpQAl\",\"drivers_flash.md\":\"TpToebZV\",\"drivers_gpio.md\":\"_T-XR1i8\",\"drivers_i2c.md\":\"CZ3yUqUc\",\"drivers_is31fl3218.md\":\"Bw4QYvxz\",\"drivers_is31fl3236.md\":\"kw_4WomB\",\"drivers_is31fl3729.md\":\"B05FYaEa\",\"drivers_is31fl3731.md\":\"EbaJGcwl\",\"drivers_is31fl3733.md\":\"CxiWN3qf\",\"drivers_is31fl3736.md\":\"CNsB35Bn\",\"drivers_is31fl3737.md\":\"dFrKnsKN\",\"drivers_is31fl3741.md\":\"DCbdJxE4\",\"drivers_is31fl3742a.md\":\"Bh9Q6Zt4\",\"drivers_is31fl3743a.md\":\"W_EODxfi\",\"drivers_is31fl3745.md\":\"Dd_riPpV\",\"drivers_is31fl3746a.md\":\"DaWS07Rz\",\"drivers_serial.md\":\"B9cdhvZY\",\"drivers_snled27351.md\":\"C202OTO_\",\"drivers_spi.md\":\"KJwJ1bDT\",\"drivers_uart.md\":\"D5bf2Ez7\",\"drivers_ws2812.md\":\"_cnUpPJK\",\"easy_maker.md\":\"B1e0p_KP\",\"faq_build.md\":\"CdMV-bmr\",\"faq_debug.md\":\"fagccKfw\",\"faq_general.md\":\"BS8dICfT\",\"faq_keymap.md\":\"CmJjc7AB\",\"faq_misc.md\":\"pugCgT23\",\"feature_advanced_keycodes.md\":\"Dc44pi1G\",\"feature_converters.md\":\"nbsy9vmL\",\"feature_debounce_type.md\":\"BcX3faCS\",\"feature_eeprom.md\":\"CrKIb9by\",\"feature_layers.md\":\"mZMmAI4u\",\"feature_layouts.md\":\"CC3GUG9e\",\"feature_macros.md\":\"bo5m05vP\",\"feature_userspace.md\":\"DzgyEZm9\",\"features_audio.md\":\"B4yww0JU\",\"features_auto_shift.md\":\"gbN4UbG7\",\"features_autocorrect.md\":\"BgqtSxc-\",\"features_backlight.md\":\"BtYuf8hb\",\"features_bluetooth.md\":\"DB3PFlcy\",\"features_bootmagic.md\":\"DBdRU139\",\"features_caps_word.md\":\"Cz8-Ua5R\",\"features_combo.md\":\"nQwtfYKJ\",\"features_command.md\":\"DDU6fJSP\",\"features_digitizer.md\":\"qNC7cDmj\",\"features_dip_switch.md\":\"DQ7jEipz\",\"features_dynamic_macros.md\":\"CmY3EYfK\",\"features_encoders.md\":\"Cw1N3hGV\",\"features_grave_esc.md\":\"RGWiZPw2\",\"features_haptic_feedback.md\":\"gCZCxZph\",\"features_hd4
</body>
</html>