mpflash 0.4.0.post3__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {mpflash-0.4.0.post3 → mpflash-0.4.2}/PKG-INFO +1 -1
  2. mpflash-0.4.2/mpflash/ask_input.py +163 -0
  3. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/cli_download.py +16 -12
  4. mpflash-0.4.2/mpflash/cli_flash.py +225 -0
  5. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/cli_group.py +20 -1
  6. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/cli_list.py +5 -4
  7. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/cli_main.py +5 -4
  8. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/common.py +24 -9
  9. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/config.py +5 -3
  10. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/download.py +58 -13
  11. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash.py +36 -24
  12. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_esp.py +11 -11
  13. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_stm32.py +1 -1
  14. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_stm32_dfu.py +4 -7
  15. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_uf2.py +1 -1
  16. mpflash-0.4.2/mpflash/mpboard_id/api.py +89 -0
  17. mpflash-0.4.2/mpflash/mpboard_id/board_info.csv +2213 -0
  18. mpflash-0.4.2/mpflash/mpboard_id/board_info.json +19910 -0
  19. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/vendored/dfu.py +9 -8
  20. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/vendored/pydfu.py +1 -1
  21. {mpflash-0.4.0.post3 → mpflash-0.4.2}/pyproject.toml +6 -1
  22. mpflash-0.4.0.post3/mpflash/basic.py +0 -41
  23. mpflash-0.4.0.post3/mpflash/cli_flash.py +0 -175
  24. mpflash-0.4.0.post3/mpflash/download_input.py +0 -69
  25. mpflash-0.4.0.post3/mpflash/inq.py +0 -66
  26. mpflash-0.4.0.post3/mpflash/mpboard_id/api.py +0 -48
  27. mpflash-0.4.0.post3/mpflash/mpboard_id/board_info.csv +0 -193
  28. mpflash-0.4.0.post3/mpflash/mpboard_id/board_info.json +0 -1730
  29. mpflash-0.4.0.post3/mpflash/mpboard_id/get_boardnames.py +0 -213
  30. {mpflash-0.4.0.post3 → mpflash-0.4.2}/LICENSE +0 -0
  31. {mpflash-0.4.0.post3 → mpflash-0.4.2}/README.md +0 -0
  32. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/__init__.py +0 -0
  33. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_stm32_cube.py +0 -0
  34. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_uf2_boardid.py +0 -0
  35. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_uf2_linux.py +0 -0
  36. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/flash_uf2_windows.py +0 -0
  37. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/logger.py +0 -0
  38. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/mpboard_id/board_id.py +0 -0
  39. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/mpremoteboard/__init__.py +0 -0
  40. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/mpremoteboard/mpy_fw_info.py +0 -0
  41. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/mpremoteboard/runner.py +0 -0
  42. {mpflash-0.4.0.post3 → mpflash-0.4.2}/mpflash/vendored/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mpflash
3
- Version: 0.4.0.post3
3
+ Version: 0.4.2
4
4
  Summary: Flash and download tool for MicroPython firmwares
5
5
  Home-page: https://github.com/Josverl/micropython-stubber/blob/main/src/mpflash/README.md
6
6
  License: MIT
@@ -0,0 +1,163 @@
1
+ """Download input handling for mpflash."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from pathlib import Path
5
+ from typing import List, Sequence, Tuple, Union
6
+
7
+ from loguru import logger as log
8
+
9
+ from mpflash.common import micropython_versions
10
+ from mpflash.config import config
11
+ from mpflash.mpboard_id.api import known_mp_boards, known_mp_ports
12
+ from mpflash.mpremoteboard import MPRemoteBoard
13
+
14
+
15
+ @dataclass
16
+ class Params:
17
+ ports: List[str] = field(default_factory=list)
18
+ boards: List[str] = field(default_factory=list)
19
+ versions: List[str] = field(default_factory=list)
20
+ fw_folder: Path = Path()
21
+
22
+
23
+ @dataclass
24
+ class DownloadParams(Params):
25
+ clean: bool = False
26
+ force: bool = False
27
+
28
+
29
+ @dataclass
30
+ class FlashParams(Params):
31
+ # TODO: Should Serial port be a list?
32
+ serial: str = ""
33
+ erase: bool = True
34
+ bootloader: bool = True
35
+ cpu: str = ""
36
+
37
+
38
+ ParamType = Union[DownloadParams, FlashParams]
39
+
40
+
41
+ def ask_missing_params(
42
+ params: ParamType,
43
+ action: str = "download",
44
+ ) -> ParamType:
45
+ if not config.interactive:
46
+ # no interactivity allowed
47
+ return params
48
+ # import only when needed to reduce load time
49
+ import inquirer
50
+
51
+ questions = []
52
+ if isinstance(params, FlashParams) and (not params.serial or "?" in params.versions):
53
+ ask_serialport(questions, action=action)
54
+
55
+ if not params.versions or "?" in params.versions:
56
+ ask_versions(questions, action=action)
57
+
58
+ if not params.boards or "?" in params.boards:
59
+ ask_port_board(questions, action=action)
60
+
61
+ answers = inquirer.prompt(questions)
62
+ if not answers:
63
+ return params
64
+ # print(repr(answers))
65
+ if isinstance(params, FlashParams) and "serial" in answers:
66
+ params.serial = answers["serial"]
67
+ if "port" in answers:
68
+ params.ports = [answers["port"]]
69
+ if "boards" in answers:
70
+ params.boards = answers["boards"]
71
+ if "versions" in answers:
72
+ # make sure it is a list
73
+ params.versions = answers["versions"] if isinstance(answers["versions"], list) else [answers["versions"]]
74
+
75
+ log.debug(repr(params))
76
+
77
+ return params
78
+
79
+
80
+ def some_boards(answers: dict) -> Sequence[Tuple[str, str]]:
81
+ if "versions" in answers:
82
+ _versions = list(answers["versions"])
83
+ if "stable" in _versions:
84
+ _versions.remove("stable")
85
+ _versions.append(micropython_versions()[-2])
86
+ if "preview" in _versions:
87
+ _versions.remove("preview")
88
+ _versions.append(micropython_versions()[-1])
89
+ _versions.append(micropython_versions()[-2])
90
+
91
+ some_boards = known_mp_boards(answers["port"], _versions) # or known_mp_boards(answers["port"])
92
+ else:
93
+ some_boards = known_mp_boards(answers["port"])
94
+
95
+ if some_boards:
96
+ # Create a dictionary where the keys are the second elements of the tuples
97
+ # This will automatically remove duplicates because dictionaries cannot have duplicate keys
98
+ unique_dict = {item[1]: item for item in some_boards}
99
+ # Get the values of the dictionary, which are the unique items from the original list
100
+ some_boards = list(unique_dict.values())
101
+ else:
102
+ some_boards = [("No boards found", "")]
103
+ return some_boards
104
+
105
+
106
+ def ask_port_board(questions: list, *, action: str):
107
+ # import only when needed to reduce load time
108
+ import inquirer
109
+
110
+ questions.extend(
111
+ (
112
+ inquirer.List(
113
+ "port",
114
+ message=f"What port do you want to {action}?",
115
+ choices=known_mp_ports(),
116
+ autocomplete=True,
117
+ ),
118
+ inquirer.Checkbox(
119
+ "boards",
120
+ message=f"What board do you want to {action}?",
121
+ choices=some_boards,
122
+ validate=lambda _, x: True if x else "Please select at least one board", # type: ignore
123
+ ),
124
+ )
125
+ )
126
+
127
+
128
+ def ask_versions(questions: list, *, action: str):
129
+ # import only when needed to reduce load time
130
+ import inquirer
131
+
132
+ input_ux = inquirer.Checkbox if action == "download" else inquirer.List
133
+ mp_versions: List[str] = micropython_versions()
134
+ mp_versions = [v for v in mp_versions if "preview" not in v]
135
+ mp_versions.append("preview")
136
+ mp_versions.reverse() # newest first
137
+ questions.append(
138
+ input_ux(
139
+ "versions",
140
+ message=f"What version(s) do you want to {action}?",
141
+ choices=mp_versions,
142
+ autocomplete=True,
143
+ validate=lambda _, x: True if x else "Please select at least one version", # type: ignore
144
+ )
145
+ )
146
+
147
+
148
+ def ask_serialport(questions: list, *, action: str):
149
+ # import only when needed to reduce load time
150
+ import inquirer
151
+
152
+ serialports = MPRemoteBoard.connected_boards()
153
+ questions.append(
154
+ inquirer.List(
155
+ "serial",
156
+ message="What serial port do you want use ?",
157
+ validate=lambda _, x: True if x else "Please enter a serial port", # type: ignore
158
+ choices=serialports,
159
+ other=True,
160
+ )
161
+ )
162
+
163
+ return questions
@@ -3,14 +3,15 @@
3
3
  from pathlib import Path
4
4
  from typing import List, Tuple
5
5
 
6
- from mpflash.common import clean_version
7
6
  import rich_click as click
8
7
 
8
+ from mpflash.common import clean_version
9
+
10
+ from .ask_input import DownloadParams, ask_missing_params
9
11
  from .cli_group import cli
12
+ from .cli_list import list_mcus
10
13
  from .config import config
11
14
  from .download import download
12
- from .download_input import DownloadParams, ask_missing_params
13
- from .cli_list import list_mcus
14
15
 
15
16
 
16
17
  def connected_ports_boards() -> Tuple[List[str], List[str]]:
@@ -27,6 +28,7 @@ def connected_ports_boards() -> Tuple[List[str], List[str]]:
27
28
  @click.option(
28
29
  "--destination",
29
30
  "-d",
31
+ "fw_folder",
30
32
  type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
31
33
  default=config.firmware_folder,
32
34
  show_default=True,
@@ -36,18 +38,21 @@ def connected_ports_boards() -> Tuple[List[str], List[str]]:
36
38
  "--version",
37
39
  "-v",
38
40
  "versions",
41
+ default=["stable"],
39
42
  multiple=True,
40
- help="The version of MicroPython to to download. Use 'preview' to include preview versions.",
41
43
  show_default=True,
42
- default=["stable"],
44
+ help="The version of MicroPython to to download.",
45
+ metavar="SEMVER, 'stable', 'preview' or '?'",
43
46
  )
44
47
  @click.option(
45
48
  "--board",
46
49
  "-b",
47
50
  "boards",
48
51
  multiple=True,
52
+ default=["?"],
49
53
  show_default=True,
50
- help="The board(s) to download the firmware for.", # Use '--board all' to download all boards.",
54
+ help="The board(s) to download the firmware for.",
55
+ metavar="BOARD_ID or ?",
51
56
  )
52
57
  @click.option(
53
58
  "--clean/--no-clean",
@@ -59,8 +64,8 @@ def connected_ports_boards() -> Tuple[List[str], List[str]]:
59
64
  "--force",
60
65
  default=False,
61
66
  is_flag=True,
62
- help="""Force download of firmware even if it already exists.""",
63
67
  show_default=True,
68
+ help="""Force download of firmware even if it already exists.""",
64
69
  )
65
70
  def cli_download(
66
71
  **kwargs,
@@ -70,18 +75,17 @@ def cli_download(
70
75
  if not params.boards:
71
76
  # nothing specified - detect connected boards
72
77
  params.ports, params.boards = connected_ports_boards()
78
+ # ask for any remaining parameters
79
+ params = ask_missing_params(params, action="download")
80
+ assert isinstance(params, DownloadParams)
73
81
 
74
82
  params.versions = [clean_version(v, drop_v=True) for v in params.versions] # remove leading v from version
75
83
 
76
- # ask for any remaining parameters
77
- params = ask_missing_params(params)
78
- # preview is not a version, it is an option to include preview versions
79
84
  download(
80
- params.destination,
85
+ params.fw_folder,
81
86
  params.ports,
82
87
  params.boards,
83
88
  params.versions,
84
89
  params.force,
85
90
  params.clean,
86
- params.preview,
87
91
  )
@@ -0,0 +1,225 @@
1
+ from pathlib import Path
2
+
3
+ import rich_click as click
4
+ from loguru import logger as log
5
+
6
+ from mpflash.mpboard_id.api import find_mp_board
7
+
8
+ from .ask_input import FlashParams, ask_missing_params
9
+ from .cli_download import connected_ports_boards
10
+ from .cli_group import cli
11
+ from .cli_list import show_mcus
12
+ from .common import clean_version
13
+ from .config import config
14
+ from .flash import WorkList, auto_update, enter_bootloader, find_firmware
15
+ from .flash_esp import flash_esp
16
+ from .flash_stm32 import flash_stm32
17
+ from .flash_uf2 import flash_uf2
18
+ from .mpremoteboard import MPRemoteBoard
19
+
20
+ # #########################################################################################################
21
+ # CLI
22
+ # #########################################################################################################
23
+
24
+
25
+ @cli.command(
26
+ "flash",
27
+ short_help="Flash one or all connected MicroPython boards with a specific firmware and version.",
28
+ )
29
+ @click.option(
30
+ "--firmware",
31
+ "-f",
32
+ "fw_folder",
33
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
34
+ default=config.firmware_folder,
35
+ show_default=True,
36
+ help="The folder to retrieve the firmware from.",
37
+ )
38
+ @click.option(
39
+ "--version",
40
+ "-v",
41
+ "version", # single version
42
+ default="stable",
43
+ multiple=False,
44
+ show_default=True,
45
+ help="The version of MicroPython to flash.",
46
+ metavar="SEMVER, 'stable', 'preview' or '?'",
47
+ )
48
+ @click.option(
49
+ "--serial",
50
+ "--serial-port",
51
+ "-s",
52
+ "serial",
53
+ default="auto",
54
+ show_default=True,
55
+ help="Which serial port(s) to flash",
56
+ metavar="SERIAL_PORT",
57
+ )
58
+ @click.option(
59
+ "--port",
60
+ "-p",
61
+ "ports",
62
+ help="The MicroPython port to flash",
63
+ metavar="PORT",
64
+ default=[],
65
+ multiple=True,
66
+ )
67
+ @click.option(
68
+ "--board",
69
+ "-b",
70
+ "boards",
71
+ multiple=False,
72
+ default=[],
73
+ help="The MicroPython board ID to flash. If not specified will try to read the BOARD_ID from the connected MCU.",
74
+ metavar="BOARD_ID or ?",
75
+ )
76
+ @click.option(
77
+ "--cpu",
78
+ "--chip",
79
+ "-c",
80
+ "cpu",
81
+ help="The CPU type to flash. If not specified will try to read the CPU from the connected MCU.",
82
+ metavar="CPU",
83
+ )
84
+ @click.option(
85
+ "--erase/--no-erase",
86
+ default=True,
87
+ show_default=True,
88
+ help="""Erase flash before writing new firmware. (not on UF2 boards)""",
89
+ )
90
+ @click.option(
91
+ "--bootloader/--no-bootloader",
92
+ default=True,
93
+ is_flag=True,
94
+ show_default=True,
95
+ help="""Enter micropython bootloader mode before flashing.""",
96
+ )
97
+ def cli_flash_board(**kwargs):
98
+ todo: WorkList = []
99
+
100
+ # version to versions
101
+ if "version" in kwargs:
102
+ kwargs["versions"] = [kwargs.pop("version")]
103
+ params = FlashParams(**kwargs)
104
+ print(f"{params=}")
105
+ # print(f"{params.version=}")
106
+ print(f"{params.versions=}")
107
+ if not params.boards:
108
+ # nothing specified - detect connected boards
109
+ params.ports, params.boards = connected_ports_boards()
110
+ # Ask for missing input if needed
111
+ params = ask_missing_params(params, action="flash")
112
+ # TODO: Just in time Download of firmware
113
+
114
+ assert isinstance(params, FlashParams)
115
+
116
+ if len(params.versions) > 1:
117
+ print(repr(params.versions))
118
+ log.error(f"Only one version can be flashed at a time, not {params.versions}")
119
+ return
120
+ params.versions = [clean_version(v) for v in params.versions]
121
+ if params.versions[0] and params.boards[0] and params.serial:
122
+ # update a single board
123
+ todo = manual_worklist(
124
+ params.versions[0],
125
+ params.fw_folder,
126
+ params.serial,
127
+ params.boards[0],
128
+ # params.ports[0],
129
+ )
130
+ elif params.serial:
131
+ if params.serial == "auto":
132
+ # Update all micropython boards to the latest version
133
+ todo = auto_worklist(params.versions[0], params.fw_folder)
134
+ else:
135
+ # just this serial port on auto
136
+ todo = oneport_worklist(
137
+ params.versions[0],
138
+ params.fw_folder,
139
+ params.serial,
140
+ )
141
+
142
+ if flashed := flash_list(
143
+ todo,
144
+ params.fw_folder,
145
+ params.erase,
146
+ params.bootloader,
147
+ ):
148
+ log.info(f"Flashed {len(flashed)} boards")
149
+ show_mcus(flashed, title="Connected boards after flashing")
150
+
151
+
152
+ def oneport_worklist(
153
+ version: str,
154
+ fw_folder: Path,
155
+ serial_port: str,
156
+ # preview: bool,
157
+ ) -> WorkList:
158
+ """Create a worklist for a single serial-port."""
159
+ conn_boards = [MPRemoteBoard(serial_port)]
160
+ todo = auto_update(conn_boards, version, fw_folder)
161
+ show_mcus(conn_boards)
162
+ return todo
163
+
164
+
165
+ def auto_worklist(version: str, fw_folder: Path) -> WorkList:
166
+ conn_boards = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards() if sp not in config.ignore_ports]
167
+ return auto_update(conn_boards, version, fw_folder)
168
+
169
+
170
+ def manual_worklist(
171
+ version: str,
172
+ fw_folder: Path,
173
+ serial_port: str,
174
+ board: str,
175
+ # port: str,
176
+ ) -> WorkList:
177
+ mcu = MPRemoteBoard(serial_port)
178
+ # TODO : Find a way to avoid needing to specify the port
179
+ # Lookup the matching port and cpu in board_info based in the board name
180
+ port = find_mp_board(board)["port"]
181
+ mcu.port = port
182
+ mcu.cpu = port if port.startswith("esp") else ""
183
+ mcu.board = board
184
+ firmwares = find_firmware(fw_folder=fw_folder, board=board, version=version, port=port)
185
+ if not firmwares:
186
+ log.error(f"No firmware found for {port} {board} version {version}")
187
+ return []
188
+ # use the most recent matching firmware
189
+ return [(mcu, firmwares[-1])]
190
+
191
+
192
+ def flash_list(
193
+ todo: WorkList,
194
+ fw_folder: Path,
195
+ erase: bool,
196
+ bootloader: bool,
197
+ ):
198
+ """Flash a list of boards with the specified firmware."""
199
+ flashed = []
200
+ for mcu, fw_info in todo:
201
+ fw_file = fw_folder / fw_info["filename"] # type: ignore
202
+ if not fw_file.exists():
203
+ log.error(f"File {fw_file} does not exist, skipping {mcu.board} on {mcu.serialport}")
204
+ continue
205
+ log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info['version']}")
206
+ updated = None
207
+ # try:
208
+ if mcu.port in ["samd", "rp2", "nrf"]: # [k for k, v in PORT_FWTYPES.items() if v == ".uf2"]:
209
+ if bootloader:
210
+ enter_bootloader(mcu)
211
+ updated = flash_uf2(mcu, fw_file=fw_file, erase=erase)
212
+ elif mcu.port in ["stm32"]:
213
+ if bootloader:
214
+ enter_bootloader(mcu)
215
+ updated = flash_stm32(mcu, fw_file, erase=erase)
216
+ elif mcu.port in ["esp32", "esp8266"]:
217
+ # bootloader is handled by esptool for esp32/esp8266
218
+ updated = flash_esp(mcu, fw_file=fw_file, erase=erase)
219
+ else:
220
+ log.error(f"Don't (yet) know how to flash {mcu.port}-{mcu.board} on {mcu.serialport}")
221
+
222
+ if updated:
223
+ flashed.append(updated)
224
+ else:
225
+ log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
@@ -13,8 +13,10 @@ def cb_verbose(ctx, param, value):
13
13
  """Callback to set the log level to DEBUG if verbose is set"""
14
14
  if value:
15
15
  set_loglevel("DEBUG")
16
+ config.verbose = True
16
17
  else:
17
18
  set_loglevel("INFO")
19
+ config.verbose = False
18
20
  return value
19
21
 
20
22
 
@@ -24,6 +26,12 @@ def cb_ignore(ctx, param, value):
24
26
  return value
25
27
 
26
28
 
29
+ def cb_interactive(ctx, param, value):
30
+ if value:
31
+ config.interactive = value
32
+ return value
33
+
34
+
27
35
  def cb_quiet(ctx, param, value):
28
36
  if value:
29
37
  make_quiet()
@@ -31,6 +39,7 @@ def cb_quiet(ctx, param, value):
31
39
 
32
40
 
33
41
  @click.group()
42
+ @click.version_option(package_name="mpflash")
34
43
  @click.option(
35
44
  "--quiet",
36
45
  "-q",
@@ -41,6 +50,16 @@ def cb_quiet(ctx, param, value):
41
50
  envvar="MPFLASH_QUIET",
42
51
  show_default=True,
43
52
  )
53
+ @click.option(
54
+ "--interactive/--no-interactive",
55
+ "-i/-x",
56
+ is_eager=True,
57
+ help="Suppresses all request for Input.",
58
+ callback=cb_interactive,
59
+ # envvar="MPFLASH_QUIET",
60
+ default=True,
61
+ show_default=True,
62
+ )
44
63
  @click.option(
45
64
  "-V",
46
65
  "--verbose",
@@ -61,7 +80,7 @@ def cb_quiet(ctx, param, value):
61
80
  show_default=True,
62
81
  metavar="SERIALPORT",
63
82
  )
64
- def cli(quiet: bool, **kwargs):
83
+ def cli(**kwargs):
65
84
  """mpflash - MicroPython Tool.
66
85
 
67
86
  A CLI to download and flash MicroPython firmware to different ports and boards.
@@ -6,10 +6,11 @@ from rich import print
6
6
  from rich.progress import track
7
7
  from rich.table import Table
8
8
 
9
+ from mpflash.mpremoteboard import MPRemoteBoard
10
+
9
11
  from .cli_group import cli
10
12
  from .config import config
11
13
  from .logger import console, make_quiet
12
- from .mpremoteboard import MPRemoteBoard
13
14
 
14
15
 
15
16
  @cli.command("list", help="List the connected MCU boards.")
@@ -67,8 +68,8 @@ def show_mcus(
67
68
  title=title,
68
69
  header_style="bold blue",
69
70
  collapse_padding=True,
70
- width=100,
71
- # row_styles=["blue", "yellow"]
71
+ width=110,
72
+ row_styles=["blue", "yellow"],
72
73
  )
73
74
  table.add_column("Serial", overflow="fold")
74
75
  table.add_column("Family")
@@ -89,7 +90,7 @@ def show_mcus(
89
90
  mcu.serialport.replace("/dev/", ""),
90
91
  mcu.family,
91
92
  mcu.port,
92
- mcu.board if mcu.board != "UNKNOWN" else mcu.description,
93
+ f"{mcu.board}\n{mcu.description}".strip(),
93
94
  # mcu.variant,
94
95
  mcu.cpu,
95
96
  mcu.version,
@@ -3,8 +3,8 @@
3
3
  # import rich_click as click
4
4
 
5
5
  from .cli_download import cli_download
6
- from .cli_group import cli
7
6
  from .cli_flash import cli_flash_board
7
+ from .cli_group import cli
8
8
  from .cli_list import cli_list_mcus
9
9
 
10
10
  # from loguru import logger as log
@@ -14,8 +14,9 @@ def mpflash():
14
14
  cli.add_command(cli_flash_board)
15
15
  cli.add_command(cli_list_mcus)
16
16
  cli.add_command(cli_download)
17
- cli(auto_envvar_prefix="MPFLASH")
17
+ # cli(auto_envvar_prefix="MPFLASH")
18
+ cli()
18
19
 
19
20
 
20
- if __name__ == "__main__":
21
- mpflash()
21
+ # if __name__ == "__main__":
22
+ mpflash()
@@ -1,7 +1,9 @@
1
+ import os
1
2
  import time
3
+ from functools import lru_cache
2
4
  from typing import TypedDict
3
5
 
4
- from github import Github
6
+ from github import Auth, Github
5
7
  from loguru import logger as log
6
8
  from packaging.version import parse
7
9
  from rich.progress import track
@@ -19,6 +21,14 @@ PORT_FWTYPES = {
19
21
  "renesas-ra": [".hex"],
20
22
  }
21
23
 
24
+ # Token with no permissions to avoid throttling
25
+ # https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#getting-a-higher-rate-limit
26
+ PAT_NO_ACCESS = (
27
+ "github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
28
+ )
29
+ PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
30
+ GH_CLIENT = Github(auth=Auth.Token(PAT))
31
+
22
32
 
23
33
  class FWInfo(TypedDict):
24
34
  filename: str
@@ -27,6 +37,7 @@ class FWInfo(TypedDict):
27
37
  variant: str
28
38
  preview: bool
29
39
  version: str
40
+ build: str
30
41
 
31
42
 
32
43
  #############################################################
@@ -52,7 +63,7 @@ def clean_version(
52
63
  if version in {"", "-"}:
53
64
  return version
54
65
  if version.lower() == "stable":
55
- _v = get_stable_version()
66
+ _v = get_stable_mp_version()
56
67
  if not _v:
57
68
  log.warning("Could not determine the latest stable version")
58
69
  return "stable"
@@ -89,12 +100,12 @@ def clean_version(
89
100
  return version
90
101
 
91
102
 
92
- def micropython_versions(minver: str = "v1.9.2"):
103
+ @lru_cache(maxsize=10)
104
+ def micropython_versions(minver: str = "v1.20"):
93
105
  """Get the list of micropython versions from github tags"""
94
106
  try:
95
- g = Github()
96
- _ = 1 / 0
97
- repo = g.get_repo("micropython/micropython")
107
+ gh_client = GH_CLIENT
108
+ repo = gh_client.get_repo("micropython/micropython")
98
109
  versions = [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)]
99
110
  except Exception:
100
111
  versions = [
@@ -116,19 +127,23 @@ def micropython_versions(minver: str = "v1.9.2"):
116
127
  "v1.12",
117
128
  "v1.11",
118
129
  "v1.10",
119
- "v1.9.4",
120
- "v1.9.3",
121
130
  ]
122
131
  versions = [v for v in versions if parse(v) >= parse(minver)]
123
132
  return sorted(versions)
124
133
 
125
134
 
126
- def get_stable_version() -> str:
135
+ def get_stable_mp_version() -> str:
127
136
  # read the versions from the git tags
128
137
  all_versions = micropython_versions(minver="v1.17")
129
138
  return [v for v in all_versions if not v.endswith(V_PREVIEW)][-1]
130
139
 
131
140
 
141
+ def get_preview_mp_version() -> str:
142
+ # read the versions from the git tags
143
+ all_versions = micropython_versions(minver="v1.17")
144
+ return [v for v in all_versions if v.endswith(V_PREVIEW)][-1]
145
+
146
+
132
147
  #############################################################
133
148
  def wait_for_restart(mcu: MPRemoteBoard, timeout: int = 10):
134
149
  """wait for the board to restart"""
@@ -1,16 +1,18 @@
1
1
  """centralized configuration for mpflash"""
2
2
 
3
- import platformdirs
4
-
5
- from typing import List
6
3
  from pathlib import Path
4
+ from typing import List
5
+
6
+ import platformdirs
7
7
 
8
8
 
9
9
  class MPtoolConfig:
10
10
  """Centralized configuration for mpflash"""
11
11
 
12
12
  quiet: bool = False
13
+ verbose: bool = False
13
14
  ignore_ports: List[str] = []
15
+ interactive: bool = True
14
16
  firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
15
17
 
16
18