opendevice 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.
- opendevice-0.1.0/.gitignore +31 -0
- opendevice-0.1.0/LICENSE +21 -0
- opendevice-0.1.0/PKG-INFO +336 -0
- opendevice-0.1.0/README.md +305 -0
- opendevice-0.1.0/README.zh-CN.md +290 -0
- opendevice-0.1.0/opendevice/__init__.py +45 -0
- opendevice-0.1.0/opendevice/__main__.py +6 -0
- opendevice-0.1.0/opendevice/ax/__init__.py +58 -0
- opendevice-0.1.0/opendevice/ax/ax.py +492 -0
- opendevice-0.1.0/opendevice/ax/dtx.py +502 -0
- opendevice-0.1.0/opendevice/cli/__init__.py +141 -0
- opendevice-0.1.0/opendevice/cli/apps.py +303 -0
- opendevice-0.1.0/opendevice/cli/commands.py +346 -0
- opendevice-0.1.0/opendevice/cli/common.py +140 -0
- opendevice-0.1.0/opendevice/cli/device.py +158 -0
- opendevice-0.1.0/opendevice/cli/media.py +426 -0
- opendevice-0.1.0/opendevice/cli/system.py +200 -0
- opendevice-0.1.0/opendevice/cli/wireless.py +116 -0
- opendevice-0.1.0/opendevice/core/__init__.py +26 -0
- opendevice-0.1.0/opendevice/core/console.py +34 -0
- opendevice-0.1.0/opendevice/core/errors.py +45 -0
- opendevice-0.1.0/opendevice/core/lock.py +256 -0
- opendevice-0.1.0/opendevice/core/log.py +130 -0
- opendevice-0.1.0/opendevice/core/output.py +91 -0
- opendevice-0.1.0/opendevice/core/paths.py +124 -0
- opendevice-0.1.0/opendevice/core/plistio.py +45 -0
- opendevice-0.1.0/opendevice/input/__init__.py +24 -0
- opendevice-0.1.0/opendevice/input/hid.py +428 -0
- opendevice-0.1.0/opendevice/input/pasteboard.py +92 -0
- opendevice-0.1.0/opendevice/media/__init__.py +42 -0
- opendevice-0.1.0/opendevice/media/media.py +173 -0
- opendevice-0.1.0/opendevice/media/rtp.py +239 -0
- opendevice-0.1.0/opendevice/media/stream.py +160 -0
- opendevice-0.1.0/opendevice/py.typed +0 -0
- opendevice-0.1.0/opendevice/remote/__init__.py +41 -0
- opendevice-0.1.0/opendevice/remote/coredevice.py +323 -0
- opendevice-0.1.0/opendevice/remote/http2.py +79 -0
- opendevice-0.1.0/opendevice/remote/ipstack.py +368 -0
- opendevice-0.1.0/opendevice/remote/remotexpc.py +114 -0
- opendevice-0.1.0/opendevice/remote/rsd.py +120 -0
- opendevice-0.1.0/opendevice/remote/tunnel.py +251 -0
- opendevice-0.1.0/opendevice/remote/xpc.py +206 -0
- opendevice-0.1.0/opendevice/services/__init__.py +23 -0
- opendevice-0.1.0/opendevice/services/afc.py +356 -0
- opendevice-0.1.0/opendevice/services/amfi.py +111 -0
- opendevice-0.1.0/opendevice/services/base.py +72 -0
- opendevice-0.1.0/opendevice/services/diagnostics.py +68 -0
- opendevice-0.1.0/opendevice/services/installation.py +190 -0
- opendevice-0.1.0/opendevice/services/mounter.py +761 -0
- opendevice-0.1.0/opendevice/services/syslog.py +88 -0
- opendevice-0.1.0/opendevice/services/tss.py +209 -0
- opendevice-0.1.0/opendevice/session/__init__.py +16 -0
- opendevice-0.1.0/opendevice/session/controls.py +123 -0
- opendevice-0.1.0/opendevice/session/display.py +99 -0
- opendevice-0.1.0/opendevice/session/link.py +228 -0
- opendevice-0.1.0/opendevice/session/services.py +126 -0
- opendevice-0.1.0/opendevice/session/tree.py +61 -0
- opendevice-0.1.0/opendevice/session/video.py +240 -0
- opendevice-0.1.0/opendevice/transport/__init__.py +16 -0
- opendevice-0.1.0/opendevice/transport/devices.py +140 -0
- opendevice-0.1.0/opendevice/transport/lockdown.py +112 -0
- opendevice-0.1.0/opendevice/transport/pairing.py +149 -0
- opendevice-0.1.0/opendevice/transport/service.py +128 -0
- opendevice-0.1.0/opendevice/transport/usbmux.py +181 -0
- opendevice-0.1.0/opendevice/wireless/__init__.py +19 -0
- opendevice-0.1.0/opendevice/wireless/rrp.py +757 -0
- opendevice-0.1.0/opendevice/wireless/wifi.py +188 -0
- opendevice-0.1.0/pyproject.toml +70 -0
- opendevice-0.1.0/requirements.txt +15 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
|
|
13
|
+
# Test / typing / lint caches
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
|
|
18
|
+
# Scratch files left by tools
|
|
19
|
+
*.log
|
|
20
|
+
|
|
21
|
+
# Developer disk images: fetched at runtime, never committed
|
|
22
|
+
*.dmg
|
|
23
|
+
*.trustcache
|
|
24
|
+
|
|
25
|
+
# Editors / OS
|
|
26
|
+
.vscode/
|
|
27
|
+
.idea/
|
|
28
|
+
*.swp
|
|
29
|
+
.DS_Store
|
|
30
|
+
Thumbs.db
|
|
31
|
+
.python-version
|
opendevice-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cyrilchan
|
|
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,336 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: opendevice
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight iOS device framework: USB/Wi-Fi link, H.265 streaming, input injection and the accessibility tree
|
|
5
|
+
Project-URL: Homepage, https://github.com/oops-workplace/opendevice
|
|
6
|
+
Project-URL: Repository, https://github.com/oops-workplace/opendevice
|
|
7
|
+
Project-URL: Issues, https://github.com/oops-workplace/opendevice/issues
|
|
8
|
+
Author: cyrilchan
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: accessibility,automation,coredevice,hevc,hid,ios,iphone,lockdownd,remote-pairing,remote-xpc,usbmux
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: System :: Hardware
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
25
|
+
Provides-Extra: wifi
|
|
26
|
+
Requires-Dist: cryptography>=42; extra == 'wifi'
|
|
27
|
+
Requires-Dist: opack2>=0.0.1; extra == 'wifi'
|
|
28
|
+
Requires-Dist: srptools>=1.0; extra == 'wifi'
|
|
29
|
+
Requires-Dist: zeroconf>=0.131; extra == 'wifi'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
<div align="center">
|
|
33
|
+
|
|
34
|
+
# opendevice
|
|
35
|
+
|
|
36
|
+
English · [简体中文](https://github.com/oops-workplace/opendevice/blob/master/README.zh-CN.md)
|
|
37
|
+
|
|
38
|
+
[](https://pypi.org/project/opendevice/)
|
|
39
|
+
[](https://pypi.org/project/opendevice/)
|
|
40
|
+
[](https://opensource.org/licenses/MIT)
|
|
41
|
+
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
A **lightweight** iOS device framework: native screen mirroring, touch and keyboard
|
|
45
|
+
input, and a control tree with coordinates — **UI automation without installing WDA**.
|
|
46
|
+
|
|
47
|
+
> **Adapted for iOS 27 only, use with caution.** Whether other versions work depends
|
|
48
|
+
> on the protocol; nothing else has been verified.
|
|
49
|
+
|
|
50
|
+
> **Accessibility coverage varies by app.** Apps that draw their UI without exposing it
|
|
51
|
+
> through the accessibility tree cannot be read.
|
|
52
|
+
|
|
53
|
+
> **Disclaimer.** This is an independent, third-party project. It is not affiliated
|
|
54
|
+
> with, endorsed by, or sponsored by Apple Inc.
|
|
55
|
+
|
|
56
|
+
## Contents
|
|
57
|
+
|
|
58
|
+
- [Demo](#demo)
|
|
59
|
+
- [Install](#install)
|
|
60
|
+
- [Commands](#commands)
|
|
61
|
+
- [Device](#device) · [UI](#ui) · [Screen](#screen) · [Apps and files](#apps-and-files) · [Wireless](#wireless) · [System](#system)
|
|
62
|
+
- [Using it as a library](#using-it-as-a-library)
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Demo
|
|
67
|
+
|
|
68
|
+
<img src="https://raw.githubusercontent.com/oops-workplace/opendevice/master/examples/webui/screenshots/demo.png" alt="webui demo" width="880">
|
|
69
|
+
|
|
70
|
+
A single-page browser UI that ties these together: live screen, touch, control-tree
|
|
71
|
+
coordinates, hardware keys, text input, USB↔Wi-Fi hot switching. The example
|
|
72
|
+
lives in the GitHub repository only and is not part of any PyPI distribution:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/oops-workplace/opendevice
|
|
76
|
+
cd opendevice
|
|
77
|
+
python examples/webui/run.py -d <udid> --port 8800
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then open <http://127.0.0.1:8800/>. USB mode needs no installation and runs the
|
|
81
|
+
opendevice copy inside the repository; Wi-Fi mode (`--tunnel network`) requires
|
|
82
|
+
`opendevice[wifi]` and Python 3.13+.
|
|
83
|
+
|
|
84
|
+
Two prerequisites are required: **developer mode on** and **a personalized image
|
|
85
|
+
mounted**. Without either, the screen, touch and control tree are unavailable. Both
|
|
86
|
+
can be done from the command line:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
opendevice devmode enable # turn on developer mode (the device reboots, then it is confirmed)
|
|
90
|
+
opendevice mounter mount # mount the personalized image (normally done automatically on connect)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`opendevice devmode status` and `opendevice mounter status` show the current state.
|
|
94
|
+
|
|
95
|
+
`webui` **is not an opendevice command** — it is that example's own entry point
|
|
96
|
+
(`run.py`), so its flags are its own (`--port` / `--bind` / `--tunnel` /
|
|
97
|
+
`--dump-h265` …). Run it with `--help`.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Install
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install opendevice # wired mode (USB, zero third-party dependencies)
|
|
105
|
+
pip install "opendevice[wifi]" # plus wireless mode (Wi-Fi, bonjour + RemotePairing)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Requirements:
|
|
109
|
+
|
|
110
|
+
* **Wired mode (USB)**: Python **3.9+**, only iTunes on the host side.
|
|
111
|
+
* **Wireless mode (Wi-Fi)**: Python **3.13+**, plus the dependencies in `opendevice[wifi]`.
|
|
112
|
+
* Both modes have **identical capabilities**; wireless only swaps the link.
|
|
113
|
+
**Screen mirroring and touch injection require iOS 27** (the only version-gated
|
|
114
|
+
piece); everything else (lockdown / AFC / install / apps / diagnostics / logs /
|
|
115
|
+
screenshot / developer mode) works on older systems per the protocol.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Commands
|
|
120
|
+
|
|
121
|
+
Use `opendevice` once installed; `python -m opendevice` is equivalent without an
|
|
122
|
+
install. `opendevice --help` shows the grouped quick reference,
|
|
123
|
+
`opendevice <command> --help` shows every flag for that command.
|
|
124
|
+
|
|
125
|
+
**27 commands, by domain** (the quick reference at the end of `--help` matches this):
|
|
126
|
+
|
|
127
|
+
| Domain | Commands |
|
|
128
|
+
|---|---|
|
|
129
|
+
| Device | `devices` `deviceinfo` |
|
|
130
|
+
| UI | `uitree` `click` `swipe` `key` `type` `hid` `sendtext` `paste` |
|
|
131
|
+
| Apps and files | `applist` `install` `uninstall` `launch` `kill` `ps` `afc` `screenshot` |
|
|
132
|
+
| Screen | `stream` |
|
|
133
|
+
| Wireless | `pairing` `unpairing` `wifi` |
|
|
134
|
+
| System | `devmode` `power` `syslog` `mounter` `paths` |
|
|
135
|
+
|
|
136
|
+
### Global options
|
|
137
|
+
|
|
138
|
+
| Option | Meaning |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `-d/--device` | target device: full UDID or a unique prefix |
|
|
141
|
+
| `--link auto\|usb\|wifi` | link: `auto` (default) / `usb` / `wifi` |
|
|
142
|
+
| `--force` | take over even when another process holds the device (streaming / syslog) |
|
|
143
|
+
| `--mux HOST:PORT` | usbmux address, `127.0.0.1:27015` on Windows by default |
|
|
144
|
+
| `-v/--verbose`, `--log`, `--log-file` | `-v` for INFO, `-vv` for every packet; `--log` writes to a log file |
|
|
145
|
+
|
|
146
|
+
`-d` is accepted before or after the command. When omitted and exactly one device is
|
|
147
|
+
connected it is used; with several it errors out and lists the candidates. It means
|
|
148
|
+
the same thing on both links.
|
|
149
|
+
|
|
150
|
+
`--link auto` prefers USB: it only falls back to Wi-Fi when no `-d` is given and no
|
|
151
|
+
device is found. `usb` / `wifi` are hard requirements — an unavailable link errors
|
|
152
|
+
out instead of silently switching.
|
|
153
|
+
|
|
154
|
+
### Device
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
opendevice devices # list devices (the Note column shows who holds each)
|
|
158
|
+
opendevice devices --json --fast # JSON; --fast asks only usbmux, not the product type / OS
|
|
159
|
+
opendevice deviceinfo # basic device info (the common keys)
|
|
160
|
+
opendevice deviceinfo --all # every lockdown key (--json for JSON)
|
|
161
|
+
opendevice deviceinfo --screen # main screen resolution (pixels / points / refresh rate / orientation)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### UI
|
|
165
|
+
|
|
166
|
+
Control tree and input:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
opendevice uitree --limit 20 # control tree of the current screen (with point coordinates, ready for click)
|
|
170
|
+
opendevice uitree --filter search # only elements whose text contains this, ancestors kept
|
|
171
|
+
opendevice uitree --json # raw fields of every control
|
|
172
|
+
opendevice uitree --tap search # find -> tap center -> re-fetch and compare
|
|
173
|
+
opendevice uitree --home # press Home first, then fetch
|
|
174
|
+
|
|
175
|
+
opendevice click 200,300 # tap: point coordinates from the tree (normalized internally; 0.5,0.5 works)
|
|
176
|
+
opendevice click search # or a text: locate it and tap its center (--exact / --no-verify)
|
|
177
|
+
opendevice swipe 200,600 200,200 # swipe: start -> end (also accepts point coordinates or 0..1)
|
|
178
|
+
opendevice key home # hardware key: home / power / volumeUp / volumeDown / mute / siri
|
|
179
|
+
opendevice type "abc" # type character by character (ASCII only; use sendtext for other scripts)
|
|
180
|
+
opendevice hid # which HID surfaces this device registered (--raw for raw JSON)
|
|
181
|
+
opendevice paste --set "text" # device pasteboard (--get to read back; --name picks the pasteboard)
|
|
182
|
+
opendevice sendtext "some text" # pasteboard + Cmd+V (--no-paste / --no-approve)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Touch and keyboard share one authentication with the media stream: when no stream is
|
|
186
|
+
open, the command opens one first and then sends.
|
|
187
|
+
|
|
188
|
+
### Screen
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
opendevice stream --seconds 10 --out screen.h265 # capture 10 seconds
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Captures the H.265 Annex-B the device sends and writes it as-is (mpv / VLC play it
|
|
195
|
+
directly), with no remuxing. Without `--out` only statistics are printed; with it the
|
|
196
|
+
bytes go to that path (relative to the current directory; the absolute path is printed
|
|
197
|
+
when done).
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
tunnel device fde4:7625:93a3::1 <-> host fde4:7625:93a3::2 mtu=16000 RSD port 49814 <- tunnel (stderr)
|
|
201
|
+
video negotiated: 1216x2624 @ 60fps (RTP payload type 100) <- what the device sends
|
|
202
|
+
|
|
203
|
+
duration 10.0 s <- results below (stdout)
|
|
204
|
+
RTP packets 2797 / 3.1 MB
|
|
205
|
+
lost / reordered 0 / 0
|
|
206
|
+
filtered out 10 (non-video packets: audio / control)
|
|
207
|
+
access units 564 (key frames 1) <- access units carried in the Annex-B
|
|
208
|
+
first packet key yes <- no = first frames don't decode, capture a bit longer
|
|
209
|
+
written <output>/screen.h265 (3.1 MB)
|
|
210
|
+
|
|
211
|
+
open it in VLC: <output>/screen.h265
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Apps and files
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
opendevice applist # your installed apps (--system / --all / --json)
|
|
218
|
+
opendevice install MyApp.ipa # install (auto-staged to /PublicStaging; --developer / --upgrade)
|
|
219
|
+
opendevice uninstall com.example.app
|
|
220
|
+
opendevice launch com.example.app # launch (--arg=-x to pass args; --keep-existing to keep the running one)
|
|
221
|
+
opendevice ps # running app processes (--all / --front / --json)
|
|
222
|
+
opendevice kill com.example.app # stop (bundle id / executable name / pid all work)
|
|
223
|
+
opendevice kill 1234 --signal 15 # send SIGTERM by pid (default 9 = SIGKILL)
|
|
224
|
+
opendevice screenshot --out s.png # one static screenshot
|
|
225
|
+
|
|
226
|
+
opendevice afc ls / # device filesystem (DCIM / Books / PhotoData …)
|
|
227
|
+
opendevice afc ls / --app com.x # an app sandbox (house_arrest): Documents / Library / tmp
|
|
228
|
+
opendevice afc pull /DCIM/100APPLE/IMG_0001.HEIC ./ # device -> host (directories recurse)
|
|
229
|
+
opendevice afc pull /Documents/a.txt ./ --documents # with --app, only its Documents
|
|
230
|
+
opendevice afc push ./a.txt /PublicStaging/a.txt # host -> device
|
|
231
|
+
opendevice afc rm /PublicStaging/a.txt # delete a file
|
|
232
|
+
opendevice afc rm /DCIM/100APPLE -r # delete including contents (directories need -r)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`afc`'s four actions are `ls` / `pull` (device to host) / `push` (host to device) /
|
|
236
|
+
`rm`, mapping one-to-one to the Python API's `Afc.listdir` / `pull` / `push` / `remove`.
|
|
237
|
+
|
|
238
|
+
`--app` goes through house_arrest: only open to developer-signed apps (with
|
|
239
|
+
`get-task-allow`); App Store apps are rejected (`InstallationLookupFailed`).
|
|
240
|
+
`--documents` additionally requires the app to declare `UIFileSharingEnabled`.
|
|
241
|
+
|
|
242
|
+
### Wireless
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
opendevice pairing # one-time: establish remote pairing over USB (--force to rebuild)
|
|
246
|
+
opendevice unpairing # the reverse: delete the local record (--all; pick one of many with -d)
|
|
247
|
+
opendevice wifi --list # devices advertising on the LAN
|
|
248
|
+
opendevice wifi --seconds 8 # the full Wi-Fi link (0 = tunnel only, no stream)
|
|
249
|
+
opendevice wifi --host 192.168.1.5 # specify an address; without it, discover via bonjour
|
|
250
|
+
opendevice wifi --identifier UUID # pick by the bonjour-advertised identifier when several are present
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### System
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
opendevice devmode status # also reveal / enable / accept
|
|
257
|
+
opendevice power info # all diagnostics (--json for JSON)
|
|
258
|
+
opendevice power ioreg --class X # IORegistry (also narrow with --name / --plane)
|
|
259
|
+
opendevice power battery # battery and power
|
|
260
|
+
opendevice power restart # reboot the device
|
|
261
|
+
opendevice power shutdown # shut down
|
|
262
|
+
opendevice power sleep # screen off
|
|
263
|
+
opendevice syslog --seconds 10 # system log (--filter to narrow; a long task that holds the device)
|
|
264
|
+
|
|
265
|
+
opendevice mounter list # image entries (the mounted one is the personalized DDI)
|
|
266
|
+
opendevice mounter images # cached images and which one works for this device
|
|
267
|
+
opendevice mounter status # whether developer mode is on
|
|
268
|
+
opendevice mounter lookup --type Personalized # signature of this type (--type Developer for the old style)
|
|
269
|
+
opendevice mounter nonce # the device nonce needed for a personalized image
|
|
270
|
+
opendevice mounter fetch <build> # fetch an image into the cache (default source: the opendevice-ddi mirror)
|
|
271
|
+
opendevice mounter unmount # unmount the image (--path for the mount point)
|
|
272
|
+
opendevice mounter mount # mount manually (completed automatically on connect when not mounted)
|
|
273
|
+
|
|
274
|
+
opendevice paths # where the data directory lives (--json for JSON)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Developer images are downloaded automatically: the cache is used when it has one, with no network;
|
|
278
|
+
otherwise `mounter mount` and the mount on connect fetch one first (`--url` / `OPENDEVICE_DDI_URL` changes the source, `--no-fetch` reads the cache only).
|
|
279
|
+
|
|
280
|
+
`stream` and `syslog` are **long tasks**: they hold the device (cross-process lock), so a
|
|
281
|
+
second long task on the same device is blocked (`--force` takes over). Short commands
|
|
282
|
+
like `deviceinfo` / `applist` / `afc` / `screenshot` run concurrently as usual.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Using it as a library
|
|
287
|
+
|
|
288
|
+
The top level is `DeviceSession` — one object per device:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from opendevice.session import DeviceSession
|
|
292
|
+
|
|
293
|
+
with DeviceSession() as device: # no udid: use the single connected device
|
|
294
|
+
device.start_stream(on_access_unit=lambda au, key: None)
|
|
295
|
+
device.tap(0.5, 0.5) # normalized coordinates
|
|
296
|
+
device.press_button("home")
|
|
297
|
+
device.send_text("some text") # pasteboard + Cmd+V
|
|
298
|
+
for element in device.elements():
|
|
299
|
+
print(element.text, element.center) # AXElement: text / role / frame / center
|
|
300
|
+
|
|
301
|
+
device.install("MyApp.ipa", on_progress=lambda p, s: print(p, s))
|
|
302
|
+
print([app["CFBundleIdentifier"] for app in device.apps()])
|
|
303
|
+
device.launch("com.example.app")
|
|
304
|
+
device.screenshot("shot.png") # static image, no stream needed
|
|
305
|
+
device.syslog(seconds=5, on_line=print)
|
|
306
|
+
with device.afc() as files: # filesystem
|
|
307
|
+
files.pull("/Documents/db.sqlite", "./db.sqlite")
|
|
308
|
+
device.restart()
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
To reach the protocol layer directly:
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
from opendevice import LockdownClient
|
|
315
|
+
from opendevice.remote.coredevice import CoreDeviceClient
|
|
316
|
+
from opendevice.remote.tunnel import open_rsd
|
|
317
|
+
|
|
318
|
+
with LockdownClient() as lockdown: # auto StartSession (incl. TLS)
|
|
319
|
+
print(lockdown.get_value(None, "DeviceName")) # get_value(domain, key); a single arg is the domain
|
|
320
|
+
print(lockdown.all_values()["ProductVersion"])
|
|
321
|
+
|
|
322
|
+
handle = open_rsd() # tunnel + RSD in one call, it owns the close order
|
|
323
|
+
print(len(handle.rsd.services), "services")
|
|
324
|
+
|
|
325
|
+
with CoreDeviceClient(handle.rsd, "com.apple.coredevice.deviceinfo") as core:
|
|
326
|
+
print(core.get_device_info())
|
|
327
|
+
handle.close()
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The Wi-Fi link just swaps the tunnel; everything above `RSD` is identical:
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
from opendevice.remote.tunnel import open_rsd
|
|
334
|
+
|
|
335
|
+
handle = open_rsd(backend="network") # equivalent to open_wifi_rsd(), plus a cleanup handle
|
|
336
|
+
```
|