mpflash 1.26.3__py3-none-any.whl → 1.26.6__py3-none-any.whl

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.
mpflash/__init__.py CHANGED
@@ -1,5 +1 @@
1
1
  """MPFlash - MicroPython firmware flashing tool."""
2
-
3
- from .logger import configure_safe_logging, setup_external_logger_safety
4
-
5
- __all__ = ["configure_safe_logging", "setup_external_logger_safety"]
mpflash/ask_input.py CHANGED
@@ -5,7 +5,7 @@ Note: The prompts can use "{version}" and "{action}" to insert the version and a
5
5
  The values are provided from the answers dictionary.
6
6
  """
7
7
 
8
- from typing import List, Sequence, Tuple, Union
8
+ from typing import Dict, List, Sequence, Tuple, Union
9
9
 
10
10
  from loguru import logger as log
11
11
 
@@ -13,7 +13,7 @@ from .common import DownloadParams, FlashParams, ParamType
13
13
  from .config import config
14
14
  from .mpboard_id import get_known_boards_for_port, known_ports, known_stored_boards
15
15
  from .mpremoteboard import MPRemoteBoard
16
- from .versions import micropython_versions
16
+ from .versions import clean_version, micropython_versions
17
17
 
18
18
 
19
19
  def ask_missing_params(
@@ -43,7 +43,7 @@ def ask_missing_params(
43
43
  action = "download" if isinstance(params, DownloadParams) else "flash"
44
44
 
45
45
  questions = []
46
- answers: dict[str, Union[str, List]] = {"action": action}
46
+ answers: Dict[str, Union[str, List]] = {"action": action}
47
47
  if not multi_select:
48
48
  if not params.serial or "?" in params.serial:
49
49
  questions.append(ask_serialport(multi_select=False, bluetooth=False))
@@ -59,7 +59,14 @@ def ask_missing_params(
59
59
  if not params.boards or "?" in params.boards:
60
60
  questions.extend(ask_port_board(multi_select=multi_select, action=action))
61
61
  if questions:
62
- answers = inquirer.prompt(questions, answers=answers) # type: ignore
62
+ # Store the pre-existing answers before prompting
63
+ pre_existing_answers = dict(answers)
64
+ prompted_answers = inquirer.prompt(questions, answers=answers) # type: ignore
65
+ if not prompted_answers:
66
+ # input cancelled by user
67
+ return [] # type: ignore
68
+ # Merge pre-existing answers with prompted answers
69
+ answers = {**pre_existing_answers, **prompted_answers}
63
70
  if not answers:
64
71
  # input cancelled by user
65
72
  return [] # type: ignore
@@ -99,6 +106,7 @@ def ask_missing_params(
99
106
  def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
100
107
  """
101
108
  Filters the known boards based on the selected versions and returns the filtered boards.
109
+ If no boards are found for the requested version(s), falls back to previous stable/preview versions.
102
110
 
103
111
  Args:
104
112
  answers (dict): The user's answers.
@@ -107,8 +115,11 @@ def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
107
115
  Sequence[Tuple[str, str]]: The filtered boards.
108
116
  """
109
117
  versions = []
118
+ original_versions = []
119
+
110
120
  # if version is not asked ; then need to get the version from the inputs
111
121
  if "versions" in answers:
122
+ original_versions = list(answers["versions"])
112
123
  versions = list(answers["versions"])
113
124
  if "stable" in versions:
114
125
  versions.remove("stable")
@@ -117,7 +128,45 @@ def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
117
128
  versions.remove("preview")
118
129
  versions.extend((micropython_versions()[-1], micropython_versions()[-2])) # latest preview and stable
119
130
 
120
- some_boards = known_stored_boards(answers["port"], versions) # or known_mp_boards(answers["port"])
131
+ some_boards = known_stored_boards(answers["port"], versions)
132
+
133
+ # If no boards found and we have specific versions, try fallback
134
+ if not some_boards and versions:
135
+ log.debug(f"No boards found for {answers['port']} with version(s) {versions}, trying fallback")
136
+
137
+ # Get all micropython versions to find fallback candidates
138
+ all_versions = micropython_versions()
139
+ fallback_versions = []
140
+
141
+ for original_version in original_versions:
142
+ if original_version == "stable":
143
+ # For stable, try previous stable versions
144
+ stable_versions = [v for v in all_versions if not v.endswith("preview")]
145
+ # Try the last 3 stable versions
146
+ fallback_versions.extend(stable_versions[-3:])
147
+ elif original_version == "preview":
148
+ # For preview, try current preview and recent stable versions
149
+ preview_versions = [v for v in all_versions if v.endswith("preview")]
150
+ stable_versions = [v for v in all_versions if not v.endswith("preview")]
151
+ fallback_versions.extend(preview_versions[-1:] + stable_versions[-2:])
152
+ else:
153
+ # For specific version, try that version and previous versions
154
+ try:
155
+ version_index = all_versions.index(original_version)
156
+ # Try current and up to 2 previous versions
157
+ start_idx = max(0, version_index - 2)
158
+ fallback_versions.extend(all_versions[start_idx : version_index + 1])
159
+ except ValueError:
160
+ # Version not found in list, try recent stable versions
161
+ stable_versions = [v for v in all_versions if not v.endswith("preview")]
162
+ fallback_versions.extend(stable_versions[-2:])
163
+
164
+ # Remove duplicates and clean versions
165
+ fallback_versions = [clean_version(v) for v in list(set(fallback_versions))]
166
+
167
+ if fallback_versions:
168
+ log.debug(f"Trying fallback versions: {fallback_versions}")
169
+ some_boards = known_stored_boards(answers["port"], fallback_versions)
121
170
 
122
171
  if some_boards:
123
172
  # Create a dictionary where the keys are the second elements of the tuples
@@ -226,7 +275,7 @@ def ask_serialport(*, multi_select: bool = False, bluetooth: bool = False):
226
275
  # import only when needed to reduce load time
227
276
  import inquirer
228
277
 
229
- comports = MPRemoteBoard.connected_boards(bluetooth=bluetooth, description=True) + ["auto"]
278
+ comports = MPRemoteBoard.connected_comports(bluetooth=bluetooth, description=True) + ["auto"]
230
279
  return inquirer.List(
231
280
  "serial",
232
281
  message="Which serial port do you want to {action} ?",
mpflash/basicgit.py CHANGED
@@ -13,17 +13,6 @@ from loguru import logger as log
13
13
 
14
14
  from mpflash.config import config
15
15
 
16
- # from github import Auth, BadCredentialsException, Github
17
-
18
- # # Token with no permissions to avoid throttling
19
- # # 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
20
- # PAT_NO_ACCESS = "github_pat_" + "11AAHPVFQ0G4NTaQ73Bw5J" + "_fAp7K9sZ1qL8VFnI9g78eUlCdmOXHB3WzSdj2jtEYb4XF3N7PDJBl32qIxq"
21
- # PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
22
-
23
- # # GH_CLIENT = Github(auth=Auth.Token(PAT))
24
-
25
- # GH_CLIENT = None
26
-
27
16
 
28
17
  def _run_local_git(
29
18
  cmd: List[str],
mpflash/cli_flash.py CHANGED
@@ -11,8 +11,8 @@ from mpflash.cli_group import cli
11
11
  from mpflash.cli_list import show_mcus
12
12
  from mpflash.common import BootloaderMethod, FlashParams, filtered_comports
13
13
  from mpflash.errors import MPFlashError
14
- from mpflash.flash import flash_list
15
- from mpflash.flash.worklist import WorkList, full_auto_worklist, manual_worklist, single_auto_worklist
14
+ from mpflash.flash import flash_tasks
15
+ from mpflash.flash.worklist import FlashTaskList, create_worklist
16
16
  from mpflash.mpremoteboard import MPRemoteBoard
17
17
  from mpflash.versions import clean_version
18
18
 
@@ -193,10 +193,10 @@ def cli_flash_board(**kwargs) -> int:
193
193
  raise MPFlashError("Only one version can be flashed at a time")
194
194
 
195
195
  params.versions = [clean_version(v) for v in params.versions]
196
- worklist: WorkList = []
196
+ tasks: FlashTaskList = []
197
197
 
198
198
  if len(params.versions) == 1 and len(params.boards) == 1 and params.serial == ["*"]:
199
- # A one or more serial port including the board / variant
199
+ # One or more serial ports including the board / variant (auto-detect ports)
200
200
  comports = filtered_comports(
201
201
  ignore=params.ignore,
202
202
  include=params.serial,
@@ -205,50 +205,49 @@ def cli_flash_board(**kwargs) -> int:
205
205
  board_id = f"{params.boards[0]}-{params.variant}" if params.variant else params.boards[0]
206
206
  log.info(f"Flashing {board_id} {params.versions[0]} to {len(comports)} serial ports")
207
207
  log.info(f"Target ports: {', '.join(comports)}")
208
- worklist = manual_worklist(
209
- comports,
208
+ tasks = create_worklist(
209
+ params.versions[0],
210
+ serial_ports=comports,
210
211
  board_id=board_id,
211
- version=params.versions[0],
212
- custom=params.custom,
212
+ custom_firmware=params.custom,
213
213
  )
214
- # if serial port == auto and there are one or more specified/detected boards
215
214
  elif params.serial == ["*"] and params.boards:
215
+ # Auto mode on detected boards with optional include/ignore filtering
216
216
  if not all_boards:
217
217
  log.trace("No boards detected yet, scanning for connected boards")
218
218
  _, _, _, all_boards = connected_ports_boards_variants(include=params.ports, ignore=params.ignore)
219
- # if variant id provided on the cmdline, treat is as an override
220
219
  if params.variant:
221
220
  for b in all_boards:
222
221
  b.variant = params.variant if (params.variant.lower() not in {"-", "none"}) else ""
223
-
224
- worklist = full_auto_worklist(
225
- all_boards=all_boards,
226
- version=params.versions[0],
227
- include=params.serial,
228
- ignore=params.ignore,
222
+ tasks = create_worklist(
223
+ params.versions[0],
224
+ connected_comports=all_boards,
225
+ include_ports=params.serial,
226
+ ignore_ports=params.ignore,
229
227
  )
230
- elif params.versions[0] and params.boards[0] and params.serial:
231
- # A one or more serial port including the board / variant
228
+ elif params.versions[0] and params.boards and params.serial:
229
+ # Manual specification of serial ports + board
232
230
  comports = filtered_comports(
233
231
  ignore=params.ignore,
234
- include=params.ports,
232
+ include=params.serial,
235
233
  bluetooth=params.bluetooth,
236
234
  )
237
- worklist = manual_worklist(
238
- comports,
235
+ tasks = create_worklist(
236
+ params.versions[0],
237
+ serial_ports=comports,
239
238
  board_id=params.boards[0],
240
- version=params.versions[0],
241
239
  )
242
240
  else:
243
- # just this serial port on auto
244
- worklist = single_auto_worklist(
245
- serial=params.serial[0],
246
- version=params.versions[0],
241
+ # Single serial port auto-detection
242
+ connected_comports = [MPRemoteBoard(params.serial[0])]
243
+ tasks = create_worklist(
244
+ params.versions[0],
245
+ connected_comports=connected_comports,
247
246
  )
248
247
  if not params.custom:
249
- jid.ensure_firmware_downloaded(worklist, version=params.versions[0], force=params.force)
250
- if flashed := flash_list(
251
- worklist,
248
+ jid.ensure_firmware_downloaded_tasks(tasks, version=params.versions[0], force=params.force)
249
+ if flashed := flash_tasks(
250
+ tasks,
252
251
  params.erase,
253
252
  params.bootloader,
254
253
  flash_mode=params.flash_mode,
mpflash/config.py CHANGED
@@ -100,7 +100,8 @@ class MPFlashConfig:
100
100
 
101
101
  # Token with no permissions to avoid throttling
102
102
  # 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
103
- PAT_NO_ACCESS = "github_pat_" + "11AAHPVFQ0G4NTaQ73Bw5J" + "_fAp7K9sZ1qL8VFnI9g78eUlCdmOXHB3WzSdj2jtEYb4XF3N7PDJBl32qIxq"
103
+ # mpflash_read_mp_versions - 31-10-2025
104
+ PAT_NO_ACCESS = "github_pat_" + "11AAHPVFQ0qUKF3mRb9iw2" + "_rwgJ0FZUDYftFFyZhilncyVcqhIZaVF4abZxbGgMOhdSTEPUEKEpAM7m2gp"
104
105
  PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
105
106
  self._gh_client = Github(auth=Auth.Token(PAT))
106
107
  return self._gh_client
mpflash/connected.py CHANGED
@@ -47,8 +47,6 @@ def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False)
47
47
  Raises:
48
48
  ConnectionError: If there is an error connecting to a board.
49
49
  """
50
- # conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
51
- vid_pid = True
52
50
  comports = filtered_portinfos(
53
51
  ignore=ignore,
54
52
  include=include,
@@ -0,0 +1 @@
1
+ v1.27.0-preview
mpflash/db/core.py CHANGED
@@ -9,7 +9,6 @@ from sqlalchemy.orm import sessionmaker
9
9
  from mpflash.config import config
10
10
  from mpflash.errors import MPFlashError
11
11
 
12
- # TODO: lazy import to avoid slowdowns ?
13
12
  from .models import Base
14
13
 
15
14
  TRACE = False
@@ -1,6 +1,9 @@
1
1
  from os import path
2
2
  from pathlib import Path
3
- from typing import List
3
+ from typing import List, Optional, Tuple
4
+
5
+ import click
6
+ from typing_extensions import TypeAlias
4
7
 
5
8
  import mpflash.basicgit as git
6
9
  from mpflash.logger import log
@@ -8,6 +11,8 @@ from mpflash.mpremoteboard import HERE
8
11
  from mpflash.vendor.board_database import Database
9
12
  from mpflash.versions import micropython_versions
10
13
 
14
+ BoardList: TypeAlias = List[Tuple[str, ...]]
15
+
11
16
  HERE = Path(__file__).parent.resolve()
12
17
 
13
18
 
@@ -47,7 +52,7 @@ def boardlist_from_repo(
47
52
  versions: List[str],
48
53
  mpy_dir: Path,
49
54
  ):
50
- longlist = []
55
+ longlist: BoardList = []
51
56
  if not mpy_dir.is_dir():
52
57
  log.error(f"Directory {mpy_dir} not found")
53
58
  return longlist
@@ -71,38 +76,63 @@ def boardlist_from_repo(
71
76
  log.info(f"{git.get_git_describe(mpy_dir)} - {build_nr}")
72
77
  # un-cached database
73
78
  db = Database(mpy_dir)
74
- shortlist = list(iter_boards(db, version=version))
79
+ shortlist: BoardList = list(iter_boards(db, version=version))
75
80
  log.info(f"boards found {len(db.boards.keys())}")
76
81
  log.info(f"boards-variants found {len(shortlist) - len(db.boards.keys())}")
77
82
  longlist.extend(shortlist)
78
83
  return longlist
79
84
 
80
85
 
81
- def create_zip_file(longlist, zip_file: Path):
82
- """Create a ZIP file containing the CSV data."""
83
- # lazy import
84
- import zipfile
86
+ def create_zip_file(longlist: BoardList, zip_file: Path):
87
+ """Create a ZIP file containing the CSV data without external deps.
85
88
 
86
- import pandas as pd
89
+ Uses the standard library csv module to minimize dependencies while
90
+ preserving identical column ordering to the prior pandas implementation.
91
+ """
92
+ import csv
93
+ import io
94
+ import zipfile # lazy import
87
95
 
88
96
  csv_filename = "micropython_boards.csv"
97
+ columns = [
98
+ "version",
99
+ "board_id",
100
+ "board_name",
101
+ "mcu",
102
+ "variant",
103
+ "port",
104
+ "path",
105
+ "description",
106
+ "family",
107
+ ]
108
+
109
+ buf = io.StringIO()
110
+ writer = csv.writer(buf, lineterminator="\n")
111
+ writer.writerow(columns)
112
+ # rows already in correct order matching columns
113
+ for row in longlist:
114
+ writer.writerow(row)
115
+ csv_data = buf.getvalue()
89
116
 
90
- columns = ["version", "board_id", "board_name", "mcu", "variant", "port", "path", "description", "family"]
91
- df = pd.DataFrame(longlist, columns=columns)
92
-
93
- # Create the ZIP file and add the CSV data directly without creating an intermediate file
94
117
  with zipfile.ZipFile(zip_file, "w", zipfile.ZIP_DEFLATED) as zipf:
95
- # Create a temporary in-memory CSV string
96
- csv_data = df.to_csv(index=False)
97
- # Write the CSV data directly to the zip file
98
118
  zipf.writestr(csv_filename, csv_data)
99
119
 
100
120
 
121
+ def write_version_file(version: str, output_path: Path):
122
+ version_file = output_path / "boards_version.txt"
123
+ with version_file.open("w", encoding="utf-8") as vf:
124
+ vf.write(version + "\n")
125
+ log.info(f"Wrote version file {version_file}")
126
+
127
+
101
128
  def package_repo(mpy_path: Path):
102
129
  mpy_path = mpy_path or Path("../repos/micropython")
103
130
  log.info(f"Packaging Micropython boards from {mpy_path}")
104
131
  mp_versions = micropython_versions(minver="1.18")
105
- # checkput
132
+ if not mp_versions:
133
+ log.error("No Micropython versions found")
134
+ return
135
+ # checkout
106
136
  longlist = boardlist_from_repo(
107
137
  versions=mp_versions,
108
138
  mpy_dir=mpy_path,
@@ -110,9 +140,29 @@ def package_repo(mpy_path: Path):
110
140
  log.info(f"Total boards-variants: {len(longlist)}")
111
141
  zip_file = HERE / "micropython_boards.zip"
112
142
  create_zip_file(longlist, zip_file=zip_file)
143
+ log.info(f"Created {zip_file} with {len(longlist)} entries")
144
+ boards_version = mp_versions[-1]
145
+ write_version_file(boards_version, HERE)
113
146
 
114
147
  assert zip_file.is_file(), f"Failed to create {zip_file}"
115
148
 
116
149
 
150
+ @click.command()
151
+ @click.option(
152
+ "--mpy-path",
153
+ "mpy_path",
154
+ type=click.Path(path_type=Path),
155
+ default=None,
156
+ help="Path to local micropython repo (default: ../repos/micropython).",
157
+ )
158
+ def cli(mpy_path: Optional[Path]):
159
+ """Package board metadata into a compressed archive.
160
+
161
+ Enumerates boards and variants from a Micropython repo, builds CSV, and
162
+ writes it into a zip archive for fast loading and distribution.
163
+ """
164
+ package_repo(mpy_path if mpy_path else Path("../repos/micropython"))
165
+
166
+
117
167
  if __name__ == "__main__":
118
- package_repo(Path("D:\\mypython\\mpflash\\repos\\micropython"))
168
+ cli()
mpflash/db/loader.py CHANGED
@@ -4,7 +4,6 @@ import json
4
4
  import re
5
5
  import zipfile
6
6
  from pathlib import Path
7
- from turtle import up
8
7
 
9
8
  from loguru import logger as log
10
9
 
@@ -108,8 +107,22 @@ def load_jsonl_to_db(jsonl_path: Path):
108
107
  return num_records
109
108
 
110
109
 
110
+ def get_boards_version() -> str:
111
+ version_file = HERE / "boards_version.txt"
112
+ if version_file.is_file():
113
+ with version_file.open("r", encoding="utf-8") as vf:
114
+ version = vf.read().strip()
115
+ log.debug(f"Boards version from file: {version}")
116
+ return version
117
+ log.warning(f"Boards version file not found: {version_file}")
118
+ return "unknown"
119
+
120
+
111
121
  def update_boards():
112
- boards_version = "v1.25.2"
122
+ # todo: check if update is needed
123
+ # load board_versions.txt
124
+
125
+ boards_version = get_boards_version()
113
126
  try:
114
127
  meta = get_metadata()
115
128
  log.debug(f"Metadata: {meta}")
Binary file
@@ -12,16 +12,16 @@ from typing import Dict, List, Optional
12
12
  # make sure that jsonlines does not mistake the MicroPython ujson for the CPython ujson
13
13
  import jsonlines
14
14
  from loguru import logger as log
15
- from rich.progress import track
16
-
17
15
  from mpflash.common import PORT_FWTYPES
18
16
  from mpflash.config import config
19
- from mpflash.db.core import Session
20
- from mpflash.db.models import Firmware, Board
21
17
  from mpflash.downloaded import clean_downloaded_firmwares
22
18
  from mpflash.errors import MPFlashError
23
19
  from mpflash.mpboard_id.alternate import add_renamed_boards
24
20
  from mpflash.versions import clean_version
21
+ from rich.progress import track
22
+
23
+ from mpflash.db.core import Session
24
+ from mpflash.db.models import Board, Firmware
25
25
 
26
26
  from .from_web import fetch_firmware_files, get_boards
27
27
  from .fwinfo import FWInfo
@@ -111,11 +111,7 @@ def download_firmwares(
111
111
 
112
112
  downloaded = 0
113
113
  versions = [] if versions is None else [clean_version(v) for v in versions]
114
-
115
- # remove the known variant suffixes from the boards
116
- # TODO: IS THIS REALLY NEEDED ?
117
- # boards = [strip_variant(b) for b in boards]
118
-
114
+
119
115
  # handle downloading firmware for renamed boards
120
116
  boards = add_renamed_boards(boards)
121
117
 
mpflash/download/jid.py CHANGED
@@ -1,56 +1,50 @@
1
1
  # Just In-time Download of firmware if not already available
2
+ import warnings
3
+
2
4
  from loguru import logger as log
3
5
 
4
- from mpflash.common import Params
5
6
  from mpflash.download import download
6
7
  from mpflash.downloaded import find_downloaded_firmware
7
8
  from mpflash.errors import MPFlashError
8
- from mpflash.flash.worklist import WorkList
9
+ from mpflash.flash.worklist import FlashTaskList
9
10
  from mpflash.mpboard_id.alternate import alternate_board_names
10
11
 
11
12
 
12
- def ensure_firmware_downloaded(worklist: WorkList, version: str, force: bool) -> None:
13
- """
14
- Ensure all firmware in the worklist is downloaded for the given version.
15
-
16
- Iterates over the worklist, downloads missing firmware, and updates the worklist
17
- with the downloaded firmware.
13
+ def ensure_firmware_downloaded_tasks(tasks: FlashTaskList, version: str, force: bool) -> None:
14
+ """Ensure firmware present for each FlashTask, updating in-place.
18
15
 
19
- Raises MPFlashError if download fails.
16
+ Mirrors ensure_firmware_downloaded logic but works directly on FlashTaskList.
20
17
  """
21
- # iterate over the worklist ann update missing firmware
22
- newlist: WorkList = []
23
- for mcu, firmware in worklist:
24
- if force:
25
- board_firmwares = []
26
- else:
27
- if firmware:
28
- # firmware is already downloaded
29
- newlist.append((mcu, firmware))
30
- continue
31
- # check if the firmware is already downloaded
32
- board_firmwares = find_downloaded_firmware(
33
- board_id=f"{mcu.board}-{mcu.variant}" if mcu.variant else mcu.board,
34
- version=version,
35
- port=mcu.port,
18
+ updated: FlashTaskList = []
19
+ for task in tasks:
20
+ mcu = task.board
21
+ fw = task.firmware
22
+ if not force and fw:
23
+ updated.append(task)
24
+ continue
25
+ # find already downloaded firmware unless forcing
26
+ if force or not fw:
27
+ found = (
28
+ find_downloaded_firmware(
29
+ board_id=f"{mcu.board}-{mcu.variant}" if mcu.variant else mcu.board,
30
+ version=version,
31
+ port=mcu.port,
32
+ )
33
+ if not force
34
+ else []
36
35
  )
37
- if not board_firmwares:
38
- # download the firmware
39
- log.info(f"Downloading {version} firmware for {mcu.board} on {mcu.serialport}.")
40
- download(ports=[mcu.port], boards=alternate_board_names(mcu.board, mcu.port), versions=[version], force=True, clean=True)
41
- new_firmware = find_downloaded_firmware(
42
- board_id=f"{mcu.board}-{mcu.variant}" if mcu.variant else mcu.board,
43
- version=version,
44
- port=mcu.port,
45
- )
46
- if not new_firmware:
47
- raise MPFlashError(f"Failed to download {version} firmware for {mcu.board} on {mcu.serialport}.")
48
- newlist.append((mcu, new_firmware[0]))
49
- else:
50
- log.info(f"Found {version} firmware {board_firmwares[-1].firmware_file} for {mcu.board} on {mcu.serialport}.")
51
- newlist.append((mcu, board_firmwares[0]))
52
-
53
- worklist.clear()
54
- worklist.extend(newlist)
55
-
56
- pass
36
+ if not found:
37
+ log.info(f"Downloading {version} firmware for {mcu.board} on {mcu.serialport}.")
38
+ download(ports=[mcu.port], boards=alternate_board_names(mcu.board, mcu.port), versions=[version], force=True, clean=True)
39
+ found = find_downloaded_firmware(
40
+ board_id=f"{mcu.board}-{mcu.variant}" if mcu.variant else mcu.board,
41
+ version=version,
42
+ port=mcu.port,
43
+ )
44
+ if not found:
45
+ raise MPFlashError(f"Failed to download {version} firmware for {mcu.board} on {mcu.serialport}.")
46
+ # choose last/newest
47
+ task.firmware = found[-1]
48
+ updated.append(task)
49
+ tasks.clear()
50
+ tasks.extend(updated)
mpflash/flash/__init__.py CHANGED
@@ -1,8 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from loguru import logger as log
4
-
5
- from mpflash.bootloader.activate import enter_bootloader
6
4
  from mpflash.common import PORT_FWTYPES, UF2_PORTS, BootloaderMethod
7
5
  from mpflash.config import config
8
6
  from mpflash.errors import MPFlashError
@@ -10,28 +8,29 @@ from mpflash.errors import MPFlashError
10
8
  from .esp import flash_esp
11
9
  from .stm32 import flash_stm32
12
10
  from .uf2 import flash_uf2
13
- from .worklist import WorkList
11
+ from .worklist import FlashTaskList
14
12
 
15
13
  # #########################################################################################################
16
14
 
17
- def flash_list(
18
- todo: WorkList,
15
+
16
+ def flash_tasks(
17
+ tasks: FlashTaskList,
19
18
  erase: bool,
20
19
  bootloader: BootloaderMethod,
21
- **kwargs
22
- ): # sourcery skip: use-named-expression
23
- """Flash a list of boards with the specified firmware."""
20
+ **kwargs,
21
+ ):
22
+ """Flash a list of FlashTask items directly."""
24
23
  flashed = []
25
- for mcu, fw_info in todo:
24
+ for task in tasks:
25
+ mcu = task.board
26
+ fw_info = task.firmware
26
27
  if not fw_info:
27
28
  log.error(f"Firmware not found for {mcu.board} on {mcu.serialport}, skipping")
28
29
  continue
29
-
30
30
  fw_file = config.firmware_folder / fw_info.firmware_file
31
31
  if not fw_file.exists():
32
32
  log.error(f"File {fw_file} does not exist, skipping {mcu.board} on {mcu.serialport}")
33
33
  continue
34
-
35
34
  log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info.version}")
36
35
  try:
37
36
  updated = flash_mcu(mcu, fw_file=fw_file, erase=erase, bootloader=bootloader, **kwargs)
@@ -40,14 +39,13 @@ def flash_list(
40
39
  continue
41
40
  if updated:
42
41
  if fw_info.custom:
43
- # Add / Update board_info.toml with the custom_id and Description
44
42
  mcu.get_board_info_toml()
45
43
  if fw_info.description:
46
44
  mcu.toml["description"] = fw_info.description
45
+ mcu.toml.setdefault("mpflash", {})
47
46
  mcu.toml["mpflash"]["board_id"] = fw_info.board_id
48
47
  mcu.toml["mpflash"]["custom_id"] = fw_info.custom_id
49
48
  mcu.set_board_info_toml()
50
-
51
49
  flashed.append(updated)
52
50
  else:
53
51
  log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
@@ -63,6 +61,8 @@ def flash_mcu(
63
61
  **kwargs
64
62
  ):
65
63
  """Flash a single MCU with the specified firmware."""
64
+ from mpflash.bootloader.activate import enter_bootloader
65
+
66
66
  updated = None
67
67
  try:
68
68
  if mcu.port in UF2_PORTS and fw_file.suffix == ".uf2":