micropython-stubber 1.17.5__py3-none-any.whl → 1.19.0__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 (74) hide show
  1. {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/METADATA +7 -6
  2. {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/RECORD +71 -52
  3. mpflash/README.md +22 -3
  4. mpflash/libusb_flash.ipynb +203 -0
  5. mpflash/mpflash/ask_input.py +234 -0
  6. mpflash/mpflash/cli_download.py +107 -0
  7. mpflash/mpflash/cli_flash.py +165 -0
  8. mpflash/mpflash/cli_group.py +41 -8
  9. mpflash/mpflash/cli_list.py +41 -0
  10. mpflash/mpflash/cli_main.py +13 -8
  11. mpflash/mpflash/common.py +33 -122
  12. mpflash/mpflash/config.py +9 -0
  13. mpflash/mpflash/{downloader.py → download.py} +112 -120
  14. mpflash/mpflash/downloaded.py +108 -0
  15. mpflash/mpflash/errors.py +5 -0
  16. mpflash/mpflash/flash.py +69 -0
  17. mpflash/mpflash/flash_esp.py +17 -23
  18. mpflash/mpflash/flash_stm32.py +16 -113
  19. mpflash/mpflash/flash_stm32_cube.py +111 -0
  20. mpflash/mpflash/flash_stm32_dfu.py +101 -0
  21. mpflash/mpflash/flash_uf2.py +8 -8
  22. mpflash/mpflash/flash_uf2_linux.py +25 -12
  23. mpflash/mpflash/flash_uf2_windows.py +24 -12
  24. mpflash/mpflash/list.py +34 -37
  25. mpflash/mpflash/logger.py +12 -13
  26. mpflash/mpflash/mpboard_id/__init__.py +96 -0
  27. mpflash/mpflash/mpboard_id/board_id.py +63 -0
  28. mpflash/mpflash/mpboard_id/board_info.csv +2213 -0
  29. mpflash/mpflash/mpboard_id/board_info.json +19910 -0
  30. mpflash/mpflash/mpremoteboard/__init__.py +208 -0
  31. mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -0
  32. {stubber/bulk → mpflash/mpflash/mpremoteboard}/runner.py +22 -5
  33. mpflash/mpflash/vendor/dfu.py +164 -0
  34. mpflash/mpflash/vendor/pydfu.py +605 -0
  35. mpflash/mpflash/vendor/readme.md +3 -0
  36. mpflash/mpflash/vendor/versions.py +113 -0
  37. mpflash/mpflash/worklist.py +147 -0
  38. mpflash/poetry.lock +411 -595
  39. mpflash/pyproject.toml +24 -8
  40. mpflash/stm32_udev_rules.md +63 -0
  41. stubber/__init__.py +1 -1
  42. stubber/basicgit.py +1 -0
  43. stubber/board/createstubs.py +10 -4
  44. stubber/board/createstubs_db.py +11 -5
  45. stubber/board/createstubs_db_min.py +61 -58
  46. stubber/board/createstubs_db_mpy.mpy +0 -0
  47. stubber/board/createstubs_mem.py +11 -5
  48. stubber/board/createstubs_mem_min.py +56 -53
  49. stubber/board/createstubs_mem_mpy.mpy +0 -0
  50. stubber/board/createstubs_min.py +54 -51
  51. stubber/board/createstubs_mpy.mpy +0 -0
  52. stubber/bulk/mcu_stubber.py +9 -5
  53. stubber/codemod/_partials/db_main.py +14 -25
  54. stubber/codemod/_partials/lvgl_main.py +2 -2
  55. stubber/codemod/board.py +10 -3
  56. stubber/commands/clone_cmd.py +7 -7
  57. stubber/commands/config_cmd.py +3 -0
  58. stubber/freeze/get_frozen.py +0 -2
  59. stubber/publish/candidates.py +1 -1
  60. stubber/publish/package.py +1 -1
  61. stubber/publish/pathnames.py +1 -1
  62. stubber/publish/stubpackage.py +1 -0
  63. stubber/rst/lookup.py +1 -1
  64. stubber/tools/manifestfile.py +5 -3
  65. stubber/utils/config.py +26 -36
  66. stubber/utils/repos.py +2 -2
  67. stubber/utils/versions.py +1 -0
  68. mpflash/mpflash/flasher.py +0 -287
  69. stubber/bulk/board_id.py +0 -40
  70. stubber/bulk/mpremoteboard.py +0 -141
  71. {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/LICENSE +0 -0
  72. {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/WHEEL +0 -0
  73. {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/entry_points.txt +0 -0
  74. /mpflash/mpflash/{uf2_boardid.py → flash_uf2_boardid.py} +0 -0
@@ -0,0 +1,234 @@
1
+ """
2
+ Interactive input for mpflash.
3
+
4
+ Note: The prompts can use "{version}" and "{action}" to insert the version and action in the prompt without needing an f-string.
5
+ The values are provided from the answers dictionary.
6
+ """
7
+
8
+ from dataclasses import dataclass, field
9
+ from pathlib import Path
10
+ from typing import Dict, List, Sequence, Tuple, Union
11
+
12
+ from loguru import logger as log
13
+
14
+ from mpflash.config import config
15
+ from mpflash.mpboard_id import known_stored_boards, local_mp_ports
16
+ from mpflash.mpremoteboard import MPRemoteBoard
17
+ from mpflash.vendor.versions import micropython_versions
18
+
19
+
20
+ @dataclass
21
+ class Params:
22
+ ports: List[str] = field(default_factory=list)
23
+ boards: List[str] = field(default_factory=list)
24
+ versions: List[str] = field(default_factory=list)
25
+ fw_folder: Path = Path()
26
+
27
+
28
+ @dataclass
29
+ class DownloadParams(Params):
30
+ clean: bool = False
31
+ force: bool = False
32
+
33
+
34
+ @dataclass
35
+ class FlashParams(Params):
36
+ # TODO: Should Serial port be a list?
37
+ serial: str = ""
38
+ erase: bool = True
39
+ bootloader: bool = True
40
+ cpu: str = ""
41
+
42
+
43
+ ParamType = Union[DownloadParams, FlashParams]
44
+
45
+
46
+ def ask_missing_params(
47
+ params: ParamType,
48
+ action: str = "download",
49
+ ) -> ParamType:
50
+ """
51
+ Asks the user for parameters that have not been supplied on the commandline and returns the updated params.
52
+
53
+ Args:
54
+ params (ParamType): The parameters to be updated.
55
+ action (str, optional): The action to be performed. Defaults to "download".
56
+
57
+ Returns:
58
+ ParamType: The updated parameters.
59
+ """
60
+ if not config.interactive:
61
+ # no interactivity allowed
62
+ return params
63
+ # import only when needed to reduce load time
64
+ import inquirer
65
+
66
+ questions = []
67
+ answers = {"action": action}
68
+ if isinstance(params, FlashParams):
69
+ if not params.serial or "?" in params.serial:
70
+ ask_serialport(questions, action=action)
71
+ else:
72
+ answers["serial"] = params.serial
73
+
74
+ if not params.versions or "?" in params.versions:
75
+ ask_versions(questions, action=action)
76
+ else:
77
+ # versions is used to show only the boards for the selected versions
78
+ answers["versions"] = params.versions # type: ignore
79
+
80
+ if not params.boards or "?" in params.boards:
81
+ ask_port_board(questions, action=action)
82
+
83
+ answers = inquirer.prompt(questions, answers=answers)
84
+ if not answers:
85
+ # input cancelled by user
86
+ return [] # type: ignore
87
+ # print(repr(answers))
88
+ if isinstance(params, FlashParams) and "serial" in answers:
89
+ params.serial = answers["serial"]
90
+ if "port" in answers:
91
+ params.ports = [answers["port"]]
92
+ if "boards" in answers:
93
+ params.boards = answers["boards"] if isinstance(answers["boards"], list) else [answers["boards"]]
94
+ if "versions" in answers:
95
+ # make sure it is a list
96
+ params.versions = answers["versions"] if isinstance(answers["versions"], list) else [answers["versions"]]
97
+
98
+ log.debug(repr(params))
99
+
100
+ return params
101
+
102
+
103
+ def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
104
+ """
105
+ Filters the known boards based on the selected versions and returns the filtered boards.
106
+
107
+ Args:
108
+ answers (dict): The user's answers.
109
+
110
+ Returns:
111
+ Sequence[Tuple[str, str]]: The filtered boards.
112
+ """
113
+ # if version is not asked ; then need to get the version from the inputs
114
+ if "versions" in answers:
115
+ _versions = list(answers["versions"])
116
+ if "stable" in _versions:
117
+ _versions.remove("stable")
118
+ _versions.append(micropython_versions()[-2]) # latest stable
119
+ if "preview" in _versions:
120
+ _versions.remove("preview")
121
+ _versions.extend((micropython_versions()[-1], micropython_versions()[-2])) # latest preview and stable
122
+
123
+ some_boards = known_stored_boards(answers["port"], _versions) # or known_mp_boards(answers["port"])
124
+ else:
125
+ some_boards = known_stored_boards(answers["port"])
126
+
127
+ if some_boards:
128
+ # Create a dictionary where the keys are the second elements of the tuples
129
+ # This will automatically remove duplicates because dictionaries cannot have duplicate keys
130
+ unique_dict = {item[1]: item for item in some_boards}
131
+ # Get the values of the dictionary, which are the unique items from the original list
132
+ some_boards = list(unique_dict.values())
133
+ else:
134
+ some_boards = [("No boards found", "")]
135
+ return some_boards
136
+
137
+
138
+ def ask_port_board(questions: list, *, action: str):
139
+ """
140
+ Asks the user for the port and board selection.
141
+
142
+ Args:
143
+ questions (list): The list of questions to be asked.
144
+ action (str): The action to be performed.
145
+
146
+ Returns:
147
+ None
148
+ """
149
+ # import only when needed to reduce load time
150
+ import inquirer
151
+
152
+ # TODO: if action = flash, Use Inquirer.List for boards
153
+ inquirer_ux = inquirer.Checkbox if action == "download" else inquirer.List
154
+ questions.extend(
155
+ (
156
+ inquirer.List(
157
+ "port",
158
+ message="Which port do you want to {action} " + "to {serial} ?" if action == "flash" else "?",
159
+ choices=local_mp_ports(),
160
+ autocomplete=True,
161
+ ),
162
+ inquirer_ux(
163
+ "boards",
164
+ message=(
165
+ "Which {port} board firmware do you want to {action} " + "to {serial} ?"
166
+ if action == "flash"
167
+ else "?"
168
+ ),
169
+ choices=filter_matching_boards,
170
+ validate=lambda _, x: True if x else "Please select at least one board", # type: ignore
171
+ ),
172
+ )
173
+ )
174
+
175
+
176
+ def ask_versions(questions: list, *, action: str):
177
+ """
178
+ Asks the user for the version selection.
179
+
180
+ Args:
181
+ questions (list): The list of questions to be asked.
182
+ action (str): The action to be performed.
183
+
184
+ Returns:
185
+ None
186
+ """
187
+ # import only when needed to reduce load time
188
+ import inquirer
189
+
190
+ input_ux = inquirer.Checkbox if action == "download" else inquirer.List
191
+ mp_versions: List[str] = micropython_versions()
192
+ mp_versions = [v for v in mp_versions if "preview" not in v]
193
+ mp_versions.append("preview")
194
+ mp_versions.reverse() # newest first
195
+ questions.append(
196
+ input_ux(
197
+ # inquirer.List(
198
+ "versions",
199
+ message="Which version(s) do you want to {action} " + ("to {serial} ?" if action == "flash" else "?"),
200
+ # Hints would be nice , but needs a hint for each and every option
201
+ # hints=["Use space to select multiple options"],
202
+ choices=mp_versions,
203
+ autocomplete=True,
204
+ validate=lambda _, x: True if x else "Please select at least one version", # type: ignore
205
+ )
206
+ )
207
+
208
+
209
+ def ask_serialport(questions: list, *, action: str):
210
+ """
211
+ Asks the user for the serial port selection.
212
+
213
+ Args:
214
+ questions (list): The list of questions to be asked.
215
+ action (str): The action to be performed.
216
+
217
+ Returns:
218
+ None
219
+ """
220
+ # import only when needed to reduce load time
221
+ import inquirer
222
+
223
+ serialports = MPRemoteBoard.connected_boards()
224
+ questions.append(
225
+ inquirer.List(
226
+ "serial",
227
+ message="Which serial port do you want to {action} ?",
228
+ choices=serialports,
229
+ other=True,
230
+ validate=lambda _, x: True if x else "Please select or enter a serial port", # type: ignore
231
+ )
232
+ )
233
+
234
+ return questions
@@ -0,0 +1,107 @@
1
+ """CLI to Download MicroPython firmware for specific ports, boards and versions."""
2
+
3
+ from pathlib import Path
4
+ from typing import List, Tuple
5
+
6
+ import rich_click as click
7
+ from loguru import logger as log
8
+
9
+ from mpflash.mpboard_id import find_stored_board
10
+ from mpflash.vendor.versions import clean_version
11
+
12
+ from .ask_input import DownloadParams, ask_missing_params
13
+ from .cli_group import cli
14
+ from .cli_list import list_mcus
15
+ from .config import config
16
+ from .download import download
17
+
18
+
19
+ @cli.command(
20
+ "download",
21
+ help="Download MicroPython firmware for specific ports, boards and versions.",
22
+ )
23
+ @click.option(
24
+ "--destination",
25
+ "-d",
26
+ "fw_folder",
27
+ type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
28
+ default=config.firmware_folder,
29
+ show_default=True,
30
+ help="The folder to download the firmware to.",
31
+ )
32
+ @click.option(
33
+ "--version",
34
+ "-v",
35
+ "versions",
36
+ default=["stable"],
37
+ multiple=True,
38
+ show_default=True,
39
+ help="The version of MicroPython to to download.",
40
+ metavar="SEMVER, 'stable', 'preview' or '?'",
41
+ )
42
+ @click.option(
43
+ "--board",
44
+ "-b",
45
+ "boards",
46
+ multiple=True,
47
+ default=[],
48
+ show_default=True,
49
+ help="The board(s) to download the firmware for.",
50
+ metavar="BOARD_ID or ?",
51
+ )
52
+ @click.option(
53
+ "--clean/--no-clean",
54
+ default=True,
55
+ show_default=True,
56
+ help="""Remove dates and hashes from the downloaded firmware filenames.""",
57
+ )
58
+ @click.option(
59
+ "--force",
60
+ default=False,
61
+ is_flag=True,
62
+ show_default=True,
63
+ help="""Force download of firmware even if it already exists.""",
64
+ )
65
+ def cli_download(
66
+ **kwargs,
67
+ ):
68
+ params = DownloadParams(**kwargs)
69
+ params.versions = list(params.versions)
70
+ params.boards = list(params.boards)
71
+ if params.boards:
72
+ pass
73
+ # TODO Clean board - same as in cli_flash.py
74
+ else:
75
+ # no boards specified - detect connected boards
76
+ params.ports, params.boards = connected_ports_boards()
77
+
78
+ params = ask_missing_params(params, action="download")
79
+ if not params: # Cancelled by user
80
+ exit(1)
81
+ params.versions = [clean_version(v, drop_v=True) for v in params.versions]
82
+ assert isinstance(params, DownloadParams)
83
+
84
+ download(
85
+ params.fw_folder,
86
+ params.ports,
87
+ params.boards,
88
+ params.versions,
89
+ params.force,
90
+ params.clean,
91
+ )
92
+
93
+
94
+ def connected_ports_boards() -> Tuple[List[str], List[str]]:
95
+ """
96
+ Returns a tuple containing lists of unique ports and boards from the connected MCUs.
97
+ Boards that are physically connected, but give no tangible response are ignored.
98
+
99
+ Returns:
100
+ A tuple containing two lists:
101
+ - A list of unique ports where MCUs are connected.
102
+ - A list of unique board names of the connected MCUs.
103
+ """
104
+ mpr_boards = [b for b in list_mcus() if b.connected]
105
+ ports = list({b.port for b in mpr_boards})
106
+ boards = list({b.board for b in mpr_boards})
107
+ return ports, boards
@@ -0,0 +1,165 @@
1
+ from pathlib import Path
2
+
3
+ import rich_click as click
4
+ from loguru import logger as log
5
+
6
+ from mpflash.errors import MPFlashError
7
+ from mpflash.mpboard_id import find_stored_board
8
+ from mpflash.vendor.versions import clean_version
9
+
10
+ from .ask_input import FlashParams, ask_missing_params
11
+ from .cli_download import connected_ports_boards
12
+ from .cli_group import cli
13
+ from .cli_list import show_mcus
14
+ from .config import config
15
+ from .flash import flash_list
16
+ from .worklist import WorkList, full_auto_worklist, manual_worklist, single_auto_worklist
17
+
18
+ # #########################################################################################################
19
+ # CLI
20
+ # #########################################################################################################
21
+
22
+
23
+ @cli.command(
24
+ "flash",
25
+ short_help="Flash one or all connected MicroPython boards with a specific firmware and version.",
26
+ )
27
+ @click.option(
28
+ "--firmware",
29
+ "-f",
30
+ "fw_folder",
31
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
32
+ default=config.firmware_folder,
33
+ show_default=True,
34
+ help="The folder to retrieve the firmware from.",
35
+ )
36
+ @click.option(
37
+ "--version",
38
+ "-v",
39
+ "version", # single version
40
+ default="stable",
41
+ multiple=False,
42
+ show_default=True,
43
+ help="The version of MicroPython to flash.",
44
+ metavar="SEMVER, 'stable', 'preview' or '?'",
45
+ )
46
+ @click.option(
47
+ "--serial",
48
+ "--serial-port",
49
+ "-s",
50
+ "serial",
51
+ default="auto",
52
+ show_default=True,
53
+ help="Which serial port(s) to flash",
54
+ metavar="SERIAL_PORT",
55
+ )
56
+ @click.option(
57
+ "--port",
58
+ "-p",
59
+ "ports",
60
+ help="The MicroPython port to flash",
61
+ metavar="PORT",
62
+ default=[],
63
+ multiple=True,
64
+ )
65
+ @click.option(
66
+ "--board",
67
+ "-b",
68
+ "board", # single board
69
+ multiple=False,
70
+ help="The MicroPython board ID to flash. If not specified will try to read the BOARD_ID from the connected MCU.",
71
+ metavar="BOARD_ID or ?",
72
+ )
73
+ @click.option(
74
+ "--cpu",
75
+ "--chip",
76
+ "-c",
77
+ "cpu",
78
+ help="The CPU type to flash. If not specified will try to read the CPU from the connected MCU.",
79
+ metavar="CPU",
80
+ )
81
+ @click.option(
82
+ "--erase/--no-erase",
83
+ default=True,
84
+ show_default=True,
85
+ help="""Erase flash before writing new firmware. (Not supported on UF2 boards)""",
86
+ )
87
+ @click.option(
88
+ "--bootloader/--no-bootloader",
89
+ default=True,
90
+ is_flag=True,
91
+ show_default=True,
92
+ help="""Enter micropython bootloader mode before flashing.""",
93
+ )
94
+ def cli_flash_board(**kwargs):
95
+ # version to versions, board to boards
96
+ kwargs["versions"] = [kwargs.pop("version")] if kwargs["version"] != None else []
97
+ if kwargs["board"] is None:
98
+ kwargs["boards"] = []
99
+ kwargs.pop("board")
100
+ else:
101
+ kwargs["boards"] = [kwargs.pop("board")]
102
+
103
+ params = FlashParams(**kwargs)
104
+ if not params.boards or params.boards == []:
105
+ # nothing specified - detect connected boards
106
+ params.ports, params.boards = connected_ports_boards()
107
+ else:
108
+ for board_id in params.boards:
109
+ if board_id == "":
110
+ params.boards.remove(board_id)
111
+ continue
112
+ if " " in board_id:
113
+ try:
114
+ info = find_stored_board(board_id)
115
+ if info:
116
+ log.info(f"Resolved board description: {info['board']}")
117
+ params.boards.remove(board_id)
118
+ params.boards.append(info["board"])
119
+ except Exception as e:
120
+ log.warning(f"unable to resolve board description: {e}")
121
+
122
+ # Ask for missing input if needed
123
+ params = ask_missing_params(params, action="flash")
124
+ if not params: # Cancelled by user
125
+ exit(1)
126
+ # TODO: Just in time Download of firmware
127
+
128
+ assert isinstance(params, FlashParams)
129
+
130
+ if len(params.versions) > 1:
131
+ log.error(f"Only one version can be flashed at a time, not {params.versions}")
132
+ raise MPFlashError("Only one version can be flashed at a time")
133
+ if len(params.boards) > 1:
134
+ log.error(f"Only one board can be flashed at a time, not {params.boards}")
135
+ raise MPFlashError("Only one board can be flashed at a time")
136
+
137
+ params.versions = [clean_version(v) for v in params.versions]
138
+ worklist: WorkList = []
139
+ # if serial port == auto and there are one or more specified/detected boards
140
+ if params.serial == "auto" and params.boards:
141
+ worklist = full_auto_worklist(version=params.versions[0], fw_folder=params.fw_folder)
142
+ elif params.versions[0] and params.boards[0] and params.serial:
143
+ # A single serial port including the board / variant
144
+ worklist = manual_worklist(
145
+ params.versions[0],
146
+ params.fw_folder,
147
+ params.serial,
148
+ params.boards[0],
149
+ )
150
+ else:
151
+ # just this serial port on auto
152
+ worklist = single_auto_worklist(
153
+ serial_port=params.serial,
154
+ version=params.versions[0],
155
+ fw_folder=params.fw_folder,
156
+ )
157
+
158
+ if flashed := flash_list(
159
+ worklist,
160
+ params.fw_folder,
161
+ params.erase,
162
+ params.bootloader,
163
+ ):
164
+ log.info(f"Flashed {len(flashed)} boards")
165
+ show_mcus(flashed, title="Updated boards after flashing")
@@ -3,54 +3,87 @@ Main entry point for the CLI group.
3
3
  Additional comands are added in the submodules.
4
4
  """
5
5
 
6
- from typing import List
7
-
8
6
  import rich_click as click
9
7
 
10
8
  from .config import config
11
- from .logger import set_loglevel
9
+ from .logger import make_quiet, set_loglevel
12
10
 
13
11
 
14
12
  def cb_verbose(ctx, param, value):
15
13
  """Callback to set the log level to DEBUG if verbose is set"""
16
14
  if value:
17
15
  set_loglevel("DEBUG")
16
+ config.verbose = True
18
17
  else:
19
18
  set_loglevel("INFO")
19
+ config.verbose = False
20
20
  return value
21
21
 
22
22
 
23
23
  def cb_ignore(ctx, param, value):
24
24
  if value:
25
25
  config.ignore_ports = list(value)
26
- print(repr(config.ignore_ports))
26
+ return value
27
+
28
+
29
+ def cb_interactive(ctx, param, value):
30
+ if value:
31
+ config.interactive = value
32
+ return value
33
+
34
+
35
+ def cb_quiet(ctx, param, value):
36
+ if value:
37
+ make_quiet()
27
38
  return value
28
39
 
29
40
 
30
41
  @click.group()
42
+ @click.version_option(package_name="mpflash")
43
+ @click.option(
44
+ "--quiet",
45
+ "-q",
46
+ is_eager=True,
47
+ is_flag=True,
48
+ help="Suppresses all output.",
49
+ callback=cb_quiet,
50
+ envvar="MPFLASH_QUIET",
51
+ show_default=True,
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
+ )
31
63
  @click.option(
32
64
  "-V",
33
65
  "--verbose",
66
+ is_eager=True,
34
67
  is_flag=True,
35
68
  help="Enables verbose mode.",
36
- is_eager=True,
37
69
  callback=cb_verbose,
38
70
  )
39
71
  @click.option(
40
72
  "--ignore",
41
73
  "-i",
42
74
  is_eager=True,
43
- help="Serial port(s) to ignore. Defaults to MPTOOL_IGNORE.",
75
+ help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
44
76
  callback=cb_ignore,
45
77
  multiple=True,
46
78
  default=[],
47
- envvar="MPTOOL_IGNORE",
79
+ envvar="MPFLASH_IGNORE",
48
80
  show_default=True,
49
81
  metavar="SERIALPORT",
50
82
  )
51
- def cli(verbose: bool, ignore: List[str], **kwargs):
83
+ def cli(**kwargs):
52
84
  """mpflash - MicroPython Tool.
53
85
 
54
86
  A CLI to download and flash MicroPython firmware to different ports and boards.
55
87
  """
88
+ # all functionality is added in the submodules
56
89
  pass
@@ -0,0 +1,41 @@
1
+ import json
2
+
3
+ import rich_click as click
4
+ from rich import print
5
+
6
+ from .cli_group import cli
7
+ from .list import list_mcus, show_mcus
8
+ from .logger import make_quiet
9
+
10
+
11
+ @cli.command("list", help="List the connected MCU boards.")
12
+ @click.option(
13
+ "--json",
14
+ "-j",
15
+ "as_json",
16
+ is_flag=True,
17
+ default=False,
18
+ show_default=True,
19
+ help="""Output in json format""",
20
+ )
21
+ @click.option(
22
+ "--progress/--no-progress",
23
+ "progress",
24
+ is_flag=True,
25
+ default=True,
26
+ show_default=True,
27
+ help="""Show progress""",
28
+ )
29
+ def cli_list_mcus(as_json: bool, progress: bool = True):
30
+ """List the connected MCU boards, and output in a nice table or json."""
31
+ if as_json:
32
+ # avoid noise in json output
33
+ make_quiet()
34
+
35
+ conn_mcus = list_mcus()
36
+ if as_json:
37
+ print(json.dumps([mcu.__dict__ for mcu in conn_mcus], indent=4))
38
+ progress = False
39
+ if progress:
40
+ show_mcus(conn_mcus, refresh=False)
41
+ return conn_mcus
@@ -1,20 +1,25 @@
1
1
  """mpflash is a CLI to download and flash MicroPython firmware to various boards."""
2
2
 
3
- import rich_click as click
3
+ # import rich_click as click
4
4
 
5
+ from .cli_download import cli_download
6
+ from .cli_flash import cli_flash_board
5
7
  from .cli_group import cli
6
- from .downloader import download
7
- from .flasher import flash_board
8
- from .list import list_boards
8
+ from .cli_list import cli_list_mcus
9
9
 
10
10
  # from loguru import logger as log
11
11
 
12
12
 
13
13
  def mpflash():
14
- # cli.add_command(flash_board)
15
- # cli.add_command(list_boards)
16
- # cli.add_command(download)
17
- cli() # auto_envvar_prefix='MPTOOL')
14
+ cli.add_command(cli_flash_board)
15
+ cli.add_command(cli_list_mcus)
16
+ cli.add_command(cli_download)
17
+ # cli(auto_envvar_prefix="MPFLASH")
18
+ try:
19
+ exit(cli())
20
+ except AttributeError as e:
21
+ print(f"Error: {e}")
22
+ exit(-1)
18
23
 
19
24
 
20
25
  if __name__ == "__main__":