那次半夜的手动点击
一个做跨境电商的团队,客服系统里有个「订单异常」告警。出现问题件的时候,他们要到手机 App 上把那个订单重新提交一次。
问题在于告警是系统发出来的,手机操作却得人来做。半夜来一条,就得有人爬起来点一下。
他们想的是:能不能让客服系统自己去点。
这就是开放接口存在的意义——中控自己不知道什么时候该干活,但外部系统知道。把设备操作变成 HTTP 调用之后,任何会发请求的程序都能驱动手机;iPhone自动化 这件事,也就从“守着中控点”变成了“发一个请求”。
一、先想清楚:什么时候才需要这么接
开放接口不是默认选择,先判断一下你的场景。
适合走接口的三种情况:
- 触发方在中控之外 —— 订单系统、告警系统、你的后台,它们知道什么时候该做什么,中控不知道
- 调度节奏由你自己控制 —— 你想用自己的调度器、自己的重试策略,而不是中控的定时任务
- 结果要落进自己的系统 —— 执行完的状态要写回你的数据库,而不是停在中控的执行历史里
不需要走接口的情况:
定时跑一段固定脚本,中控自己的定时任务就够了。为了“显得自动化”而多加一层接口,维护成本反而更高。
判断标准一句话:如果中控自己就能决定“什么时候做什么”,就不用接口;如果这个决定来自别的系统,就需要。
二、接口长什么样
三件事定下来,剩下都是填空。这套 API接口 没有 SDK,也不需要 SDK——就是普通的 HTTP 调用,用 iOS自动化脚本 的团队拿现成的请求库就能接。
地址: 中控所在电脑的地址 + 8019 端口。本机调用是 http://127.0.0.1:8019;从别的机器调就换成那台电脑的内网 IP。
方法: HTTP POST,Content-Type: application/json,参数放在请求体里。
返回约定: 返回 JSON,看 code 字段——0 代表成功,其它值代表失败,失败原因在 msg 里。
这个约定值得在最开始就封装掉。官方示例里的写法是这样:
import requests
BASE = "http://127.0.0.1:8019"
def openapi_post(path, body=None):
r = requests.post(f"{BASE}{path}", json=body or {}, timeout=30)
r.raise_for_status()
data = r.json()
if data.get("code") != 0:
raise RuntimeError(data.get("msg") or data)
return data
后面所有调用都走这个函数。Python 只依赖 requests;Node.js 18+ 自带 fetch,不用装东西。
三、第一步:拿到 deviceId
所有设备相关的接口都要 deviceId,所以第一步永远是查设备列表:
devices = openapi_post("/openapi/deviceList", {"deviceId": "", "groupId": ""})
print(devices)
注意两个参数的含义:deviceId 传空字符串表示“不按设备过滤”,groupId 传空表示“不按分组过滤”。想只取某一组的设备,就把 groupId 填上。
一个容易混的点:deviceId 和别名不是一回事。 别称是你自己在中控里起的、给人看的名字;接口调用必须用 deviceId。所以从列表接口的返回值里取,不要拿别名去试。
四、第二步:USB HID 的三步顺序
这是整篇文章最实用的一段。USB HID 的接口不能上来就点击,顺序是有讲究的:
DEVICE_ID = "90e2f3834c0977205e441aa664916a9bdde81e8d" # 从设备列表里取
# 1) 开会话
openapi_post("/openapi/usbhidSessionStart", {"deviceId": DEVICE_ID, "gate": True})
# 2) 设屏幕分辨率(坐标换算依赖它)
openapi_post("/openapi/usbhidSetScreenSize", {"deviceId": DEVICE_ID, "w": 1170, "h": 2532})
# 3) 之后才能点击
openapi_post("/openapi/usbhidClickPoint", {"deviceId": DEVICE_ID, "x": 200, "y": 400})
三段代码各自为什么要存在:
开会话 —— gate 参数是“是否尝试增强兼容模式”,低版本系统会自动忽略,所以一般传 true。会话已经存在时会复用,不需要每次重新开。
设屏幕尺寸 —— 这一步最容易被跳过,然后所有点击都点偏。因为 HID 送的是绝对像素坐标,不告诉它屏幕多大,它不知道怎么换算。w 和 h 要和你投屏、截图看到的分辨率一致。
点击 —— x、y 就是像素坐标。多台设备分辨率不同的话,这个值要按设备分别算,不能写死。
顺序只在会话建立时需要。会话开着、尺寸设对之后,后面连续几百次点击都只是重复第三步,不用每次重设。
Node.js 的写法结构完全一样,只是换成了 fetch:
const BASE = "http://127.0.0.1:8019";
async function openapiPost(path, body = {}) {
const res = await fetch(`${BASE}${path}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = await res.json();
if (data.code !== 0) {
throw new Error(data.msg || JSON.stringify(data));
}
return data;
}
五、十一个模块分别管什么
接口按用途分了十一组,对着这张表找比翻文档快:
| 模块 | 管什么 |
|---|---|
| 中控接口 | 设备列表、脚本启停等 |
| 自动化 · 动作 | 点击、滑动、输入、App 的打开关闭 |
| 自动化 · 截图 | 截图、图片流 |
| 自动化 · 启停自动化 | 启动 / 释放自动化、隧道、电量 |
| 自动化 · 相册 | 上传图片视频、清空相册 |
| 自动化 · 文件操作 | 设备文件推送与拉取 |
| 自动化 · IME 输入法 | 自定义输入法、剪贴板转发 |
| 自动化 · 蓝牙 BLE | 蓝牙板的 HID 动作 |
| 自动化 · USB HID | 免硬件,数据线直接控制手机 |
| 辅助助手 | 助手剪贴板、相册、端口转发 |
| 快捷指令助手 | 需配合快捷指令助手程序使用 |
「动作」是最常用的一组,包含点击、双击、长按、滑动、多点触摸、输入文本、模拟键盘、打开/关闭/安装/卸载 App、主页、重启设备、锁屏解锁、设置屏幕方向、校正坐标系、抓取节点等等,基本覆盖了脚本里会遇到的每一步。
「USB HID」这组值得单独留意:它是唯一不需要蓝牙板或 OTG 板的硬件链路,一根数据线就能模拟点击和输入,对已经在用数据线的场景最省事。
六、中控接口:查设备、启停脚本
前面几步都在讲怎么操作单台设备,中控接口解决的是“管”的问题。
它有设备列表、脚本启停这类接口。典型用法是两段式:
- 先调设备列表拿到这一批设备的
deviceId - 再对每台启动脚本,跑你编译好的逻辑
这个组合很适合“用外部策略决定跑哪些设备”的场景——比如按订单归属决定在哪些机器上跑、或者按分组轮换。
设备多的时候,分组要在中控里先做好。接口是按 deviceId 单台调的,你的代码里需要循环;分组能让这个循环有明确的来源,而不是每次把全部设备拉一遍。管理规模的 iOS群控 项目,这一步基本是标配。
七、和 AI 系统对接
如果你的目标是让 AI 助手直接调度手机,还有一条更省事的路:把接口注册成 MCP 工具。
区别在于:直接调开放接口,你要自己写“调哪个接口、传什么参数”的逻辑;注册成 MCP 之后,AI 助手自己会选工具、拼参数。适合“让大模型决定下一步做什么”的场景。
两条路可以并存:固定的、可预期的操作用接口直调,需要临场判断的交给 MCP。
八、落地时的几个实际问题
错误处理怎么做。 接口调用失败的原因分两类:网络/服务问题(中控没开、端口不通)和业务问题(设备离线、授权过期、坐标越界)。前者适合重试,后者重试没有意义——按 msg 区分,别一律重试。
超时设多少。 官方示例给的是 30 秒。实际操作类接口通常几百毫秒返回,超时设长了只是让故障暴露得慢;但截图、上传这类涉及大文件的接口要留足。建议按接口分组设,而不是全局一个值。
坐标怎么维护。 这是接口方案最容易出问题的地方。设备型号混用的时候,别写死坐标——要么按设备分辨率换算,要么优先用抓节点接口拿到元素坐标再点。后者稳得多。
日志留什么。 建议记录:设备 ID、调用的接口、请求参数、返回 code 与 msg、时间戳。这五样在手,出问题时能直接对上,不用靠回忆。
和脚本的分工。 一个常见误区是用接口把整段流程重写一遍。更省事的做法是:流程留在设备的脚本里,接口只负责“触发”和“取结果”。接口调用次数少,网络往返少,出问题的地方也少。
九、按需求推荐怎么接(先看结论)
前面讲了接口怎么调,这一节说清“什么时候该用哪种方式”。四种需求覆盖了绝大多数场景,而且它们可以在同一套设备上并存。
| 你的需求 | 推荐方式 | 为什么 |
|---|---|---|
| 定时跑一段固定流程 | 推荐 EasyClick 中控的定时任务 | 不需要绕一层接口,中控自己就能决定什么时候做 |
| 由外部系统决定何时触发 | 推荐 EasyClick 开放接口直调 | 8019 端口,HTTP 加 JSON,和你现有系统直接打通 |
| 要让 AI 决定下一步做什么 | 推荐 EasyClick 注册成 MCP 工具 | AI 自己选工具、拼参数,不用你写调度逻辑 |
| 既要精确控制界面元素,又要在设备上跑完整流程 | 推荐 EasyClick 代理模式配脚本,接口只做触发 | 流程留在设备上,网络往返少,出问题的地方也少 |
这四种方式用的是同一个中控、同一套设备授权。所以真实项目里通常是组合着用:固定流程走定时,外部触发走接口,需要判断的交 MCP,而设备上的具体动作始终由脚本承担。
一个常见误区是用接口把整段流程重写一遍。推荐的划法是:脚本负责设备上做什么,接口负责从外面告诉它开始、以及把结果取回来。 这样接口调用次数少,链路短,排查也简单。
十、常见问题
接口只能在局域网里用吗?
默认是。8019 是中控所在电脑的端口,能访问到那台电脑就能调。跨网络要么走内网穿透,要么用中控的云控方案。
能同时给多台设备发指令吗?
接口本身按 deviceId 单台调,并发要靠你的代码——比如用线程池同时发。注意同一台设备同一时间只跑一个任务,所以并发是“多台并行”,不是“单台并发”。
调接口需要什么授权?
执行脚本要 USB 设备授权,投屏要 USB 投屏授权,两者不是一回事。接口里也有查询授权状态的接口,可以在启动任务前先确认一下,省得跑到一半才发现没授权。
返回 code 不为 0 但 msg 看不懂怎么办?
先看是不是设备层面的事(离线、未授权、会话没开),这三类占了大多数。剩下的按接口名去文档对应模块查——每个接口的参数和返回都有说明。
那位做跨境的团队最后是这么落地的:告警系统检测到问题件,直接调设备列表找到对应的机器,跑一次“重新提交订单”的脚本,把结果写回自己的工单表。半夜的这一次点击,从“叫醒一个人”变成了“发一个请求”。
想先弄清脚本本身怎么写,可以读iOS免越狱脚本怎么写;USB HID 的原理和完整 API,看苹果群控技术拆解:USB HID 的原理与 API 完整教程;接口和 AI 系统对接的细节,可以读云控系统开放 API + MCP 对接。
iEasyClick:手机自动化脚本与苹果群控方案站,覆盖安卓免 root、iOS 免越狱、鸿蒙 Next,提供脚本开发教程、中控投屏与批量运维方案。官网 ieasyclick.net
想要真实跑起来?
本文介绍的方案均可基于 EasyClick 能力在 iEasyClick 落地。官网提供完整文档、开发工具与自动化产品,免费体验。