micropython-stubber 1.20.1__py3-none-any.whl → 1.20.2__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.
- {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/METADATA +3 -3
- {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/RECORD +56 -49
- mpflash/README.md +16 -5
- mpflash/mpflash/add_firmware.py +98 -0
- mpflash/mpflash/ask_input.py +97 -120
- mpflash/mpflash/cli_download.py +42 -25
- mpflash/mpflash/cli_flash.py +70 -32
- mpflash/mpflash/cli_group.py +14 -12
- mpflash/mpflash/cli_list.py +39 -3
- mpflash/mpflash/cli_main.py +17 -6
- mpflash/mpflash/common.py +125 -12
- mpflash/mpflash/config.py +2 -0
- mpflash/mpflash/connected.py +74 -0
- mpflash/mpflash/download.py +56 -42
- mpflash/mpflash/downloaded.py +9 -9
- mpflash/mpflash/flash.py +2 -2
- mpflash/mpflash/flash_esp.py +2 -2
- mpflash/mpflash/flash_uf2.py +16 -8
- mpflash/mpflash/flash_uf2_linux.py +5 -16
- mpflash/mpflash/flash_uf2_macos.py +78 -0
- mpflash/mpflash/flash_uf2_windows.py +1 -1
- mpflash/mpflash/list.py +57 -57
- mpflash/mpflash/mpboard_id/__init__.py +37 -44
- mpflash/mpflash/mpboard_id/add_boards.py +255 -0
- mpflash/mpflash/mpboard_id/board.py +37 -0
- mpflash/mpflash/mpboard_id/board_id.py +38 -34
- mpflash/mpflash/mpboard_id/board_info.zip +0 -0
- mpflash/mpflash/mpboard_id/store.py +42 -0
- mpflash/mpflash/mpremoteboard/__init__.py +18 -6
- mpflash/mpflash/uf2disk.py +12 -0
- mpflash/mpflash/vendor/basicgit.py +288 -0
- mpflash/mpflash/vendor/dfu.py +1 -0
- mpflash/mpflash/vendor/versions.py +7 -3
- mpflash/mpflash/worklist.py +71 -48
- mpflash/poetry.lock +164 -138
- mpflash/pyproject.toml +18 -15
- stubber/__init__.py +1 -1
- stubber/board/createstubs.py +4 -3
- stubber/board/createstubs_db.py +5 -7
- stubber/board/createstubs_db_min.py +329 -825
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +6 -7
- stubber/board/createstubs_mem_min.py +304 -765
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +293 -975
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/board/modulelist.txt +1 -0
- stubber/commands/get_core_cmd.py +7 -6
- stubber/commands/get_docstubs_cmd.py +8 -3
- stubber/commands/get_frozen_cmd.py +5 -2
- stubber/publish/publish.py +18 -7
- stubber/utils/makeversionhdr.py +3 -2
- stubber/utils/versions.py +2 -1
- mpflash/mpflash/mpboard_id/board_info.csv +0 -2213
- mpflash/mpflash/mpboard_id/board_info.json +0 -19910
- {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/entry_points.txt +0 -0
mpflash/mpflash/cli_download.py
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
"""CLI to Download MicroPython firmware for specific ports, boards and versions."""
|
2
2
|
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import List, Tuple
|
5
4
|
|
6
5
|
import rich_click as click
|
7
6
|
from loguru import logger as log
|
8
7
|
|
8
|
+
from mpflash.connected import connected_ports_boards
|
9
9
|
from mpflash.errors import MPFlashError
|
10
|
-
from mpflash.mpboard_id import
|
10
|
+
from mpflash.mpboard_id import find_known_board
|
11
11
|
from mpflash.vendor.versions import clean_version
|
12
12
|
|
13
|
-
from .ask_input import
|
13
|
+
from .ask_input import ask_missing_params
|
14
14
|
from .cli_group import cli
|
15
|
-
from .
|
15
|
+
from .common import DownloadParams
|
16
16
|
from .config import config
|
17
17
|
from .download import download
|
18
18
|
|
@@ -50,6 +50,28 @@ from .download import download
|
|
50
50
|
help="The board(s) to download the firmware for.",
|
51
51
|
metavar="BOARD_ID or ?",
|
52
52
|
)
|
53
|
+
@click.option(
|
54
|
+
"--serial",
|
55
|
+
"--serial-port",
|
56
|
+
"-s",
|
57
|
+
"serial",
|
58
|
+
default=["*"],
|
59
|
+
show_default=True,
|
60
|
+
multiple=True,
|
61
|
+
help="Which serial port(s) to flash",
|
62
|
+
metavar="SERIALPORT",
|
63
|
+
)
|
64
|
+
@click.option(
|
65
|
+
"--ignore",
|
66
|
+
"-i",
|
67
|
+
is_eager=True,
|
68
|
+
help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
|
69
|
+
multiple=True,
|
70
|
+
default=[],
|
71
|
+
envvar="MPFLASH_IGNORE",
|
72
|
+
show_default=True,
|
73
|
+
metavar="SERIALPORT",
|
74
|
+
)
|
53
75
|
@click.option(
|
54
76
|
"--clean/--no-clean",
|
55
77
|
default=True,
|
@@ -67,14 +89,25 @@ def cli_download(**kwargs) -> int:
|
|
67
89
|
params = DownloadParams(**kwargs)
|
68
90
|
params.versions = list(params.versions)
|
69
91
|
params.boards = list(params.boards)
|
92
|
+
params.serial = list(params.serial)
|
93
|
+
params.ignore = list(params.ignore)
|
94
|
+
|
95
|
+
# all_boards: List[MPRemoteBoard] = []
|
70
96
|
if params.boards:
|
71
|
-
|
72
|
-
|
97
|
+
if not params.ports:
|
98
|
+
# no ports specified - resolve ports from specified boards by resolving board IDs
|
99
|
+
for board in params.boards:
|
100
|
+
if board != "?":
|
101
|
+
try:
|
102
|
+
board_ = find_known_board(board)
|
103
|
+
params.ports.append(board_.port)
|
104
|
+
except MPFlashError as e:
|
105
|
+
log.error(f"{e}")
|
73
106
|
else:
|
74
|
-
# no boards specified - detect connected boards
|
75
|
-
params.ports, params.boards = connected_ports_boards()
|
107
|
+
# no boards specified - detect connected ports and boards
|
108
|
+
params.ports, params.boards, _ = connected_ports_boards(include=params.serial, ignore=params.ignore)
|
76
109
|
|
77
|
-
params = ask_missing_params(params
|
110
|
+
params = ask_missing_params(params)
|
78
111
|
if not params: # Cancelled by user
|
79
112
|
return 2
|
80
113
|
params.versions = [clean_version(v, drop_v=True) for v in params.versions]
|
@@ -93,19 +126,3 @@ def cli_download(**kwargs) -> int:
|
|
93
126
|
except MPFlashError as e:
|
94
127
|
log.error(f"{e}")
|
95
128
|
return 1
|
96
|
-
|
97
|
-
|
98
|
-
def connected_ports_boards() -> Tuple[List[str], List[str]]:
|
99
|
-
"""
|
100
|
-
Returns a tuple containing lists of unique ports and boards from the connected MCUs.
|
101
|
-
Boards that are physically connected, but give no tangible response are ignored.
|
102
|
-
|
103
|
-
Returns:
|
104
|
-
A tuple containing two lists:
|
105
|
-
- A list of unique ports where MCUs are connected.
|
106
|
-
- A list of unique board names of the connected MCUs.
|
107
|
-
"""
|
108
|
-
mpr_boards = [b for b in list_mcus() if b.connected]
|
109
|
-
ports = list({b.port for b in mpr_boards})
|
110
|
-
boards = list({b.board for b in mpr_boards})
|
111
|
-
return ports, boards
|
mpflash/mpflash/cli_flash.py
CHANGED
@@ -1,19 +1,23 @@
|
|
1
1
|
from pathlib import Path
|
2
|
+
from typing import List
|
2
3
|
|
3
4
|
import rich_click as click
|
4
5
|
from loguru import logger as log
|
5
6
|
|
7
|
+
# from mpflash.common import filtered_comports
|
6
8
|
from mpflash.errors import MPFlashError
|
7
|
-
from mpflash.mpboard_id import
|
9
|
+
from mpflash.mpboard_id import find_known_board
|
10
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
8
11
|
from mpflash.vendor.versions import clean_version
|
9
12
|
|
10
|
-
from .ask_input import
|
13
|
+
from .ask_input import ask_missing_params
|
11
14
|
from .cli_download import connected_ports_boards
|
12
15
|
from .cli_group import cli
|
13
16
|
from .cli_list import show_mcus
|
17
|
+
from .common import FlashParams
|
14
18
|
from .config import config
|
15
19
|
from .flash import flash_list
|
16
|
-
from .worklist import
|
20
|
+
from .worklist import WorkList, full_auto_worklist, manual_worklist, single_auto_worklist
|
17
21
|
|
18
22
|
# #########################################################################################################
|
19
23
|
# CLI
|
@@ -48,10 +52,22 @@ from .worklist import MPRemoteBoard, WorkList, full_auto_worklist, manual_workli
|
|
48
52
|
"--serial-port",
|
49
53
|
"-s",
|
50
54
|
"serial",
|
51
|
-
default="
|
55
|
+
default=["*"],
|
56
|
+
multiple=True,
|
52
57
|
show_default=True,
|
53
58
|
help="Which serial port(s) to flash",
|
54
|
-
metavar="
|
59
|
+
metavar="SERIALPORT",
|
60
|
+
)
|
61
|
+
@click.option(
|
62
|
+
"--ignore",
|
63
|
+
"-i",
|
64
|
+
is_eager=True,
|
65
|
+
help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
|
66
|
+
multiple=True,
|
67
|
+
default=[],
|
68
|
+
envvar="MPFLASH_IGNORE",
|
69
|
+
show_default=True,
|
70
|
+
metavar="SERIALPORT",
|
55
71
|
)
|
56
72
|
@click.option(
|
57
73
|
"--port",
|
@@ -101,31 +117,31 @@ def cli_flash_board(**kwargs) -> int:
|
|
101
117
|
kwargs["boards"] = [kwargs.pop("board")]
|
102
118
|
|
103
119
|
params = FlashParams(**kwargs)
|
120
|
+
params.versions = list(params.versions)
|
121
|
+
params.boards = list(params.boards)
|
122
|
+
params.serial = list(params.serial)
|
123
|
+
params.ignore = list(params.ignore)
|
124
|
+
|
125
|
+
# make it simple for the user to flash one board by asking for the serial port if not specified
|
126
|
+
if params.boards == ["?"] and params.serial == "*":
|
127
|
+
params.serial = ["?"]
|
128
|
+
|
129
|
+
# Detect connected boards if not specified,
|
130
|
+
# and ask for input if boards cannot be detected
|
131
|
+
all_boards: List[MPRemoteBoard] = []
|
104
132
|
if not params.boards or params.boards == []:
|
105
133
|
# nothing specified - detect connected boards
|
106
|
-
params.ports, params.boards = connected_ports_boards()
|
134
|
+
params.ports, params.boards, all_boards = connected_ports_boards(include=params.ports, ignore=params.ignore)
|
107
135
|
if params.boards == []:
|
108
136
|
# No MicroPython boards detected, but it could be unflashed or not in bootloader mode
|
109
137
|
# Ask for serial port and board_id to flash
|
110
|
-
params.serial = "?"
|
138
|
+
params.serial = ["?"]
|
111
139
|
params.boards = ["?"]
|
112
140
|
else:
|
113
|
-
|
114
|
-
if board_id == "":
|
115
|
-
params.boards.remove(board_id)
|
116
|
-
continue
|
117
|
-
if " " in board_id:
|
118
|
-
try:
|
119
|
-
info = find_stored_board(board_id)
|
120
|
-
if info:
|
121
|
-
log.info(f"Resolved board description: {info['board']}")
|
122
|
-
params.boards.remove(board_id)
|
123
|
-
params.boards.append(info["board"])
|
124
|
-
except Exception as e:
|
125
|
-
log.warning(f"unable to resolve board description: {e}")
|
141
|
+
resolve_board_ids(params)
|
126
142
|
|
127
143
|
# Ask for missing input if needed
|
128
|
-
params = ask_missing_params(params
|
144
|
+
params = ask_missing_params(params)
|
129
145
|
if not params: # Cancelled by user
|
130
146
|
return 2
|
131
147
|
# TODO: Just in time Download of firmware
|
@@ -135,27 +151,33 @@ def cli_flash_board(**kwargs) -> int:
|
|
135
151
|
if len(params.versions) > 1:
|
136
152
|
log.error(f"Only one version can be flashed at a time, not {params.versions}")
|
137
153
|
raise MPFlashError("Only one version can be flashed at a time")
|
138
|
-
# if len(params.boards) > 1:
|
139
|
-
# log.error(f"Only one board can be flashed at a time, not {params.boards}")
|
140
|
-
# raise MPFlashError("Only one board can be flashed at a time")
|
141
154
|
|
142
155
|
params.versions = [clean_version(v) for v in params.versions]
|
143
156
|
worklist: WorkList = []
|
144
157
|
# if serial port == auto and there are one or more specified/detected boards
|
145
|
-
if params.serial == "
|
146
|
-
|
158
|
+
if params.serial == ["*"] and params.boards:
|
159
|
+
if not all_boards:
|
160
|
+
log.trace("No boards detected yet, scanning for connected boards")
|
161
|
+
_, _, all_boards = connected_ports_boards(include=params.ports, ignore=params.ignore)
|
162
|
+
worklist = full_auto_worklist(
|
163
|
+
all_boards=all_boards,
|
164
|
+
version=params.versions[0],
|
165
|
+
fw_folder=params.fw_folder,
|
166
|
+
include=params.serial,
|
167
|
+
ignore=params.ignore,
|
168
|
+
)
|
147
169
|
elif params.versions[0] and params.boards[0] and params.serial:
|
148
|
-
# A
|
170
|
+
# A one or more serial port including the board / variant
|
149
171
|
worklist = manual_worklist(
|
150
|
-
params.
|
151
|
-
params.
|
152
|
-
params.
|
153
|
-
params.
|
172
|
+
params.serial[0],
|
173
|
+
board_id=params.boards[0],
|
174
|
+
version=params.versions[0],
|
175
|
+
fw_folder=params.fw_folder,
|
154
176
|
)
|
155
177
|
else:
|
156
178
|
# just this serial port on auto
|
157
179
|
worklist = single_auto_worklist(
|
158
|
-
|
180
|
+
serial=params.serial[0],
|
159
181
|
version=params.versions[0],
|
160
182
|
fw_folder=params.fw_folder,
|
161
183
|
)
|
@@ -172,3 +194,19 @@ def cli_flash_board(**kwargs) -> int:
|
|
172
194
|
else:
|
173
195
|
log.error("No boards were flashed")
|
174
196
|
return 1
|
197
|
+
|
198
|
+
|
199
|
+
def resolve_board_ids(params):
|
200
|
+
"""Resolve board descriptions to board_id, and remove empty strings from list of boards"""
|
201
|
+
for board_id in params.boards:
|
202
|
+
if board_id == "":
|
203
|
+
params.boards.remove(board_id)
|
204
|
+
continue
|
205
|
+
if " " in board_id:
|
206
|
+
try:
|
207
|
+
if info := find_known_board(board_id):
|
208
|
+
log.info(f"Resolved board description: {info.board_id}")
|
209
|
+
params.boards.remove(board_id)
|
210
|
+
params.boards.append(info.board_id)
|
211
|
+
except Exception as e:
|
212
|
+
log.warning(f"Unable to resolve board description: {e}")
|
mpflash/mpflash/cli_group.py
CHANGED
@@ -12,23 +12,26 @@ from .logger import make_quiet, set_loglevel
|
|
12
12
|
def cb_verbose(ctx, param, value):
|
13
13
|
"""Callback to set the log level to DEBUG if verbose is set"""
|
14
14
|
if value and not config.quiet:
|
15
|
-
set_loglevel("DEBUG")
|
16
15
|
config.verbose = True
|
16
|
+
if value > 1:
|
17
|
+
set_loglevel("TRACE")
|
18
|
+
else:
|
19
|
+
set_loglevel("DEBUG")
|
17
20
|
else:
|
18
21
|
set_loglevel("INFO")
|
19
22
|
config.verbose = False
|
20
23
|
return value
|
21
24
|
|
22
25
|
|
23
|
-
def
|
26
|
+
def cb_interactive(ctx, param, value):
|
24
27
|
if value:
|
25
|
-
config.
|
28
|
+
config.interactive = value
|
26
29
|
return value
|
27
30
|
|
28
31
|
|
29
|
-
def
|
32
|
+
def cb_test(ctx, param, value):
|
30
33
|
if value:
|
31
|
-
config.
|
34
|
+
config.tests = value
|
32
35
|
return value
|
33
36
|
|
34
37
|
|
@@ -64,21 +67,20 @@ def cb_quiet(ctx, param, value):
|
|
64
67
|
"-V",
|
65
68
|
"--verbose",
|
66
69
|
is_eager=True,
|
67
|
-
|
70
|
+
count=True,
|
68
71
|
help="Enables verbose mode.",
|
69
72
|
callback=cb_verbose,
|
70
73
|
)
|
71
74
|
@click.option(
|
72
|
-
"--
|
73
|
-
"-i",
|
75
|
+
"--test",
|
74
76
|
is_eager=True,
|
75
|
-
help="
|
76
|
-
callback=
|
77
|
+
help="test a specific feature",
|
78
|
+
callback=cb_test,
|
77
79
|
multiple=True,
|
78
80
|
default=[],
|
79
|
-
envvar="
|
81
|
+
envvar="MPFLASH_TEST",
|
80
82
|
show_default=True,
|
81
|
-
metavar="
|
83
|
+
metavar="TEST",
|
82
84
|
)
|
83
85
|
def cli(**kwargs):
|
84
86
|
"""mpflash - MicroPython Tool.
|
mpflash/mpflash/cli_list.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
import json
|
2
|
+
from typing import List
|
2
3
|
|
3
4
|
import rich_click as click
|
4
5
|
from rich import print
|
5
6
|
|
6
7
|
from .cli_group import cli
|
7
|
-
from .list import
|
8
|
+
from .list import show_mcus
|
8
9
|
from .logger import make_quiet
|
10
|
+
from .connected import list_mcus
|
9
11
|
|
10
12
|
|
11
13
|
@cli.command("list", help="List the connected MCU boards.")
|
@@ -18,21 +20,55 @@ from .logger import make_quiet
|
|
18
20
|
show_default=True,
|
19
21
|
help="""Output in json format""",
|
20
22
|
)
|
23
|
+
@click.option(
|
24
|
+
"--serial",
|
25
|
+
"--serial-port",
|
26
|
+
"-s",
|
27
|
+
"serial",
|
28
|
+
default=["*"],
|
29
|
+
multiple=True,
|
30
|
+
show_default=True,
|
31
|
+
help="Which serial port(s) to list. ",
|
32
|
+
metavar="SERIALPORT",
|
33
|
+
)
|
34
|
+
@click.option(
|
35
|
+
"--ignore",
|
36
|
+
"-i",
|
37
|
+
is_eager=True,
|
38
|
+
help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
|
39
|
+
multiple=True,
|
40
|
+
default=[],
|
41
|
+
envvar="MPFLASH_IGNORE",
|
42
|
+
show_default=True,
|
43
|
+
metavar="SERIALPORT",
|
44
|
+
)
|
45
|
+
@click.option(
|
46
|
+
"--bluetooth/--no-bluetooth",
|
47
|
+
"-b/-nb",
|
48
|
+
is_flag=True,
|
49
|
+
default=False,
|
50
|
+
show_default=True,
|
51
|
+
help="""Include bluetooth ports in the list""",
|
52
|
+
)
|
21
53
|
@click.option(
|
22
54
|
"--progress/--no-progress",
|
55
|
+
# "-p/-np", -p is already used for --port
|
23
56
|
"progress",
|
24
57
|
is_flag=True,
|
25
58
|
default=True,
|
26
59
|
show_default=True,
|
27
60
|
help="""Show progress""",
|
28
61
|
)
|
29
|
-
def cli_list_mcus(as_json: bool, progress: bool = True) -> int:
|
62
|
+
def cli_list_mcus(serial: List[str], ignore: List[str], bluetooth: bool, as_json: bool, progress: bool = True) -> int:
|
30
63
|
"""List the connected MCU boards, and output in a nice table or json."""
|
64
|
+
serial = list(serial)
|
65
|
+
ignore = list(ignore)
|
31
66
|
if as_json:
|
32
67
|
# avoid noise in json output
|
33
68
|
make_quiet()
|
69
|
+
# TODO? Ask user to select a serialport if [?] is given ?
|
34
70
|
|
35
|
-
conn_mcus = list_mcus()
|
71
|
+
conn_mcus = list_mcus(ignore=ignore, include=serial, bluetooth=bluetooth)
|
36
72
|
if as_json:
|
37
73
|
print(json.dumps([mcu.__dict__ for mcu in conn_mcus], indent=4))
|
38
74
|
progress = False
|
mpflash/mpflash/cli_main.py
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
|
3
3
|
# import rich_click as click
|
4
4
|
|
5
|
+
import os
|
6
|
+
|
5
7
|
import click
|
8
|
+
from loguru import logger as log
|
6
9
|
|
7
10
|
from .cli_download import cli_download
|
8
11
|
from .cli_flash import cli_flash_board
|
@@ -11,16 +14,24 @@ from .cli_list import cli_list_mcus
|
|
11
14
|
|
12
15
|
|
13
16
|
def mpflash():
|
14
|
-
cli.add_command(cli_flash_board)
|
15
17
|
cli.add_command(cli_list_mcus)
|
16
18
|
cli.add_command(cli_download)
|
19
|
+
cli.add_command(cli_flash_board)
|
20
|
+
|
17
21
|
# cli(auto_envvar_prefix="MPFLASH")
|
18
|
-
|
22
|
+
if False and os.environ.get("COMPUTERNAME") == "JOSVERL-S4":
|
23
|
+
# intentional less error suppression on dev machine
|
19
24
|
result = cli(standalone_mode=False)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
25
|
+
else:
|
26
|
+
try:
|
27
|
+
result = cli(standalone_mode=False)
|
28
|
+
exit(result)
|
29
|
+
except AttributeError as e:
|
30
|
+
log.error(f"Error: {e}")
|
31
|
+
exit(-1)
|
32
|
+
except click.exceptions.ClickException as e:
|
33
|
+
log.error(f"Error: {e}")
|
34
|
+
exit(-2)
|
24
35
|
|
25
36
|
|
26
37
|
if __name__ == "__main__":
|
mpflash/mpflash/common.py
CHANGED
@@ -1,11 +1,16 @@
|
|
1
|
+
import fnmatch
|
1
2
|
import os
|
2
|
-
import
|
3
|
-
from
|
3
|
+
import sys
|
4
|
+
from dataclasses import dataclass, field
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import List, Optional, Union
|
4
7
|
|
5
8
|
from github import Auth, Github
|
6
|
-
from
|
9
|
+
from serial.tools import list_ports
|
10
|
+
from serial.tools.list_ports_common import ListPortInfo
|
11
|
+
|
12
|
+
from .logger import log
|
7
13
|
|
8
|
-
from mpflash.errors import MPFlashError
|
9
14
|
# from mpflash.mpremoteboard import MPRemoteBoard
|
10
15
|
|
11
16
|
PORT_FWTYPES = {
|
@@ -14,6 +19,7 @@ PORT_FWTYPES = {
|
|
14
19
|
"esp8266": [".bin"],
|
15
20
|
"rp2": [".uf2"],
|
16
21
|
"samd": [".uf2"],
|
22
|
+
# below this not yet implemented / tested
|
17
23
|
"mimxrt": [".hex"],
|
18
24
|
"nrf": [".uf2"],
|
19
25
|
"renesas-ra": [".hex"],
|
@@ -28,11 +34,118 @@ PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
|
|
28
34
|
GH_CLIENT = Github(auth=Auth.Token(PAT))
|
29
35
|
|
30
36
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
@dataclass
|
38
|
+
class FWInfo:
|
39
|
+
"""
|
40
|
+
Downloaded Firmware information
|
41
|
+
is somewhat related to the BOARD class in the mpboard_id module
|
42
|
+
"""
|
43
|
+
|
44
|
+
port: str # MicroPython port
|
45
|
+
board: str # MicroPython board
|
46
|
+
filename: str = field(default="") # relative filename of the firmware image
|
47
|
+
firmware: str = field(default="") # url or path to original firmware image
|
48
|
+
variant: str = field(default="") # MicroPython variant
|
49
|
+
preview: bool = field(default=False) # True if the firmware is a preview version
|
50
|
+
version: str = field(default="") # MicroPython version
|
51
|
+
url: str = field(default="") # url to the firmware image download folder
|
52
|
+
build: str = field(default="0") # The build = number of commits since the last release
|
53
|
+
ext: str = field(default="") # the file extension of the firmware
|
54
|
+
family: str = field(default="micropython") # The family of the firmware
|
55
|
+
custom: bool = field(default=False) # True if the firmware is a custom build
|
56
|
+
description: str = field(default="") # Description used by this firmware (custom only)
|
57
|
+
|
58
|
+
def to_dict(self) -> dict:
|
59
|
+
"""Convert the object to a dictionary"""
|
60
|
+
return self.__dict__
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def from_dict(cls, data: dict) -> "FWInfo":
|
64
|
+
"""Create a FWInfo object from a dictionary"""
|
65
|
+
# add missing keys
|
66
|
+
if "ext" not in data:
|
67
|
+
data["ext"] = Path(data["firmware"]).suffix
|
68
|
+
if "family" not in data:
|
69
|
+
data["family"] = "micropython"
|
70
|
+
return cls(**data)
|
71
|
+
|
72
|
+
|
73
|
+
@dataclass
|
74
|
+
class Params:
|
75
|
+
"""Common parameters for downloading and flashing firmware"""
|
76
|
+
|
77
|
+
ports: List[str] = field(default_factory=list)
|
78
|
+
boards: List[str] = field(default_factory=list)
|
79
|
+
versions: List[str] = field(default_factory=list)
|
80
|
+
fw_folder: Path = Path()
|
81
|
+
serial: List[str] = field(default_factory=list)
|
82
|
+
ignore: List[str] = field(default_factory=list)
|
83
|
+
|
84
|
+
|
85
|
+
@dataclass
|
86
|
+
class DownloadParams(Params):
|
87
|
+
"""Parameters for downloading firmware"""
|
88
|
+
|
89
|
+
clean: bool = False
|
90
|
+
force: bool = False
|
91
|
+
|
92
|
+
|
93
|
+
@dataclass
|
94
|
+
class FlashParams(Params):
|
95
|
+
"""Parameters for flashing a board"""
|
96
|
+
|
97
|
+
erase: bool = True
|
98
|
+
bootloader: bool = True
|
99
|
+
cpu: str = ""
|
100
|
+
|
101
|
+
|
102
|
+
ParamType = Union[DownloadParams, FlashParams]
|
103
|
+
|
104
|
+
|
105
|
+
def filtered_comports(
|
106
|
+
ignore: Optional[List[str]] = None,
|
107
|
+
include: Optional[List[str]] = None,
|
108
|
+
bluetooth: bool = False,
|
109
|
+
) -> List[ListPortInfo]: # sourcery skip: assign-if-exp
|
110
|
+
"""
|
111
|
+
Get a list of filtered comports.
|
112
|
+
"""
|
113
|
+
if not ignore:
|
114
|
+
ignore = []
|
115
|
+
elif not isinstance(ignore, list): # type: ignore
|
116
|
+
ignore = list(ignore)
|
117
|
+
if not include:
|
118
|
+
include = ["*"]
|
119
|
+
elif not isinstance(include, list): # type: ignore
|
120
|
+
include = list(include)
|
121
|
+
|
122
|
+
# remove ports that are to be ignored
|
123
|
+
log.trace(f"{include=}, {ignore=}, {bluetooth=}")
|
124
|
+
comports = [p for p in list_ports.comports() if not any(fnmatch.fnmatch(p.device, i) for i in ignore)]
|
125
|
+
log.trace(f"comports: {[p.device for p in comports]}")
|
126
|
+
# remove bluetooth ports
|
127
|
+
|
128
|
+
if include != ["*"]:
|
129
|
+
# if there are explicit ports to include, add them to the list
|
130
|
+
explicit = [p for p in list_ports.comports() if any(fnmatch.fnmatch(p.device, i) for i in include)]
|
131
|
+
log.trace(f"explicit: {[p.device for p in explicit]}")
|
132
|
+
if ignore == []:
|
133
|
+
# if nothing to ignore, just use the explicit list as a sinple sane default
|
134
|
+
comports = explicit
|
135
|
+
else:
|
136
|
+
# if there are ports to ignore, add the explicit list to the filtered list
|
137
|
+
comports = list(set(explicit) | set(comports))
|
138
|
+
if not bluetooth:
|
139
|
+
# filter out bluetooth ports
|
140
|
+
comports = [p for p in comports if "bluetooth" not in p.description.lower()]
|
141
|
+
comports = [p for p in comports if "BTHENUM" not in p.hwid]
|
142
|
+
if sys.platform == "darwin":
|
143
|
+
comports = [p for p in comports if ".Bluetooth" not in p.device]
|
144
|
+
log.trace(f"no Bluetooth: {[p.device for p in comports]}")
|
145
|
+
log.debug(f"filtered_comports: {[p.device for p in comports]}")
|
146
|
+
# sort
|
147
|
+
if sys.platform == "win32":
|
148
|
+
# Windows sort of comports by number - but fallback to device name
|
149
|
+
return sorted(comports, key=lambda x: int(x.device.split()[0][3:]) if x.device.split()[0][3:].isdigit() else x)
|
150
|
+
# sort by device name
|
151
|
+
return sorted(comports, key=lambda x: x.device)
|
mpflash/mpflash/config.py
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
from typing import List, Tuple
|
2
|
+
|
3
|
+
from rich import print
|
4
|
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
5
|
+
from rich.table import Column
|
6
|
+
|
7
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
8
|
+
|
9
|
+
from .common import filtered_comports
|
10
|
+
|
11
|
+
|
12
|
+
def connected_ports_boards(
|
13
|
+
*, include: List[str], ignore: List[str]
|
14
|
+
) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
|
15
|
+
"""
|
16
|
+
Returns a tuple containing lists of unique ports and boards from the connected MCUs.
|
17
|
+
Boards that are physically connected, but give no tangible response are ignored.
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
A tuple containing three lists:
|
21
|
+
- A list of unique ports where MCUs are connected.
|
22
|
+
- A list of unique board names of the connected MCUs.
|
23
|
+
- A list of MPRemoteBoard instances of the connected MCUs.
|
24
|
+
"""
|
25
|
+
mpr_boards = [b for b in list_mcus(include=include, ignore=ignore) if b.connected]
|
26
|
+
ports = list({b.port for b in mpr_boards})
|
27
|
+
boards = list({b.board for b in mpr_boards})
|
28
|
+
return (ports, boards, mpr_boards)
|
29
|
+
|
30
|
+
|
31
|
+
# #########################################################################################################
|
32
|
+
rp_spinner = SpinnerColumn(finished_text="✅")
|
33
|
+
rp_text = TextColumn("{task.description} {task.fields[device]}", table_column=Column())
|
34
|
+
rp_bar = BarColumn(bar_width=None, table_column=Column())
|
35
|
+
|
36
|
+
|
37
|
+
def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False):
|
38
|
+
"""
|
39
|
+
Retrieves information about connected microcontroller boards.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
List[MPRemoteBoard]: A list of MPRemoteBoard instances with board information.
|
43
|
+
Raises:
|
44
|
+
ConnectionError: If there is an error connecting to a board.
|
45
|
+
"""
|
46
|
+
# conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
|
47
|
+
|
48
|
+
comports = filtered_comports(
|
49
|
+
ignore=ignore,
|
50
|
+
include=include,
|
51
|
+
bluetooth=bluetooth,
|
52
|
+
)
|
53
|
+
conn_mcus = [MPRemoteBoard(c.device) for c in comports]
|
54
|
+
|
55
|
+
# a lot of boilerplate to show a progress bar with the comport currently scanned
|
56
|
+
# low update rate to facilitate screen readers/narration
|
57
|
+
with Progress(rp_spinner, rp_text, rp_bar, TimeElapsedColumn(), refresh_per_second=2) as progress:
|
58
|
+
tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
|
59
|
+
progress.tasks[tsk_scan].fields["device"] = "..."
|
60
|
+
progress.tasks[tsk_scan].visible = True
|
61
|
+
progress.start_task(tsk_scan)
|
62
|
+
try:
|
63
|
+
for mcu in conn_mcus:
|
64
|
+
progress.update(tsk_scan, device=mcu.serialport.replace("/dev/", ""))
|
65
|
+
try:
|
66
|
+
mcu.get_mcu_info()
|
67
|
+
except ConnectionError as e:
|
68
|
+
print(f"Error: {e}")
|
69
|
+
continue
|
70
|
+
finally:
|
71
|
+
# transient
|
72
|
+
progress.stop_task(tsk_scan)
|
73
|
+
progress.tasks[tsk_scan].visible = False
|
74
|
+
return conn_mcus
|