mpflash 1.25.0rc4__tar.gz → 1.26.0__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 (73) hide show
  1. {mpflash-1.25.0rc4 → mpflash-1.26.0}/PKG-INFO +63 -10
  2. {mpflash-1.25.0rc4 → mpflash-1.26.0}/README.md +61 -9
  3. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/basicgit.py +23 -2
  4. mpflash-1.26.0/mpflash/cli_add.py +131 -0
  5. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/cli_download.py +3 -14
  6. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/cli_flash.py +44 -26
  7. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/cli_group.py +1 -0
  8. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/cli_list.py +2 -0
  9. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/cli_main.py +7 -0
  10. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/common.py +14 -2
  11. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/config.py +22 -1
  12. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/connected.py +8 -6
  13. mpflash-1.26.0/mpflash/custom/__init__.py +144 -0
  14. mpflash-1.26.0/mpflash/custom/naming.py +91 -0
  15. mpflash-1.26.0/mpflash/db/core.py +139 -0
  16. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/db/gather_boards.py +10 -4
  17. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/db/loader.py +4 -3
  18. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/db/meta.py +4 -3
  19. mpflash-1.26.0/mpflash/db/micropython_boards.zip +0 -0
  20. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/db/models.py +2 -0
  21. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/download/__init__.py +58 -1
  22. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/download/fwinfo.py +1 -1
  23. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/download/jid.py +1 -1
  24. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/downloaded.py +8 -4
  25. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/__init__.py +10 -1
  26. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/uf2/windows.py +1 -1
  27. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/worklist.py +40 -19
  28. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/list.py +2 -0
  29. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/logger.py +27 -7
  30. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpboard_id/board_id.py +12 -17
  31. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpboard_id/known.py +8 -2
  32. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpremoteboard/__init__.py +61 -3
  33. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpremoteboard/runner.py +1 -0
  34. {mpflash-1.25.0rc4 → mpflash-1.26.0}/pyproject.toml +4 -1
  35. mpflash-1.25.0rc4/mpflash/add_firmware.py +0 -125
  36. mpflash-1.25.0rc4/mpflash/db/core.py +0 -61
  37. mpflash-1.25.0rc4/mpflash/db/micropython_boards.zip +0 -0
  38. {mpflash-1.25.0rc4 → mpflash-1.26.0}/LICENSE +0 -0
  39. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/__init__.py +0 -0
  40. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/ask_input.py +0 -0
  41. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/bootloader/__init__.py +0 -0
  42. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/bootloader/activate.py +0 -0
  43. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/bootloader/detect.py +0 -0
  44. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/bootloader/manual.py +0 -0
  45. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/bootloader/micropython.py +0 -0
  46. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/bootloader/touch1200.py +0 -0
  47. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/db/__init__.py +0 -0
  48. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/db/tools.py +0 -0
  49. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/download/from_web.py +0 -0
  50. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/errors.py +0 -0
  51. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/esp.py +0 -0
  52. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/stm32.py +0 -0
  53. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/stm32_dfu.py +0 -0
  54. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/uf2/__init__.py +0 -0
  55. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/uf2/boardid.py +0 -0
  56. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/uf2/linux.py +0 -0
  57. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/uf2/macos.py +0 -0
  58. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/flash/uf2/uf2disk.py +0 -0
  59. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpboard_id/__init__.py +0 -0
  60. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpboard_id/alternate.py +0 -0
  61. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpboard_id/board_info.json +0 -0
  62. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpboard_id/board_info.zip +0 -0
  63. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpboard_id/resolve.py +0 -0
  64. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/mpremoteboard/mpy_fw_info.py +0 -0
  65. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/py.typed +0 -0
  66. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/vendor/board_database.py +0 -0
  67. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/vendor/click_aliases.py +0 -0
  68. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/vendor/dfu.py +0 -0
  69. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/vendor/pico-universal-flash-nuke/LICENSE.txt +0 -0
  70. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/vendor/pico-universal-flash-nuke/universal_flash_nuke.uf2 +0 -0
  71. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/vendor/pydfu.py +0 -0
  72. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/vendor/readme.md +0 -0
  73. {mpflash-1.25.0rc4 → mpflash-1.26.0}/mpflash/versions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mpflash
3
- Version: 1.25.0rc4
3
+ Version: 1.26.0
4
4
  Summary: Flash and download tool for MicroPython firmwares
5
5
  License: MIT
6
6
  Keywords: MicroPython,firmware,flash,download,UF2,esptool
@@ -37,6 +37,7 @@ Requires-Dist: requests (>=2.31.0,<3.0.0)
37
37
  Requires-Dist: rich-click (>=1.8.1,<2.0.0)
38
38
  Requires-Dist: sqlalchemy (>=2.0.41,<3.0.0)
39
39
  Requires-Dist: tenacity (==9.0.0)
40
+ Requires-Dist: tomli-w (>=1.2.0,<2.0.0)
40
41
  Project-URL: Homepage, https://github.com/Josverl/mpflash/blob/main/README.md
41
42
  Project-URL: Repository, https://github.com/Josverl/mpflash
42
43
  Description-Content-Type: text/markdown
@@ -58,9 +59,22 @@ This tool was initially created to be used in a CI/CD pipeline to automate the p
58
59
  - `samd`, using ` .uf2`, using filecopy
59
60
  - `esp32`, using `.bin`, using esptool,
60
61
  - `esp8266`, using `.bin`, using esptool
61
- - `stm32`, using ` .dfu`, using pydfu
62
+ - `stm32`, using ` .dfu`, using pydfu (also in Windows)
62
63
 
63
64
  Not yet implemented: `nrf`, `cc3200`, `mimxrt`, `renesas`
65
+
66
+ ## Release v1.25.0(.post2)
67
+
68
+ This release includes several new features and improvements:
69
+ - **New features:**
70
+ - Added support for `--variant` option to specify a specific variant of the board when flashing.
71
+ - mpflash now uses a slqlite database to store information on all possible micropython firmwares, and the management of the downloaded firmware files.
72
+ - This allows for a better identification of boards, and matches to the correct firmware.
73
+ - Use the MicroPython v1.25.0 `sys.implementation._build` to as board_id when avaialable
74
+ - Automatically try to download firmware if not yet available locally. No lonmger need to specify the `--download` option.
75
+ - Restructured mpboard_id to use a SQLite db to be able to ID more boards and variants
76
+ - vendored and adapted `board_database.py` from mpflash, kudos @mattytrentini
77
+
64
78
 
65
79
  ## Features
66
80
  1. List the connected boards including their firmware details, in a tabular or json format
@@ -79,15 +93,54 @@ You can use mpflash to perform various operations on your MicroPython boards. He
79
93
  | Command | Description |
80
94
  |---------|-------------|
81
95
  | `mpflash list` | List the connected board(s) including their firmware details |
96
+ | `mpflash flash` | Flash the latest stable firmware to the connected board(s), downloading the firmware if needed |
82
97
  | `mpflash download` | Download the MicroPython firmware(s) for the connected board(s) |
83
- | `mpflash flash` | Flash the latest stable firmware to the connected board(s) |
84
98
 
85
- ## selecting or ignoring specific serial ports
99
+ **Listing connected boards:**
100
+ `mpflash list` will list all connected boards in a table , including their serial port, family, board name, CPU, version and build number.
101
+ Options are available to list the boards in a json format, or to filter the list by serial port or board type.
102
+
103
+
104
+ **Flashing boards with new firmware:**
105
+ `mpflash flash` will flash the latest stable firmware to all connected boards, downloading the firmware if needed.
106
+ It will try to determine the current micropython borad and variant, download the firmware if needed, and flash the correct firmware to each board.
107
+
108
+ Common options are:
109
+
110
+ - `--version` to specify the version of the firmware to flash, defaults to the latest stable version.
111
+ - `--serial` to specify the serial port(s) to flash, defaults to all connected boards.
112
+ - `--board` to specify which firmware to flash to a single board
113
+ - `--variant` to specify a specific variant of the board
114
+
115
+ **Downloading firmware:**
116
+ `mpflash download` will download the latest stable firmware for all connected boards, or a specific board if specified. It will download the firmware from the official MicroPython website and save it in your `Downloads/firmware` directory.
117
+ When a board is specified for which multiple variants are available, all variants will be downloaded.
118
+
119
+ Common options are:
120
+
121
+ - `--version` to specify the version of the firmware to download, defaults to the latest stable version. (e.g. `stable`, `preview`, `x.y.z`)
122
+ - `--serial` to specify the serial port(s) to flash, defaults to all connected boards.
123
+ - `--board` to specify which firmware to flash to a single board
86
124
 
87
- You can use the `--serial` option to select a specific serial port to flash, or the `--ignore` option to ignore a specific serial port.
88
- both options can be specified multiple times
89
- Both can be globs (e.g. COM*) or exact port names (e.g. COM1)
90
- in addition there is a --bluetooth option to simplify ignoring bluetooth ports
125
+ ## Setting the Firmware Files and Database Location
126
+
127
+ You can override the default location for firmware files and the MPFlash database by setting the `MPFLASH_FIRMWARE` environment variable. For example, in a Bash shell:
128
+
129
+ ```bash
130
+ export MPFLASH_FIRMWARE="/path/to/custom/firmware"
131
+ ```
132
+
133
+ When this variable is set, `mpflash` will use that location to store firmware files and estabish it's database.
134
+
135
+ ## Selecting or ignoring specific serial ports
136
+
137
+ You can use the `--serial` option to select a specific serial port(s) to flash,
138
+ Or you can use the `--ignore` option to ignore a specific serial port(s).
139
+
140
+ Either option can be specified multiple times, can be globs (e.g. COM*) or exact port names (e.g. /dev/ttyUSB0).
141
+ To permenently ignore a port, you can set the `MPFLASH_IGNORE` environment variable to a space-separated list of serial ports or globs.
142
+
143
+ In addition there is a --bluetooth option to simplify ignoring bluetooth ports, where the default is to ignore bluetooth ports.
91
144
 
92
145
  ```
93
146
  --serial,--serial-port -s SERIALPORT Serial port(s) (or globs) to list. [default: *] > > --ignore -i SERIALPORT Serial port(s) (or globs) to ignore. Defaults to MPFLASH_IGNORE. │
@@ -102,9 +155,9 @@ This file can contain a description of the board, which will be shown in the lis
102
155
  description = "Blue Norwegian actuator"
103
156
  ```
104
157
 
105
- If you want the board to be ignored by mpflash, you can add the following to the board_info.toml file:
158
+ If you want the board to be ignored by mpflash, no matter which serial port it is connected to, you can add the following to the `board_info.toml` file:
106
159
  ```toml
107
- description = "Blue Norwegian feeder"
160
+ description = "Blue Norwegian actuator"
108
161
  [mpflash]
109
162
  ignore = true
110
163
  ```
@@ -15,9 +15,22 @@ This tool was initially created to be used in a CI/CD pipeline to automate the p
15
15
  - `samd`, using ` .uf2`, using filecopy
16
16
  - `esp32`, using `.bin`, using esptool,
17
17
  - `esp8266`, using `.bin`, using esptool
18
- - `stm32`, using ` .dfu`, using pydfu
18
+ - `stm32`, using ` .dfu`, using pydfu (also in Windows)
19
19
 
20
20
  Not yet implemented: `nrf`, `cc3200`, `mimxrt`, `renesas`
21
+
22
+ ## Release v1.25.0(.post2)
23
+
24
+ This release includes several new features and improvements:
25
+ - **New features:**
26
+ - Added support for `--variant` option to specify a specific variant of the board when flashing.
27
+ - mpflash now uses a slqlite database to store information on all possible micropython firmwares, and the management of the downloaded firmware files.
28
+ - This allows for a better identification of boards, and matches to the correct firmware.
29
+ - Use the MicroPython v1.25.0 `sys.implementation._build` to as board_id when avaialable
30
+ - Automatically try to download firmware if not yet available locally. No lonmger need to specify the `--download` option.
31
+ - Restructured mpboard_id to use a SQLite db to be able to ID more boards and variants
32
+ - vendored and adapted `board_database.py` from mpflash, kudos @mattytrentini
33
+
21
34
 
22
35
  ## Features
23
36
  1. List the connected boards including their firmware details, in a tabular or json format
@@ -36,15 +49,54 @@ You can use mpflash to perform various operations on your MicroPython boards. He
36
49
  | Command | Description |
37
50
  |---------|-------------|
38
51
  | `mpflash list` | List the connected board(s) including their firmware details |
52
+ | `mpflash flash` | Flash the latest stable firmware to the connected board(s), downloading the firmware if needed |
39
53
  | `mpflash download` | Download the MicroPython firmware(s) for the connected board(s) |
40
- | `mpflash flash` | Flash the latest stable firmware to the connected board(s) |
41
54
 
42
- ## selecting or ignoring specific serial ports
55
+ **Listing connected boards:**
56
+ `mpflash list` will list all connected boards in a table , including their serial port, family, board name, CPU, version and build number.
57
+ Options are available to list the boards in a json format, or to filter the list by serial port or board type.
58
+
59
+
60
+ **Flashing boards with new firmware:**
61
+ `mpflash flash` will flash the latest stable firmware to all connected boards, downloading the firmware if needed.
62
+ It will try to determine the current micropython borad and variant, download the firmware if needed, and flash the correct firmware to each board.
63
+
64
+ Common options are:
65
+
66
+ - `--version` to specify the version of the firmware to flash, defaults to the latest stable version.
67
+ - `--serial` to specify the serial port(s) to flash, defaults to all connected boards.
68
+ - `--board` to specify which firmware to flash to a single board
69
+ - `--variant` to specify a specific variant of the board
70
+
71
+ **Downloading firmware:**
72
+ `mpflash download` will download the latest stable firmware for all connected boards, or a specific board if specified. It will download the firmware from the official MicroPython website and save it in your `Downloads/firmware` directory.
73
+ When a board is specified for which multiple variants are available, all variants will be downloaded.
74
+
75
+ Common options are:
76
+
77
+ - `--version` to specify the version of the firmware to download, defaults to the latest stable version. (e.g. `stable`, `preview`, `x.y.z`)
78
+ - `--serial` to specify the serial port(s) to flash, defaults to all connected boards.
79
+ - `--board` to specify which firmware to flash to a single board
43
80
 
44
- You can use the `--serial` option to select a specific serial port to flash, or the `--ignore` option to ignore a specific serial port.
45
- both options can be specified multiple times
46
- Both can be globs (e.g. COM*) or exact port names (e.g. COM1)
47
- in addition there is a --bluetooth option to simplify ignoring bluetooth ports
81
+ ## Setting the Firmware Files and Database Location
82
+
83
+ You can override the default location for firmware files and the MPFlash database by setting the `MPFLASH_FIRMWARE` environment variable. For example, in a Bash shell:
84
+
85
+ ```bash
86
+ export MPFLASH_FIRMWARE="/path/to/custom/firmware"
87
+ ```
88
+
89
+ When this variable is set, `mpflash` will use that location to store firmware files and estabish it's database.
90
+
91
+ ## Selecting or ignoring specific serial ports
92
+
93
+ You can use the `--serial` option to select a specific serial port(s) to flash,
94
+ Or you can use the `--ignore` option to ignore a specific serial port(s).
95
+
96
+ Either option can be specified multiple times, can be globs (e.g. COM*) or exact port names (e.g. /dev/ttyUSB0).
97
+ To permenently ignore a port, you can set the `MPFLASH_IGNORE` environment variable to a space-separated list of serial ports or globs.
98
+
99
+ In addition there is a --bluetooth option to simplify ignoring bluetooth ports, where the default is to ignore bluetooth ports.
48
100
 
49
101
  ```
50
102
  --serial,--serial-port -s SERIALPORT Serial port(s) (or globs) to list. [default: *] > > --ignore -i SERIALPORT Serial port(s) (or globs) to ignore. Defaults to MPFLASH_IGNORE. │
@@ -59,9 +111,9 @@ This file can contain a description of the board, which will be shown in the lis
59
111
  description = "Blue Norwegian actuator"
60
112
  ```
61
113
 
62
- If you want the board to be ignored by mpflash, you can add the following to the board_info.toml file:
114
+ If you want the board to be ignored by mpflash, no matter which serial port it is connected to, you can add the following to the `board_info.toml` file:
63
115
  ```toml
64
- description = "Blue Norwegian feeder"
116
+ description = "Blue Norwegian actuator"
65
117
  [mpflash]
66
118
  ignore = true
67
119
  ```
@@ -135,6 +135,25 @@ def get_local_tags(repo: Optional[Path] = None, minver: Optional[str] = None) ->
135
135
  return sorted(tags)
136
136
 
137
137
 
138
+ def get_current_branch(repo: Optional[Union[Path, str]] = None) -> Optional[str]:
139
+ """
140
+ Get the current branch name of a local repository.
141
+
142
+ Args:
143
+ repo: Path to the repository directory
144
+
145
+ Returns:
146
+ Current branch name or None if error
147
+ """
148
+ cmd = ["git", "branch", "--show-current"]
149
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
150
+ if not result:
151
+ return None
152
+
153
+ branch = result.stdout.strip()
154
+ return branch if branch else None
155
+
156
+
138
157
  @cachetools.func.ttl_cache(maxsize=16, ttl=60) # 60 seconds
139
158
  def get_tags(repo: str, minver: Optional[str] = None) -> List[str]:
140
159
  """
@@ -242,7 +261,7 @@ def fetch(repo: Union[Path, str]) -> bool:
242
261
  return result.returncode == 0 if result else False
243
262
 
244
263
 
245
- def pull(repo: Union[Path, str], branch: str = "main") -> bool:
264
+ def pull(repo: Union[Path, str], branch: str = "main", force: bool = True) -> bool:
246
265
  """
247
266
  pull a repo origin into main
248
267
  repo should be in the form of : path/.git
@@ -253,7 +272,9 @@ def pull(repo: Union[Path, str], branch: str = "main") -> bool:
253
272
  raise NotADirectoryError
254
273
  repo = Path(repo)
255
274
  # first checkout HEAD
256
- cmd = ["git", "checkout", branch, "--quiet", "--force"]
275
+ cmd = ["git", "checkout", branch, "--quiet"]
276
+ if force:
277
+ cmd.append("--force")
257
278
  result = _run_local_git(cmd, repo=repo, expect_stderr=True)
258
279
  if not result:
259
280
  log.error("error during git checkout main", result)
@@ -0,0 +1,131 @@
1
+ """CLI to add a custom MicroPython firmware."""
2
+
3
+ from pathlib import Path
4
+ from typing import Union
5
+
6
+ import rich_click as click
7
+ from loguru import logger as log
8
+
9
+ from mpflash.connected import connected_ports_boards_variants
10
+ from mpflash.custom import Firmware, add_firmware, custom_fw_from_path
11
+ from mpflash.downloaded import clean_downloaded_firmwares
12
+ from mpflash.errors import MPFlashError
13
+ from mpflash.mpboard_id import find_known_board
14
+ from mpflash.mpboard_id.alternate import add_renamed_boards
15
+ from mpflash.versions import clean_version
16
+
17
+ from .ask_input import ask_missing_params
18
+ from .cli_group import cli
19
+ from .config import config
20
+ from .download import download
21
+
22
+
23
+ @cli.command(
24
+ "add",
25
+ help="Add a custom MicroPython firmware.",
26
+ )
27
+ # @click.option(
28
+ # "--version",
29
+ # "-v",
30
+ # "versions",
31
+ # default=["stable"],
32
+ # multiple=False,
33
+ # show_default=True,
34
+ # help="The version of MicroPython to to download.",
35
+ # metavar="SEMVER, 'stable', 'preview' or '?'",
36
+ # )
37
+ @click.option(
38
+ "--path",
39
+ "-p",
40
+ "fw_path",
41
+ multiple=False,
42
+ default="",
43
+ show_default=False,
44
+ help="a local path to the firmware file to add.",
45
+ metavar="FIRMWARE_PATH",
46
+ )
47
+ @click.option(
48
+ "--description",
49
+ "-d",
50
+ "description",
51
+ default="",
52
+ help="An Optional description for the firmware.",
53
+ metavar="TXT",
54
+ )
55
+ # @click.option(
56
+ # "--board",
57
+ # "-b",
58
+ # "boards",
59
+ # multiple=True,
60
+ # default=[],
61
+ # show_default=True,
62
+ # help="The board(s) to download the firmware for.",
63
+ # metavar="BOARD_ID or ?",
64
+ # )
65
+ # @click.option(
66
+ # "--serial",
67
+ # "--serial-port",
68
+ # "-s",
69
+ # "serial",
70
+ # default=["*"],
71
+ # show_default=True,
72
+ # multiple=True,
73
+ # help="Which serial port(s) (or globs) to flash",
74
+ # metavar="SERIALPORT",
75
+ # )
76
+ # @click.option(
77
+ # "--ignore",
78
+ # "-i",
79
+ # is_eager=True,
80
+ # help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
81
+ # multiple=True,
82
+ # default=[],
83
+ # envvar="MPFLASH_IGNORE",
84
+ # show_default=True,
85
+ # metavar="SERIALPORT",
86
+ # )
87
+ # @click.option(
88
+ # "--clean/--no-clean",
89
+ # default=True,
90
+ # show_default=True,
91
+ # help="""Remove dates and hashes from the downloaded firmware filenames.""",
92
+ # )
93
+ @click.option(
94
+ "--force",
95
+ "-f",
96
+ default=False,
97
+ is_flag=True,
98
+ show_default=True,
99
+ help="""Overwrite existing firmware.""",
100
+ )
101
+ def cli_add_custom(
102
+ fw_path: Union[Path, str],
103
+ force: bool = False,
104
+ description: str = "",
105
+ ) -> int:
106
+ """Add a custom MicroPython firmware from a local file."""
107
+ if not fw_path:
108
+ log.error("No firmware path provided. Use --path to specify a firmware file.")
109
+ return 1
110
+ fw_path = Path(fw_path).expanduser().resolve()
111
+ if not fw_path.exists():
112
+ log.error(f"Firmware file does not exist: {fw_path}")
113
+ return 1
114
+
115
+ try:
116
+ fw_dict = custom_fw_from_path(fw_path)
117
+ if description:
118
+ fw_dict["description"] = description
119
+ if add_firmware(
120
+ source=fw_path,
121
+ fw_info=fw_dict,
122
+ custom=True,
123
+ force=force,
124
+ ):
125
+ log.success(f"Added custom firmware: {fw_dict['custom_id']} for {fw_dict['firmware_file']}")
126
+ return 0
127
+ else:
128
+ return 1
129
+ except MPFlashError as e:
130
+ log.error(f"{e}")
131
+ return 1
@@ -2,10 +2,11 @@
2
2
 
3
3
  from pathlib import Path
4
4
 
5
+ from pytest import param
5
6
  import rich_click as click
6
7
  from loguru import logger as log
7
8
 
8
- from mpflash.connected import connected_ports_boards
9
+ from mpflash.connected import connected_ports_boards_variants
9
10
  from mpflash.downloaded import clean_downloaded_firmwares
10
11
  from mpflash.errors import MPFlashError
11
12
  from mpflash.mpboard_id import find_known_board
@@ -23,15 +24,6 @@ from .download import download
23
24
  "download",
24
25
  help="Download MicroPython firmware for specific ports, boards and versions.",
25
26
  )
26
- @click.option(
27
- "--destination",
28
- "-d",
29
- "fw_folder",
30
- type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
31
- default=None,
32
- show_default=False,
33
- help="The folder to download the firmware to.",
34
- )
35
27
  @click.option(
36
28
  "--version",
37
29
  "-v",
@@ -94,9 +86,6 @@ def cli_download(**kwargs) -> int:
94
86
  params.boards = list(params.boards)
95
87
  params.serial = list(params.serial)
96
88
  params.ignore = list(params.ignore)
97
- if params.fw_folder:
98
- config.firmware_folder = Path(params.fw_folder)
99
- # all_boards: List[MPRemoteBoard] = []
100
89
  if params.boards:
101
90
  if not params.ports:
102
91
  # no ports specified - resolve ports from specified boards by resolving board IDs
@@ -109,7 +98,7 @@ def cli_download(**kwargs) -> int:
109
98
  log.error(f"{e}")
110
99
  else:
111
100
  # no boards specified - detect connected ports and boards
112
- params.ports, params.boards, _ = connected_ports_boards(include=params.serial, ignore=params.ignore)
101
+ params.ports, params.boards, _, _ = connected_ports_boards_variants(include=params.serial, ignore=params.ignore)
113
102
 
114
103
  params = ask_missing_params(params)
115
104
  if not params: # Cancelled by user
@@ -1,4 +1,3 @@
1
- from pathlib import Path
2
1
  from typing import List
3
2
 
4
3
  import rich_click as click
@@ -7,11 +6,10 @@ from loguru import logger as log
7
6
  import mpflash.download.jid as jid
8
7
  import mpflash.mpboard_id as mpboard_id
9
8
  from mpflash.ask_input import ask_missing_params
10
- from mpflash.cli_download import connected_ports_boards
9
+ from mpflash.cli_download import connected_ports_boards_variants
11
10
  from mpflash.cli_group import cli
12
11
  from mpflash.cli_list import show_mcus
13
- from mpflash.common import BootloaderMethod, FlashParams, Params
14
- from mpflash.config import config
12
+ from mpflash.common import BootloaderMethod, FlashParams, filtered_comports
15
13
  from mpflash.errors import MPFlashError
16
14
  from mpflash.flash import flash_list
17
15
  from mpflash.flash.worklist import WorkList, full_auto_worklist, manual_worklist, single_auto_worklist
@@ -27,15 +25,6 @@ from mpflash.versions import clean_version
27
25
  "flash",
28
26
  short_help="Flash one or all connected MicroPython boards with a specific firmware and version.",
29
27
  )
30
- @click.option(
31
- "--firmware",
32
- "-f",
33
- "fw_folder",
34
- type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
35
- default=None,
36
- show_default=False,
37
- help="The folder to retrieve the firmware from.",
38
- )
39
28
  @click.option(
40
29
  "--version",
41
30
  "-v",
@@ -95,7 +84,7 @@ from mpflash.versions import clean_version
95
84
  )
96
85
  @click.option(
97
86
  "--variant",
98
- "-var",
87
+ "--var",
99
88
  "variant", # single board
100
89
  multiple=False,
101
90
  help="The board VARIANT to flash or '-'. If not specified will try to read the variant from the connected MCU.",
@@ -104,7 +93,6 @@ from mpflash.versions import clean_version
104
93
  @click.option(
105
94
  "--cpu",
106
95
  "--chip",
107
- "-c",
108
96
  "cpu",
109
97
  help="The CPU type to flash. If not specified will try to read the CPU from the connected MCU.",
110
98
  metavar="CPU",
@@ -134,12 +122,20 @@ from mpflash.versions import clean_version
134
122
  )
135
123
  @click.option(
136
124
  "--flash_mode",
137
- "-fm",
125
+ "--fm",
138
126
  type=click.Choice(["keep", "qio", "qout", "dio", "dout"]),
139
127
  default="keep",
140
128
  show_default=True,
141
129
  help="""Flash mode for ESP boards. (default: keep)""",
142
130
  )
131
+ @click.option(
132
+ "--custom",
133
+ "-c",
134
+ default=False,
135
+ is_flag=True,
136
+ show_default=True,
137
+ help="""Flash a custom firmware""",
138
+ )
143
139
  def cli_flash_board(**kwargs) -> int:
144
140
  # version to versions, board to boards
145
141
  kwargs["versions"] = [kwargs.pop("version")] if kwargs["version"] is not None else []
@@ -164,16 +160,18 @@ def cli_flash_board(**kwargs) -> int:
164
160
  # No bard specified
165
161
  params.boards = ["?"]
166
162
 
167
- if params.fw_folder:
168
- config.firmware_folder = Path(params.fw_folder)
169
163
  # Detect connected boards if not specified,
170
164
  # and ask for input if boards cannot be detected
171
165
  all_boards: List[MPRemoteBoard] = []
172
166
  if not params.boards:
173
167
  # nothing specified - detect connected boards
174
- params.ports, params.boards, all_boards = connected_ports_boards(
175
- include=params.ports, ignore=params.ignore, bluetooth=params.bluetooth
168
+ params.ports, params.boards, variants, all_boards = connected_ports_boards_variants(
169
+ include=params.ports,
170
+ ignore=params.ignore,
171
+ bluetooth=params.bluetooth,
176
172
  )
173
+ if variants and len(variants) >= 1:
174
+ params.variant = variants[0]
177
175
  if params.boards == []:
178
176
  # No MicroPython boards detected, but it could be unflashed or in bootloader mode
179
177
  # Ask for serial port and board_id to flash
@@ -197,11 +195,27 @@ def cli_flash_board(**kwargs) -> int:
197
195
  params.versions = [clean_version(v) for v in params.versions]
198
196
  worklist: WorkList = []
199
197
 
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
200
+ comports = filtered_comports(
201
+ ignore=params.ignore,
202
+ include=params.serial,
203
+ bluetooth=params.bluetooth,
204
+ )
205
+ board_id = f"{params.boards[0]}-{params.variant}" if params.variant else params.boards[0]
206
+ log.info(f"Flashing {board_id} {params.versions[0]} to {len(comports)} serial ports")
207
+ log.info(f"Target ports: {', '.join(comports)}")
208
+ worklist = manual_worklist(
209
+ comports,
210
+ board_id=board_id,
211
+ version=params.versions[0],
212
+ custom=params.custom,
213
+ )
200
214
  # if serial port == auto and there are one or more specified/detected boards
201
- if params.serial == ["*"] and params.boards:
215
+ elif params.serial == ["*"] and params.boards:
202
216
  if not all_boards:
203
217
  log.trace("No boards detected yet, scanning for connected boards")
204
- _, _, all_boards = connected_ports_boards(include=params.ports, ignore=params.ignore)
218
+ _, _, _, all_boards = connected_ports_boards_variants(include=params.ports, ignore=params.ignore)
205
219
  # if variant id provided on the cmdline, treat is as an override
206
220
  if params.variant:
207
221
  for b in all_boards:
@@ -215,8 +229,13 @@ def cli_flash_board(**kwargs) -> int:
215
229
  )
216
230
  elif params.versions[0] and params.boards[0] and params.serial:
217
231
  # A one or more serial port including the board / variant
232
+ comports = filtered_comports(
233
+ ignore=params.ignore,
234
+ include=params.ports,
235
+ bluetooth=params.bluetooth,
236
+ )
218
237
  worklist = manual_worklist(
219
- params.serial[0],
238
+ comports,
220
239
  board_id=params.boards[0],
221
240
  version=params.versions[0],
222
241
  )
@@ -226,7 +245,8 @@ def cli_flash_board(**kwargs) -> int:
226
245
  serial=params.serial[0],
227
246
  version=params.versions[0],
228
247
  )
229
- jid.ensure_firmware_downloaded(worklist, version=params.versions[0], force=params.force)
248
+ if not params.custom:
249
+ jid.ensure_firmware_downloaded(worklist, version=params.versions[0], force=params.force)
230
250
  if flashed := flash_list(
231
251
  worklist,
232
252
  params.erase,
@@ -239,5 +259,3 @@ def cli_flash_board(**kwargs) -> int:
239
259
  else:
240
260
  log.error("No boards were flashed")
241
261
  return 1
242
-
243
-
@@ -14,6 +14,7 @@ from .logger import log, make_quiet, set_loglevel
14
14
  set_loglevel("INFO")
15
15
  config.verbose = False
16
16
 
17
+
17
18
  def cb_verbose(ctx, param, value):
18
19
  """Callback to set the log level to DEBUG if verbose is set"""
19
20
  if value and not config.quiet:
@@ -86,6 +86,8 @@ def cli_list_mcus(serial: List[str], ignore: List[str], bluetooth: bool, as_json
86
86
  if mcu.family == "circuitpython":
87
87
  # CircuitPython boards need a special reset command
88
88
  mcu.run_command(["exec", "--no-follow", "import microcontroller,time;time.sleep(0.01);microcontroller.reset()"], resume=False)
89
+ elif mcu.family == "unknown":
90
+ continue
89
91
  else:
90
92
  mcu.run_command("reset")
91
93
  return 0 if conn_mcus else 1
@@ -5,6 +5,9 @@ import os
5
5
  import click.exceptions as click_exceptions
6
6
  from loguru import logger as log
7
7
 
8
+ from mpflash.errors import MPFlashError
9
+
10
+ from .cli_add import cli_add_custom
8
11
  from .cli_download import cli_download
9
12
  from .cli_flash import cli_flash_board
10
13
  from .cli_group import cli
@@ -19,6 +22,7 @@ def mpflash():
19
22
  cli.add_command(cli_list_mcus)
20
23
  cli.add_command(cli_download)
21
24
  cli.add_command(cli_flash_board)
25
+ cli.add_command(cli_add_custom)
22
26
 
23
27
  # cli(auto_envvar_prefix="MPFLASH")
24
28
  if False and os.environ.get("COMPUTERNAME").upper().startswith("JOSVERL"):
@@ -37,6 +41,9 @@ def mpflash():
37
41
  except click_exceptions.Abort:
38
42
  # Aborted - Ctrl-C
39
43
  exit(-3)
44
+ except MPFlashError as e:
45
+ log.error(f"MPFlashError: {e}")
46
+ exit(-4)
40
47
 
41
48
 
42
49
  if __name__ == "__main__":