Skip to content

Commit 0bc35e8

Browse files
authored
Add Find My Phone sound playing script using Playwright (#992)
1 parent b966052 commit 0bc35e8

File tree

6 files changed

+147
-0
lines changed

6 files changed

+147
-0
lines changed

commands/README.md

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

commands/apps/find-my/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ICLOUD_EMAIL=
2+
ICLOUD_PASSWORD=
3+
DEVICE=

commands/apps/find-my/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Find My Phone Raycast
2+
3+
I lose my phone often, so I made this.
4+
5+
This is a Node.js script that uses Playwright to automate iCloud Find My and trigger a sound on your Apple device. It's perfect for people like me who misplace their phones and want a quick, automated way to make them ring—with Raycast.
6+
7+
## Usage
8+
9+
```sh
10+
git clone https://github.com/vsvaidya27/fmp-raycast
11+
cd fmp-raycast
12+
npm install
13+
cp .env.example .env
14+
# Edit .env with your real credentials
15+
chmod +x fmp.js
16+
```
17+
18+
### Add to Raycast
19+
20+
1. Open Raycast and go to **Extensions**.
21+
2. Click **Add**.
22+
3. Select **Add Script Directory**.
23+
4. Choose the `fmp-raycast` directory you just cloned.
24+
5. Set fmp as your alias for calling the script.
25+
26+
Now you can trigger the script directly from Raycast!
27+
28+
## How it works
29+
30+
- Automates login to iCloud Find My using Playwright
31+
- Selects your device by name
32+
- Triggers the "Play Sound" feature to help you locate your device
33+
34+
**Note:** You'll need to provide your iCloud credentials and device name in the `.env` file. Sometimes a 2FA check may pop up, but usually they allow you to bypass this and manual intervention is not neccesary (since you often need the very thing you are trying to find for 2FA).
35+
36+
---
37+
38+
## 🛠 Troubleshooting
39+
40+
### If you get the error `env: node: No such file or directory`
41+
42+
Raycast uses a limited shell environment, so it may not find your Node.js install.
43+
44+
To fix it:
45+
46+
1. Run this in Terminal to find your full Node path:
47+
```bash
48+
which node
49+
```
50+
2. Edit the top of `fmp.js`:
51+
Replace:
52+
```sh
53+
#!/usr/bin/env node
54+
```
55+
With:
56+
```sh
57+
#!/full/path/to/node
58+
```

commands/apps/find-my/fmp.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env node
2+
3+
// Required parameters:
4+
// @raycast.schemaVersion 1
5+
// @raycast.title Find My Phone (Auto Sound)
6+
// @raycast.mode silent
7+
// @raycast.packageName FindMy
8+
// @raycast.icon images/find-my-icon.png
9+
10+
require('dotenv').config();
11+
const { chromium } = require('playwright');
12+
13+
(async () => {
14+
const browser = await chromium.launch({ headless: false });
15+
const page = await browser.newPage();
16+
17+
// 1. Go to iCloud Find Devices
18+
await page.goto('https://www.icloud.com/find');
19+
20+
// 2. Click "Sign In"
21+
await page.waitForSelector('text=Sign In', { timeout: 20000 });
22+
await page.click('text=Sign In');
23+
24+
// 3. Wait for the Apple login iframe to appear
25+
const frameLocator = page.frameLocator('iframe[name="aid-auth-widget"]');
26+
27+
// 4. Wait for the email/phone input inside the iframe
28+
await frameLocator.getByRole('textbox', { name: 'Email or Phone Number' }).waitFor({ timeout: 20000 });
29+
30+
// 5. Fill in the email/phone
31+
await frameLocator.getByRole('textbox', { name: 'Email or Phone Number' }).fill(process.env.ICLOUD_EMAIL);
32+
33+
// 6. Click the right-arrow button
34+
await frameLocator.getByRole('button').first().click();
35+
36+
// 7. Wait for the "Continue with Password" button to appear, then click it
37+
await frameLocator.getByRole('button', { name: /Continue with Password/i }).waitFor({ timeout: 20000 });
38+
await frameLocator.getByRole('button', { name: /Continue with Password/i }).click();
39+
40+
// 8. Wait for password input
41+
await frameLocator.getByRole('textbox', { name: 'Password' }).waitFor({ timeout: 20000 });
42+
await frameLocator.getByRole('textbox', { name: 'Password' }).fill(process.env.ICLOUD_PASSWORD);
43+
44+
// 9. Click the arrow button to continue
45+
await frameLocator.getByRole('button').first().click();
46+
47+
// 10. Wait for 2FA if needed; Hopefully not because this kinda defeats the purpose of automation even though its prob for the best security-wise :(
48+
console.log('If prompted, please complete 2FA in the browser window. :(');
49+
50+
// 12. Click on your iPhone using the precise selector inside the second iframe
51+
await page.locator('iframe').nth(1).contentFrame().getByTitle(process.env.DEVICE).getByTestId('show-device-name').click();
52+
53+
// 13. Wait for the device details panel to load
54+
await page.waitForTimeout(2000);
55+
56+
// 14. Click the "Play Sound" button
57+
await page.locator('iframe').nth(1).contentFrame().getByRole('button', { name: 'Play Sound' }).click();
58+
59+
console.log('✅ Play Sound triggered for ' + process.env.DEVICE + '!');
60+
await browser.close();
61+
})();
62+
145 KB
Loading

commands/apps/find-my/package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "findmyraycast",
3+
"version": "1.0.0",
4+
"main": "fmp.js",
5+
"scripts": {
6+
"start": "node fmp.js"
7+
},
8+
"keywords": [],
9+
"author": "",
10+
"license": "ISC",
11+
"description": "",
12+
"dependencies": {
13+
"dotenv": "^16.5.0",
14+
"playwright": "^1.52.0"
15+
}
16+
}

0 commit comments

Comments
 (0)