1515from ...exceptions import FatalError
1616from ...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)
2429from ...repo import Repo
2530from ...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
123157class 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 )
0 commit comments