HTTP API
curl, PowerShell, Python requests, fetch, 노코드 도구에서 일반 HTTP 요청으로 폰을 다룹니다. 엔드포인트, 요청 본문, 응답, 상태 코드, 토큰을 설명합니다.
번역된 페이지입니다. 영어 문서와 내용이 다르면 영어 문서가 기준입니다. English
내 코드가 접속하는 포트 localhost:8780은 HTTP도 받습니다. 그래서 웹 요청을 보낼 수 있는 언어나 도구라면 무엇에서든 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 | 성공 |
400 | BAD_ARGS, DEVICE_AMBIGUOUS |
401 | UNAUTHORIZED |
404 | UNKNOWN_CMD, DEVICE_NOT_FOUND |
409 | NO_ACCESSIBILITY, NO_IME, NO_PERMISSION |
422 | NOT_FOUND, AMBIGUOUS, MACRO_FAILED, 그 밖에 폰이 해내지 못한 일 |
503 | DEVICE_OFFLINE, AGENT_RESTARTED |
504 | TIMEOUT |
상태 코드에만 의존하지 말고 본문의 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_...'
이렇게 하기 전에 보안을 읽으세요.
웹 페이지에서 호출하기
서버는 출처(origin)가 client.allowed_origins에 없는 웹 페이지의 요청을 거부합니다. 그래서 우연히 방문한 사이트가 내 폰을 조작할 수 없습니다. 내 로컬 웹 앱에서 API를 호출하려면 그 출처를 추가하고(예: allowed_origins = ["http://localhost:5173"]) 서버를 다시 시작하세요.