fastled 1.0.11__py2.py3-none-any.whl → 1.0.15__py2.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.
- fastled/__init__.py +1 -1
- fastled/app.py +34 -10
- fastled/compile_server.py +11 -6
- fastled/keyboard.py +91 -89
- fastled/web_compile.py +291 -277
- fastled-1.0.15.dist-info/METADATA +196 -0
- {fastled-1.0.11.dist-info → fastled-1.0.15.dist-info}/RECORD +11 -11
- fastled-1.0.11.dist-info/METADATA +0 -126
- {fastled-1.0.11.dist-info → fastled-1.0.15.dist-info}/LICENSE +0 -0
- {fastled-1.0.11.dist-info → fastled-1.0.15.dist-info}/WHEEL +0 -0
- {fastled-1.0.11.dist-info → fastled-1.0.15.dist-info}/entry_points.txt +0 -0
- {fastled-1.0.11.dist-info → fastled-1.0.15.dist-info}/top_level.txt +0 -0
fastled/__init__.py
CHANGED
fastled/app.py
CHANGED
@@ -20,8 +20,8 @@ from fastled.docker_manager import DockerManager
|
|
20
20
|
from fastled.filewatcher import FileWatcherProcess
|
21
21
|
from fastled.keyboard import SpaceBarWatcher
|
22
22
|
from fastled.open_browser import open_browser_thread
|
23
|
-
from fastled.sketch import looks_like_sketch_directory
|
24
|
-
from fastled.web_compile import web_compile
|
23
|
+
from fastled.sketch import looks_like_fastled_repo, looks_like_sketch_directory
|
24
|
+
from fastled.web_compile import ConnectionResult, find_good_connection, web_compile
|
25
25
|
|
26
26
|
machine = platform.machine().lower()
|
27
27
|
IS_ARM: bool = "arm" in machine or "aarch64" in machine
|
@@ -93,8 +93,9 @@ def parse_args() -> argparse.Namespace:
|
|
93
93
|
)
|
94
94
|
build_mode.add_argument(
|
95
95
|
"--localhost",
|
96
|
+
"--local",
|
96
97
|
action="store_true",
|
97
|
-
help="Use localhost for web compilation from an instance of fastled --server",
|
98
|
+
help="Use localhost for web compilation from an instance of fastled --server, creating it if necessary",
|
98
99
|
)
|
99
100
|
build_mode.add_argument(
|
100
101
|
"--server",
|
@@ -108,13 +109,20 @@ def parse_args() -> argparse.Namespace:
|
|
108
109
|
help="Skips the test to see if the current directory is a valid FastLED sketch directory",
|
109
110
|
)
|
110
111
|
|
112
|
+
cwd_is_fastled = looks_like_fastled_repo(Path(os.getcwd()))
|
113
|
+
|
111
114
|
args = parser.parse_args()
|
115
|
+
if not cwd_is_fastled and not args.localhost and not args.web and not args.server:
|
116
|
+
print(f"Using web compiler at {DEFAULT_URL}")
|
117
|
+
args.web = DEFAULT_URL
|
118
|
+
if cwd_is_fastled and not args.web:
|
119
|
+
print("Forcing --local mode because we are in the FastLED repo")
|
120
|
+
args.localhost = True
|
112
121
|
if args.localhost:
|
113
122
|
args.web = "localhost"
|
114
|
-
if args.
|
115
|
-
|
116
|
-
|
117
|
-
parser.error("--server and --web are mutually exclusive")
|
123
|
+
if args.interactive and not args.server:
|
124
|
+
print("--interactive forces --server mode")
|
125
|
+
args.server = True
|
118
126
|
if args.directory is None and not args.server:
|
119
127
|
# does current directory look like a sketch?
|
120
128
|
maybe_sketch_dir = Path(os.getcwd())
|
@@ -125,6 +133,7 @@ def parse_args() -> argparse.Namespace:
|
|
125
133
|
"\nYou either need to specify a sketch directory or run in --server mode."
|
126
134
|
)
|
127
135
|
sys.exit(1)
|
136
|
+
|
128
137
|
return args
|
129
138
|
|
130
139
|
|
@@ -188,7 +197,20 @@ def run_web_compiler(
|
|
188
197
|
|
189
198
|
|
190
199
|
def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServer:
|
191
|
-
|
200
|
+
is_local_host = "localhost" in args.web or "127.0.0.1" in args.web or args.localhost
|
201
|
+
|
202
|
+
# test to see if there is already a local host server
|
203
|
+
local_host_needs_server = False
|
204
|
+
if is_local_host:
|
205
|
+
addr = "localhost" if args.localhost else args.web
|
206
|
+
result: ConnectionResult | None = find_good_connection([addr])
|
207
|
+
if result is not None:
|
208
|
+
print(f"Found local server at {result.host}")
|
209
|
+
return result.host
|
210
|
+
else:
|
211
|
+
local_host_needs_server = True
|
212
|
+
|
213
|
+
if not local_host_needs_server and args.web:
|
192
214
|
if isinstance(args.web, str):
|
193
215
|
return args.web
|
194
216
|
if isinstance(args.web, bool):
|
@@ -196,6 +218,7 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
|
|
196
218
|
return args.web
|
197
219
|
else:
|
198
220
|
try:
|
221
|
+
print("No local server found, starting one...")
|
199
222
|
compile_server = CompileServer()
|
200
223
|
print("Waiting for the local compiler to start...")
|
201
224
|
if not compile_server.wait_for_startup():
|
@@ -211,7 +234,7 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
|
|
211
234
|
|
212
235
|
def run_client(args: argparse.Namespace) -> int:
|
213
236
|
compile_server: CompileServer | None = None
|
214
|
-
open_web_browser = not args.just_compile
|
237
|
+
open_web_browser = not args.just_compile and not args.interactive
|
215
238
|
profile = args.profile
|
216
239
|
if not args.force_compile and not looks_like_sketch_directory(Path(args.directory)):
|
217
240
|
print(
|
@@ -398,8 +421,9 @@ def main() -> int:
|
|
398
421
|
|
399
422
|
if __name__ == "__main__":
|
400
423
|
try:
|
401
|
-
os.chdir("../fastled")
|
424
|
+
# os.chdir("../fastled")
|
402
425
|
sys.argv.append("examples/SdCard")
|
426
|
+
sys.argv.append("--localhost")
|
403
427
|
sys.exit(main())
|
404
428
|
except KeyboardInterrupt:
|
405
429
|
print("\nExiting from main...")
|
fastled/compile_server.py
CHANGED
@@ -12,6 +12,8 @@ _DEFAULT_CONTAINER_NAME = "fastled-wasm-compiler"
|
|
12
12
|
|
13
13
|
SERVER_PORT = 9021
|
14
14
|
|
15
|
+
SERVER_OPTIONS = ["--allow-shutdown", "--no-auto-update"]
|
16
|
+
|
15
17
|
|
16
18
|
def find_available_port(start_port: int = SERVER_PORT) -> int:
|
17
19
|
"""Find an available port starting from the given port."""
|
@@ -48,6 +50,12 @@ class CompileServer:
|
|
48
50
|
self.fastled_src_dir: Path | None = fastled_src_dir
|
49
51
|
self.interactive = interactive
|
50
52
|
self._port = self._start()
|
53
|
+
# fancy print
|
54
|
+
if not interactive:
|
55
|
+
msg = f"# FastLED Compile Server started at {self.url()} #"
|
56
|
+
print("\n" + "#" * len(msg))
|
57
|
+
print(msg)
|
58
|
+
print("#" * len(msg) + "\n")
|
51
59
|
|
52
60
|
def using_fastled_src_dir_volume(self) -> bool:
|
53
61
|
return self.fastled_src_dir is not None
|
@@ -124,8 +132,9 @@ class CompileServer:
|
|
124
132
|
if self.interactive:
|
125
133
|
server_command = ["/bin/bash"]
|
126
134
|
else:
|
127
|
-
server_command = ["python", "/js/run.py", "server"
|
128
|
-
|
135
|
+
server_command = ["python", "/js/run.py", "server"] + SERVER_OPTIONS
|
136
|
+
server_cmd_str = subprocess.list2cmdline(server_command)
|
137
|
+
print(f"Started Docker container with command: {server_cmd_str}")
|
129
138
|
ports = {port: 80}
|
130
139
|
volumes = None
|
131
140
|
if self.fastled_src_dir:
|
@@ -135,10 +144,6 @@ class CompileServer:
|
|
135
144
|
volumes = {
|
136
145
|
str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
|
137
146
|
}
|
138
|
-
if not self.interactive:
|
139
|
-
# no auto-update because the source directory is mapped in.
|
140
|
-
# This should be automatic now.
|
141
|
-
server_command.append("--no-auto-update") # stop git repo updates.
|
142
147
|
self.running_process = self.docker.run_container(
|
143
148
|
server_command, ports=ports, volumes=volumes, tty=self.interactive
|
144
149
|
)
|
fastled/keyboard.py
CHANGED
@@ -1,89 +1,91 @@
|
|
1
|
-
import os
|
2
|
-
import select
|
3
|
-
import sys
|
4
|
-
import time
|
5
|
-
from multiprocessing import Process, Queue
|
6
|
-
from queue import Empty
|
7
|
-
|
8
|
-
|
9
|
-
class SpaceBarWatcher:
|
10
|
-
def __init__(self) -> None:
|
11
|
-
self.queue: Queue = Queue()
|
12
|
-
self.queue_cancel: Queue = Queue()
|
13
|
-
self.process = Process(target=self._watch_for_space)
|
14
|
-
self.process.start()
|
15
|
-
|
16
|
-
def _watch_for_space(self) -> None:
|
17
|
-
# Set stdin to non-blocking mode
|
18
|
-
fd = sys.stdin.fileno()
|
19
|
-
|
20
|
-
|
21
|
-
import
|
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
|
-
self.
|
70
|
-
self.
|
71
|
-
self.
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
1
|
+
import os
|
2
|
+
import select
|
3
|
+
import sys
|
4
|
+
import time
|
5
|
+
from multiprocessing import Process, Queue
|
6
|
+
from queue import Empty
|
7
|
+
|
8
|
+
|
9
|
+
class SpaceBarWatcher:
|
10
|
+
def __init__(self) -> None:
|
11
|
+
self.queue: Queue = Queue()
|
12
|
+
self.queue_cancel: Queue = Queue()
|
13
|
+
self.process = Process(target=self._watch_for_space)
|
14
|
+
self.process.start()
|
15
|
+
|
16
|
+
def _watch_for_space(self) -> None:
|
17
|
+
# Set stdin to non-blocking mode
|
18
|
+
fd = sys.stdin.fileno()
|
19
|
+
|
20
|
+
if os.name == "nt": # Windows
|
21
|
+
import msvcrt
|
22
|
+
|
23
|
+
while True:
|
24
|
+
# Check for cancel signal
|
25
|
+
try:
|
26
|
+
self.queue_cancel.get(timeout=0.1)
|
27
|
+
break
|
28
|
+
except Empty:
|
29
|
+
pass
|
30
|
+
|
31
|
+
# Check if there's input ready
|
32
|
+
if msvcrt.kbhit(): # type: ignore
|
33
|
+
char = msvcrt.getch().decode() # type: ignore
|
34
|
+
if char == " ":
|
35
|
+
self.queue.put(ord(" "))
|
36
|
+
|
37
|
+
else: # Unix-like systems
|
38
|
+
import termios
|
39
|
+
import tty
|
40
|
+
|
41
|
+
old_settings = termios.tcgetattr(fd) # type: ignore
|
42
|
+
try:
|
43
|
+
tty.setraw(fd) # type: ignore
|
44
|
+
while True:
|
45
|
+
# Check for cancel signal
|
46
|
+
try:
|
47
|
+
self.queue_cancel.get(timeout=0.1)
|
48
|
+
break
|
49
|
+
except Empty:
|
50
|
+
pass
|
51
|
+
|
52
|
+
# Check if there's input ready
|
53
|
+
if select.select([sys.stdin], [], [], 0.1)[0]:
|
54
|
+
char = sys.stdin.read(1)
|
55
|
+
if char == " ":
|
56
|
+
self.queue.put(ord(" "))
|
57
|
+
finally:
|
58
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) # type: ignore
|
59
|
+
|
60
|
+
def space_bar_pressed(self) -> bool:
|
61
|
+
found = False
|
62
|
+
while not self.queue.empty():
|
63
|
+
key = self.queue.get()
|
64
|
+
if key == ord(" "):
|
65
|
+
found = True
|
66
|
+
return found
|
67
|
+
|
68
|
+
def stop(self) -> None:
|
69
|
+
self.queue_cancel.put(True)
|
70
|
+
self.process.terminate()
|
71
|
+
self.process.join()
|
72
|
+
self.queue.close()
|
73
|
+
self.queue.join_thread()
|
74
|
+
|
75
|
+
|
76
|
+
def main() -> None:
|
77
|
+
watcher = SpaceBarWatcher()
|
78
|
+
try:
|
79
|
+
while True:
|
80
|
+
if watcher.space_bar_pressed():
|
81
|
+
break
|
82
|
+
time.sleep(1)
|
83
|
+
finally:
|
84
|
+
watcher.stop()
|
85
|
+
|
86
|
+
|
87
|
+
if __name__ == "__main__":
|
88
|
+
try:
|
89
|
+
main()
|
90
|
+
except KeyboardInterrupt:
|
91
|
+
print("Keyboard interrupt detected.")
|
fastled/web_compile.py
CHANGED
@@ -1,277 +1,291 @@
|
|
1
|
-
import io
|
2
|
-
import json
|
3
|
-
import os
|
4
|
-
import shutil
|
5
|
-
import tempfile
|
6
|
-
import zipfile
|
7
|
-
from concurrent.futures import ProcessPoolExecutor, as_completed
|
8
|
-
from dataclasses import dataclass
|
9
|
-
from pathlib import Path
|
10
|
-
|
11
|
-
import httpx
|
12
|
-
|
13
|
-
from fastled.build_mode import BuildMode
|
14
|
-
from fastled.compile_server import SERVER_PORT
|
15
|
-
from fastled.sketch import get_sketch_files
|
16
|
-
from fastled.util import hash_file
|
17
|
-
|
18
|
-
DEFAULT_HOST = "https://fastled.onrender.com"
|
19
|
-
ENDPOINT_COMPILED_WASM = "compile/wasm"
|
20
|
-
_TIMEOUT = 60 * 4 # 2 mins timeout
|
21
|
-
_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
|
22
|
-
ENABLE_EMBEDDED_DATA = True
|
23
|
-
_EXECUTOR = ProcessPoolExecutor(max_workers=8)
|
24
|
-
|
25
|
-
|
26
|
-
@dataclass
|
27
|
-
class ConnectionResult:
|
28
|
-
host: str
|
29
|
-
success: bool
|
30
|
-
ipv4: bool
|
31
|
-
|
32
|
-
|
33
|
-
@dataclass
|
34
|
-
class WebCompileResult:
|
35
|
-
success: bool
|
36
|
-
stdout: str
|
37
|
-
hash_value: str | None
|
38
|
-
zip_bytes: bytes
|
39
|
-
|
40
|
-
def __bool__(self) -> bool:
|
41
|
-
return self.success
|
42
|
-
|
43
|
-
|
44
|
-
def _sanitize_host(host: str) -> str:
|
45
|
-
if host.startswith("http"):
|
46
|
-
return host
|
47
|
-
is_local_host = "localhost" in host or "127.0.0.1" in host or "0.0.0.0" in host
|
48
|
-
use_https = not is_local_host
|
49
|
-
if use_https:
|
50
|
-
return host if host.startswith("https://") else f"https://{host}"
|
51
|
-
return host if host.startswith("http://") else f"http://{host}"
|
52
|
-
|
53
|
-
|
54
|
-
def _test_connection(host: str, use_ipv4: bool) -> ConnectionResult:
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
return
|
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
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
1
|
+
import io
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
import shutil
|
5
|
+
import tempfile
|
6
|
+
import zipfile
|
7
|
+
from concurrent.futures import Future, ProcessPoolExecutor, as_completed
|
8
|
+
from dataclasses import dataclass
|
9
|
+
from pathlib import Path
|
10
|
+
|
11
|
+
import httpx
|
12
|
+
|
13
|
+
from fastled.build_mode import BuildMode
|
14
|
+
from fastled.compile_server import SERVER_PORT
|
15
|
+
from fastled.sketch import get_sketch_files
|
16
|
+
from fastled.util import hash_file
|
17
|
+
|
18
|
+
DEFAULT_HOST = "https://fastled.onrender.com"
|
19
|
+
ENDPOINT_COMPILED_WASM = "compile/wasm"
|
20
|
+
_TIMEOUT = 60 * 4 # 2 mins timeout
|
21
|
+
_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
|
22
|
+
ENABLE_EMBEDDED_DATA = True
|
23
|
+
_EXECUTOR = ProcessPoolExecutor(max_workers=8)
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass
|
27
|
+
class ConnectionResult:
|
28
|
+
host: str
|
29
|
+
success: bool
|
30
|
+
ipv4: bool
|
31
|
+
|
32
|
+
|
33
|
+
@dataclass
|
34
|
+
class WebCompileResult:
|
35
|
+
success: bool
|
36
|
+
stdout: str
|
37
|
+
hash_value: str | None
|
38
|
+
zip_bytes: bytes
|
39
|
+
|
40
|
+
def __bool__(self) -> bool:
|
41
|
+
return self.success
|
42
|
+
|
43
|
+
|
44
|
+
def _sanitize_host(host: str) -> str:
|
45
|
+
if host.startswith("http"):
|
46
|
+
return host
|
47
|
+
is_local_host = "localhost" in host or "127.0.0.1" in host or "0.0.0.0" in host
|
48
|
+
use_https = not is_local_host
|
49
|
+
if use_https:
|
50
|
+
return host if host.startswith("https://") else f"https://{host}"
|
51
|
+
return host if host.startswith("http://") else f"http://{host}"
|
52
|
+
|
53
|
+
|
54
|
+
def _test_connection(host: str, use_ipv4: bool) -> ConnectionResult:
|
55
|
+
# Function static cache
|
56
|
+
connection_cache = _test_connection.__dict__.setdefault("_CONNECTION_CACHE", {})
|
57
|
+
key = f"{host}_{use_ipv4}"
|
58
|
+
cached_result = connection_cache.get(key)
|
59
|
+
if cached_result is not None:
|
60
|
+
return cached_result
|
61
|
+
transport = httpx.HTTPTransport(local_address="0.0.0.0") if use_ipv4 else None
|
62
|
+
try:
|
63
|
+
with httpx.Client(
|
64
|
+
timeout=_TIMEOUT,
|
65
|
+
transport=transport,
|
66
|
+
) as test_client:
|
67
|
+
test_response = test_client.get(
|
68
|
+
f"{host}/healthz", timeout=3, follow_redirects=True
|
69
|
+
)
|
70
|
+
result = ConnectionResult(host, test_response.status_code == 200, use_ipv4)
|
71
|
+
except Exception:
|
72
|
+
result = ConnectionResult(host, False, use_ipv4)
|
73
|
+
connection_cache[key] = result
|
74
|
+
return result
|
75
|
+
|
76
|
+
|
77
|
+
def _file_info(file_path: Path) -> str:
|
78
|
+
hash_txt = hash_file(file_path)
|
79
|
+
file_size = file_path.stat().st_size
|
80
|
+
json_str = json.dumps({"hash": hash_txt, "size": file_size})
|
81
|
+
return json_str
|
82
|
+
|
83
|
+
|
84
|
+
@dataclass
|
85
|
+
class ZipResult:
|
86
|
+
zip_bytes: bytes
|
87
|
+
zip_embedded_bytes: bytes | None
|
88
|
+
success: bool
|
89
|
+
error: str | None
|
90
|
+
|
91
|
+
|
92
|
+
def zip_files(directory: Path, build_mode: BuildMode) -> ZipResult | Exception:
|
93
|
+
print("Zipping files...")
|
94
|
+
try:
|
95
|
+
files = get_sketch_files(directory)
|
96
|
+
if not files:
|
97
|
+
raise FileNotFoundError(f"No files found in {directory}")
|
98
|
+
for f in files:
|
99
|
+
print(f"Adding file: {f}")
|
100
|
+
# Create in-memory zip file
|
101
|
+
has_embedded_zip = False
|
102
|
+
zip_embedded_buffer = io.BytesIO()
|
103
|
+
zip_buffer = io.BytesIO()
|
104
|
+
with zipfile.ZipFile(
|
105
|
+
zip_embedded_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
|
106
|
+
) as emebedded_zip_file:
|
107
|
+
with zipfile.ZipFile(
|
108
|
+
zip_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
|
109
|
+
) as zip_file:
|
110
|
+
for file_path in files:
|
111
|
+
relative_path = file_path.relative_to(directory)
|
112
|
+
achive_path = str(Path("wasm") / relative_path)
|
113
|
+
if str(relative_path).startswith("data") and ENABLE_EMBEDDED_DATA:
|
114
|
+
_file_info_str = _file_info(file_path)
|
115
|
+
zip_file.writestr(
|
116
|
+
achive_path + ".embedded.json", _file_info_str
|
117
|
+
)
|
118
|
+
emebedded_zip_file.write(file_path, relative_path)
|
119
|
+
has_embedded_zip = True
|
120
|
+
else:
|
121
|
+
zip_file.write(file_path, achive_path)
|
122
|
+
# write build mode into the file as build.txt so that sketches are fingerprinted
|
123
|
+
# based on the build mode. Otherwise the same sketch with different build modes
|
124
|
+
# will have the same fingerprint.
|
125
|
+
zip_file.writestr(
|
126
|
+
str(Path("wasm") / "build_mode.txt"), build_mode.value
|
127
|
+
)
|
128
|
+
result = ZipResult(
|
129
|
+
zip_bytes=zip_buffer.getvalue(),
|
130
|
+
zip_embedded_bytes=(
|
131
|
+
zip_embedded_buffer.getvalue() if has_embedded_zip else None
|
132
|
+
),
|
133
|
+
success=True,
|
134
|
+
error=None,
|
135
|
+
)
|
136
|
+
return result
|
137
|
+
except Exception as e:
|
138
|
+
return e
|
139
|
+
|
140
|
+
|
141
|
+
def find_good_connection(
|
142
|
+
urls: list[str], filter_out_bad=True, use_ipv6: bool = True
|
143
|
+
) -> ConnectionResult | None:
|
144
|
+
futures: list[Future] = []
|
145
|
+
for url in urls:
|
146
|
+
f = _EXECUTOR.submit(_test_connection, url, use_ipv4=True)
|
147
|
+
futures.append(f)
|
148
|
+
if use_ipv6:
|
149
|
+
f_v6 = _EXECUTOR.submit(_test_connection, url, use_ipv4=False)
|
150
|
+
futures.append(f_v6)
|
151
|
+
|
152
|
+
try:
|
153
|
+
# Return first successful result
|
154
|
+
for future in as_completed(futures):
|
155
|
+
result: ConnectionResult = future.result()
|
156
|
+
if result.success or not filter_out_bad:
|
157
|
+
return result
|
158
|
+
finally:
|
159
|
+
# Cancel any remaining futures
|
160
|
+
for future in futures:
|
161
|
+
future.cancel()
|
162
|
+
return None
|
163
|
+
|
164
|
+
|
165
|
+
def web_compile(
|
166
|
+
directory: Path,
|
167
|
+
host: str | None = None,
|
168
|
+
auth_token: str | None = None,
|
169
|
+
build_mode: BuildMode | None = None,
|
170
|
+
profile: bool = False,
|
171
|
+
) -> WebCompileResult:
|
172
|
+
host = _sanitize_host(host or DEFAULT_HOST)
|
173
|
+
build_mode = build_mode or BuildMode.QUICK
|
174
|
+
print("Compiling on", host)
|
175
|
+
auth_token = auth_token or _AUTH_TOKEN
|
176
|
+
if not directory.exists():
|
177
|
+
raise FileNotFoundError(f"Directory not found: {directory}")
|
178
|
+
zip_result = zip_files(directory, build_mode=build_mode)
|
179
|
+
if isinstance(zip_result, Exception):
|
180
|
+
return WebCompileResult(
|
181
|
+
success=False, stdout=str(zip_result), hash_value=None, zip_bytes=b""
|
182
|
+
)
|
183
|
+
zip_bytes = zip_result.zip_bytes
|
184
|
+
archive_size = len(zip_bytes)
|
185
|
+
print(f"Web compiling on {host}...")
|
186
|
+
try:
|
187
|
+
urls = [host]
|
188
|
+
domain = host.split("://")[-1]
|
189
|
+
if ":" not in domain:
|
190
|
+
urls.append(f"{host}:{SERVER_PORT}")
|
191
|
+
|
192
|
+
connection_result = find_good_connection(urls)
|
193
|
+
if connection_result is None:
|
194
|
+
print("Connection failed to all endpoints")
|
195
|
+
return WebCompileResult(
|
196
|
+
success=False,
|
197
|
+
stdout="Connection failed",
|
198
|
+
hash_value=None,
|
199
|
+
zip_bytes=b"",
|
200
|
+
)
|
201
|
+
|
202
|
+
ipv4_stmt = "IPv4" if connection_result.ipv4 else "IPv6"
|
203
|
+
transport = (
|
204
|
+
httpx.HTTPTransport(local_address="0.0.0.0")
|
205
|
+
if connection_result.ipv4
|
206
|
+
else None
|
207
|
+
)
|
208
|
+
with httpx.Client(
|
209
|
+
transport=transport,
|
210
|
+
timeout=_TIMEOUT,
|
211
|
+
) as client:
|
212
|
+
headers = {
|
213
|
+
"accept": "application/json",
|
214
|
+
"authorization": auth_token,
|
215
|
+
"build": (
|
216
|
+
build_mode.value.lower()
|
217
|
+
if build_mode
|
218
|
+
else BuildMode.QUICK.value.lower()
|
219
|
+
),
|
220
|
+
"profile": "true" if profile else "false",
|
221
|
+
}
|
222
|
+
|
223
|
+
url = f"{connection_result.host}/{ENDPOINT_COMPILED_WASM}"
|
224
|
+
print(f"Compiling on {url} via {ipv4_stmt}. Zip size: {archive_size} bytes")
|
225
|
+
files = {"file": ("wasm.zip", zip_bytes, "application/x-zip-compressed")}
|
226
|
+
response = client.post(
|
227
|
+
url,
|
228
|
+
follow_redirects=True,
|
229
|
+
files=files,
|
230
|
+
headers=headers,
|
231
|
+
timeout=_TIMEOUT,
|
232
|
+
)
|
233
|
+
|
234
|
+
if response.status_code != 200:
|
235
|
+
json_response = response.json()
|
236
|
+
detail = json_response.get("detail", "Could not compile")
|
237
|
+
return WebCompileResult(
|
238
|
+
success=False, stdout=detail, hash_value=None, zip_bytes=b""
|
239
|
+
)
|
240
|
+
|
241
|
+
print(f"Response status code: {response}")
|
242
|
+
# Create a temporary directory to extract the zip
|
243
|
+
with tempfile.TemporaryDirectory() as extract_dir:
|
244
|
+
extract_path = Path(extract_dir)
|
245
|
+
|
246
|
+
# Write the response content to a temporary zip file
|
247
|
+
temp_zip = extract_path / "response.zip"
|
248
|
+
temp_zip.write_bytes(response.content)
|
249
|
+
|
250
|
+
# Extract the zip
|
251
|
+
shutil.unpack_archive(temp_zip, extract_path, "zip")
|
252
|
+
|
253
|
+
if zip_result.zip_embedded_bytes:
|
254
|
+
# extract the embedded bytes, which were not sent to the server
|
255
|
+
temp_zip.write_bytes(zip_result.zip_embedded_bytes)
|
256
|
+
shutil.unpack_archive(temp_zip, extract_path, "zip")
|
257
|
+
|
258
|
+
# we don't need the temp zip anymore
|
259
|
+
temp_zip.unlink()
|
260
|
+
|
261
|
+
# Read stdout from out.txt if it exists
|
262
|
+
stdout_file = extract_path / "out.txt"
|
263
|
+
hash_file = extract_path / "hash.txt"
|
264
|
+
stdout = stdout_file.read_text() if stdout_file.exists() else ""
|
265
|
+
hash_value = hash_file.read_text() if hash_file.exists() else None
|
266
|
+
|
267
|
+
# now rezip the extracted files since we added the embedded json files
|
268
|
+
out_buffer = io.BytesIO()
|
269
|
+
with zipfile.ZipFile(
|
270
|
+
out_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
|
271
|
+
) as out_zip:
|
272
|
+
for root, _, _files in os.walk(extract_path):
|
273
|
+
for file in _files:
|
274
|
+
file_path = Path(root) / file
|
275
|
+
relative_path = file_path.relative_to(extract_path)
|
276
|
+
out_zip.write(file_path, relative_path)
|
277
|
+
|
278
|
+
return WebCompileResult(
|
279
|
+
success=True,
|
280
|
+
stdout=stdout,
|
281
|
+
hash_value=hash_value,
|
282
|
+
zip_bytes=out_buffer.getvalue(),
|
283
|
+
)
|
284
|
+
except KeyboardInterrupt:
|
285
|
+
print("Keyboard interrupt")
|
286
|
+
raise
|
287
|
+
except httpx.HTTPError as e:
|
288
|
+
print(f"Error: {e}")
|
289
|
+
return WebCompileResult(
|
290
|
+
success=False, stdout=str(e), hash_value=None, zip_bytes=b""
|
291
|
+
)
|
@@ -0,0 +1,196 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: fastled
|
3
|
+
Version: 1.0.15
|
4
|
+
Summary: FastLED Wasm Compiler
|
5
|
+
Home-page: https://github.com/zackees/fastled-wasm
|
6
|
+
Maintainer: Zachary Vorhies
|
7
|
+
License: BSD 3-Clause License
|
8
|
+
Keywords: template-python-cmd
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Requires-Python: >=3.7
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: docker
|
14
|
+
Requires-Dist: httpx
|
15
|
+
Requires-Dist: watchdog
|
16
|
+
Requires-Dist: livereload
|
17
|
+
Requires-Dist: download
|
18
|
+
Requires-Dist: filelock
|
19
|
+
|
20
|
+
# FastLED Wasm compiler
|
21
|
+
|
22
|
+
Compiles an Arduino/Platformio sketch into a wasm binary that can be run directly in the web browser.
|
23
|
+
|
24
|
+
|
25
|
+
[](https://github.com/zackees/fastled-wasm/actions/workflows/lint.yml)
|
26
|
+
[](https://github.com/zackees/fastled-wasm/actions/workflows/build_multi_docker_image.yml)
|
27
|
+
[](https://github.com/zackees/fastled-wasm/actions/workflows/test_macos.yml)
|
28
|
+
[](https://github.com/zackees/fastled-wasm/actions/workflows/test_ubuntu.yml)
|
29
|
+
[](https://github.com/zackees/fastled-wasm/actions/workflows/test_win.yml)
|
30
|
+
|
31
|
+
|
32
|
+
# About
|
33
|
+
|
34
|
+
This python app will compile your FastLED style sketches into html/js/wasm output that runs directly in the browser.
|
35
|
+
|
36
|
+
Compile times are extremely fast, thanks to aggressive object caching for C++ and sketch fingerprinting with a zip file cache. Recompilation of sketch files with minimal changes will occure in less than a second.
|
37
|
+
|
38
|
+
By default the web compiler will always be used unless that user specifies `--local`, in which case this compiler will invoke docker to bring in a runtime necessary to run the compiler toolchain.
|
39
|
+
|
40
|
+
The local compiler will be much faster than the web version in most circumstances after the first compile. The web compiler
|
41
|
+
has the advantage that as a persistant service the compile cache will remain much more up to date.
|
42
|
+
|
43
|
+
|
44
|
+
https://github.com/user-attachments/assets/64ae0e6c-5f8b-4830-ab87-dcc25bc61218
|
45
|
+
|
46
|
+
# Demo
|
47
|
+
|
48
|
+
https://zackees.github.io/fastled-wasm/
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
# Install
|
53
|
+
|
54
|
+
```bash
|
55
|
+
pip install fastled
|
56
|
+
```
|
57
|
+
|
58
|
+
**Note that you may need to install x86 docker emulation on Mac-m1 and later, as this is an x86 only image at the prsent.**
|
59
|
+
|
60
|
+
# Use
|
61
|
+
|
62
|
+
Change to the directory where the sketch lives and run, will run the compilation
|
63
|
+
on the web compiler.
|
64
|
+
|
65
|
+
```bash
|
66
|
+
cd <SKETCH-DIRECTORY>
|
67
|
+
fastled
|
68
|
+
```
|
69
|
+
|
70
|
+
Or if you have docker you can run a server automatically.
|
71
|
+
|
72
|
+
```bash
|
73
|
+
cd <SKETCH-DIRECTORY>
|
74
|
+
fastled --local
|
75
|
+
```
|
76
|
+
|
77
|
+
You can also spawn a server in one process and then access it in another, like this:
|
78
|
+
|
79
|
+
```bash
|
80
|
+
fastled --server
|
81
|
+
# now launch the client
|
82
|
+
fastled examples/wasm --local
|
83
|
+
```
|
84
|
+
|
85
|
+
After compilation a web browser windows will pop up. Changes to the sketch will automatically trigger a recompilation.
|
86
|
+
|
87
|
+
# Hot reload by default
|
88
|
+
|
89
|
+
Once launched, the compiler will remain open, listening to changes and recompiling as necessary and hot-reloading the sketch into the current browser.
|
90
|
+
|
91
|
+
This style of development should be familiar to those doing web development.
|
92
|
+
|
93
|
+
# Hot Reload for working with the FastLED repo
|
94
|
+
|
95
|
+
If you launch `fastled` in the FastLED repo then this tool will automatically detect this and map the src directory into the
|
96
|
+
host container. Whenever there are changes in the source code from the mapped directory, then these will be re-compiled
|
97
|
+
on the next change or if you hit the space bar when prompted. Unlike a sketch folder, a re-compile on the FastLED src
|
98
|
+
can be much longer, for example if you modify a header file.
|
99
|
+
|
100
|
+
# Data
|
101
|
+
|
102
|
+
Huge blobs of data like video will absolutely kill the compile performance as these blobs would normally have to be shuffled
|
103
|
+
back and forth. Therefore a special directory `data/` is implicitly used to hold this blob data. Any data in this directory
|
104
|
+
will be replaced with a stub containing the size and hash of the file during upload. On download these stubs are swapped back
|
105
|
+
with their originals.
|
106
|
+
|
107
|
+
The wasm compiler will also recongize all files in the `data/` directory and generate a `files.json` manifest and can be used
|
108
|
+
in your wasm sketch using an emulated SD card system mounted at `/data/` on the SD Card. In order to increase load speed, these
|
109
|
+
files will be asynchroniously streamed into the running sketch instance during runtime. The only caveat here is that although these files will be available during the setup() phase of the sketch, they will not be fully hydrated, so if you do a seek(end) of these files the results are undefined.
|
110
|
+
|
111
|
+
For an example of how to use this see `examples/SdCard` which is fully wasm compatible.
|
112
|
+
|
113
|
+
# Compile Speed
|
114
|
+
|
115
|
+
The compile speeds for this compiler have been optimized pretty much to the max. There are three compile settings available to the user. The default is `--quick`. Aggressive optimizations are done with `--release` which will aggressively optimize for size. The speed difference between `--release` and `--quick` seems negligable. But `--release` will produce a ~1/3 smaller binary. There is also `--debug`, which will include symbols necessary for debugging and getting the C++ function symbols working correctly in the browser during step through debugging. It works better than expected, but don't expect to have gdb or msvc debugger level of debugging experience.
|
116
|
+
|
117
|
+
We use `ccache` to cache object files. This seems actually help a lot and is better than platformio's method of tracking what needs to be rebuilt. This works as a two tier cache system. What Platformio misses will be covered by ccache's more advanced file changing system.
|
118
|
+
|
119
|
+
The compilation to wasm will happen under a lock. Removing this lock requires removing the platformio toolchain as the compiler backend which enforces it's own internal lock preventing parallel use.
|
120
|
+
|
121
|
+
Simple syntax errors will be caught by the pre-processing step. This happens without a lock to reduce the single lock bottleneck.
|
122
|
+
|
123
|
+
# Sketch Cache
|
124
|
+
|
125
|
+
Sketchs are aggresively finger-printed and stored in a cache. White space, comments, and other superficial data will be stripped out during pre-processing and minimization for fingerprinting. This source file decimation is only used for finger
|
126
|
+
printing while the actual source files are sent to compiler to preserve line numbers and file names.
|
127
|
+
|
128
|
+
This pre-processing done is done via gcc and special regex's and will happen without a lock. This will allow you to have extremely quick recompiles for whitespace and changes in comments even if the compiler is executing under it's lock.
|
129
|
+
|
130
|
+
# Local compiles
|
131
|
+
|
132
|
+
If the web-compiler get's congested then it's recommend that you run the compiler locally. This requires docker and will be invoked whenever you pass in `--local`. This will first pull the most recent Docker image of the Fastled compiler, launching a webserver and then connecting to it with the client once it's been up.
|
133
|
+
|
134
|
+
# Auto updates
|
135
|
+
|
136
|
+
In server mode the git repository will be cloned as a side repo and then periodically updated and rsync'd to the src directory. This allows a long running instance to stay updated.
|
137
|
+
|
138
|
+
### Wasm compatibility with Arduino sketchs
|
139
|
+
|
140
|
+
The compatibility is actually pretty good. Most simple sketchs should compile out of the box. Even some of the avr platform includes are stubbed out to make it work. The familiar `digitalWrite()`, `Serial.println()` and other common functions work. Although `digitalRead()` will always return 0 and `analogRead()` will return random numbers.
|
141
|
+
|
142
|
+
### Faqs
|
143
|
+
|
144
|
+
Q: How often is the docker image updated?
|
145
|
+
A: It's scheduled for rebuild once a day at 3am Pacific time, and also on every change to this repo.
|
146
|
+
|
147
|
+
Q: How can I run my own cloud instance of the FastLED wasm compiler?
|
148
|
+
A: Render.com (which fastled is hosted on) or DigialOcean can accept a github repo and auto-build the docker image.
|
149
|
+
|
150
|
+
Q: Why does FastLED tend to become choppy when the browser is in the background?
|
151
|
+
A: FastLED Wasm currently runs on the main thread and therefor Chrome will begin throttling the event loop when the browser is not in the foreground. The solution to this is to move FastLED to a web worker where it will get a background thread that Chrome / Firefox won't throttle.
|
152
|
+
|
153
|
+
Q: Why does a long `delay()` cause the browser to freeze and become sluggish?
|
154
|
+
A: `delay()` will block `loop()` which blocks the main thread of the browser. The solution is a webworker which will not affect main thread performance of the browser.
|
155
|
+
|
156
|
+
|
157
|
+
Q: How can I get the compiled size of my FastLED sketch smaller?
|
158
|
+
A: A big chunk of space is being used by unnecessary javascript `emscripten` is bundling. This can be tweeked by the wasm_compiler_settings.py file in the FastLED repo.
|
159
|
+
|
160
|
+
|
161
|
+
# Revisions
|
162
|
+
|
163
|
+
* 1.1.15 - Fixed logic for considering ipv6 addresses. Auto selection of ipv6 is now restored.
|
164
|
+
* 1.1.14 - Fixes for regression in using --server and --localhost as two instances, this is now under test.
|
165
|
+
* 1.1.13 - Disable the use of ipv6. It produces random timeouts on the onrender server we are using for the web compiler.
|
166
|
+
* 1.1.12 - By default, fastled will default to the web compiler. `--localhost` to either attach to an existing server launched with `--server` or else one will be created automatically and launched.
|
167
|
+
* 1.1.11 - Dev improvement: FastLED src code volume mapped into docker will just in time update without having to manually trigger it.
|
168
|
+
* 1.1.10 - Swap large assets with embedded placeholders. This helps video sketches upload and compile instantly. Assets are re-added on after compile artifacts are returned.
|
169
|
+
* 1.1.9 - Remove auto server and instead tell the user corrective action to take.
|
170
|
+
* 1.1.8 - Program now knows it's own version which will be displayed with help file. Use `--version` to get it directly.
|
171
|
+
* 1.1.7 - Sketch cache re-enabled, but selectively invalidated on cpp/h updates. Cleaned up deprecated args. Fixed double thread running for containers that was causing slowdown.
|
172
|
+
* 1.1.6 - Use the fast src volume map allow quick updates to fastled when developing on the source code.
|
173
|
+
* 1.1.5 - Filter out hidden files and directories from being included in the sketch archive sent to the compiler.
|
174
|
+
* 1.1.4 - Fix regression introduced by testing out ipv4/ipv6 connections from a thread pool.
|
175
|
+
* 1.1.3 - Live editing of *.h and *.cpp files is now possible. Sketch cache will be disabled in this mode.
|
176
|
+
* 1.1.2 - `--server` will now volume map fastled src directory if it detects this. This was also implemented on the docker side.
|
177
|
+
* 1.1.1 - `--interactive` is now supported to debug the container. Volume maps and better compatibilty with ipv4/v6 by concurrent connection finding.
|
178
|
+
* 1.1.0 - Use `fastled` as the command for the wasm compiler.
|
179
|
+
* 1.0.17 - Pulls updates when necessary. Removed dependency on keyring.
|
180
|
+
* 1.0.16 - `fastled-wasm` package name has been changed to `fled`
|
181
|
+
* 1.0.15 - `fled` is an alias of `fastled-wasm` and will eventually replace it. `--web-host` was folded into `--web`, which if unspecified will attempt to run a local docker server and fallback to the cloud server if that fails. Specifying `--web` with no arguments will default to the cloud server while an argument (like `localhost`) will cause it to bind to that already running server for compilation.
|
182
|
+
* 1.0.14 - For non significant changes (comments, whitespace) in C++/ino/*.h files, compilation is skipped. This significantly reduces load on the server and prevents unnecessary local client browser refreshes.
|
183
|
+
* 1.0.13 - Increase speed of local compiles by running the server version of the compiler so it can keep it's cache and not have to pay docker startup costs because now it's a persistant server until exit.
|
184
|
+
* 1.0.12 - Added suppport for compile modes. Pass in `--release`, `--quick`, `--debug` for different compile options. We also support `--profile` to profile the build process.
|
185
|
+
* 1.0.11 - `--web` compile will automatically be enabled if the local build using docker fails.
|
186
|
+
* 1.0.10 - Watching files is now available for `--web`
|
187
|
+
* 1.0.9 - Enabled web compile. Access it with `--web` or `--web-host`
|
188
|
+
* 1.0.8 - Allow more than one fastled-wasm browser instances to co-exist by searching for unused ports after 8081.
|
189
|
+
* 1.0.7 - Docker multi image build implemented, tool now points to new docker image compile.
|
190
|
+
* 1.0.6 - Removed `--no-open` and `--watch`, `--watch` is now assumed unless `--just-compile` is used.
|
191
|
+
* 1.0.5 - Implemented `--update` to update the compiler image from the docker registry.
|
192
|
+
* 1.0.4 - Implemented `--watch` which will watch for changes and then re-launch the compilation step.
|
193
|
+
* 1.0.3 - Integrated `live-server` to launch when available.
|
194
|
+
* 1.0.2 - Small bug with new installs.
|
195
|
+
* 1.0.1 - Re-use is no longer the default, due to problems.
|
196
|
+
* 1.0.0 - Initial release.
|
@@ -1,20 +1,20 @@
|
|
1
|
-
fastled/__init__.py,sha256=
|
2
|
-
fastled/app.py,sha256=
|
1
|
+
fastled/__init__.py,sha256=zQpKt-iwGAnHw59eTxrM5GT1LFsHFobeuFTRsfgMaAI,64
|
2
|
+
fastled/app.py,sha256=fKIpWYKed8ENfPrWD0jFXZJfh6E4D9_W2xB3w0xgr7o,15054
|
3
3
|
fastled/build_mode.py,sha256=joMwsV4K1y_LijT4gEAcjx69RZBoe_KmFmHZdPYbL_4,631
|
4
4
|
fastled/cli.py,sha256=CNR_pQR0sNVPNuv8e_nmm-0PI8sU-eUBUgnWgWkzW9c,237
|
5
|
-
fastled/compile_server.py,sha256
|
5
|
+
fastled/compile_server.py,sha256=baXSd8V3C2tA--oYl8-_TPEvJxtNYck788W-iT-xyi8,8022
|
6
6
|
fastled/docker_manager.py,sha256=gaDZpFV7E-9JhYIn6ahkP--9dGT32-WR5wZaU-o--6g,9107
|
7
7
|
fastled/filewatcher.py,sha256=fJNMQRDCpihSL4nQeYPqbD4m1Jzjcz_-YRAo-wlPW6k,6518
|
8
|
-
fastled/keyboard.py,sha256=
|
8
|
+
fastled/keyboard.py,sha256=vlyJDae9yOHkGQ0R1RgYwNCIafg5lslGZ_41Jz_EC1g,2667
|
9
9
|
fastled/open_browser.py,sha256=RRHcsZ5Vzsw1AuZUEYuSfjKmf_9j3NGMDUR-FndHmqs,1483
|
10
10
|
fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
|
11
11
|
fastled/sketch.py,sha256=KhhPFqlFVlBk8YrzFy7-ioe7zEzecgrVLhyFbLpBp7k,1845
|
12
12
|
fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
|
13
|
-
fastled/web_compile.py,sha256=
|
13
|
+
fastled/web_compile.py,sha256=PfNTYazlFCJhJxYGsBsv4HaLVIvmF-rPSc1NE9gIaPo,10674
|
14
14
|
fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
15
|
-
fastled-1.0.
|
16
|
-
fastled-1.0.
|
17
|
-
fastled-1.0.
|
18
|
-
fastled-1.0.
|
19
|
-
fastled-1.0.
|
20
|
-
fastled-1.0.
|
15
|
+
fastled-1.0.15.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
16
|
+
fastled-1.0.15.dist-info/METADATA,sha256=30X9yHNZlNBJ_MpRQ5722V7Yu2aqgNEwcQvXcZkjXgI,13230
|
17
|
+
fastled-1.0.15.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
|
18
|
+
fastled-1.0.15.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
|
19
|
+
fastled-1.0.15.dist-info/top_level.txt,sha256=xfG6Z_ol9V5YmBROkZq2QTRwjbS2ouCUxaTJsOwfkOo,14
|
20
|
+
fastled-1.0.15.dist-info/RECORD,,
|
@@ -1,126 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: fastled
|
3
|
-
Version: 1.0.11
|
4
|
-
Summary: FastLED Wasm Compiler
|
5
|
-
Home-page: https://github.com/zackees/fastled-wasm
|
6
|
-
Maintainer: Zachary Vorhies
|
7
|
-
License: BSD 3-Clause License
|
8
|
-
Keywords: template-python-cmd
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
10
|
-
Requires-Python: >=3.7
|
11
|
-
Description-Content-Type: text/markdown
|
12
|
-
License-File: LICENSE
|
13
|
-
Requires-Dist: docker
|
14
|
-
Requires-Dist: httpx
|
15
|
-
Requires-Dist: watchdog
|
16
|
-
Requires-Dist: livereload
|
17
|
-
Requires-Dist: download
|
18
|
-
Requires-Dist: filelock
|
19
|
-
Requires-Dist: windows-curses; platform_system == "Windows"
|
20
|
-
|
21
|
-
# FastLED wasm compiler
|
22
|
-
|
23
|
-
Compiles an Arduino/Platformio sketch into a wasm binary that can be run directly in the web browser.
|
24
|
-
|
25
|
-
|
26
|
-
[](https://github.com/zackees/fastled-wasm/actions/workflows/lint.yml)
|
27
|
-
[](https://github.com/zackees/fastled-wasm/actions/workflows/build_multi_docker_image.yml)
|
28
|
-
[](https://github.com/zackees/fastled-wasm/actions/workflows/test_macos.yml)
|
29
|
-
[](https://github.com/zackees/fastled-wasm/actions/workflows/test_ubuntu.yml)
|
30
|
-
[](https://github.com/zackees/fastled-wasm/actions/workflows/test_win.yml)
|
31
|
-
|
32
|
-
|
33
|
-
# About
|
34
|
-
|
35
|
-
This python app will compile your FastLED style sketches into html/js/wasm output that runs directly in the browser.
|
36
|
-
|
37
|
-
Compile times are extremely fast - I've seen as low as 5 seconds but 8-15 seconds is typical.
|
38
|
-
|
39
|
-
This works on Windows/Linux/Mac(arm/x64).
|
40
|
-
|
41
|
-
Docker is required.
|
42
|
-
|
43
|
-
https://github.com/user-attachments/assets/64ae0e6c-5f8b-4830-ab87-dcc25bc61218
|
44
|
-
|
45
|
-
# Demo
|
46
|
-
|
47
|
-
https://zackees.github.io/fastled-wasm/
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# Install
|
52
|
-
|
53
|
-
```bash
|
54
|
-
pip install fastled-wasm
|
55
|
-
```
|
56
|
-
|
57
|
-
**Note that you may need to install x86 docker emulation on Mac-m1 and later, as this is an x86 only image at the prsent.**
|
58
|
-
|
59
|
-
# Use
|
60
|
-
|
61
|
-
Change to the directory where the sketch lives and run
|
62
|
-
|
63
|
-
```bash
|
64
|
-
cd <SKETCH-DIRECTORY>
|
65
|
-
fastled-wasm
|
66
|
-
```
|
67
|
-
|
68
|
-
Or if you don't have docker then use our web compiler
|
69
|
-
|
70
|
-
```bash
|
71
|
-
cd <SKETCH-DIRECTORY>
|
72
|
-
fastled-wasm --web
|
73
|
-
```
|
74
|
-
|
75
|
-
After compilation a web browser windows will pop up.
|
76
|
-
|
77
|
-
# Hot reload by default
|
78
|
-
|
79
|
-
Once launched, the compiler will remain open, listening to changes and recompiling as necessary and hot-reloading the sketch into the current browser.
|
80
|
-
|
81
|
-
This style of development should be familiar to those doing web development.
|
82
|
-
|
83
|
-
# Data
|
84
|
-
|
85
|
-
If you want to embed data, then do so in the `data/` directory at the project root. The files will appear in the `data/` director of any spawned FileSystem or SDCard.
|
86
|
-
|
87
|
-
|
88
|
-
### About the compilation.
|
89
|
-
|
90
|
-
Pre-processing is done to your source files. A fake Arduino.h will be inserted into your source files that will
|
91
|
-
provide shims for most of the common api points.
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# Revisions
|
96
|
-
|
97
|
-
* 1.1.11 - Dev improvement: FastLED src code volume mapped into docker will just in time update without having to manually trigger it.
|
98
|
-
* 1.1.10 - Swap large assets with embedded placeholders. This helps video sketches upload and compile instantly. Assets are re-added on after compile artifacts are returned.
|
99
|
-
* 1.1.9 - Remove auto server and instead tell the user corrective action to take.
|
100
|
-
* 1.1.8 - Program now knows it's own version which will be displayed with help file. Use `--version` to get it directly.
|
101
|
-
* 1.1.7 - Sketch cache re-enabled, but selectively invalidated on cpp/h updates. Cleaned up deprecated args. Fixed double thread running for containers that was causing slowdown.
|
102
|
-
* 1.1.6 - Use the fast src volume map allow quick updates to fastled when developing on the source code.
|
103
|
-
* 1.1.5 - Filter out hidden files and directories from being included in the sketch archive sent to the compiler.
|
104
|
-
* 1.1.4 - Fix regression introduced by testing out ipv4/ipv6 connections from a thread pool.
|
105
|
-
* 1.1.3 - Live editing of *.h and *.cpp files is now possible. Sketch cache will be disabled in this mode.
|
106
|
-
* 1.1.2 - `--server` will now volume map fastled src directory if it detects this. This was also implemented on the docker side.
|
107
|
-
* 1.1.1 - `--interactive` is now supported to debug the container. Volume maps and better compatibilty with ipv4/v6 by concurrent connection finding.
|
108
|
-
* 1.1.0 - Use `fastled` as the command for the wasm compiler.
|
109
|
-
* 1.0.17 - Pulls updates when necessary. Removed dependency on keyring.
|
110
|
-
* 1.0.16 - `fastled-wasm` package name has been changed to `fled`
|
111
|
-
* 1.0.15 - `fled` is an alias of `fastled-wasm` and will eventually replace it. `--web-host` was folded into `--web`, which if unspecified will attempt to run a local docker server and fallback to the cloud server if that fails. Specifying `--web` with no arguments will default to the cloud server while an argument (like `localhost`) will cause it to bind to that already running server for compilation.
|
112
|
-
* 1.0.14 - For non significant changes (comments, whitespace) in C++/ino/*.h files, compilation is skipped. This significantly reduces load on the server and prevents unnecessary local client browser refreshes.
|
113
|
-
* 1.0.13 - Increase speed of local compiles by running the server version of the compiler so it can keep it's cache and not have to pay docker startup costs because now it's a persistant server until exit.
|
114
|
-
* 1.0.12 - Added suppport for compile modes. Pass in `--release`, `--quick`, `--debug` for different compile options. We also support `--profile` to profile the build process.
|
115
|
-
* 1.0.11 - `--web` compile will automatically be enabled if the local build using docker fails.
|
116
|
-
* 1.0.10 - Watching files is now available for `--web`
|
117
|
-
* 1.0.9 - Enabled web compile. Access it with `--web` or `--web-host`
|
118
|
-
* 1.0.8 - Allow more than one fastled-wasm browser instances to co-exist by searching for unused ports after 8081.
|
119
|
-
* 1.0.7 - Docker multi image build implemented, tool now points to new docker image compile.
|
120
|
-
* 1.0.6 - Removed `--no-open` and `--watch`, `--watch` is now assumed unless `--just-compile` is used.
|
121
|
-
* 1.0.5 - Implemented `--update` to update the compiler image from the docker registry.
|
122
|
-
* 1.0.4 - Implemented `--watch` which will watch for changes and then re-launch the compilation step.
|
123
|
-
* 1.0.3 - Integrated `live-server` to launch when available.
|
124
|
-
* 1.0.2 - Small bug with new installs.
|
125
|
-
* 1.0.1 - Re-use is no longer the default, due to problems.
|
126
|
-
* 1.0.0 - Initial release.
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|