88
99from contextlib import nullcontext
1010from importlib .resources import path
11- from subprocess import DEVNULL , call , check_call
11+ from subprocess import DEVNULL , PIPE , call , check_call , run
1212from tempfile import TemporaryDirectory
1313from typing import BinaryIO , ContextManager
1414
2424MINDSTORMS_INVENTOR_PID = 0x0011
2525
2626
27- SPIKE_PRIME_DEVICE = f"0x{ LEGO_VID :04x} :0x{ SPIKE_PRIME_PID :04x} "
28- MINDSTORMS_INVENTOR_DEVICE = f"0x{ LEGO_VID :04x} :0x{ MINDSTORMS_INVENTOR_PID :04x} "
27+ SPIKE_PRIME_DEVICE = f"{ LEGO_VID :04x} :{ SPIKE_PRIME_PID :04x} "
28+ MINDSTORMS_INVENTOR_DEVICE = f"{ LEGO_VID :04x} :{ MINDSTORMS_INVENTOR_PID :04x} "
29+ ALL_DEVICES = [SPIKE_PRIME_DEVICE , MINDSTORMS_INVENTOR_DEVICE ]
2930
3031
3132def _get_dfu_util () -> ContextManager [os .PathLike ]:
@@ -54,6 +55,28 @@ def _get_dfu_util() -> ContextManager[os.PathLike]:
5455 return nullcontext (dfu_util )
5556
5657
58+ def _get_vid_pid (dfu_util : os .PathLike ) -> str :
59+ """
60+ Gets the VID and PID of a connected LEGO DFU device.
61+
62+ Returns: The first matching LEGO DFU device from ``dfu-util --list``
63+
64+ Raises: RuntimeError: No matching hubs found.
65+ """
66+ proc = run ([dfu_util , "--list" ], stdout = PIPE , check = True )
67+
68+ for line in proc .stdout .splitlines ():
69+ if not line .startswith (b"Found DFU:" ):
70+ continue
71+
72+ dev_id = line [line .index (b"[" ) + 1 : line .index (b"]" )].decode ()
73+
74+ if dev_id in ALL_DEVICES :
75+ return dev_id
76+
77+ raise RuntimeError ("No LEGO DFU USB device found" )
78+
79+
5780def backup_dfu (file : BinaryIO ) -> None :
5881 """Backs up device data via DFU.
5982
@@ -79,7 +102,7 @@ def backup_dfu(file: BinaryIO) -> None:
79102 [
80103 dfu_util ,
81104 "--device" ,
82- f",{ SPIKE_PRIME_DEVICE } , { MINDSTORMS_INVENTOR_DEVICE } " ,
105+ f",{ _get_vid_pid ( dfu_util ) } " ,
83106 "--alt" ,
84107 "0" ,
85108 "--dfuse-address" ,
@@ -119,7 +142,7 @@ def restore_dfu(file: BinaryIO) -> None:
119142 [
120143 dfu_util ,
121144 "--device" ,
122- f",{ SPIKE_PRIME_DEVICE } , { MINDSTORMS_INVENTOR_DEVICE } " ,
145+ f",{ _get_vid_pid ( dfu_util ) } " ,
123146 "--alt" ,
124147 "0" ,
125148 "--dfuse-address" ,
@@ -197,16 +220,16 @@ def flash_dfu(firmware_bin: bytes, metadata: dict) -> None:
197220
198221 with _get_dfu_util () as dfu_util :
199222
200- # Exact device product ID doesn't matter here since we are using
201- # the --device command line option below.
202- _dfu_create .build (outfile , [[target ]], SPIKE_PRIME_DEVICE )
223+ dev_id = _get_vid_pid ( dfu_util )
224+
225+ _dfu_create .build (outfile , [[target ]], dev_id )
203226
204227 exit (
205228 call (
206229 [
207230 dfu_util ,
208231 "--device" ,
209- f",{ SPIKE_PRIME_DEVICE } , { MINDSTORMS_INVENTOR_DEVICE } " ,
232+ f",{ dev_id } " ,
210233 "--alt" ,
211234 "0" ,
212235 "--download" ,
0 commit comments