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/download.py
CHANGED
@@ -18,9 +18,11 @@ from bs4 import BeautifulSoup
|
|
18
18
|
from loguru import logger as log
|
19
19
|
from rich.progress import track
|
20
20
|
|
21
|
-
from mpflash.common import PORT_FWTYPES
|
21
|
+
from mpflash.common import PORT_FWTYPES, FWInfo
|
22
22
|
from mpflash.errors import MPFlashError
|
23
|
+
from mpflash.mpboard_id import get_known_ports
|
23
24
|
|
25
|
+
# avoid conflict with the ujson used by MicroPython
|
24
26
|
jsonlines.ujson = None # type: ignore
|
25
27
|
# #########################################################################################################
|
26
28
|
|
@@ -50,6 +52,10 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
|
|
50
52
|
|
51
53
|
Args:
|
52
54
|
page_url (str): The url of the page to get the board urls from.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
List[Dict[str, str]]: A list of dictionaries containing the board name and url.
|
58
|
+
|
53
59
|
"""
|
54
60
|
downloads_html = get_page(page_url)
|
55
61
|
soup = BeautifulSoup(downloads_html, "html.parser")
|
@@ -88,14 +94,10 @@ def board_firmware_urls(board_url: str, base_url: str, ext: str) -> List[str]:
|
|
88
94
|
return links
|
89
95
|
|
90
96
|
|
91
|
-
# type alias for the firmware info
|
92
|
-
FirmwareInfo = Dict[str, str]
|
93
|
-
|
94
|
-
|
95
97
|
# boards we are interested in ( this avoids getting a lot of boards we don't care about)
|
96
98
|
# The first run takes ~60 seconds to run for 4 ports , all boards
|
97
99
|
# so it makes sense to cache the results and skip boards as soon as possible
|
98
|
-
def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[
|
100
|
+
def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]:
|
99
101
|
"""
|
100
102
|
Retrieves a list of firmware information for the specified ports and boards.
|
101
103
|
|
@@ -105,57 +107,68 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[Firmwar
|
|
105
107
|
clean (bool): A flag indicating whether to perform a clean retrieval.
|
106
108
|
|
107
109
|
Returns:
|
108
|
-
List[
|
110
|
+
List[FWInfo]: A list of firmware information for the specified ports and boards.
|
109
111
|
|
110
112
|
"""
|
111
|
-
board_urls: List[
|
113
|
+
board_urls: List[FWInfo] = []
|
114
|
+
if ports is None:
|
115
|
+
ports = get_known_ports()
|
112
116
|
for port in ports:
|
113
117
|
download_page_url = f"{MICROPYTHON_ORG_URL}download/?port={port}"
|
114
|
-
|
118
|
+
urls = get_board_urls(download_page_url)
|
115
119
|
# filter out boards we don't care about
|
116
|
-
|
120
|
+
urls = [board for board in urls if board["board"] in boards]
|
117
121
|
# add the port to the board urls
|
118
|
-
for board in
|
122
|
+
for board in urls:
|
119
123
|
board["port"] = port
|
120
124
|
|
121
|
-
for board in track(
|
125
|
+
for board in track(urls, description=f"Checking {port} download pages", transient=True, refresh_per_second=2):
|
122
126
|
# add a board to the list for each firmware found
|
123
|
-
|
127
|
+
firmware_urls: List[str] = []
|
124
128
|
for ext in PORT_FWTYPES[port]:
|
125
|
-
|
126
|
-
|
127
|
-
for _url in firmwares:
|
129
|
+
firmware_urls += board_firmware_urls(board["url"], MICROPYTHON_ORG_URL, ext)
|
130
|
+
for _url in firmware_urls:
|
128
131
|
board["firmware"] = _url
|
129
|
-
board["preview"] = "preview" in _url # type: ignore
|
130
|
-
if ver_match := re.search(RE_VERSION_PREVIEW, _url):
|
131
|
-
board["version"] = ver_match[1]
|
132
|
-
else:
|
133
|
-
board["version"] = ""
|
134
|
-
if "preview." in board["version"]:
|
135
|
-
board["build"] = board["version"].split("preview.")[-1]
|
136
|
-
else:
|
137
|
-
board["build"] = "0"
|
138
132
|
fname = Path(board["firmware"]).name
|
139
133
|
if clean:
|
140
134
|
# remove date from firmware name
|
141
135
|
fname = re.sub(RE_DATE, "-", fname)
|
142
136
|
# remove hash from firmware name
|
143
137
|
fname = re.sub(RE_HASH, ".", fname)
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
138
|
+
fw_info = FWInfo(
|
139
|
+
filename=fname,
|
140
|
+
port=port,
|
141
|
+
board=board["board"],
|
142
|
+
preview="preview" in _url,
|
143
|
+
firmware=_url,
|
144
|
+
version="",
|
145
|
+
)
|
146
|
+
# board["firmware"] = _url
|
147
|
+
# board["preview"] = "preview" in _url # type: ignore
|
148
|
+
if ver_match := re.search(RE_VERSION_PREVIEW, _url):
|
149
|
+
fw_info.version = ver_match[1]
|
150
|
+
# else:
|
151
|
+
# board.$1= ""
|
152
|
+
if "preview." in fw_info.version:
|
153
|
+
fw_info.build = fw_info.version.split("preview.")[-1]
|
154
|
+
else:
|
155
|
+
fw_info.build = "0"
|
156
|
+
|
157
|
+
fw_info.ext = Path(fw_info.firmware).suffix
|
158
|
+
fw_info.variant = fw_info.filename.split("-v")[0] if "-v" in fw_info.filename else ""
|
159
|
+
|
160
|
+
board_urls.append(fw_info)
|
148
161
|
return board_urls
|
149
162
|
|
150
163
|
|
151
|
-
def key_fw_ver_pre_ext_bld(x:
|
164
|
+
def key_fw_ver_pre_ext_bld(x: FWInfo):
|
152
165
|
"sorting key for the retrieved board urls"
|
153
|
-
return x
|
166
|
+
return x.variant, x.version, x.preview, x.ext, x.build
|
154
167
|
|
155
168
|
|
156
|
-
def key_fw_var_pre_ext(x:
|
169
|
+
def key_fw_var_pre_ext(x: FWInfo):
|
157
170
|
"Grouping key for the retrieved board urls"
|
158
|
-
return x
|
171
|
+
return x.variant, x.preview, x.ext
|
159
172
|
|
160
173
|
|
161
174
|
def download_firmwares(
|
@@ -170,10 +183,11 @@ def download_firmwares(
|
|
170
183
|
skipped = downloaded = 0
|
171
184
|
if versions is None:
|
172
185
|
versions = []
|
186
|
+
|
173
187
|
unique_boards = get_firmware_list(ports, boards, versions, clean)
|
174
188
|
|
175
189
|
for b in unique_boards:
|
176
|
-
log.debug(b
|
190
|
+
log.debug(b.filename)
|
177
191
|
# relevant
|
178
192
|
|
179
193
|
log.info(f"Found {len(unique_boards)} relevant unique firmwares")
|
@@ -182,23 +196,23 @@ def download_firmwares(
|
|
182
196
|
|
183
197
|
with jsonlines.open(firmware_folder / "firmware.jsonl", "a") as writer:
|
184
198
|
for board in unique_boards:
|
185
|
-
filename = firmware_folder / board
|
199
|
+
filename = firmware_folder / board.port / board.filename
|
186
200
|
filename.parent.mkdir(exist_ok=True)
|
187
201
|
if filename.exists() and not force:
|
188
202
|
skipped += 1
|
189
203
|
log.debug(f" {filename} already exists, skip download")
|
190
204
|
continue
|
191
|
-
log.info(f"Downloading {board
|
205
|
+
log.info(f"Downloading {board.firmware}")
|
192
206
|
log.info(f" to {filename}")
|
193
207
|
try:
|
194
|
-
r = requests.get(board
|
208
|
+
r = requests.get(board.firmware, allow_redirects=True)
|
195
209
|
with open(filename, "wb") as fw:
|
196
210
|
fw.write(r.content)
|
197
|
-
board
|
211
|
+
board.filename = str(filename.relative_to(firmware_folder))
|
198
212
|
except requests.RequestException as e:
|
199
213
|
log.exception(e)
|
200
214
|
continue
|
201
|
-
writer.write(board)
|
215
|
+
writer.write(board.to_dict())
|
202
216
|
downloaded += 1
|
203
217
|
log.info(f"Downloaded {downloaded} firmwares, skipped {skipped} existing files.")
|
204
218
|
return downloaded + skipped
|
@@ -215,7 +229,7 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
|
|
215
229
|
clean : A flag indicating whether to perform a clean check.
|
216
230
|
|
217
231
|
Returns:
|
218
|
-
List[
|
232
|
+
List[FWInfo]: A list of unique firmware information.
|
219
233
|
|
220
234
|
"""
|
221
235
|
|
@@ -227,12 +241,12 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
|
|
227
241
|
relevant = [
|
228
242
|
board
|
229
243
|
for board in board_urls
|
230
|
-
if board
|
244
|
+
if board.board in boards and (board.version in versions or board.preview and preview)
|
231
245
|
# and b["port"] in ["esp32", "rp2"]
|
232
246
|
]
|
233
247
|
log.debug(f"Matching firmwares: {len(relevant)}")
|
234
248
|
# select the unique boards
|
235
|
-
unique_boards: List[
|
249
|
+
unique_boards: List[FWInfo] = []
|
236
250
|
for _, g in itertools.groupby(relevant, key=key_fw_var_pre_ext):
|
237
251
|
# list is aleady sorted by build so we can just get the last item
|
238
252
|
sub_list = list(g)
|
mpflash/mpflash/downloaded.py
CHANGED
@@ -16,11 +16,11 @@ def downloaded_firmwares(fw_folder: Path) -> List[FWInfo]:
|
|
16
16
|
firmwares: List[FWInfo] = []
|
17
17
|
try:
|
18
18
|
with jsonlines.open(fw_folder / "firmware.jsonl") as reader:
|
19
|
-
firmwares.
|
19
|
+
firmwares = [FWInfo.from_dict(item) for item in reader]
|
20
20
|
except FileNotFoundError:
|
21
21
|
log.error(f"No firmware.jsonl found in {fw_folder}")
|
22
22
|
# sort by filename
|
23
|
-
firmwares.sort(key=lambda x: x
|
23
|
+
firmwares.sort(key=lambda x: x.filename)
|
24
24
|
return firmwares
|
25
25
|
|
26
26
|
|
@@ -68,7 +68,7 @@ def find_downloaded_firmware(
|
|
68
68
|
)
|
69
69
|
# hope we have a match now for the board
|
70
70
|
# sort by filename
|
71
|
-
fw_list.sort(key=lambda x: x
|
71
|
+
fw_list.sort(key=lambda x: x.filename)
|
72
72
|
return fw_list
|
73
73
|
|
74
74
|
|
@@ -84,23 +84,23 @@ def filter_downloaded_fwlist(
|
|
84
84
|
"""Filter the downloaded firmware list based on the provided parameters"""
|
85
85
|
if "preview" in version:
|
86
86
|
# never get a preview for an older version
|
87
|
-
fw_list = [fw for fw in fw_list if fw
|
87
|
+
fw_list = [fw for fw in fw_list if fw.preview]
|
88
88
|
else:
|
89
|
-
fw_list = [fw for fw in fw_list if fw
|
89
|
+
fw_list = [fw for fw in fw_list if fw.version == version]
|
90
90
|
|
91
91
|
# filter by port
|
92
92
|
if port:
|
93
|
-
fw_list = [fw for fw in fw_list if fw
|
93
|
+
fw_list = [fw for fw in fw_list if fw.port == port]
|
94
94
|
|
95
95
|
if board_id:
|
96
96
|
if variants:
|
97
97
|
# any variant of this board_id
|
98
|
-
fw_list = [fw for fw in fw_list if fw
|
98
|
+
fw_list = [fw for fw in fw_list if fw.board == board_id]
|
99
99
|
else:
|
100
100
|
# the firmware variant should match exactly the board_id
|
101
|
-
fw_list = [fw for fw in fw_list if fw
|
101
|
+
fw_list = [fw for fw in fw_list if fw.variant == board_id]
|
102
102
|
if selector and port in selector:
|
103
|
-
fw_list = [fw for fw in fw_list if fw
|
103
|
+
fw_list = [fw for fw in fw_list if fw.filename.endswith(selector[port])]
|
104
104
|
return fw_list
|
105
105
|
|
106
106
|
|
mpflash/mpflash/flash.py
CHANGED
@@ -23,11 +23,11 @@ def flash_list(
|
|
23
23
|
"""Flash a list of boards with the specified firmware."""
|
24
24
|
flashed = []
|
25
25
|
for mcu, fw_info in todo:
|
26
|
-
fw_file = fw_folder / fw_info
|
26
|
+
fw_file = fw_folder / fw_info.filename
|
27
27
|
if not fw_file.exists():
|
28
28
|
log.error(f"File {fw_file} does not exist, skipping {mcu.board} on {mcu.serialport}")
|
29
29
|
continue
|
30
|
-
log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info
|
30
|
+
log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info.version}")
|
31
31
|
updated = None
|
32
32
|
# try:
|
33
33
|
if mcu.port in [port for port, exts in PORT_FWTYPES.items() if ".uf2" in exts] and fw_file.suffix == ".uf2":
|
mpflash/mpflash/flash_esp.py
CHANGED
@@ -10,7 +10,7 @@ from typing import List, Optional
|
|
10
10
|
import esptool
|
11
11
|
from loguru import logger as log
|
12
12
|
|
13
|
-
from mpflash.mpboard_id import
|
13
|
+
from mpflash.mpboard_id import find_known_board
|
14
14
|
from mpflash.mpremoteboard import MPRemoteBoard
|
15
15
|
|
16
16
|
|
@@ -22,7 +22,7 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
|
|
22
22
|
log.info(f"Flashing {fw_file} on {mcu.board} on {mcu.serialport}")
|
23
23
|
if not mcu.cpu:
|
24
24
|
# Lookup CPU based on the board name
|
25
|
-
mcu.cpu =
|
25
|
+
mcu.cpu = find_known_board(mcu.board).cpu
|
26
26
|
|
27
27
|
cmds: List[List[str]] = []
|
28
28
|
if erase:
|
mpflash/mpflash/flash_uf2.py
CHANGED
@@ -14,7 +14,9 @@ from rich.progress import track
|
|
14
14
|
from mpflash.mpremoteboard import MPRemoteBoard
|
15
15
|
|
16
16
|
from .common import PORT_FWTYPES
|
17
|
-
from .
|
17
|
+
from .config import config
|
18
|
+
from .flash_uf2_linux import dismount_uf2_linux, wait_for_UF2_linux
|
19
|
+
from .flash_uf2_macos import wait_for_UF2_macos
|
18
20
|
from .flash_uf2_windows import wait_for_UF2_windows
|
19
21
|
|
20
22
|
|
@@ -27,9 +29,9 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
27
29
|
- copy the firmware file to the drive
|
28
30
|
- wait for the device to restart (5s)
|
29
31
|
|
30
|
-
for
|
31
|
-
|
32
|
-
|
32
|
+
for Linux - to support headless operation ( GH Actions ) :
|
33
|
+
pmount and pumount are used to mount and unmount the drive
|
34
|
+
as this is not done automatically by the OS in headless mode.
|
33
35
|
"""
|
34
36
|
if ".uf2" not in PORT_FWTYPES[mcu.port]:
|
35
37
|
log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
|
@@ -41,9 +43,15 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
41
43
|
destination = wait_for_UF2_linux()
|
42
44
|
elif sys.platform == "win32":
|
43
45
|
destination = wait_for_UF2_windows()
|
46
|
+
elif sys.platform == "darwin":
|
47
|
+
log.warning(f"OS {sys.platform} not tested/supported")
|
48
|
+
# TODO: test which of the options is best
|
49
|
+
if "macos_uf2" in config.tests:
|
50
|
+
destination = wait_for_UF2_macos()
|
51
|
+
else:
|
52
|
+
destination = wait_for_UF2_linux()
|
44
53
|
else:
|
45
54
|
log.warning(f"OS {sys.platform} not tested/supported")
|
46
|
-
destination = wait_for_UF2_linux()
|
47
55
|
return None
|
48
56
|
|
49
57
|
if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
|
@@ -54,8 +62,8 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
54
62
|
log.info(f"Copying {fw_file} to {destination}.")
|
55
63
|
shutil.copy(fw_file, destination)
|
56
64
|
log.success("Done copying, resetting the board and wait for it to restart")
|
57
|
-
if sys.platform in ["linux"
|
58
|
-
|
59
|
-
for _ in track(range(5 + 2), description="Waiting for the board to restart", transient=True):
|
65
|
+
if sys.platform in ["linux"]:
|
66
|
+
dismount_uf2_linux()
|
67
|
+
for _ in track(range(5 + 2), description="Waiting for the board to restart", transient=True, refresh_per_second=2):
|
60
68
|
time.sleep(1) # 5 secs to short on linux
|
61
69
|
return mcu
|
@@ -13,28 +13,15 @@ from loguru import logger as log
|
|
13
13
|
from rich.progress import track
|
14
14
|
|
15
15
|
from .flash_uf2_boardid import get_board_id
|
16
|
+
from .uf2disk import UF2Disk
|
16
17
|
|
17
18
|
glb_dismount_me: List[UF2Disk] = []
|
18
19
|
|
19
20
|
|
20
|
-
class UF2Disk:
|
21
|
-
"""Info to support mounting and unmounting of UF2 drives on linux"""
|
22
|
-
|
23
|
-
device_path: str
|
24
|
-
label: str
|
25
|
-
mountpoint: str
|
26
|
-
|
27
|
-
def __repr__(self):
|
28
|
-
return repr(self.__dict__)
|
29
|
-
|
30
|
-
|
31
21
|
def get_uf2_drives():
|
32
22
|
"""
|
33
23
|
Get a list of all the (un)mounted UF2 drives
|
34
24
|
"""
|
35
|
-
if sys.platform != "linux":
|
36
|
-
log.error("pumount only works on Linux")
|
37
|
-
return
|
38
25
|
# import blkinfo only on linux
|
39
26
|
from blkinfo import BlkDiskInfo
|
40
27
|
|
@@ -101,7 +88,7 @@ def pumount(disk: UF2Disk):
|
|
101
88
|
log.warning(f"{disk.label} already dismounted")
|
102
89
|
|
103
90
|
|
104
|
-
def
|
91
|
+
def dismount_uf2_linux():
|
105
92
|
global glb_dismount_me
|
106
93
|
for disk in glb_dismount_me:
|
107
94
|
pumount(disk)
|
@@ -113,7 +100,9 @@ def wait_for_UF2_linux(s_max: int = 10):
|
|
113
100
|
wait = 10
|
114
101
|
uf2_drives = []
|
115
102
|
# while not destination and wait > 0:
|
116
|
-
for _ in track(
|
103
|
+
for _ in track(
|
104
|
+
range(s_max), description="Waiting for mcu to mount as a drive", transient=True, refresh_per_second=2
|
105
|
+
):
|
117
106
|
# log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
118
107
|
uf2_drives += list(get_uf2_drives())
|
119
108
|
for drive in get_uf2_drives():
|
@@ -0,0 +1,78 @@
|
|
1
|
+
""" Flashing UF2 based MCU on macos"""
|
2
|
+
|
3
|
+
# sourcery skip: snake-case-functions
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
import sys
|
7
|
+
import time
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
from loguru import logger as log
|
11
|
+
from rich.progress import track
|
12
|
+
|
13
|
+
from .flash_uf2_boardid import get_board_id
|
14
|
+
from .uf2disk import UF2Disk
|
15
|
+
|
16
|
+
|
17
|
+
def get_uf2_drives():
|
18
|
+
"""
|
19
|
+
Get a list of all the (un)mounted UF2 drives
|
20
|
+
"""
|
21
|
+
if sys.platform != "linux":
|
22
|
+
log.error("pumount only works on Linux")
|
23
|
+
return
|
24
|
+
# import blkinfo only on linux
|
25
|
+
from blkinfo import BlkDiskInfo
|
26
|
+
|
27
|
+
myblkd = BlkDiskInfo()
|
28
|
+
filters = {
|
29
|
+
"tran": "usb",
|
30
|
+
}
|
31
|
+
usb_disks = myblkd.get_disks(filters)
|
32
|
+
for disk in usb_disks:
|
33
|
+
if disk["fstype"] == "vfat":
|
34
|
+
uf2_part = disk
|
35
|
+
# unpartioned usb disk or partition (e.g. /dev/sdb )
|
36
|
+
# SEEED WIO Terminal is unpartioned
|
37
|
+
# print( json.dumps(uf2_part, indent=4))
|
38
|
+
uf2 = UF2Disk()
|
39
|
+
uf2.device_path = "/dev/" + uf2_part["name"]
|
40
|
+
uf2.label = uf2_part["label"]
|
41
|
+
uf2.mountpoint = uf2_part["mountpoint"]
|
42
|
+
yield uf2
|
43
|
+
elif disk["type"] == "disk" and disk.get("children") and len(disk.get("children")) > 0:
|
44
|
+
if disk.get("children")[0]["type"] == "part" and disk.get("children")[0]["fstype"] == "vfat":
|
45
|
+
uf2_part = disk.get("children")[0]
|
46
|
+
# print( json.dumps(uf2_part, indent=4))
|
47
|
+
uf2 = UF2Disk()
|
48
|
+
uf2.device_path = "/dev/" + uf2_part["name"]
|
49
|
+
uf2.label = uf2_part["label"]
|
50
|
+
uf2.mountpoint = uf2_part["mountpoint"]
|
51
|
+
yield uf2
|
52
|
+
|
53
|
+
|
54
|
+
def wait_for_UF2_macos(s_max: int = 10):
|
55
|
+
destination = ""
|
56
|
+
wait = 10
|
57
|
+
uf2_drives = []
|
58
|
+
# while not destination and wait > 0:
|
59
|
+
for _ in track(
|
60
|
+
range(s_max), description="Waiting for mcu to mount as a drive", transient=True, refresh_per_second=2
|
61
|
+
):
|
62
|
+
# log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
63
|
+
uf2_drives += list(get_uf2_drives())
|
64
|
+
for drive in get_uf2_drives():
|
65
|
+
time.sleep(1)
|
66
|
+
try:
|
67
|
+
if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
|
68
|
+
board_id = get_board_id(Path(drive.mountpoint)) # type: ignore
|
69
|
+
destination = Path(drive.mountpoint)
|
70
|
+
break
|
71
|
+
except PermissionError:
|
72
|
+
log.debug(f"Permission error on {drive.mountpoint}")
|
73
|
+
continue
|
74
|
+
if destination:
|
75
|
+
break
|
76
|
+
time.sleep(1)
|
77
|
+
wait -= 1
|
78
|
+
return destination
|
@@ -17,7 +17,7 @@ def wait_for_UF2_windows(s_max: int = 10):
|
|
17
17
|
if s_max < 1:
|
18
18
|
s_max = 10
|
19
19
|
destination = ""
|
20
|
-
for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True):
|
20
|
+
for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True,refresh_per_second=2):
|
21
21
|
# log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
|
22
22
|
drives = [drive.device for drive in psutil.disk_partitions()]
|
23
23
|
for drive in drives:
|
mpflash/mpflash/list.py
CHANGED
@@ -1,89 +1,89 @@
|
|
1
1
|
from typing import List
|
2
2
|
|
3
|
-
from rich import
|
4
|
-
from rich.
|
5
|
-
from rich.table import Column, Table
|
3
|
+
from rich.progress import track
|
4
|
+
from rich.table import Table
|
6
5
|
|
7
6
|
from mpflash.mpremoteboard import MPRemoteBoard
|
8
7
|
from mpflash.vendor.versions import clean_version
|
9
8
|
|
10
|
-
from .config import config
|
11
9
|
from .logger import console
|
12
10
|
|
13
|
-
rp_spinner = SpinnerColumn(finished_text="✅")
|
14
|
-
rp_text = TextColumn("{task.description} {task.fields[device]}", table_column=Column())
|
15
|
-
rp_bar = BarColumn(bar_width=None, table_column=Column())
|
16
11
|
|
12
|
+
def show_mcus(
|
13
|
+
conn_mcus: List[MPRemoteBoard],
|
14
|
+
title: str = "Connected boards",
|
15
|
+
refresh: bool = True,
|
16
|
+
):
|
17
|
+
console.print(mcu_table(conn_mcus, title, refresh))
|
17
18
|
|
18
|
-
def list_mcus(bluetooth: bool = False):
|
19
|
-
"""
|
20
|
-
Retrieves information about connected microcontroller boards.
|
21
|
-
|
22
|
-
Returns:
|
23
|
-
List[MPRemoteBoard]: A list of MPRemoteBoard instances with board information.
|
24
|
-
Raises:
|
25
|
-
ConnectionError: If there is an error connecting to a board.
|
26
|
-
"""
|
27
|
-
conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
|
28
19
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
progress.start_task(tsk_scan)
|
35
|
-
try:
|
36
|
-
for mcu in conn_mcus:
|
37
|
-
progress.update(tsk_scan, device=mcu.serialport.replace("/dev/", ""))
|
38
|
-
try:
|
39
|
-
mcu.get_mcu_info()
|
40
|
-
except ConnectionError as e:
|
41
|
-
print(f"Error: {e}")
|
42
|
-
continue
|
43
|
-
finally:
|
44
|
-
# transient
|
45
|
-
progress.stop_task(tsk_scan)
|
46
|
-
progress.tasks[tsk_scan].visible = False
|
47
|
-
return conn_mcus
|
20
|
+
def abbrv_family(family: str, is_wide: bool) -> str:
|
21
|
+
if not is_wide:
|
22
|
+
ABRV = {"micropython": "upy", "circuitpython": "cpy", "unknown": "?"}
|
23
|
+
return ABRV.get(family, family[:4])
|
24
|
+
return family
|
48
25
|
|
49
26
|
|
50
|
-
def
|
27
|
+
def mcu_table(
|
51
28
|
conn_mcus: List[MPRemoteBoard],
|
52
29
|
title: str = "Connected boards",
|
53
30
|
refresh: bool = True,
|
54
|
-
):
|
55
|
-
"""
|
31
|
+
):
|
32
|
+
"""
|
33
|
+
builds a rich table with the connected boards information
|
34
|
+
The columns of the table are adjusted to the terminal width
|
35
|
+
the columns are :
|
36
|
+
Narrow Wide
|
37
|
+
- Serial Yes Yes
|
38
|
+
- Family abbrv. Yes
|
39
|
+
- Port - yes
|
40
|
+
- Board Yes Yes BOARD_ID and Description
|
41
|
+
- CPU - Yes
|
42
|
+
- Version Yes Yes
|
43
|
+
- Build * * only if any of the mcus have a build
|
44
|
+
"""
|
56
45
|
table = Table(
|
57
46
|
title=title,
|
58
47
|
title_style="magenta",
|
59
48
|
header_style="bold magenta",
|
60
49
|
collapse_padding=True,
|
61
|
-
|
50
|
+
padding=(0, 0),
|
62
51
|
)
|
63
|
-
|
64
|
-
|
65
|
-
|
52
|
+
# check if the terminal is wide enough to show all columns or if we need to collapse some
|
53
|
+
is_wide = console.width > 99
|
54
|
+
needs_build = any(mcu.build for mcu in conn_mcus)
|
55
|
+
|
56
|
+
table.add_column("Serial" if is_wide else "Ser.", overflow="fold")
|
57
|
+
table.add_column("Family" if is_wide else "Fam.", overflow="crop", max_width=None if is_wide else 4)
|
58
|
+
if is_wide:
|
59
|
+
table.add_column("Port")
|
66
60
|
table.add_column("Board", overflow="fold")
|
67
61
|
# table.add_column("Variant") # TODO: add variant
|
68
|
-
|
69
|
-
|
70
|
-
table.add_column("
|
62
|
+
if is_wide:
|
63
|
+
table.add_column("CPU")
|
64
|
+
table.add_column("Version", overflow="fold", min_width=5, max_width=16)
|
65
|
+
if needs_build:
|
66
|
+
table.add_column("Build" if is_wide else "Bld", justify="right")
|
71
67
|
|
72
|
-
for mcu in track(conn_mcus, description="Updating board info", transient=True,
|
68
|
+
for mcu in track(conn_mcus, description="Updating board info", transient=True, refresh_per_second=2):
|
73
69
|
if refresh:
|
74
70
|
try:
|
75
71
|
mcu.get_mcu_info()
|
76
72
|
except ConnectionError:
|
77
73
|
continue
|
78
74
|
description = f"[italic bright_cyan]{mcu.description}" if mcu.description else ""
|
79
|
-
|
75
|
+
row = [
|
80
76
|
mcu.serialport.replace("/dev/", ""),
|
81
|
-
mcu.family,
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
mcu.
|
88
|
-
)
|
89
|
-
|
77
|
+
abbrv_family(mcu.family, is_wide),
|
78
|
+
]
|
79
|
+
if is_wide:
|
80
|
+
row.append(mcu.port)
|
81
|
+
row.append(f"{mcu.board}\n{description}".strip())
|
82
|
+
if is_wide:
|
83
|
+
row.append(mcu.cpu)
|
84
|
+
row.append(clean_version(mcu.version))
|
85
|
+
if needs_build:
|
86
|
+
row.append(mcu.build)
|
87
|
+
|
88
|
+
table.add_row(*row)
|
89
|
+
return table
|