Skip to content

Commit af636fb

Browse files
committed
Support board revisions in keyboard add/list commands
"zmk keyboard add" nows understand board revisions. If you attempt to add a board which has multiple revisions (and you don't provide a revision with a parameter like "-c nice_nano@2"), it will now prompt you to select a revision. When copying .keymap and .conf files to your config repo, if the keyboard is a board with a revision, it will now check for versioned files before checking for a common file with no revision. "zmk keyboard list" now supports a --revisions flag which prints each board revision as a separate item.
1 parent a3cbcce commit af636fb

File tree

6 files changed

+310
-31
lines changed

6 files changed

+310
-31
lines changed

zmk/commands/config.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
from .. import styles
1111
from ..config import Config, get_config
1212

13-
console = Console(
14-
highlighter=styles.KeyValueHighlighter(), theme=styles.KEY_VALUE_THEME
15-
)
13+
console = Console(highlighter=styles.KeyValueHighlighter(), theme=styles.THEME)
1614

1715

1816
def _path_callback(ctx: typer.Context, value: bool):

zmk/commands/keyboard/add.py

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@
1515
from ...exceptions import FatalError
1616
from ...hardware import (
1717
Board,
18+
Hardware,
1819
Keyboard,
1920
Shield,
21+
append_revision,
2022
get_hardware,
2123
is_compatible,
24+
normalize_revision,
2225
show_hardware_menu,
26+
show_revision_menu,
27+
split_revision,
2328
)
2429
from ...repo import Repo
2530
from ...util import spinner
@@ -59,12 +64,21 @@ def keyboard_add(
5964

6065
keyboard = None
6166
controller = None
67+
revision = None
6268

6369
if keyboard_id:
70+
keyboard_id, keyboard_revision = split_revision(keyboard_id)
71+
6472
keyboard = hardware.find_keyboard(keyboard_id)
6573
if keyboard is None:
6674
raise KeyboardNotFound(keyboard_id)
6775

76+
# If the keyboard ID contained a revision, use that.
77+
# Make sure it is valid before continuing to any other prompts.
78+
if keyboard_revision:
79+
revision = keyboard_revision
80+
_check_revision(keyboard, revision)
81+
6882
if controller_id:
6983
if not isinstance(keyboard, Shield):
7084
raise FatalError(
@@ -77,12 +91,20 @@ def keyboard_add(
7791
raise ControllerNotFound(controller_id)
7892

7993
elif controller_id:
94+
controller_id, controller_revision = split_revision(controller_id)
95+
8096
# User specified a controller but not a keyboard. Filter the keyboard
8197
# list to just those compatible with the controller.
8298
controller = hardware.find_controller(controller_id)
8399
if controller is None:
84100
raise ControllerNotFound(controller_id)
85101

102+
# If the controller ID contained a revision, use that.
103+
# Make sure it is valid before continuing to any other prompts.
104+
if controller_revision:
105+
revision = controller_revision
106+
_check_revision(controller, revision)
107+
86108
hardware.keyboards = [
87109
kb
88110
for kb in hardware.keyboards
@@ -108,16 +130,28 @@ def keyboard_add(
108130
f'Keyboard "{keyboard.id}" is not compatible with controller "{controller.id}"'
109131
)
110132

133+
# Check if the controller needs a revision.
134+
revision = _get_revision(controller, revision)
135+
else:
136+
# If the keyboard isn't a shield, it may need a revision.
137+
revision = _get_revision(keyboard, revision)
138+
111139
name = keyboard.id
112140
if controller:
113141
name += ", " + controller.id
114142

115-
if _add_keyboard(repo, keyboard, controller):
143+
if revision:
144+
revision = normalize_revision(revision)
145+
name = append_revision(name, revision)
146+
147+
if _add_keyboard(repo, keyboard, controller, revision):
116148
console.print(f'Added "{name}".')
117149
else:
118150
console.print(f'"{name}" is already in the build matrix.')
119151

120-
console.print(f'Run "zmk code {keyboard.id}" to edit the keymap.')
152+
keymap_name = keyboard.get_keymap_path(revision).with_suffix("").name
153+
154+
console.print(f'Run "zmk code {keymap_name}" to edit the keymap.')
121155

122156

123157
class KeyboardNotFound(FatalError):
@@ -140,23 +174,26 @@ def _copy_keyboard_file(repo: Repo, path: Path):
140174
shutil.copy2(path, dest_path)
141175

142176

143-
def _get_build_items(keyboard: Keyboard, controller: Board | None):
177+
def _get_build_items(
178+
keyboard: Keyboard, controller: Board | None, revision: str | None
179+
):
144180
boards = []
145181
shields = []
146182

147183
match keyboard:
148184
case Shield(id=shield_id, siblings=siblings):
149185
if controller is None:
150-
raise ValueError("controller may not be None if keyboard is a shield")
186+
raise FatalError("controller may not be None if keyboard is a shield")
151187

152188
shields = siblings or [shield_id]
153-
boards = [controller.id]
189+
boards = [append_revision(controller.id, revision)]
154190

155191
case Board(id=board_id, siblings=siblings):
156192
boards = siblings or [board_id]
193+
boards = [append_revision(board, revision) for board in boards]
157194

158195
case _:
159-
raise ValueError("Unexpected keyboard/controller combination")
196+
raise FatalError("Unexpected keyboard/controller combination")
160197

161198
if shields:
162199
return [
@@ -166,11 +203,35 @@ def _get_build_items(keyboard: Keyboard, controller: Board | None):
166203
return [BuildItem(board=b) for b in boards]
167204

168205

169-
def _add_keyboard(repo: Repo, keyboard: Keyboard, controller: Board | None):
170-
_copy_keyboard_file(repo, keyboard.keymap_path)
171-
_copy_keyboard_file(repo, keyboard.config_path)
206+
def _get_revision(board: Hardware, revision: str | None):
207+
# If no revision was specified and the board uses revisions, prompt to
208+
# select a revision.
209+
return revision if revision else show_revision_menu(board)
210+
211+
212+
def _check_revision(board: Hardware, revision: str):
213+
if board.has_revision(revision):
214+
# Revision is OK
215+
return
216+
217+
supported_revisions = board.get_revisions()
218+
219+
if not supported_revisions:
220+
raise FatalError(f"{board.id} does not have any revisions.")
221+
222+
raise FatalError(
223+
f'{board.id} does not support revision "@{revision}". Use one of:\n'
224+
+ "\n".join(f" @{normalize_revision(rev)}" for rev in supported_revisions)
225+
)
226+
227+
228+
def _add_keyboard(
229+
repo: Repo, keyboard: Keyboard, controller: Board | None, revision: str | None
230+
):
231+
_copy_keyboard_file(repo, keyboard.get_keymap_path(revision))
232+
_copy_keyboard_file(repo, keyboard.get_config_path(revision))
172233

173-
items = _get_build_items(keyboard, controller)
234+
items = _get_build_items(keyboard, controller, revision)
174235

175236
matrix = BuildMatrix.from_repo(repo)
176237
added = matrix.append(items)

zmk/commands/keyboard/list.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,31 @@
22
"zmk keyboard list" command.
33
"""
44

5+
import itertools
56
from collections.abc import Iterable
67
from typing import Annotated
78

8-
import rich
99
import typer
1010
from rich import box
1111
from rich.columns import Columns
12+
from rich.console import Console
1213
from rich.table import Table
1314

15+
from zmk import styles
16+
1417
from ...backports import StrEnum
1518
from ...build import BuildItem, BuildMatrix
1619
from ...config import get_config
1720
from ...exceptions import FatalError
18-
from ...hardware import Board, Hardware, Shield, get_hardware, is_compatible
21+
from ...hardware import (
22+
Board,
23+
Hardware,
24+
Shield,
25+
append_revision,
26+
get_hardware,
27+
is_compatible,
28+
normalize_revision,
29+
)
1930
from ...util import spinner
2031

2132
# TODO: allow output as unformatted list
@@ -36,7 +47,12 @@ def _list_build_matrix(ctx: typer.Context, value: bool):
3647
if not value:
3748
return
3849

39-
console = rich.get_console()
50+
console = Console(
51+
highlighter=styles.chain_highlighters(
52+
[styles.BoardIdHighlighter(), styles.CommandLineHighlighter()]
53+
),
54+
theme=styles.THEME,
55+
)
4056

4157
cfg = get_config(ctx)
4258
repo = cfg.get_repo()
@@ -48,7 +64,12 @@ def _list_build_matrix(ctx: typer.Context, value: bool):
4864
has_cmake_args = any(item.cmake_args for item in include)
4965
has_artifact_name = any(item.artifact_name for item in include)
5066

51-
table = Table(box=box.SQUARE, border_style="dim blue", header_style="bright_cyan")
67+
table = Table(
68+
box=box.SQUARE,
69+
border_style="dim blue",
70+
header_style="bright_cyan",
71+
highlight=True,
72+
)
5273
table.add_column("Board")
5374
table.add_column("Shield")
5475
if has_snippet:
@@ -129,10 +150,16 @@ def keyboard_list(
129150
"--standalone", help="List only keyboards with onboard controllers."
130151
),
131152
] = False,
153+
revisions: Annotated[
154+
bool,
155+
typer.Option(
156+
"--revisions", "--rev", "-r", help="Display revisions for each board."
157+
),
158+
] = False,
132159
) -> None:
133160
"""List supported keyboards or keyboards in the build matrix."""
134161

135-
console = rich.get_console()
162+
console = Console(highlighter=styles.BoardIdHighlighter(), theme=styles.THEME)
136163

137164
cfg = get_config(ctx)
138165
repo = cfg.get_repo()
@@ -187,7 +214,15 @@ def keyboard_list(
187214
list_type = ListType.KEYBOARD
188215

189216
def print_items(header: str, items: Iterable[Hardware]):
190-
names = [item.id for item in items]
217+
if revisions:
218+
names = [
219+
append_revision(item.id, normalize_revision(rev))
220+
for item in items
221+
for rev in (item.get_revisions() or [None])
222+
]
223+
else:
224+
names = [item.id for item in items]
225+
191226
if not names:
192227
return
193228

0 commit comments

Comments
 (0)