-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstall.py
202 lines (177 loc) · 6.17 KB
/
install.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
from typing import Set
import os
import time
import shutil
import glob
import sys
import subprocess
import argparse
LS_CMD = "/dev/tty.usbmodem*"
class TextColors:
RESET = "\033[0m"
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
PURPLE = "\033[95m"
CYAN = "\033[96m"
def print_color(text: str, color: str = TextColors.BLUE, end: bool = True):
"""Helper function to print colored text, optionally without newlines."""
_text = f"{color}{text}{TextColors.RESET}"
if end:
print(_text)
else:
print(_text, end="", flush=True)
def breadcrumb(n: int = 5, color: str = TextColors.BLUE) -> None:
"""Helper function to visually wait for an event."""
for _ in range(n):
time.sleep(1)
print_color(".", color=color, end=False)
print("")
applescript_code = """tell application "System Events"
try
set _groups to groups of UI element 1 of scroll area 1 of group 1 of window "Notification Center" of application process "NotificationCenter"
repeat with _group in _groups
set temp to value of static text 1 of _group
if temp contains "Disk Not Ejected Properly" then
perform (first action of _group where description is "Close")
end if
end repeat
end try
end tell"""
def execute_applescript(code: str):
devnull = subprocess.DEVNULL
subprocess.run(["osascript", "-e", code], stdout=devnull, stderr=devnull)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Pico Firmware Flasher")
parser.add_argument(
"-p",
"--uf2_file_path",
metavar="PATH",
type=str,
default=None,
help="PATH: location of a uf2 formatted flash file",
)
parser.add_argument(
"-v",
"--volume_path",
type=str,
choices=["/Volumes/RPI-RP2", "/Volumes/RP2350"],
default=None,
help="Path to the volume (optional)",
)
parser.add_argument(
"-a",
"--applescript",
type=bool,
default=True,
help="Whether to auto-dismiss 'Disk Not Ejected Properly' warnings",
)
parser.add_argument(
"-c",
"--copy",
type=bool,
default=True,
help="Whether to copy this repo's /bin/* files to the device",
)
# Parse the command-line arguments
args = parser.parse_args()
return args
def update_firmware(args: argparse.Namespace) -> bool:
"""Update a pico's firmware."""
print_color("Pico detected, attempting to write firmware")
print_color("DO NOT CTRL+C!", color=TextColors.RED)
try:
# Copy the UF2 file to the RP2 drive
shutil.copy2(args.uf2_file_path, args.volume_path)
# Wait for the completed copy operation before printing the message
file_name: str = os.path.basename(args.uf2_file_path)
copied_path = os.path.join(args.volume_path, file_name)
with open(copied_path, "rb") as f:
os.fsync(f.fileno())
return True
except PermissionError:
print_color(
"Permission Error: Unable to copy firmware to the Pico."
+ " Please reconnect the Pico and try again.",
color=TextColors.RED,
)
return False
except FileNotFoundError:
print_color(
"Error: UF2 file not found. Please check the file location.",
color=TextColors.RED,
)
return False
except Exception as e:
expected_file_error = (
f"[Errno 2] No such file or directory: '{args.volume_path}'"
)
if str(e) == expected_file_error:
print_color(
"Error: Volume path '{}' not found.".format(args.volume_path)
+ " Please check the volume path.",
TextColors.RED,
)
else:
print_color(f"Unknown Error: {e}", color=TextColors.RED)
return False
def copy_build_files(pre_search: Set[str]) -> None:
"""Copy a build directory."""
while True:
search = list(set(glob.glob(LS_CMD)) - pre_search)
if len(search) == 1:
print_color(
f"Serial connection detected {search[0]}, copying files",
color=TextColors.CYAN,
)
print_color("DO NOT CTRL+C!", color=TextColors.RED)
try:
return_code = subprocess.run(
["sh", "scripts/copy.sh", search[0]], check=True
).returncode
except subprocess.CalledProcessError as err:
return_code = err.returncode
if return_code == 0:
return
else:
print_color(
f"Copy was not successful, searching for serial: {LS_CMD}",
color=TextColors.RED,
)
else:
print_color(
"No unique serial connection found, skipping...",
color=TextColors.RED,
)
breadcrumb(color=TextColors.CYAN)
def run(args: argparse.Namespace) -> None:
"""Main event loop listening for mounted drives and serial connections."""
pre_search = glob.glob(LS_CMD)
if args.volume_path:
print_color(f"Watching for volume: {args.volume_path}")
try:
while True:
# Check if the RP2 drive is available
if args.volume_path and os.path.exists(args.volume_path):
if update_firmware(args=args):
if args.applescript:
execute_applescript(applescript_code)
if args.copy:
copy_build_files(pre_search=set(pre_search))
print_color("SAFE TO CTRL+C!", color=TextColors.GREEN)
breadcrumb()
except KeyboardInterrupt:
print("")
sys.exit(0)
def print_welcome_message() -> None:
print("----------------------------------")
print_color("Press CTRL+C to exit")
print_color(
"WARNING: Exiting while updating firmware could damage the Pico!",
color=TextColors.RED,
)
print("----------------------------------")
if __name__ == "__main__":
print_welcome_message()
run(args=parse_args())