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.
- turboadb-0.1.0/LICENSE +21 -0
- turboadb-0.1.0/PKG-INFO +477 -0
- turboadb-0.1.0/README.md +453 -0
- turboadb-0.1.0/pyproject.toml +48 -0
- turboadb-0.1.0/setup.cfg +4 -0
- turboadb-0.1.0/tests/test_offline.py +113 -0
- turboadb-0.1.0/turboadb/README.md +453 -0
- turboadb-0.1.0/turboadb/__init__.py +65 -0
- turboadb-0.1.0/turboadb/__main__.py +6 -0
- turboadb-0.1.0/turboadb/assets/icon.ico +0 -0
- turboadb-0.1.0/turboadb/assets/icon.png +0 -0
- turboadb-0.1.0/turboadb/bin/turboadb-gui.exe +0 -0
- turboadb-0.1.0/turboadb/cli.py +505 -0
- turboadb-0.1.0/turboadb/config.py +115 -0
- turboadb-0.1.0/turboadb/core.py +1040 -0
- turboadb-0.1.0/turboadb/devices.py +92 -0
- turboadb-0.1.0/turboadb/exceptions.py +49 -0
- turboadb-0.1.0/turboadb/gui/__init__.py +15 -0
- turboadb-0.1.0/turboadb/gui/__main__.py +6 -0
- turboadb-0.1.0/turboadb/gui/app.py +91 -0
- turboadb-0.1.0/turboadb/gui/apps_panel.py +141 -0
- turboadb-0.1.0/turboadb/gui/device_tab.py +264 -0
- turboadb-0.1.0/turboadb/gui/file_browser.py +202 -0
- turboadb-0.1.0/turboadb/gui/log_panel.py +61 -0
- turboadb-0.1.0/turboadb/gui/logcat_view.py +169 -0
- turboadb-0.1.0/turboadb/gui/main_window.py +403 -0
- turboadb-0.1.0/turboadb/gui/session_dialog.py +96 -0
- turboadb-0.1.0/turboadb/gui/sessions.py +51 -0
- turboadb-0.1.0/turboadb/gui/settings.py +50 -0
- turboadb-0.1.0/turboadb/gui/settings_dialog.py +109 -0
- turboadb-0.1.0/turboadb/gui/terminal.py +108 -0
- turboadb-0.1.0/turboadb/gui/theme.py +133 -0
- turboadb-0.1.0/turboadb/results.py +153 -0
- turboadb-0.1.0/turboadb/scrcpy.py +71 -0
- turboadb-0.1.0/turboadb/tools.py +172 -0
- turboadb-0.1.0/turboadb.egg-info/PKG-INFO +477 -0
- turboadb-0.1.0/turboadb.egg-info/SOURCES.txt +39 -0
- turboadb-0.1.0/turboadb.egg-info/dependency_links.txt +1 -0
- turboadb-0.1.0/turboadb.egg-info/entry_points.txt +7 -0
- turboadb-0.1.0/turboadb.egg-info/requires.txt +6 -0
- 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.
|
turboadb-0.1.0/PKG-INFO
ADDED
|
@@ -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).
|