HTTP API

用纯 HTTP 请求操控手机,可以来自 curl、PowerShell、Python requests、fetch 或任何无代码工具:端点、请求体、响应、状态码和令牌。

本页为译文。如与英文版不一致,以英文版为准。 English

你的代码连接的端口 localhost:8780 也支持 HTTP。因此,任何能发送 Web 请求的语言或工具都能使用 Droidline:curl、PowerShell、无代码自动化工具,或者没有 SDK 的语言。一个请求执行一条命令,并返回一个 JSON 响应。

端点

请求作用
GET /devices列出已配对的手机
POST /devices/{device}/{command}执行一条手机命令。JSON 请求体包含它的参数。
GET /devices/{device}/screenshot.png(或 .jpg)截图图片本身。也支持 ?scale=0.5&quality=70。
POST /server/{command}执行服务器命令:pair、pair_qr、rename、revoke
GET /ws一个 WebSocket,传输与原始套接字相同的数据行

{device} 是手机的名称或 ID,也可以写 _,表示“唯一在线的手机”。{command} 是命令参考中的任意命令名,请求体使用那里列出的参数名。

第一个请求

curl -s -X POST localhost:8780/devices/_/touch \
  -H 'content-type: application/json' \
  -d '{"by":"text","value":"OK"}'
Invoke-RestMethod -Method Post -Uri http://localhost:8780/devices/_/touch `
  -ContentType "application/json" -Body '{"by":"text","value":"OK"}'
import requests

r = requests.post("http://localhost:8780/devices/_/touch",
                  json={"by": "text", "value": "OK"})
print(r.status_code, r.json())
const r = await fetch("http://localhost:8780/devices/_/touch", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ by: "text", value: "OK" }),
});
console.log(r.status, await r.json());

每个 POST 都需要两样东西:

  • 请求头 Content-Type: application/json。没有它,服务器会返回 415,即使是不带参数的命令也一样。
  • 一个 JSON 对象作为请求体。对于没有参数的命令,发送 {},或者干脆不发请求体。

响应

命令成功时返回 200,响应中带有 "ok": true,结果的各个字段直接展开在这个对象中。只返回一个值的命令把它放在 value 下:

{"ok":true,"via":"node","ms":208}
{"ok":true,"value":"12,500"}
{"ok":true,"package":"dev.droidline.demo","activity":".MainActivity"}

命令失败时,返回其错误码对应的 HTTP 状态,响应仍是同样结构的 JSON,只是带有 "ok": false:

{"ok":false,"error":"NOT_FOUND","msg":"Could not find text 'Nope' within 1s. Current screen: dev.droidline.demo / .MainActivity","retryable":true,"screen":"dev.droidline.demo/.MainActivity","target":"text 'Nope'","timeout":1}
状态典型错误码
200成功
400BAD_ARGS、DEVICE_AMBIGUOUS
401UNAUTHORIZED
404UNKNOWN_CMD、DEVICE_NOT_FOUND
409NO_ACCESSIBILITY、NO_IME、NO_PERMISSION
422NOT_FOUND、AMBIGUOUS、MACRO_FAILED,以及其他手机无法完成的操作
503DEVICE_OFFLINE、AGENT_RESTARTED
504TIMEOUT

请从响应体中读取 error 和 retryable,不要只依赖状态码。

常用请求

# 哪些手机已配对,它们是否在线?
curl -s localhost:8780/devices

# 在指定名称的手机上打开应用
curl -s -X POST localhost:8780/devices/shelf-01/launch \
  -H 'content-type: application/json' -d '{"package":"com.android.settings"}'

# 屏幕上有没有某个元素?
curl -s -X POST localhost:8780/devices/_/exists \
  -H 'content-type: application/json' -d '{"by":"text","value":"Close ad"}'

# 等待比默认更长的时间
curl -s -X POST localhost:8780/devices/_/touch \
  -H 'content-type: application/json' -d '{"by":"text","value":"Done","timeout":30}'

# 保存界面树和截图
curl -s -X POST localhost:8780/devices/_/dump -H 'content-type: application/json' > screen.json
curl -s "localhost:8780/devices/_/screenshot.jpg?scale=0.5" -o shot.jpg

# 更换移动网络 IP,并等待手机重新上线
curl -s -X POST localhost:8780/devices/_/batch -H 'content-type: application/json' \
  -d '{"steps":[["airplane",true],["sleep",3000],["airplane",false]],"wait":true}'
# 哪些手机已配对,它们是否在线?
(Invoke-RestMethod http://localhost:8780/devices).value | Format-Table name, online, route

# 在指定名称的手机上打开应用
Invoke-RestMethod -Method Post -Uri http://localhost:8780/devices/shelf-01/launch `
  -ContentType "application/json" -Body '{"package":"com.android.settings"}'

# 屏幕上有没有某个元素?用哈希表构建请求体。
$body = @{ by = "text"; value = "Close ad" } | ConvertTo-Json
(Invoke-RestMethod -Method Post -Uri http://localhost:8780/devices/_/exists `
  -ContentType "application/json" -Body $body).value

# 保存截图
Invoke-WebRequest "http://localhost:8780/devices/_/screenshot.jpg?scale=0.5" -OutFile shot.jpg

# 捕获错误并读取错误码
try {
  Invoke-RestMethod -Method Post -Uri http://localhost:8780/devices/_/touch `
    -ContentType "application/json" -Body '{"by":"text","value":"Nope","timeout":1}'
} catch {
  $err = $_.ErrorDetails.Message | ConvertFrom-Json
  "$($err.error): $($err.msg)"
}

会切断网络的命令

data(false)、wifi(false)、airplane(true) 以及包含它们的 batch 会断开手机自身的连接。通过 HTTP 调用时,请始终发送 "wait": true:这样请求会一直保持,直到手机重新上线,再返回最终结果。不这样做的话,你只会得到 {"ok":true,"accepted":true},最终结果则会发往一个已经关闭的连接。

令牌与其他机器

默认情况下,服务器只接受来自同一台电脑的连接,这些连接不需要令牌。如果你把端口开放给网络(在 config.toml 中设置 client.listen = "0.0.0.0:8780"),每个请求都需要在 Authorization 请求头中带上客户端令牌:

droidline token create "office laptop"     # 只输出一次 dlc_...

curl -s -X POST http://192.168.0.12:8780/devices/_/current \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer dlc_...'

这样做之前,请先阅读安全。

来自网页的调用

服务器会拒绝来自网页的请求,除非网页的来源列在 client.allowed_origins 中,所以你碰巧访问的网站无法操控你的手机。要从你自己的本地 Web 应用调用这个 API,添加它的来源,例如 allowed_origins = ["http://localhost:5173"],然后重启服务器。