📱 Control Your Phone from Your Laptop · สั่งมือถือจากแล็ปท็อป · 用笔记本电脑控制手机

Step by step, in Python. Put your Android phone on your Mac screen, click it with your mouse — then hand the mouse to a Python script and let it press the buttons for you. · ทีละขั้นตอน ด้วย Python เอาหน้าจอมือถือ Android มาไว้บนจอ Mac คลิกด้วยเมาส์ได้เลย แล้วค่อยส่งเมาส์ต่อให้สคริปต์ Python กดปุ่มแทนคุณ · 用 Python 一步一步来。把安卓手机的画面搬到 Mac 屏幕上,用鼠标点它 —— 然后把鼠标交给 Python 脚本,让它替你按按钮。

Pixel · 192.168.0.51
Settings
Network
Wi-Fi ← tapped
Bluetooth
Display
Sound
$ python3 phone_control.py connected to Google Pixel screen 1080 x 2400 found the search box typed: wifi --- what is on screen --- Network Wi-Fi Bluetooth saved phone.png done.

The phone is on the left, live, on your laptop screen. The Python script on the right is what tapped Wi-Fi. Nobody touched the phone. มือถืออยู่ทางซ้าย ภาพสด บนจอแล็ปท็อปของคุณ ส่วนสคริปต์ Python ทางขวาคือตัวที่กด Wi-Fi ไม่มีใครแตะมือถือเลย 左边是手机,实时画面,就在你的笔记本屏幕上。右边的 Python 脚本才是点了 Wi-Fi 的那个。没有人碰过手机。

🇬🇧 English

There are two different things on this page, and it helps to know which one you want.

Seeing and clicking — the phone appears in a window on your Mac. You use your own keyboard and mouse. Good for teaching a class from the projector.

Scripting — Python opens apps, taps buttons and types words while you watch. Good for anything boring you do twenty times.

They share the same plumbing, so you may as well set up both.

🇹🇭 ไทย

หน้านี้มีของ สองอย่าง ที่ต่างกัน รู้ไว้ก่อนว่าคุณอยากได้อันไหนจะง่ายขึ้น

ดูและคลิก — มือถือจะโผล่มาเป็นหน้าต่างบน Mac คุณใช้คีย์บอร์ดกับเมาส์ของตัวเองได้เลย เหมาะกับการสอนหน้าห้องผ่านโปรเจกเตอร์

เขียนสคริปต์ — Python เปิดแอป กดปุ่ม และพิมพ์ข้อความให้ ขณะที่คุณนั่งดู เหมาะกับงานน่าเบื่อที่ต้องทำซ้ำยี่สิบรอบ

ทั้งสองอย่างใช้ท่อเดียวกัน ติดตั้งไปพร้อมกันเลยก็ได้

🇨🇳 中文

这一页讲的其实是两件不同的事,先弄清楚你要哪一件会轻松很多。

看得见、点得动 —— 手机变成 Mac 上的一个窗口,你用自己的键盘和鼠标操作。用投影仪给全班上课时特别好用。

写脚本 —— Python 替你开应用、点按钮、打字,你在旁边看着。凡是要重复二十遍的无聊活儿都适合。

两者用的是同一套底层管道,所以干脆一起装好。

0 What you need · ต้องมีอะไรบ้าง · 你需要什么

🇬🇧 English

An Android phone, for steps 1 to 8. Have an iPhone? The road is different. Jump to step 9.

A Mac, and Homebrew on it. If brew --version prints nothing, install it from Start Python first.

Both machines on the same wifi. No cable needed, as long as your phone is Android 11 or newer.

🇹🇭 ไทย

มือถือ Android สำหรับขั้นที่ 1 ถึง 8 ถ้าใช้ iPhone เส้นทางจะต่างออกไป ข้ามไปขั้นที่ 9

เครื่อง Mac ที่มี Homebrew ถ้าพิมพ์ brew --version แล้วไม่ขึ้นอะไร ให้ไปติดตั้งจาก เริ่มเขียน Python ก่อน

ทั้งสองเครื่องต้องอยู่บน ไวไฟวงเดียวกัน ไม่ต้องใช้สาย ถ้ามือถือเป็น Android 11 ขึ้นไป

🇨🇳 中文

一部 安卓 手机,用于第 1 到第 8 步。用 iPhone?路不一样。跳到第 9 步

一台 Mac,装好 Homebrew。如果 brew --version 什么也不打印,先去开始学 Python 装它。

两台机器连同一个 wifi。只要手机是 Android 11 或更新,就不需要数据线。

CHECK Older than Android 11? Everything below still works, but you need a USB cable once — see step 8. เก่ากว่า Android 11? ทุกอย่างข้างล่างยังใช้ได้ แต่ต้องต่อสาย USB หนึ่งครั้ง ดูขั้นที่ 8 比 Android 11 还旧?下面的步骤照样能用,但需要插一次数据线 —— 见第 8 步。

1 Wake up the hidden menu on the phone · ปลุกเมนูที่ซ่อนอยู่ในมือถือ · 叫醒手机里藏起来的菜单

🇬🇧 English

Every Android phone has a developer menu. It is hidden, because most people never need it.

  1. Settings → About phone
  2. Find Build number and tap it seven times
  3. It counts down at you, then says You are now a developer
  4. Go back to Settings → SystemDeveloper options
  5. Turn on Wireless debugging

Leave that screen open. You will need the numbers on it in a minute.

🇹🇭 ไทย

มือถือ Android ทุกเครื่องมีเมนูนักพัฒนา มันถูกซ่อนไว้ เพราะคนส่วนใหญ่ไม่ได้ใช้

  1. ตั้งค่า → เกี่ยวกับโทรศัพท์
  2. หา หมายเลขบิลด์ แล้วแตะ เจ็ดครั้ง
  3. มันจะนับถอยหลังให้ดู แล้วขึ้นว่า ตอนนี้คุณเป็นนักพัฒนาแล้ว
  4. กลับไปที่ ตั้งค่า → ระบบตัวเลือกสำหรับนักพัฒนา
  5. เปิด การแก้ไขข้อบกพร่องผ่าน Wi-Fi

เปิดหน้าจอนั้นค้างไว้ อีกเดี๋ยวจะต้องใช้ตัวเลขบนหน้านั้น

🇨🇳 中文

每一部安卓手机都有一个开发者菜单。它被藏起来了,因为大多数人一辈子用不上。

  1. 设置 → 关于手机
  2. 找到版本号,连点七下
  3. 它会倒数给你看,然后说你现在是开发者了
  4. 回到 设置 → 系统开发者选项
  5. 打开无线调试

这个界面别关。等一下要用上面的数字。

ON THE PHONE Samsung hides Build number under Settings → About phone → Software information. Xiaomi calls it MIUI version. Samsung ซ่อนหมายเลขบิลด์ไว้ที่ ตั้งค่า → เกี่ยวกับโทรศัพท์ → ข้อมูลซอฟต์แวร์ ส่วน Xiaomi เรียกว่า เวอร์ชัน MIUI 三星把版本号放在 设置 → 关于手机 → 软件信息 里。小米叫它 MIUI 版本。

2 Install the three tools · ติดตั้งเครื่องมือสามอย่าง · 装三个工具

🇬🇧 English

adb is the road between the two machines. Everything else drives on it.

scrcpy is the window that shows the phone.

uiautomator2 is the Python library that presses the buttons.

Open Terminal on the Mac and paste these in, one line at a time.

🇹🇭 ไทย

adb คือถนนระหว่างสองเครื่อง ทุกอย่างที่เหลือวิ่งอยู่บนถนนเส้นนี้

scrcpy คือหน้าต่างที่แสดงภาพมือถือ

uiautomator2 คือไลบรารี Python ที่ทำหน้าที่กดปุ่ม

เปิด Terminal บน Mac แล้ววางคำสั่งพวกนี้ ทีละบรรทัด

🇨🇳 中文

adb 是两台机器之间的那条路。其余的东西都跑在它上面。

scrcpy 是显示手机画面的那个窗口。

uiautomator2 是负责按按钮的 Python 库。

在 Mac 上打开「终端」,一行一行地粘进去。

# the road — Google's own tool
brew install --cask android-platform-tools

# the window
brew install scrcpy

# the Python library
pip3 install uiautomator2

# check all three answered
adb version
scrcpy --version
python3 -c "import uiautomator2; print('ok')"
IF PIP REFUSES Newer Macs answer externally-managed-environment. Make a little room of your own: python3 -m venv ~/phone then source ~/phone/bin/activate, and run the pip3 install again inside it. Mac รุ่นใหม่จะตอบว่า externally-managed-environment ให้สร้างห้องเล็ก ๆ ของตัวเอง python3 -m venv ~/phone แล้ว source ~/phone/bin/activate จากนั้นสั่ง pip3 install ใหม่ข้างใน 较新的 Mac 会回你一句 externally-managed-environment。给自己开一个小房间:python3 -m venv ~/phone,再 source ~/phone/bin/activate,然后在里面重新跑 pip3 install

3 Introduce the two machines · แนะนำให้สองเครื่องรู้จักกัน · 让两台机器互相认识

🇬🇧 English

This happens once, and it happens in two halves. First they exchange a secret. Then they open a line.

On the phone, in the Wireless debugging screen, tap Pair device with pairing code. You get a six-digit code and an address like 192.168.0.51:41234.

That pairing port is not the same as the connecting port. It is the single most common thing people get wrong here.

🇹🇭 ไทย

ทำ ครั้งเดียว และแบ่งเป็นสองช่วง ช่วงแรกคือแลกรหัสลับกัน ช่วงที่สองคือเปิดสาย

ที่มือถือ ในหน้าการแก้ไขข้อบกพร่องผ่าน Wi-Fi ให้แตะ จับคู่อุปกรณ์ด้วยรหัสการจับคู่ คุณจะได้รหัสหกหลักกับที่อยู่แบบ 192.168.0.51:41234

พอร์ตสำหรับจับคู่ ไม่ใช่ พอร์ตเดียวกับพอร์ตสำหรับเชื่อมต่อ นี่คือจุดที่คนพลาดกันมากที่สุด

🇨🇳 中文

这一步只做一次,分成两半。先交换一个暗号,再打开一条线路。

在手机的「无线调试」界面里,点使用配对码配对设备。你会拿到一个六位数的码,和一个像 192.168.0.51:41234 的地址。

配对用的端口不是连接用的端口。这是这一步里最多人搞错的地方。

# half one — use the pairing address and code the phone is showing
adb pair 192.168.0.51:41234
# it asks: Enter pairing code:  → type the six digits

# half two — use the OTHER address, the one at the top of the screen
adb connect 192.168.0.51:37199

# who is there?
adb devices
$ adb pair 192.168.0.51:41234 Enter pairing code: 314159 Successfully paired to 192.168.0.51:41234 $ adb connect 192.168.0.51:37199 connected to 192.168.0.51:37199 $ adb devices List of devices attached 192.168.0.51:37199 device

The word you are waiting for is device. If it says unauthorized, look at the phone — it is asking you a question. คำที่คุณรออยู่คือ device ถ้าขึ้นว่า unauthorized ให้มองที่มือถือ มันกำลังถามอะไรคุณอยู่ 你要等的那个词是 device。如果写的是 unauthorized,看一眼手机 —— 它正在问你一个问题。

4 Put the phone on your screen · เอามือถือขึ้นมาไว้บนจอ · 把手机放到你的屏幕上

🇬🇧 English

One word. That is the whole command.

A window opens with your phone in it. Click things. Type with your real keyboard. Copy text on the Mac and paste it into the phone.

For a classroom, --turn-screen-off is the good one: the projector shows the phone, but the phone in your hand stays dark and saves its battery. Press Ctrl+Shift+o to wake it again.

🇹🇭 ไทย

คำเดียว นั่นคือคำสั่งทั้งหมด

หน้าต่างจะเปิดขึ้นมาพร้อมมือถือของคุณอยู่ข้างใน คลิกได้ พิมพ์ด้วยคีย์บอร์ดจริงได้ ก๊อปข้อความบน Mac แล้ววางลงในมือถือก็ได้

ถ้าใช้ในห้องเรียน --turn-screen-off คืออันที่ดี โปรเจกเตอร์เห็นภาพมือถือ แต่มือถือในมือคุณดับอยู่ ประหยัดแบต กด Ctrl+Shift+o เพื่อปลุกอีกที

🇨🇳 中文

一个单词。整条命令就这么长。

一个窗口打开,你的手机就在里面。点它、用真键盘打字、在 Mac 上复制文字再粘到手机里,都行。

上课的话,--turn-screen-off 最好用:投影仪上看得见手机画面,而你手里的手机是黑的,还省电。按 Ctrl+Shift+o 再点亮。

# the whole thing
scrcpy

# for the classroom projector
scrcpy --turn-screen-off --stay-awake

# smaller and smoother over wifi
scrcpy --max-size=1024 --max-fps=30
In the windowในหน้าต่าง在窗口里 Does whatทำอะไร干什么
Ctrl+Shift+oTurn the phone's own screen back onเปิดหน้าจอมือถือกลับมา把手机自己的屏幕点回来
Ctrl+hHomeกลับหน้าหลัก回主屏
Ctrl+bBackย้อนกลับ返回
Drag a file inลากไฟล์เข้ามา把文件拖进来Copies it onto the phoneคัดลอกไฟล์นั้นลงมือถือ把它复制到手机里

5 Let Python press the buttons · ให้ Python เป็นคนกดปุ่ม · 让 Python 来按按钮

🇬🇧 English

Leave scrcpy running while you do this. You get to watch the script work, and that is the fastest way to see why it did not.

The first line pushes a small helper onto the phone. That takes a few seconds the first time, and never again.

🇹🇭 ไทย

เปิด scrcpy ค้างไว้ระหว่างทำขั้นนี้ คุณจะได้เห็นสคริปต์ทำงาน และนั่นคือวิธีที่เร็วที่สุดที่จะรู้ว่าทำไมมันไม่ทำงาน

บรรทัดแรกจะส่งตัวช่วยเล็ก ๆ ไปติดตั้งบนมือถือ ครั้งแรกใช้เวลาไม่กี่วินาที แล้วจะไม่ทำอีกเลย

🇨🇳 中文

做这一步的时候让 scrcpy 一直开着。你能看着脚本干活 —— 这也是搞清楚它为什么没干成的最快办法。

第一行会往手机上推一个小助手。第一次要花几秒,以后再也不用了。

python3

import uiautomator2 as u2

d = u2.connect()                    # the only phone adb can see

print(d.device_info)                # brand, model, Android version
d.screen_on()
d.unlock()

d.app_start("com.android.settings", stop=True)
d(text="Network & internet").click()
d.screenshot("phone.png")
d.press("home")

🇬🇧 English

Read that middle line again: d(text="Network & internet").click().

You did not say click 540 pixels across and 890 down. You said click the thing that says this. The phone finds it.

🇹🇭 ไทย

อ่านบรรทัดกลางอีกรอบ d(text="Network & internet").click()

คุณไม่ได้บอกว่า คลิกที่ 540 พิกเซลจากซ้าย 890 จากบน คุณบอกว่า คลิกสิ่งที่เขียนว่าอย่างนี้ แล้วมือถือไปหาเอง

🇨🇳 中文

把中间那行再读一遍:d(text="Network & internet").click()

你说的不是点横着 540、竖着 890 那个像素,你说的是点写着这句话的那个东西。手机自己去找。

6 Names, not places · ใช้ชื่อ ไม่ใช่ตำแหน่ง · 用名字,不用位置

🇬🇧 English

This is the idea worth taking away from the whole page.

You can tell a phone to tap a coordinate. It works today and breaks tomorrow — on a bigger screen, in another language, after one update moved the row down by six pixels.

Android already keeps a list of everything on screen and what each thing is called. It keeps it for blind users, so screen readers can speak the buttons out loud. uiautomator2 reads that same list.

So ask by name. It survives.

🇹🇭 ไทย

นี่คือแนวคิดที่ควรเก็บกลับไปจากทั้งหน้านี้

คุณ สั่งได้ ให้มือถือแตะที่พิกัดหนึ่ง วันนี้ใช้ได้ พรุ่งนี้พัง พอเจอจอใหญ่กว่า เจอภาษาอื่น หรือแค่อัปเดตทีเดียวแล้วแถวเลื่อนลงหกพิกเซล

Android เก็บรายการของทุกอย่างบนหน้าจอไว้อยู่แล้ว พร้อมชื่อของแต่ละอัน มันเก็บไว้เพื่อผู้ใช้ตาบอด เพื่อให้โปรแกรมอ่านหน้าจอพูดชื่อปุ่มออกมาได้ uiautomator2 อ่านรายการเดียวกันนั้น

ฉะนั้นให้ถามด้วยชื่อ แล้วมันจะอยู่รอด

🇨🇳 中文

整页里最值得带走的就是这个想法。

可以让手机去点某个坐标。今天能用,明天就坏 —— 换个大屏幕、换个语言、一次更新把那一行往下挪了六个像素,就完了。

安卓本来就存着一份「屏幕上有什么、每样东西叫什么」的清单。它是为盲人用户存的,好让读屏软件把按钮念出来。uiautomator2 读的就是同一份清单。

所以按名字问。这样才活得久。

Ask forสั่งแบบนี้这样写 Findsได้อะไร找到什么
d(text="Wi-Fi")The thing whose label is exactly thatสิ่งที่มีข้อความตรงเป๊ะ文字完全等于这句的那个
d(textContains="Wi")Anything with those letters in itอะไรก็ตามที่มีตัวอักษรนั้นอยู่凡是含这几个字的
d(description="Back")Icons with no words — the label a screen reader speaksไอคอนที่ไม่มีตัวหนังสือ ใช้ชื่อที่โปรแกรมอ่านหน้าจอพูด没有文字的图标 —— 读屏软件念的那个名字
d(resourceId="…:id/search")The name the programmer gave it. Best of all — it never gets translatedชื่อที่โปรแกรมเมอร์ตั้งไว้ ดีที่สุด เพราะไม่มีการแปลภาษา程序员给它起的名字。最稳 —— 它永远不会被翻译
.wait(timeout=5)Waits for it to appear instead of racing the phoneรอให้มันโผล่มา แทนที่จะแข่งกับมือถือ等它出现,而不是跟手机抢时间
d.dump_hierarchy()The whole list, as text. Run it when you cannot guess a nameรายการทั้งหมดเป็นข้อความ ใช้ตอนเดาชื่อไม่ออก整份清单,纯文字。猜不出名字时就跑它
TRY IT Put the phone on any screen, then run the loop below. It prints every button the phone is willing to be asked about. เปิดมือถือค้างไว้ที่หน้าจอไหนก็ได้ แล้วรันลูปข้างล่าง มันจะพิมพ์ทุกปุ่มที่มือถือยอมให้ถามถึง 把手机停在任意一个界面,然后跑下面这个循环。它会打印出手机愿意让你问的每一个按钮。

python3

for node in d.xpath('//*[@clickable="true"]').all():
    label = node.text or node.attrib.get("content-desc") or ""
    if label.strip():
        print(label.strip())

7 The whole script · สคริปต์ทั้งตัว · 完整的脚本

🇬🇧 English

One file. It connects, wakes the phone, opens Settings, types a word, lists what it can see, saves a picture and goes home.

Every piece of it is something you met above.

🇹🇭 ไทย

ไฟล์เดียว มันเชื่อมต่อ ปลุกมือถือ เปิดตั้งค่า พิมพ์คำหนึ่ง ไล่ดูว่าเห็นอะไรบ้าง บันทึกภาพ แล้วกลับหน้าหลัก

ทุกชิ้นในนั้นคือสิ่งที่คุณเจอมาแล้วข้างบน

🇨🇳 中文

一个文件。它连上手机、把它叫醒、打开设置、打一个词、列出看得见的东西、存一张图,然后回主屏。

里面的每一块,你上面都已经见过了。

# make sure the phone is still there
adb devices

# watch it happen
scrcpy &
python3 phone_control.py

8 When it goes wrong · เมื่อมันไม่ได้ดั่งใจ · 出问题的时候

What you seeสิ่งที่เห็น你看到的 Whyเพราะอะไร为什么 Do thisทำอย่างนี้这样做
failed to connect You used the pairing port to connectคุณเอาพอร์ตจับคู่ไปใช้เชื่อมต่อ你把配对端口拿去连接了 Use the port at the top of the Wireless debugging screen, not the one in the pairing popupใช้พอร์ตที่อยู่ด้านบนของหน้าการแก้ไขข้อบกพร่องผ่าน Wi-Fi ไม่ใช่พอร์ตในป๊อปอัปจับคู่用「无线调试」界面顶部那个端口,不是配对弹窗里的那个
unauthorized The phone is asking permission and nobody answeredมือถือกำลังขออนุญาต แต่ไม่มีใครตอบ手机在问你要授权,没人回答 Look at the phone. Tick Always allow and acceptมองที่มือถือ ติ๊ก อนุญาตเสมอ แล้วกดยอมรับ看手机。勾上一律允许再确认
It worked yesterday, not todayเมื่อวานใช้ได้ วันนี้ไม่ได้昨天还好好的,今天不行 Wireless debugging picks a new port every time it restartsการแก้ไขข้อบกพร่องผ่าน Wi-Fi สุ่มพอร์ตใหม่ทุกครั้งที่เริ่มใหม่无线调试每次重启都换一个新端口 Read the new port off the phone and adb connect again. The pairing is remembered; the port is notอ่านพอร์ตใหม่จากมือถือ แล้ว adb connect อีกครั้ง การจับคู่ถูกจำไว้ แต่พอร์ตไม่ถูกจำ从手机上读新端口,再 adb connect 一次。配对会被记住,端口不会
adb server version … doesn't match Two different copies of adb on the Mac, fightingมี adb สองชุดบน Mac กำลังตีกันMac 上有两份不同的 adb 在打架 adb kill-server, then which -a adb and delete the one you did not installadb kill-server แล้ว which -a adb จากนั้นลบตัวที่คุณไม่ได้ติดตั้งเองadb kill-server,再 which -a adb,把不是你装的那份删掉
The script stops halfwayสคริปต์หยุดกลางคัน脚本跑到一半停了 It asked for something before the phone had drawn itมันถามหาของก่อนที่มือถือจะวาดเสร็จ它在手机画出来之前就去要了 Put .wait(timeout=5) before the .click()ใส่ .wait(timeout=5) ก่อน .click().click() 前面加 .wait(timeout=5)
Phone older than Android 11มือถือเก่ากว่า Android 11手机比 Android 11 旧 No wireless pairing on that versionเวอร์ชันนั้นยังไม่มีการจับคู่แบบไร้สาย那个版本还没有无线配对 Plug in a USB cable once, run adb tcpip 5555, unplug, then adb connect <phone ip>:5555ต่อสาย USB หนึ่งครั้ง สั่ง adb tcpip 5555 ถอดสาย แล้ว adb connect <ไอพีมือถือ>:5555先插一次数据线,跑 adb tcpip 5555,拔线,再 adb connect <手机 ip>:5555
Nothing connects at all, at schoolที่โรงเรียนเชื่อมไม่ติดเลย在学校完全连不上 School wifi often stops devices talking to each otherไวไฟโรงเรียนมักกันไม่ให้อุปกรณ์คุยกันเอง学校 wifi 常常禁止设备之间互相通话 Turn on the phone's own hotspot and join the Mac to it. Now it is your networkเปิดฮอตสปอตของมือถือเอง แล้วให้ Mac เข้ามาต่อ ทีนี้มันเป็นเครือข่ายของคุณเอง打开手机自己的热点,让 Mac 连上去。这下网络是你自己的了

9 And if it is an iPhone · ถ้าเป็น iPhone · 如果是 iPhone

🇬🇧 English

An iPhone can be driven too. The door is narrower, and it is Apple's door, so the rules are Apple's.

The same two halves: seeing and clicking is easy now, and scripting costs you an afternoon.

Everything below needs a Mac. On Windows or Linux there is no honest path.

🇹🇭 ไทย

iPhone ก็สั่งได้เหมือนกัน แต่ประตูแคบกว่า และเป็นประตูของ Apple กฎก็เลยเป็นของ Apple

แบ่งเป็นสองช่วงเหมือนเดิม ดูและคลิก ตอนนี้ง่ายแล้ว ส่วน เขียนสคริปต์ ต้องใช้เวลาทั้งบ่าย

ทุกอย่างข้างล่างนี้ต้องใช้ Mac ถ้าเป็น Windows หรือ Linux ไม่มีทางที่ตรงไปตรงมาเลย

🇨🇳 中文

iPhone 也能被操控。只是门更窄,而且是苹果的门,所以规矩也是苹果定的。

还是那两半:看得见、点得动现在很容易,写脚本要花你一个下午。

下面的每一步都需要一台 Mac。在 Windows 或 Linux 上没有一条老实的路。

Seeing and clicking · ดูและคลิก · 看得见、点得动

Way inวิธี方式 Needsต้องมี需要 Can you click it?คลิกได้ไหม能点吗
iPhone Mirroring
Apple's own appแอปของ Apple เอง苹果自己的应用
macOS Sequoia + iOS 18, same Apple ID, phone nearby and lockedmacOS Sequoia + iOS 18, Apple ID เดียวกัน มือถืออยู่ใกล้และล็อกอยู่macOS Sequoia + iOS 18,同一个 Apple ID,手机在旁边且锁屏 Yes — mouse and keyboard, the way scrcpy does itได้ ใช้เมาส์กับคีย์บอร์ด แบบเดียวกับ scrcpy —— 鼠标键盘都行,跟 scrcpy 一样
QuickTime Player
already on every Macมีอยู่แล้วในทุก Mac每台 Mac 都自带
A USB cable. Any macOSสาย USB เส้นเดียว macOS รุ่นไหนก็ได้一根数据线。任何 macOS No — you watch only. Still fine for a projectorไม่ได้ ดูได้อย่างเดียว แต่ใช้กับโปรเจกเตอร์ได้ดี不能 —— 只能看。投影仪用足够了
AirPlay
Screen Mirroringการมิเรอร์หน้าจอ屏幕镜像
Same wifi, AirPlay Receiver on in System Settingsไวไฟวงเดียวกัน เปิด AirPlay Receiver ในการตั้งค่าระบบ同一个 wifi,系统设置里打开 AirPlay 接收器 No — but no cable eitherไม่ได้ แต่ก็ไม่ต้องใช้สาย不能 —— 但也不用线
QUICKTIME File → New Movie Recording, then click the small arrow next to the record button and pick your iPhone. Do not press record. The window is the point. ไฟล์ → อัดภาพยนตร์ใหม่ แล้วคลิกลูกศรเล็ก ๆ ข้างปุ่มอัด เลือก iPhone ของคุณ อย่ากดอัด หน้าต่างที่เห็นนั่นแหละคือสิ่งที่ต้องการ 文件 → 新建影片录制,再点录制键旁边那个小箭头,选你的 iPhone。别按录制。要的就是那个窗口。

Scripting · เขียนสคริปต์ · 写脚本

🇬🇧 English

There is no adb for iPhone. Apple's door is WebDriverAgent — a small app you build in Xcode and leave sitting on the phone. Python then talks to that app.

Xcode is free but about 10 GB. A free Apple ID signs the app for 7 days; then you build it again. A paid developer account buys a year.

That is the whole cost. After it, the Python looks almost exactly like the Android Python.

🇹🇭 ไทย

iPhone ไม่มี adb ประตูของ Apple คือ WebDriverAgent แอปเล็ก ๆ ที่คุณสร้างด้วย Xcode แล้วปล่อยไว้ในมือถือ จากนั้น Python จะคุยกับแอปตัวนั้น

Xcode ฟรี แต่ใหญ่ราว 10 GB ถ้าใช้ Apple ID ฟรี แอปจะอยู่ได้ 7 วัน แล้วต้องสร้างใหม่ ถ้าเป็นบัญชีนักพัฒนาแบบเสียเงิน จะได้หนึ่งปี

ราคาทั้งหมดมีเท่านั้น พ้นจากนี้ไป โค้ด Python แทบจะเหมือนฝั่ง Android เลย

🇨🇳 中文

iPhone 没有 adb。苹果的门叫 WebDriverAgent —— 一个你用 Xcode 编译好、留在手机上的小应用。然后 Python 去跟这个应用说话。

Xcode 免费,但大约 10 GB。免费的 Apple ID 只能给它签 7 天,之后要重新编译一次。付费开发者账号可以管一年。

代价就这些。过了这一关,Python 代码跟安卓那边几乎一模一样。

# the Python side — both are pure Python, nothing to compile
pip3 install facebook-wda tidevice3

# find the phone (plug it in, tap Trust on the phone)
tidevice3 list

# open the tunnel and LEAVE THIS WINDOW OPEN
tidevice3 -u <udid> wdaproxy -B com.facebook.WebDriverAgentRunner.xctrunner -p 8100
THE XCODE BIT Once, before any of that: clone WebDriverAgent, open it in Xcode, set your own Apple ID as the signing team, pick your phone and press Run. It installs and quits. That is all Xcode is for here. ทำครั้งเดียวก่อนทุกอย่าง โคลน WebDriverAgent เปิดใน Xcode ตั้ง Apple ID ของคุณเป็นทีมเซ็น เลือกมือถือ แล้วกด Run มันจะติดตั้งแล้วปิดตัวเอง Xcode มีหน้าที่แค่นี้ 开始前做一次:把 WebDriverAgent 克隆下来,用 Xcode 打开,把签名团队设成你自己的 Apple ID,选中你的手机,按 Run。它装好就退出了。Xcode 在这里就干这一件事。

python3

import wda

c = wda.Client("http://localhost:8100")
c.wait_ready(timeout=20)

print(c.device_info())              # name, iOS version
c.home()
c.app_start("com.apple.Preferences")

c(label="Wi-Fi").click()             # the VoiceOver label
c.screenshot("iphone.png")
c.home()

🇬🇧 English

Look at c(label="Wi-Fi") and then look back at step 6.

It is the same idea, and for the same reason. label is what VoiceOver reads out to a blind user. Apple built that list for accessibility, exactly as Google did, and automation borrows it.

Two companies who agree on almost nothing both ended up here, because naming things is how you make them findable.

🇹🇭 ไทย

ดู c(label="Wi-Fi") แล้วย้อนกลับไปดูขั้นที่ 6

มันคือแนวคิดเดียวกัน และด้วยเหตุผลเดียวกัน label คือสิ่งที่ VoiceOver อ่านให้ผู้ใช้ตาบอดฟัง Apple สร้างรายการนั้นขึ้นมาเพื่อการเข้าถึง เหมือนที่ Google ทำ แล้วงานอัตโนมัติก็มาขอยืมใช้

สองบริษัทที่แทบไม่เห็นตรงกันเลย กลับมาลงเอยที่จุดเดียวกัน เพราะการตั้งชื่อให้สิ่งของ คือวิธีทำให้มันถูกค้นเจอ

🇨🇳 中文

看一眼 c(label="Wi-Fi"),再回头看第 6 步。

是同一个想法,也是同一个原因。label 就是 VoiceOver 念给盲人用户听的那句话。苹果为无障碍建了这份清单,跟谷歌做的一模一样,自动化只是借来用。

两家几乎在任何事上都谈不拢的公司,最后走到了同一个地方 —— 因为给东西起名字,就是让它变得找得到的办法。

Androidฝั่ง Android安卓这边 iPhoneฝั่ง iPhoneiPhone 那边
u2.connect()wda.Client("http://localhost:8100")
d(text="Wi-Fi")c(label="Wi-Fi")
d(textContains="Wi")c(labelContains="Wi")
d.app_start("com.android.settings")c.app_start("com.apple.Preferences")
d.press("home")c.home()
d.dump_hierarchy()c.source()
HONEST The Android road takes twenty minutes. The iPhone road takes an afternoon, and breaks every seven days on a free Apple ID. If you only need the screen on a projector, use iPhone Mirroring and write no code at all. เส้นทางฝั่ง Android ใช้เวลายี่สิบนาที เส้นทางฝั่ง iPhone ใช้เวลาทั้งบ่าย และพังทุกเจ็ดวันถ้าใช้ Apple ID ฟรี ถ้าคุณแค่อยากเอาหน้าจอขึ้นโปรเจกเตอร์ ใช้ iPhone Mirroring แล้วไม่ต้องเขียนโค้ดเลย 安卓那条路二十分钟。iPhone 这条路要一个下午,而且用免费 Apple ID 的话每七天坏一次。如果你只是想把屏幕投到投影仪上,用 iPhone Mirroring,一行代码都不用写。

10 Now change it · ลองแก้ดู · 现在动手改它

🇬🇧 English

You now have a phone that does what a Python file tells it. Pick something you actually repeat.

🇹🇭 ไทย

ตอนนี้คุณมีมือถือที่ทำตามไฟล์ Python สั่ง เลือกงานที่คุณทำซ้ำอยู่จริง ๆ สักอย่าง

🇨🇳 中文

现在你有一部听 Python 文件指挥的手机了。挑一件你真的会重复做的事。

Try thisลองทำ试试这个 Howทำยังไง怎么做 Levelระดับ难度
Screenshot every screen of an appเก็บภาพทุกหน้าจอของแอป把一个应用每个界面都截下来A loop of .click() and d.screenshot(f"{i}.png") — free lesson materialลูปของ .click() กับ d.screenshot(f"{i}.png") ได้สื่อการสอนฟรี.click()d.screenshot(f"{i}.png") 循环 —— 免费的教学素材easy
Put the phone on the projectorฉายมือถือขึ้นโปรเจกเตอร์把手机投到投影仪上scrcpy --turn-screen-off --stay-awakeeasy
Read a word out of an appดึงคำจากในแอปออกมา从应用里读出一个词d(resourceId="…").get_text(), then do anything Python can do with itd(resourceId="…").get_text() แล้วเอาไปทำอะไรก็ได้ที่ Python ทำได้d(resourceId="…").get_text(),然后拿它去做 Python 能做的任何事harder
Let the class drive itให้ทั้งห้องเป็นคนสั่ง让全班来操作它Wrap the script in Flask from Put It on the School Wifiห่อสคริปต์ด้วย Flask จาก เอาขึ้นไวไฟโรงเรียน放到学校 wifi 上里的 Flask 把脚本包起来harder
Have the AI teacher decide what to tapให้ครู AI ตัดสินใจว่าจะกดอะไร让 AI 老师来决定点什么Feed d.dump_hierarchy() to the model in Make Your Own AI Teacherป้อน d.dump_hierarchy() ให้โมเดลใน สร้างครู AI ของคุณเองd.dump_hierarchy() 喂给做一个你自己的 AI 老师里的模型harder