turboadb 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. turboadb-0.1.0/LICENSE +21 -0
  2. turboadb-0.1.0/PKG-INFO +477 -0
  3. turboadb-0.1.0/README.md +453 -0
  4. turboadb-0.1.0/pyproject.toml +48 -0
  5. turboadb-0.1.0/setup.cfg +4 -0
  6. turboadb-0.1.0/tests/test_offline.py +113 -0
  7. turboadb-0.1.0/turboadb/README.md +453 -0
  8. turboadb-0.1.0/turboadb/__init__.py +65 -0
  9. turboadb-0.1.0/turboadb/__main__.py +6 -0
  10. turboadb-0.1.0/turboadb/assets/icon.ico +0 -0
  11. turboadb-0.1.0/turboadb/assets/icon.png +0 -0
  12. turboadb-0.1.0/turboadb/bin/turboadb-gui.exe +0 -0
  13. turboadb-0.1.0/turboadb/cli.py +505 -0
  14. turboadb-0.1.0/turboadb/config.py +115 -0
  15. turboadb-0.1.0/turboadb/core.py +1040 -0
  16. turboadb-0.1.0/turboadb/devices.py +92 -0
  17. turboadb-0.1.0/turboadb/exceptions.py +49 -0
  18. turboadb-0.1.0/turboadb/gui/__init__.py +15 -0
  19. turboadb-0.1.0/turboadb/gui/__main__.py +6 -0
  20. turboadb-0.1.0/turboadb/gui/app.py +91 -0
  21. turboadb-0.1.0/turboadb/gui/apps_panel.py +141 -0
  22. turboadb-0.1.0/turboadb/gui/device_tab.py +264 -0
  23. turboadb-0.1.0/turboadb/gui/file_browser.py +202 -0
  24. turboadb-0.1.0/turboadb/gui/log_panel.py +61 -0
  25. turboadb-0.1.0/turboadb/gui/logcat_view.py +169 -0
  26. turboadb-0.1.0/turboadb/gui/main_window.py +403 -0
  27. turboadb-0.1.0/turboadb/gui/session_dialog.py +96 -0
  28. turboadb-0.1.0/turboadb/gui/sessions.py +51 -0
  29. turboadb-0.1.0/turboadb/gui/settings.py +50 -0
  30. turboadb-0.1.0/turboadb/gui/settings_dialog.py +109 -0
  31. turboadb-0.1.0/turboadb/gui/terminal.py +108 -0
  32. turboadb-0.1.0/turboadb/gui/theme.py +133 -0
  33. turboadb-0.1.0/turboadb/results.py +153 -0
  34. turboadb-0.1.0/turboadb/scrcpy.py +71 -0
  35. turboadb-0.1.0/turboadb/tools.py +172 -0
  36. turboadb-0.1.0/turboadb.egg-info/PKG-INFO +477 -0
  37. turboadb-0.1.0/turboadb.egg-info/SOURCES.txt +39 -0
  38. turboadb-0.1.0/turboadb.egg-info/dependency_links.txt +1 -0
  39. turboadb-0.1.0/turboadb.egg-info/entry_points.txt +7 -0
  40. turboadb-0.1.0/turboadb.egg-info/requires.txt +6 -0
  41. turboadb-0.1.0/turboadb.egg-info/top_level.txt +1 -0
turboadb-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TurboADB contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,477 @@
1
+ Metadata-Version: 2.4
2
+ Name: turboadb
3
+ Version: 0.1.0
4
+ Summary: TurboADB — Android ADB + scrcpy device toolkit for automotive/embedded (Android Automotive OS / IVI) & general Android: Python API, CLI, and a full PyQt5 GUI.
5
+ Author: TurboADB contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://pypi.org/project/turboadb/
8
+ Project-URL: Source, https://pypi.org/project/turboadb/
9
+ Keywords: adb,scrcpy,android,automotive,android-automotive,ivi,logcat,automation,pyqt5,testing,embedded
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Software Development :: Testing
13
+ Classifier: Topic :: System :: Hardware
14
+ Classifier: Topic :: Utilities
15
+ Classifier: Intended Audience :: Developers
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Provides-Extra: gui
20
+ Requires-Dist: PyQt5>=5.15; extra == "gui"
21
+ Provides-Extra: all
22
+ Requires-Dist: PyQt5>=5.15; extra == "all"
23
+ Dynamic: license-file
24
+
25
+ # TurboADB
26
+
27
+ **An Android ADB + scrcpy device toolkit for automotive/embedded Android
28
+ (Android Automotive OS / IVI head units) and general Android work.**
29
+
30
+ TurboADB wraps Google's `adb` and `scrcpy` behind one robust, structured layer
31
+ you can drive three ways from a single `pip install turboadb`:
32
+
33
+ 1. **Python API** — `import turboadb` for scripts and test frameworks.
34
+ 2. **CLI** — `turboadb …`, fully argument-driven (argparse).
35
+ 3. **Desktop GUI** — `turboadb-gui`, a full PyQt5 app shipped as a prebuilt
36
+ Windows `.exe` (PyQt5 baked in) so it runs even where PyQt5 has no wheel
37
+ (e.g. Windows ARM64) — **no PyQt5 install required**.
38
+
39
+ Every action returns a structured result (`CommandResult` / `TransferResult` /
40
+ `StreamResult`), with a **safe mode** that returns an `OperationResult` instead
41
+ of raising, and a typed exception hierarchy (`ADBError` base). All blocking work
42
+ can run on background threads, so it never blocks or crashes the caller.
43
+
44
+ ---
45
+
46
+ ## Table of contents
47
+
48
+ - [Install](#install)
49
+ - [The GUI](#the-gui)
50
+ - [Quick start (API)](#quick-start-api)
51
+ - [Connecting — USB vs network (Wi-Fi / Ethernet)](#connecting--usb-vs-network-wifi--ethernet)
52
+ - [Running shell commands](#running-shell-commands)
53
+ - [Live logcat (continuous logs)](#live-logcat-continuous-logs)
54
+ - [File transfer (push / pull)](#file-transfer-push--pull)
55
+ - [App management](#app-management)
56
+ - [Media — screenshot & screen record](#media--screenshot--screen-record)
57
+ - [Port forwarding (forward / reverse)](#port-forwarding-forward--reverse)
58
+ - [scrcpy mirroring (the visual session)](#scrcpy-mirroring-the-visual-session)
59
+ - [Automotive (Android Automotive OS / IVI)](#automotive-android-automotive-os--ivi)
60
+ - [Result objects & error handling](#result-objects--error-handling)
61
+ - [CLI reference](#cli-reference)
62
+ - [API map](#api-map)
63
+ - [Building the GUI exe & releasing](#building-the-gui-exe--releasing)
64
+ - [License](#license)
65
+
66
+ ---
67
+
68
+ ## Install
69
+
70
+ ```bash
71
+ pip install turboadb
72
+ ```
73
+
74
+ That gives you the Python API, the `turboadb` CLI, and `turboadb-gui` (which
75
+ launches the bundled Windows exe; on other platforms install the GUI extra:
76
+ `pip install "turboadb[gui]"`).
77
+
78
+ TurboADB needs Google's **platform-tools (`adb`)** and, for mirroring,
79
+ **`scrcpy`**. It auto-detects them on your `PATH`, in the Android SDK, or in
80
+ common install locations. If they're missing it tells you exactly how to install
81
+ them — or set explicit paths:
82
+
83
+ ```bash
84
+ turboadb doctor # shows where adb / scrcpy were found (or how to get them)
85
+ ```
86
+
87
+ - `adb`: <https://developer.android.com/tools/releases/platform-tools>
88
+ - `scrcpy`: <https://github.com/Genymobile/scrcpy>
89
+ (Windows: `winget install scrcpy` · macOS: `brew install scrcpy` · Linux: `apt install scrcpy`)
90
+
91
+ Override detection any time with `ADBConfig(adb_path=..., scrcpy_path=...)`,
92
+ the `TURBOADB_ADB` / `TURBOADB_SCRCPY` environment variables, or the GUI's
93
+ Settings dialog.
94
+
95
+ ---
96
+
97
+ ## The GUI
98
+
99
+ ```bash
100
+ turboadb-gui # or: python -m turboadb.gui (from source)
101
+ turboadb-shortcut # make a Desktop shortcut (Windows)
102
+ ```
103
+
104
+ A tabbed, multi-device workspace:
105
+
106
+ - **Tabbed multi-device sessions** — each device opens its own tab; tabs are
107
+ closable, movable, and there's a **`+`** new-tab button. A **Split/tile** view
108
+ shows several devices at once.
109
+ - **Sidebar device manager** — saved targets **plus a LIVE `adb devices`** list
110
+ that auto-refreshes; a **Quick connect** filter box; right-click context menu
111
+ (New / Open / Edit / Duplicate / Delete). Double-click a live device to open it.
112
+ - **Ribbon toolbar** with colorful buttons: Device, Shell, Logcat, Files, Apps,
113
+ Scrcpy (mirror), Screenshot, Split, Settings, Help, Exit.
114
+ - **Per-device panels:** an interactive **Shell** terminal; a live **Logcat**
115
+ viewer (regex filter, level filter, pause/clear/save); a **Files** browser
116
+ (adb push/pull with file *and* folder pickers for both directions, progress
117
+ bar); an **Apps** manager (install via file dialog incl. split APKs, list,
118
+ uninstall, clear, start, stop); and a one-click **Mirror (scrcpy)** button.
119
+ - A dark, color-coded **log dock** at the bottom.
120
+ - **Settings** (persisted to `~/.turboadb/settings.json`, applied live):
121
+ dark/light theme, terminal font + size, default scrcpy options, adb/scrcpy
122
+ paths, logcat format. Defaults to a clean **black** dark theme; the light
123
+ theme isn't glaring.
124
+ - **Crash-proof:** a startup-failure native popup + crash log, a global
125
+ exception hook that logs and shows a non-fatal popup, and clean thread
126
+ shutdown when a tab closes.
127
+
128
+ The window/app icon is an automotive **speedometer** fused with the **Android
129
+ robot** and a terminal prompt.
130
+
131
+ ---
132
+
133
+ ## Quick start (API)
134
+
135
+ ```python
136
+ from turboadb import ADBHandler, ADBConfig
137
+
138
+ # USB: the only attached device (or pass serial="..." to pick one)
139
+ with ADBHandler() as dev:
140
+ print(dev.device_info().value if False else dev.shell("getprop ro.build.version.release").text)
141
+ dev.push("app.apk", "/data/local/tmp/app.apk")
142
+ dev.install("app.apk", grant_perms=True)
143
+
144
+ # live logs, with a regex match + tee to a file
145
+ dev.logcat(tag="ActivityManager", match=r"ANR|FATAL",
146
+ on_line=print, save_to="boot.log", stop_on_match=True)
147
+
148
+ dev.screenshot("shot.png")
149
+ dev.mirror(max_size=1280) # launch scrcpy
150
+ ```
151
+
152
+ The raw adb path is always available at `dev.adb_path`, and any adb command is a
153
+ call away: `dev.adb("shell", "wm", "size")`.
154
+
155
+ ---
156
+
157
+ ## Connecting — USB vs network (Wi-Fi / Ethernet)
158
+
159
+ **USB (local):**
160
+
161
+ ```python
162
+ from turboadb import ADBHandler, ADBConfig, list_devices
163
+
164
+ for d in list_devices():
165
+ print(d) # serial, state, model…
166
+
167
+ with ADBHandler(ADBConfig(serial="emulator-5554")) as dev:
168
+ ...
169
+ ```
170
+
171
+ **Network (remote head unit / IVI on the bench LAN):**
172
+
173
+ ```python
174
+ # Enable TCP mode once while on USB, then connect wirelessly:
175
+ with ADBHandler() as usb:
176
+ usb.tcpip(5555)
177
+
178
+ with ADBHandler(ADBConfig(host="192.168.1.50", port=5555)) as hu:
179
+ print(hu.shell("getprop ro.build.characteristics").text)
180
+ ```
181
+
182
+ **Android 11+ wireless pairing:**
183
+
184
+ ```python
185
+ dev = ADBHandler(ADBConfig(host="192.168.1.50", port=5555))
186
+ dev.pair("192.168.1.50", 37123, "482913") # host:pairing_port + code
187
+ dev.connect()
188
+ ```
189
+
190
+ `connect()` auto-runs `adb connect host:port` for network targets, waits for the
191
+ device, and verifies it's `device` (not `unauthorized`/`offline`) with a clear,
192
+ actionable error if not.
193
+
194
+ ---
195
+
196
+ ## Running shell commands
197
+
198
+ ```python
199
+ res = dev.shell("pm list packages -3")
200
+ print(res.ok, res.exit_code, res.duration)
201
+ for line in res.lines:
202
+ print(line)
203
+
204
+ dev.shell("settings put global development_settings_enabled 1", check=True)
205
+ dev.shell("svc power stayon true", su=True) # wrap in su -c for rooted devices
206
+
207
+ # an interactive shell session (used by the GUI terminal, usable in scripts)
208
+ sh = dev.open_shell()
209
+ sh.send_line("top -n 1")
210
+ import time; time.sleep(1)
211
+ print(sh.read().decode(errors="replace"))
212
+ sh.close()
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Live logcat (continuous logs)
218
+
219
+ Stream logcat **live, line by line**, cleanly formatted, with regex matching,
220
+ match callbacks, stop-on-match, and tee-to-file — plus tag/priority filters and
221
+ buffer selection.
222
+
223
+ ```python
224
+ # tag + minimum priority, collect ANR/FATAL, save everything, stop on first hit
225
+ res = dev.logcat(tag="ActivityManager", priority="I",
226
+ match=r"ANR|FATAL", on_match=lambda l: print("HIT:", l),
227
+ save_to="session.log", stop_on_match=True,
228
+ buffers=["main", "system", "crash"], clear_first=True)
229
+ print(res.lines, "lines,", len(res.matches), "matches")
230
+
231
+ # arbitrary streaming command + a stop event from another thread
232
+ import threading
233
+ stop = threading.Event()
234
+ dev.logcat(on_line=print, stop_event=stop) # stop.set() to end
235
+
236
+ dev.logcat_clear() # adb logcat -c
237
+ ```
238
+
239
+ `fmt=` sets the `-v` format (default `threadtime`); `dump=True` does `-d`
240
+ (dump current buffer and exit); `filterspecs=[...]` passes explicit `TAG:LEVEL`
241
+ specs. Lines are ANSI/control-char cleaned by default (`clean=True`).
242
+
243
+ ---
244
+
245
+ ## File transfer (push / pull)
246
+
247
+ Files **and** folders, with a live percent callback (the GUI shows a progress
248
+ bar):
249
+
250
+ ```python
251
+ dev.push("local_dir/", "/sdcard/local_dir", on_progress=lambda p: print(p, "%"))
252
+ dev.pull("/sdcard/Download", "out/", on_progress=print)
253
+
254
+ tr = dev.push("big.bin", "/data/local/tmp/big.bin")
255
+ print(tr.human_size, tr.human_speed, tr.duration) # 12.0MB 48.0MB/s 0.25
256
+ ```
257
+
258
+ ---
259
+
260
+ ## App management
261
+
262
+ ```python
263
+ dev.install("app.apk", replace=True, grant_perms=True) # adb install -r -g
264
+ dev.install_multiple(["base.apk", "split_config.en.apk"]) # split APKs
265
+ dev.uninstall("com.example.app", keep_data=False)
266
+
267
+ dev.list_packages(third_party=True) # -> ["com.foo", ...]
268
+ dev.clear_app("com.example.app") # pm clear
269
+ dev.start_app("com.example.app") # launcher intent
270
+ dev.start_activity("com.example.app/.MainActivity")
271
+ dev.stop_app("com.example.app") # am force-stop
272
+ dev.grant("com.example.app", "android.permission.CAMERA")
273
+ dev.revoke("com.example.app", "android.permission.CAMERA")
274
+ print(dev.current_activity()) # foreground component
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Media — screenshot & screen record
280
+
281
+ ```python
282
+ dev.screenshot("shot.png") # exec-out screencap -p
283
+ png_bytes = dev.screenshot() # raw PNG bytes if no path
284
+
285
+ # record on-device, then pull it (stop early with a stop_event)
286
+ dev.screen_record("clip.mp4", time_limit=20, size="1280x720", bit_rate="8M")
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Port forwarding (forward / reverse)
292
+
293
+ Stoppable handles (`adb forward` / `adb reverse`):
294
+
295
+ ```python
296
+ fwd = dev.forward("tcp:9222", "localabstract:chrome_devtools_remote")
297
+ # ... use 127.0.0.1:9222 on the host ...
298
+ fwd.close() # adb forward --remove
299
+
300
+ with dev.reverse("tcp:8000", "tcp:8000"): # device reaches your PC:8000
301
+ ...
302
+ print(dev.list_forwards())
303
+ ```
304
+
305
+ ---
306
+
307
+ ## scrcpy mirroring (the visual session)
308
+
309
+ Launch real-time screen mirroring + control — the visual analog of opening a
310
+ remote desktop for a host:
311
+
312
+ ```python
313
+ from turboadb import ScrcpyOptions
314
+
315
+ sess = dev.mirror(max_size=1280, bit_rate="8M", stay_awake=True)
316
+ # ... a scrcpy window is now mirroring & controlling the device ...
317
+ sess.stop()
318
+
319
+ # full control via ScrcpyOptions (crop is great for IVI displays):
320
+ opts = ScrcpyOptions(crop="1920x1080:0:0", display_id=0,
321
+ record="drive.mp4", turn_screen_off=True)
322
+ dev.mirror(opts)
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Automotive (Android Automotive OS / IVI)
328
+
329
+ ```python
330
+ with ADBHandler(ADBConfig(host="192.168.1.50")) as hu:
331
+ info = hu.device_info()
332
+ print(info["manufacturer"], info["model"], "Android", info["android_version"])
333
+ if hu.is_automotive():
334
+ print("This is an Android Automotive OS head unit.")
335
+ # multi-display head units: mirror a specific display
336
+ hu.mirror(display_id=1, crop="1280x720:0:0")
337
+ ```
338
+
339
+ `device_info()` includes an `automotive` flag (from the automotive hardware
340
+ feature / build characteristics), so you can branch IVI-specific flows.
341
+
342
+ ---
343
+
344
+ ## Result objects & error handling
345
+
346
+ Every operation returns a structured result:
347
+
348
+ | Result | Key fields / props |
349
+ |-----------------|----------------------------------------------------------------|
350
+ | `CommandResult` | `.ok`, `.exit_code`, `.stdout`, `.stderr`, `.duration`, `.text`, `.lines` |
351
+ | `TransferResult`| `.size_bytes`, `.duration`, `.files`, `.human_size`, `.human_speed` |
352
+ | `StreamResult` | `.lines`, `.matches`, `.matched`, `.saved_to` |
353
+ | `OperationResult`| `.success`/`bool`, `.value`, `.error`, `.unwrap()` (safe mode) |
354
+
355
+ **Raise mode (default)** — great for test automation:
356
+
357
+ ```python
358
+ try:
359
+ dev.shell("false", check=True)
360
+ except ADBError as exc:
361
+ print("failed:", exc)
362
+ ```
363
+
364
+ **Safe mode** — great for GUIs (never throws):
365
+
366
+ ```python
367
+ dev = ADBHandler(cfg, safe=True)
368
+ res = dev.install("app.apk")
369
+ if res: # OperationResult is falsy on failure
370
+ print("ok:", res.value)
371
+ else:
372
+ print("error:", res.error)
373
+ ```
374
+
375
+ Exception hierarchy (catch `ADBError` for everything):
376
+ `ADBNotFoundError`, `ADBConnectionError`, `ADBTimeoutError`,
377
+ `ADBNotConnectedError`, `ADBCommandError`, `ADBTransferError`,
378
+ `ADBInstallError`, `ScrcpyError`.
379
+
380
+ ---
381
+
382
+ ## CLI reference
383
+
384
+ ```
385
+ turboadb doctor # is adb / scrcpy installed?
386
+ turboadb devices # list attached/known devices
387
+ turboadb info [-s S] # device identity / build / automotive flag
388
+ turboadb shell [-s S] -- CMD… # one-shot adb shell (use --su to wrap in su -c)
389
+ turboadb logcat [-s S] [--tag T --priority I --match RE --save F --stop-on-match --clear --dump]
390
+ turboadb logcat-clear[-s S]
391
+ turboadb push [-s S] LOCAL REMOTE
392
+ turboadb pull [-s S] REMOTE LOCAL
393
+ turboadb install [-s S] APK [APK…] [--grant --downgrade --no-replace]
394
+ turboadb uninstall [-s S] PKG [--keep-data]
395
+ turboadb packages [-s S] [--third-party --system] [FILTER]
396
+ turboadb clear [-s S] PKG
397
+ turboadb start|stop [-s S] PKG
398
+ turboadb screenshot [-s S] PATH
399
+ turboadb record [-s S] PATH [--time-limit 30 --size 1280x720 --bit-rate 8M]
400
+ turboadb forward [-s S] LOCAL REMOTE # stays until Ctrl+C
401
+ turboadb reverse [-s S] REMOTE LOCAL
402
+ turboadb scrcpy [-s S] [--max-size 1280 --bit-rate 8M --record F --turn-screen-off --no-control --wait]
403
+ turboadb connect HOST:PORT
404
+ turboadb disconnect [HOST:PORT]
405
+ turboadb tcpip [-s S] [PORT]
406
+ turboadb pair HOST:PAIRPORT CODE
407
+ turboadb reboot [-s S] [recovery|bootloader|sideload]
408
+ turboadb root [-s S]
409
+ turboadb gui # launch the desktop GUI
410
+ ```
411
+
412
+ `-s` accepts a USB serial **or** a `host:port` network target. Add `--json` to
413
+ most commands for machine-readable output. Examples:
414
+
415
+ ```bash
416
+ turboadb -s 192.168.1.50:5555 shell -- dumpsys power | findstr mWakefulness
417
+ turboadb logcat --tag ActivityManager --priority I --match "ANR|FATAL" --save boot.log
418
+ turboadb install base.apk split_config.en.apk --grant
419
+ turboadb screenshot dash.png
420
+ turboadb scrcpy --max-size 1280 --bit-rate 8M
421
+ ```
422
+
423
+ Other console scripts: `turboadb-gui`, `turboadb-docs`, `turboadb-shortcut`.
424
+
425
+ ---
426
+
427
+ ## API map
428
+
429
+ ```
430
+ turboadb
431
+ ├── ADBHandler(config=ADBConfig|None, *, serial=, safe=, quiet=, log_callback=)
432
+ │ ├── connect() / disconnect() / is_connected / get_state() / wait_for_device()
433
+ │ ├── tcpip(port) / connect_tcp(host, port) / pair(host, port, code)
434
+ │ ├── reboot(mode) / root() / unroot() / remount()
435
+ │ ├── getprop(name?) / device_info() / is_automotive()
436
+ │ ├── shell(cmd, su=, check=) / shell_many() / open_shell() -> ShellSession
437
+ │ ├── iter_lines(args) / stream(args, …) / logcat(…) / logcat_clear()
438
+ │ ├── push(local, remote, on_progress=) / pull(remote, local, on_progress=)
439
+ │ ├── install() / install_multiple() / uninstall() / list_packages()
440
+ │ ├── clear_app() / start_app() / start_activity() / stop_app()
441
+ │ ├── grant() / revoke() / current_activity()
442
+ │ ├── screenshot(path?) / screen_record(path, …)
443
+ │ ├── forward(local, remote) / reverse(remote, local) -> ForwardHandle
444
+ │ ├── list_forwards() / remove_all_forwards()
445
+ │ ├── mirror(options?|**opts) -> ScrcpySession
446
+ │ └── adb(*args) · adb_path · serial
447
+ ├── ADBConfig / ScrcpyOptions
448
+ ├── Device / list_devices() / first_online()
449
+ ├── launch_scrcpy() / ScrcpySession
450
+ ├── find_adb() / find_scrcpy() / adb_available() / scrcpy_available() / diagnose()
451
+ ├── CommandResult / TransferResult / StreamResult / OperationResult / strip_ansi
452
+ └── ADBError + ADBNotFoundError / ADBConnectionError / ADBTimeoutError /
453
+ ADBNotConnectedError / ADBCommandError / ADBTransferError /
454
+ ADBInstallError / ScrcpyError
455
+ ```
456
+
457
+ ---
458
+
459
+ ## Building the GUI exe & releasing
460
+
461
+ ```bash
462
+ python scripts/make_icon.py # (re)generate the icon
463
+ python scripts/build_exe.py # -> dist/turboadb-gui.exe (PyQt5 baked in)
464
+ # copy the exe into turboadb/bin/ so the wheel ships it, then:
465
+ python scripts/release.py patch # bump -> test -> build -> twine check -> upload
466
+ ```
467
+
468
+ `turboadb-gui` runs the bundled exe when present and falls back to running from
469
+ source (`turboadb[gui]`) otherwise. The release helper reads the PyPI token from
470
+ `TWINE_PASSWORD` (never hard-coded) and supports `--wheel-only` if a flaky
471
+ network makes the sdist+wheel upload hang.
472
+
473
+ ---
474
+
475
+ ## License
476
+
477
+ MIT — see [LICENSE](LICENSE).