67 lines
3.9 KiB
TypeScript
67 lines
3.9 KiB
TypeScript
/**
|
|
* 首页长按卡片→多选→批量删除(快),保留指定设备(woan 房间:Lock 6D / Lock Pro DE / Blind Tilt 42)。
|
|
* 每轮:长按第一个"非保留"卡进多选 → 勾选同屏其它非保留卡(跨房间会让 Delete 消失,取消该卡)→ Delete → 确认。循环到只剩保留项。
|
|
* 安全开关:RESET_EXCEPT=1。 用法:RESET_EXCEPT=1 PLATFORM=android npx ts-node scripts/reset-except.ts
|
|
*/
|
|
import * as dotenv from 'dotenv';
|
|
import * as path from 'path';
|
|
dotenv.config({ path: path.resolve(__dirname, '../.env') });
|
|
import { createDriver } from '../drivers/factory';
|
|
|
|
const KEEP = (process.env.KEEP_DEVICES || 'Lock 6D,Lock Pro DE,Blind Tilt 42').split(',').map(s => s.trim());
|
|
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
|
|
|
|
// 首页设备卡名 + 中心坐标(nameText / nameTextMeter)
|
|
function devices(src: string): { name: string; cx: number; cy: number }[] {
|
|
const out: { name: string; cx: number; cy: number }[] = [];
|
|
for (const n of src.split('<')) {
|
|
if (!/resource-id="[^"]*\/(nameText|nameTextMeter)"/.test(n)) continue;
|
|
const t = (n.match(/text="([^"]*)"/) || [])[1];
|
|
const b = n.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
|
|
if (t && t.trim() && b) out.push({ name: t.trim(), cx: Math.round((+b[1] + +b[3]) / 2), cy: Math.round((+b[2] + +b[4]) / 2) });
|
|
}
|
|
return out;
|
|
}
|
|
const isKept = (name: string) => KEEP.some(k => name === k);
|
|
|
|
async function main() {
|
|
if (process.env.RESET_EXCEPT !== '1') { console.error('已阻止。RESET_EXCEPT=1 重跑。'); process.exit(2); }
|
|
const driver = createDriver();
|
|
await driver.createSession();
|
|
const d: any = driver;
|
|
const hasDelete = () => driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Delete")');
|
|
const exitSelect = async () => { const f = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Finish")'); if (f) { await driver.tapElement(f); await sleep(700); } };
|
|
try {
|
|
console.log('保留:', KEEP.join(', '));
|
|
let total = 0;
|
|
for (let round = 0; round < 30; round++) {
|
|
await driver.goBackToHomepage(); await sleep(1800);
|
|
await driver.dismissPopupIfPresent();
|
|
const all = devices(await driver.getSource());
|
|
const nonKept = all.filter(x => !isKept(x.name));
|
|
if (!nonKept.length) { console.log('只剩保留项,结束'); break; }
|
|
// 长按第一个非保留卡进多选
|
|
const first = nonKept[0];
|
|
await d.longPress(first.cx, first.cy, 1.0); await sleep(1000);
|
|
if (!(await hasDelete())) { await exitSelect(); console.log(`长按 ${first.name} 未进多选,跳过本轮`); continue; }
|
|
let batch = [first.name];
|
|
// 勾选同屏其它非保留卡(重新读坐标,避免多选模式下偏移)
|
|
for (const x of devices(await driver.getSource()).filter(o => !isKept(o.name) && o.name !== first.name)) {
|
|
await driver.tap(x.cx, x.cy); await sleep(400);
|
|
if (await hasDelete()) { batch.push(x.name); }
|
|
else { await driver.tap(x.cx, x.cy); await sleep(300); } // 跨房间→取消该卡
|
|
}
|
|
const del = await hasDelete();
|
|
if (!del) { await exitSelect(); continue; }
|
|
await driver.tapElement(del); await sleep(900);
|
|
for (let r = 0; r < 4; r++) { const s = await driver.getSource(); if (/Cancel|取消|delete|删除/i.test(s)) { let c: string | null = null; for (const t of ['Delete', 'OK', '删除', 'Confirm']) { c = await driver.findElementRaw('-android uiautomator', `new UiSelector().text("${t}")`); if (c) break; } if (c) { await driver.tapElement(c); break; } } await sleep(500); }
|
|
await sleep(2000);
|
|
total += batch.length;
|
|
console.log(`本批删 ${batch.length}: ${batch.join(', ')}`);
|
|
}
|
|
await driver.goBackToHomepage(); await sleep(1500);
|
|
console.log(`\n共删 ${total}。剩余:`, devices(await driver.getSource()).map(x => x.name).join(', ') || '(无)');
|
|
} finally { await driver.destroySession(); }
|
|
}
|
|
main().catch(e => { console.error('ERR:', e.message); process.exit(1); });
|