mpflash 1.25.0.post1__py3-none-any.whl → 1.25.0rc2__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.
Files changed (50) hide show
  1. mpflash/add_firmware.py +43 -16
  2. mpflash/ask_input.py +4 -3
  3. mpflash/basicgit.py +1 -1
  4. mpflash/bootloader/manual.py +1 -1
  5. mpflash/cli_download.py +8 -5
  6. mpflash/cli_flash.py +17 -33
  7. mpflash/cli_group.py +3 -0
  8. mpflash/cli_list.py +8 -3
  9. mpflash/cli_main.py +4 -0
  10. mpflash/common.py +1 -36
  11. mpflash/config.py +21 -0
  12. mpflash/db/__init__.py +2 -0
  13. mpflash/db/core.py +61 -0
  14. mpflash/db/gather_boards.py +112 -0
  15. mpflash/db/loader.py +122 -0
  16. mpflash/db/meta.py +78 -0
  17. mpflash/db/micropython_boards.zip +0 -0
  18. mpflash/db/models.py +93 -0
  19. mpflash/db/tools.py +27 -0
  20. mpflash/download/__init__.py +46 -64
  21. mpflash/download/from_web.py +26 -36
  22. mpflash/download/fwinfo.py +41 -0
  23. mpflash/download/jid.py +53 -0
  24. mpflash/downloaded.py +79 -93
  25. mpflash/flash/__init__.py +7 -3
  26. mpflash/flash/esp.py +2 -1
  27. mpflash/flash/worklist.py +16 -28
  28. mpflash/list.py +3 -3
  29. mpflash/logger.py +2 -2
  30. mpflash/mpboard_id/__init__.py +3 -9
  31. mpflash/mpboard_id/alternate.py +56 -0
  32. mpflash/mpboard_id/board_id.py +11 -94
  33. mpflash/mpboard_id/known.py +44 -56
  34. mpflash/mpboard_id/resolve.py +19 -0
  35. mpflash/mpremoteboard/__init__.py +1 -1
  36. mpflash/mpremoteboard/mpy_fw_info.py +1 -0
  37. mpflash/mpremoteboard/runner.py +5 -2
  38. mpflash/vendor/pydfu.py +4 -5
  39. mpflash/versions.py +3 -0
  40. {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0rc2.dist-info}/METADATA +2 -2
  41. mpflash-1.25.0rc2.dist-info/RECORD +69 -0
  42. mpflash/db/boards.py +0 -63
  43. mpflash/db/downloads.py +0 -87
  44. mpflash/mpboard_id/add_boards.py +0 -260
  45. mpflash/mpboard_id/board.py +0 -40
  46. mpflash/mpboard_id/store.py +0 -47
  47. mpflash-1.25.0.post1.dist-info/RECORD +0 -62
  48. {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0rc2.dist-info}/LICENSE +0 -0
  49. {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0rc2.dist-info}/WHEEL +0 -0
  50. {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0rc2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,112 @@
1
+ from os import path
2
+ from pathlib import Path
3
+ from mpflash.mpremoteboard import HERE
4
+ from mpflash.vendor.board_database import Database
5
+ from mpflash.logger import log
6
+
7
+ from typing import List
8
+ import mpflash.basicgit as git
9
+ from mpflash.versions import micropython_versions
10
+
11
+ HERE = Path(__file__).parent.resolve()
12
+
13
+
14
+ ## iterator to flatten the board database into a list of tuples
15
+ def iter_boards(db: Database, version: str = ""):
16
+ """Iterate over the boards in the database and yield tuples of board information."""
17
+ version = version.strip()
18
+ for b in db.boards:
19
+ board = db.boards[b]
20
+ yield (
21
+ version,
22
+ board.name,
23
+ board.name,
24
+ board.mcu,
25
+ "", # no variant
26
+ board.port.name if board.port else "",
27
+ board.path.split("/micropython/", 1)[1], # TODO - remove hack
28
+ board.description,
29
+ "micropython", # family
30
+ )
31
+ if board.variants:
32
+ for v in board.variants:
33
+ yield (
34
+ version,
35
+ f"{board.name}-{v.name}",
36
+ board.name,
37
+ board.mcu,
38
+ v.name,
39
+ board.port.name if board.port else "",
40
+ board.path.split("/micropython/", 1)[1], # TODO - remove hack
41
+ v.description,
42
+ "micropython", # family
43
+ )
44
+
45
+
46
+ def boardlist_from_repo(
47
+ versions: List[str],
48
+ mpy_dir: Path,
49
+ ):
50
+ longlist = []
51
+ if not mpy_dir.is_dir():
52
+ log.error(f"Directory {mpy_dir} not found")
53
+ return longlist
54
+ for version in versions:
55
+ build_nr = ""
56
+ if "preview" in version:
57
+ ok = git.checkout_tag("master", mpy_dir)
58
+ if describe := git.get_git_describe(mpy_dir):
59
+ parts = describe.split("-", 3)
60
+ if len(parts) >= 3:
61
+ build_nr = parts[2]
62
+ else:
63
+ ok = git.checkout_tag(version, mpy_dir)
64
+ if not ok:
65
+ log.warning(f"Failed to checkout {version} in {mpy_dir}")
66
+ continue
67
+
68
+ log.info(f"{git.get_git_describe(mpy_dir)} - {build_nr}")
69
+ # un-cached database
70
+ db = Database(mpy_dir)
71
+ shortlist = list(iter_boards(db, version=version))
72
+ log.info(f"boards found {len(db.boards.keys())}")
73
+ log.info(f"boards-variants found {len(shortlist) - len(db.boards.keys())}")
74
+ longlist.extend(shortlist)
75
+ return longlist
76
+
77
+
78
+ def create_zip_file(longlist, zip_file: Path):
79
+ """Create a ZIP file containing the CSV data."""
80
+ # lazy import
81
+ import zipfile
82
+ import pandas as pd
83
+
84
+ csv_filename = "micropython_boards.csv"
85
+
86
+ columns = ["version", "board_id", "board_name", "mcu", "variant", "port", "path", "description", "family"]
87
+ df = pd.DataFrame(longlist, columns=columns)
88
+
89
+ # Create the ZIP file and add the CSV data directly without creating an intermediate file
90
+ with zipfile.ZipFile(zip_file, "w", zipfile.ZIP_DEFLATED) as zipf:
91
+ # Create a temporary in-memory CSV string
92
+ csv_data = df.to_csv(index=False)
93
+ # Write the CSV data directly to the zip file
94
+ zipf.writestr(csv_filename, csv_data)
95
+
96
+
97
+ def package_repo(mpy_path: Path):
98
+ mpy_path = mpy_path or Path("../repos/micropython")
99
+ mp_versions = micropython_versions(minver="1.18")
100
+ longlist = boardlist_from_repo(
101
+ versions=mp_versions,
102
+ mpy_dir=mpy_path,
103
+ )
104
+ log.info(f"Total boards-variants: {len(longlist)}")
105
+ zip_file = HERE / "micropython_boards.zip"
106
+ create_zip_file(longlist, zip_file=zip_file)
107
+
108
+ assert zip_file.is_file(), f"Failed to create {zip_file}"
109
+
110
+
111
+ if __name__ == "__main__":
112
+ package_repo(Path("D:\\mypython\\mpflash\\repos\\micropython"))
mpflash/db/loader.py ADDED
@@ -0,0 +1,122 @@
1
+ import csv
2
+ import io
3
+ import json
4
+ import re
5
+ import zipfile
6
+ from pathlib import Path
7
+ from turtle import up
8
+
9
+ from loguru import logger as log
10
+
11
+ from mpflash.errors import MPFlashError
12
+
13
+ from .core import Session
14
+ from .meta import get_metadata, set_metadata_value
15
+ from .models import Board, Firmware
16
+
17
+ HERE = Path(__file__).parent.resolve()
18
+
19
+
20
+ def load_data_from_zip(zip_file: Path) -> int:
21
+ log.debug("Loading data from zip file")
22
+ csv_filename = "micropython_boards.csv" # name of the .csv inside the .zip
23
+ # Check if the zip file exists
24
+ if not zip_file.exists() or not zip_file.is_file():
25
+ log.error(f"Zip file {zip_file} not found.")
26
+ return 0
27
+ count = 0
28
+ # Load data directly from the zip file
29
+ with zipfile.ZipFile(zip_file, "r") as zipf:
30
+ # Read the CSV file from the zip
31
+ with zipf.open(csv_filename) as csv_file:
32
+ log.info("Reading CSV data...")
33
+ reader = csv.DictReader(io.TextIOWrapper(csv_file, "utf-8"))
34
+
35
+ # save to database
36
+ with Session() as session:
37
+ for row in reader:
38
+ # Create a board instance from the row data
39
+ board = Board(**row)
40
+ # Use merge to update existing or insert new record
41
+ # based on primary key (board_id and version)
42
+ session.merge(board)
43
+ count += 1
44
+ session.commit()
45
+ log.info(f"Loaded {count} boards from {zip_file}")
46
+ return count
47
+
48
+
49
+ def load_jsonl_to_db(jsonl_path: Path):
50
+ """
51
+ Load a JSONL file into a SQLite database
52
+
53
+ Args:
54
+ jsonl_path (Path): Path to the JSONL file
55
+ conn (sqlite3.Connection): SQLite database connection
56
+ table_name (str): Name of the table to insert data into
57
+
58
+ Returns:
59
+ int: Number of records imported
60
+ """
61
+ log.debug("Loading JSONL file into database")
62
+ # Ensure file exists
63
+ if not jsonl_path.exists():
64
+ raise FileNotFoundError(f"JSONL file not found: {jsonl_path}")
65
+ num_records = 0
66
+ with jsonl_path.open("r", encoding="utf-8") as file:
67
+ with Session() as session:
68
+ for line in file:
69
+ record = json.loads(line.strip())
70
+ # Clean up the record
71
+
72
+ if "variant" in record:
73
+ record["board_id"] = record.pop("variant") # Rename 'variant' to 'board_id'
74
+ if "firmware" in record:
75
+ record["source"] = record.pop("firmware") # Rename 'firmware' to 'source'
76
+ if "preview" in record:
77
+ record["version"] = f"{record['version']}-preview" if record["preview"] else record["version"]
78
+ record.pop("preview", None) # Remove 'preview' column
79
+ if not "custom" in record:
80
+ record["custom"] = False
81
+ firmware_file = str(Path(record["filename"]).as_posix()) if record["filename"] else ""
82
+
83
+ # Check if Firmware with this firmware_file exists
84
+ existing_fw = session.query(Firmware).filter_by(firmware_file=firmware_file).first()
85
+ if existing_fw:
86
+ # Update fields
87
+ existing_fw.board_id = record["board_id"]
88
+ existing_fw.version = record["version"]
89
+ existing_fw.source = record["source"]
90
+ existing_fw.build = record["build"]
91
+ existing_fw.custom = record["custom"]
92
+ existing_fw.port = record["port"]
93
+ else:
94
+ # Add new Firmware
95
+ fw = Firmware(
96
+ board_id=record["board_id"],
97
+ version=record["version"],
98
+ firmware_file=firmware_file,
99
+ source=record["source"],
100
+ build=record["build"],
101
+ custom=record["custom"],
102
+ port=record["port"],
103
+ )
104
+ session.merge(fw)
105
+ num_records += 1
106
+ # commit once after all records are processed
107
+ session.commit()
108
+ return num_records
109
+
110
+
111
+ def update_boards():
112
+ try:
113
+ meta = get_metadata()
114
+ log.debug(f"Metadata: {meta}")
115
+ if meta.get("boards_version", "") < "v1.25.0":
116
+ log.info("Update boards from CSV to SQLite database.")
117
+ # Load data from the zip file into the database
118
+ load_data_from_zip(HERE / "micropython_boards.zip")
119
+ set_metadata_value("boards_version", "v1.25.0")
120
+ meta = get_metadata()
121
+ except Exception as e:
122
+ raise MPFlashError(f"Error updating boards table: {e}") from e
mpflash/db/meta.py ADDED
@@ -0,0 +1,78 @@
1
+
2
+ from typing import Optional
3
+
4
+ from loguru import logger as log
5
+
6
+ from .core import DatabaseError, OperationalError, Session
7
+ from .models import Metadata
8
+
9
+
10
+ def get_metadata() -> dict:
11
+ """
12
+ Get all metadata from the database.
13
+
14
+ Returns:
15
+ dict: Dictionary of metadata name-value pairs.
16
+ """
17
+ try:
18
+ with Session() as session:
19
+ metadata = session.query(Metadata).all()
20
+ return {m.name: m.value for m in metadata}
21
+ except (DatabaseError, OperationalError) as e:
22
+ log.error(f"Error retrieving metadata: {e}")
23
+ return {}
24
+
25
+ def set_metadata(metadata: dict):
26
+ """
27
+ Set metadata in the database.
28
+ Args:
29
+ metadata (dict): Dictionary of metadata name-value pairs.
30
+ Returns:
31
+ None
32
+ """
33
+ with Session() as session:
34
+ for name, value in metadata.items():
35
+ existing_metadata = session.query(Metadata).filter(Metadata.name == name).first()
36
+ if existing_metadata:
37
+ existing_metadata.value = value
38
+ else:
39
+ new_metadata = Metadata(name=name, value=value)
40
+ session.add(new_metadata)
41
+ session.commit()
42
+
43
+
44
+ def get_metadata_value(name: str) -> Optional[str]:
45
+ """
46
+ Get metadata value by name.
47
+
48
+ Args:
49
+ session (Session): SQLAlchemy session.
50
+ name (str): Name of the metadata.
51
+
52
+ Returns:
53
+ Optional[str]: Metadata value or None if not found.
54
+ """
55
+ with Session() as session:
56
+ metadata = session.query(Metadata).filter(Metadata.name == name).first()
57
+ return metadata.value if metadata else None
58
+
59
+ def set_metadata_value(name: str, value: str):
60
+ """
61
+ Set metadata value by name.
62
+
63
+ Args:
64
+ session (Session): SQLAlchemy session.
65
+ name (str): Name of the metadata.
66
+ value (str): Value to set.
67
+
68
+ Returns:
69
+ None
70
+ """
71
+ with Session() as session:
72
+ metadata = session.query(Metadata).filter(Metadata.name == name).first()
73
+ if metadata:
74
+ metadata.value = value
75
+ else:
76
+ new_metadata = Metadata(name=name, value=value)
77
+ session.add(new_metadata)
78
+ session.commit()
Binary file
mpflash/db/models.py ADDED
@@ -0,0 +1,93 @@
1
+ from pathlib import Path
2
+
3
+ import sqlalchemy as sa
4
+ from sqlalchemy import Index, String
5
+ from sqlalchemy.orm import DeclarativeBase, Mapped, composite, mapped_column, relationship
6
+
7
+
8
+ class Base(DeclarativeBase):
9
+ pass
10
+
11
+
12
+ class Metadata(Base):
13
+ """
14
+ Configuration information.
15
+ """
16
+
17
+ __tablename__ = "metadata"
18
+ name: Mapped[str] = mapped_column(primary_key=True, unique=True)
19
+ value: Mapped[str] = mapped_column()
20
+
21
+ def __repr__(self) -> str:
22
+ return f"Config(boards_version={self.name!r}, schema_version={self.value!r})"
23
+
24
+
25
+ class Board(Base):
26
+ """
27
+ All known Boards model for storing board information.
28
+ """
29
+
30
+ __tablename__ = "boards"
31
+ __table_args__ = (sa.UniqueConstraint("board_id", "version"),)
32
+
33
+ board_id: Mapped[str] = mapped_column(String(40), primary_key=True, unique=False)
34
+ version: Mapped[str] = mapped_column(String(12), primary_key=True, unique=False)
35
+ board_name: Mapped[str] = mapped_column()
36
+ mcu: Mapped[str] = mapped_column()
37
+ variant: Mapped[str] = mapped_column(default="")
38
+ port: Mapped[str] = mapped_column(String(30))
39
+ path: Mapped[str] = mapped_column(comment="Path in micropyton repo as_posix()")
40
+ description: Mapped[str] = mapped_column()
41
+ family: Mapped[str] = mapped_column(default="micropython")
42
+ custom: Mapped[bool] = mapped_column(default=False, comment="True if this is a custom board")
43
+ firmwares = relationship(
44
+ "Firmware",
45
+ back_populates="board",
46
+ lazy="joined",
47
+ )
48
+
49
+ board_key = composite(lambda board_id, version: f"{board_id}_{version}", board_id, version)
50
+
51
+ def __repr__(self) -> str:
52
+ return f"Board(board_id={self.board_id!r}, version={self.version!r}, board_name={self.board_name!r})"
53
+
54
+
55
+ class Firmware(Base):
56
+ """
57
+ Firmware model for storing firmware information.
58
+ """
59
+
60
+ __tablename__ = "firmwares"
61
+ __table_args__ = (
62
+ sa.ForeignKeyConstraint(["board_id", "version"], ["boards.board_id", "boards.version"]),
63
+ {"sqlite_autoincrement": False},
64
+ )
65
+
66
+ board_id: Mapped[str] = mapped_column(String(40), primary_key=True)
67
+ version: Mapped[str] = mapped_column(String(12), primary_key=True)
68
+ firmware_file: Mapped[str] = mapped_column(String, primary_key=True, index=True, comment="Path to the firmware file")
69
+ # Relationship to Board
70
+ board: Mapped["Board"] = relationship(
71
+ "Board",
72
+ back_populates="firmwares",
73
+ lazy="joined",
74
+ primaryjoin="and_(Firmware.board_id==Board.board_id, Firmware.version==Board.version)",
75
+ )
76
+ port: Mapped[str] = mapped_column(String(20), default="") # duplicate of board.port
77
+ description: Mapped[str] = mapped_column(default="")
78
+ source: Mapped[str] = mapped_column()
79
+ build: Mapped[int] = mapped_column(default=0, comment="Build number")
80
+ custom: Mapped[bool] = mapped_column(default=False, comment="True if this is a custom firmware")
81
+
82
+ @property
83
+ def preview(self) -> bool:
84
+ "Check if the firmware is a preview version."
85
+ return "preview" in self.firmware_file
86
+
87
+ @property
88
+ def ext(self) -> str:
89
+ "Get the file extension of the firmware file."
90
+ return Path(self.firmware_file).suffix
91
+
92
+ def __repr__(self) -> str:
93
+ return f"Firmware(board_id={self.board_id!r}, version={self.version!r}, firmware_file={self.firmware_file!r})"
mpflash/db/tools.py ADDED
@@ -0,0 +1,27 @@
1
+ import sqlite3
2
+ from pathlib import Path
3
+ from typing import List
4
+
5
+ from mpflash.config import config
6
+ from mpflash.errors import MPFlashError
7
+ from mpflash.logger import log
8
+
9
+
10
+ def backup_db(source_db: Path, backup_path: Path):
11
+ """
12
+ Backup the SQLite database to a specified path.
13
+
14
+ Args:
15
+ conn (sqlite3.Connection): SQLite connection object
16
+ backup_path (str or Path): Path to save the backup file
17
+ """
18
+
19
+ # Ensure the backup directory exists
20
+ backup_path.parent.mkdir(parents=True, exist_ok=True)
21
+ with sqlite3.connect(source_db) as conn:
22
+ # Perform the backup
23
+ with open(backup_path, "wb") as f:
24
+ for line in conn.iterdump():
25
+ f.write(f"{line}\n".encode("utf-8"))
26
+
27
+ log.info(f"Backup created at {backup_path}")
@@ -5,7 +5,6 @@ Uses the micropython.org website to get the available versions and locations to
5
5
 
6
6
  import itertools
7
7
  from pathlib import Path
8
- import sqlite3
9
8
  from typing import Dict, List, Optional
10
9
 
11
10
  # #########################################################################################################
@@ -14,27 +13,27 @@ import jsonlines
14
13
  from loguru import logger as log
15
14
  from rich.progress import track
16
15
 
17
- from mpflash.common import PORT_FWTYPES, FWInfo
18
- from .from_web import get_boards, fetch_firmware_files
16
+ from mpflash.common import PORT_FWTYPES
17
+ from mpflash.config import config
18
+ from mpflash.db.core import Session
19
+ from mpflash.db.models import Firmware
19
20
  from mpflash.downloaded import clean_downloaded_firmwares
20
21
  from mpflash.errors import MPFlashError
22
+ from mpflash.mpboard_id.alternate import add_renamed_boards
21
23
  from mpflash.versions import clean_version
22
- from mpflash.config import config
23
- from mpflash.db.downloads import upsert_download
24
+
25
+ from .from_web import fetch_firmware_files, get_boards
26
+ from .fwinfo import FWInfo
27
+
24
28
  # avoid conflict with the ujson used by MicroPython
25
29
  jsonlines.ujson = None # type: ignore
26
30
  # #########################################################################################################
27
31
 
28
32
 
29
33
 
30
- def key_fw_ver_pre_ext_bld(x: FWInfo):
31
- "sorting key for the retrieved board urls"
32
- return x.variant, x.version, x.preview, x.ext, x.build
33
-
34
-
35
- def key_fw_var_pre_ext(x: FWInfo):
34
+ def key_fw_boardid_preview_ext(fw: Firmware):
36
35
  "Grouping key for the retrieved board urls"
37
- return x.variant, x.preview, x.ext
36
+ return fw.board_id, fw.preview, fw.ext
38
37
 
39
38
 
40
39
  def download_firmwares(
@@ -59,18 +58,18 @@ def download_firmwares(
59
58
  """
60
59
 
61
60
 
62
- skipped = downloaded = 0
61
+ downloaded = 0
63
62
  versions = [] if versions is None else [clean_version(v) for v in versions]
64
- # handle renamed boards
63
+ # handle downloading firmware for renamed boards
65
64
  boards = add_renamed_boards(boards)
66
65
 
67
66
  available_firmwares = get_firmware_list(ports, boards, versions, clean)
68
67
 
69
68
  for b in available_firmwares:
70
- log.debug(b.filename)
69
+ log.debug(b.firmware_file)
71
70
  # relevant
72
71
 
73
- log.info(f"Found {len(available_firmwares)} relevant unique firmwares")
72
+ log.info(f"Found {len(available_firmwares)} potentially relevant firmwares")
74
73
  if not available_firmwares:
75
74
  log.error("No relevant firmwares could be found on https://micropython.org/download")
76
75
  log.info(f"{versions=} {ports=} {boards=}")
@@ -83,7 +82,7 @@ def download_firmwares(
83
82
  log.success(f"Downloaded {downloaded} firmware images." )
84
83
  return downloaded
85
84
 
86
- def download_firmware_files(available_firmwares :List[FWInfo],firmware_folder:Path, force:bool ):
85
+ def download_firmware_files(available_firmwares :List[Firmware],firmware_folder:Path, force:bool ):
87
86
  """
88
87
  Downloads the firmware files to the specified folder.
89
88
  Args:
@@ -94,23 +93,23 @@ def download_firmware_files(available_firmwares :List[FWInfo],firmware_folder:Pa
94
93
  """
95
94
 
96
95
  # with jsonlines.open(firmware_folder / "firmware.jsonl", "a") as writer:
97
- with sqlite3.connect(config.db_path) as conn:
96
+ with Session() as session:
98
97
  # skipped, downloaded = fetch_firmware_files(available_firmwares, firmware_folder, force, requests, writer)
99
98
  downloaded = 0
100
99
  for fw in fetch_firmware_files(available_firmwares, firmware_folder, force):
101
- upsert_download(conn, fw)
102
- # writer.write(fw)
103
- log.debug(f" {fw.filename} downloaded")
100
+ session.merge(fw)
101
+ log.debug(f" {fw.firmware_file} downloaded")
104
102
  downloaded += 1
103
+ session.commit()
105
104
  if downloaded > 0:
106
- clean_downloaded_firmwares(firmware_folder)
105
+ clean_downloaded_firmwares()
107
106
  return downloaded
108
107
 
109
108
 
110
109
 
111
110
  def get_firmware_list(ports: List[str], boards: List[str], versions: List[str], clean: bool = True):
112
111
  """
113
- Retrieves a list of unique firmware files available om micropython.org > downloads
112
+ Retrieves a list of unique firmware files potentially available on micropython.org > downloads
114
113
  based on the specified ports, boards, versions, and clean flag.
115
114
 
116
115
  Args:
@@ -126,36 +125,43 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
126
125
 
127
126
  log.trace("Checking MicroPython download pages")
128
127
  versions = [clean_version(v, drop_v=False) for v in versions]
129
- preview = "preview" in versions
128
+ preview = any("preview" in v for v in versions)
130
129
 
131
- board_urls = sorted(get_boards(ports, boards, clean), key=key_fw_ver_pre_ext_bld)
130
+ # board_urls = sorted(get_boards(ports, boards, clean), key=key_fw_ver_pre_ext_bld)
131
+ board_urls = get_boards(ports, boards, clean)
132
132
 
133
133
  log.debug(f"Total {len(board_urls)} firmwares")
134
+ if versions:
135
+ # filter out the boards that are not in the versions list
136
+ relevant = [
137
+ board for board in board_urls if (
138
+ board.version in versions
139
+ # or (preview and board.preview )
140
+ # and board.board_id in boards
141
+ # and board.build == "0"
142
+ # and not board.preview
143
+ )
144
+ ]
145
+ else:
146
+ relevant = board_urls
134
147
 
135
- relevant = [
136
- board for board in board_urls if board.version in versions and board.build == "0" and board.board in boards and not board.preview
137
- ]
138
-
139
- if preview:
140
- relevant.extend([board for board in board_urls if board.board in boards and board.preview])
141
148
  log.debug(f"Matching firmwares: {len(relevant)}")
142
149
  # select the unique boards
143
- unique_boards: List[FWInfo] = []
144
- for _, g in itertools.groupby(relevant, key=key_fw_var_pre_ext):
145
- # list is aleady sorted by build so we can just get the last item
150
+ unique_boards: List[Firmware] = []
151
+ for _, g in itertools.groupby(relevant, key=key_fw_boardid_preview_ext):
152
+ # list is aleady sorted by build (desc) so we can just get the first item
146
153
  sub_list = list(g)
147
- unique_boards.append(sub_list[-1])
148
- log.debug(f"Last preview only: {len(unique_boards)}")
154
+ unique_boards.append(sub_list[0])
155
+ log.debug(f"Including preview: {len(unique_boards)}")
149
156
  return unique_boards
150
157
 
151
158
 
152
159
  def download(
153
- destination: Path,
154
160
  ports: List[str],
155
161
  boards: List[str],
156
162
  versions: List[str],
157
- force: bool,
158
- clean: bool,
163
+ force: bool = False,
164
+ clean: bool = True,
159
165
  ) -> int:
160
166
  """
161
167
  Downloads firmware files based on the specified destination, ports, boards, versions, force flag, and clean flag.
@@ -177,7 +183,7 @@ def download(
177
183
  """
178
184
  # Just in time import
179
185
  import requests
180
-
186
+ destination = config.firmware_folder
181
187
  if not boards:
182
188
  log.critical("No boards found, please connect a board or specify boards to download firmware for.")
183
189
  raise MPFlashError("No boards found")
@@ -195,27 +201,3 @@ def download(
195
201
 
196
202
  return result
197
203
 
198
-
199
- def add_renamed_boards(boards: List[str]) -> List[str]:
200
- """
201
- Adds the renamed boards to the list of boards.
202
-
203
- Args:
204
- boards : The list of boards to add the renamed boards to.
205
-
206
- Returns:
207
- List[str]: The list of boards with the renamed boards added.
208
- """
209
-
210
- renamed = {
211
- "PICO": ["RPI_PICO"],
212
- "PICO_W": ["RPI_PICO_W"],
213
- "GENERIC": ["ESP32_GENERIC", "ESP8266_GENERIC"], # just add both of them
214
- }
215
- _boards = boards.copy()
216
- for board in boards:
217
- if board in renamed and renamed[board] not in boards:
218
- _boards.extend(renamed[board])
219
- if board != board.upper() and board.upper() not in boards:
220
- _boards.append(board.upper())
221
- return _boards