125 lines
6.0 KiB
TypeScript
125 lines
6.0 KiB
TypeScript
/**
|
|
* 全局偏好设置(Profile → Preferences)。温度单位是**全局**设置,不在各设备功能页 —— 在此切换,
|
|
* 影响所有温度设备(Meter / Hub / Outdoor Meter 等)。用 driver 导航(不要 adb 手点自绘首页)。
|
|
*/
|
|
import { DeviceDriver } from '../../drivers/types';
|
|
import { sleep } from './element.helper';
|
|
import { goToProfile } from './auth.helper';
|
|
|
|
/** 是否已在 Preferences 列表页(温度单位/湿度类型行可见)。 */
|
|
async function onPreferencesList(driver: DeviceDriver): Promise<boolean> {
|
|
const s = await driver.getSource().catch(() => '');
|
|
return /Temperature Unit/.test(s) && /Humidity Type/.test(s);
|
|
}
|
|
|
|
/** 点导航栏左上角**真实返回按钮**(iOS 找左上小按钮元素,Android 用系统返回)。比盲点 (33,69) 稳。 */
|
|
async function tapNavBack(driver: DeviceDriver): Promise<void> {
|
|
if (driver.platform === 'ios') {
|
|
const btns = await driver.findElementsRaw('class name', 'XCUIElementTypeButton').catch(() => [] as string[]);
|
|
for (const b of btns.slice(0, 8)) {
|
|
const r = await driver.getElementRect(b).catch(() => null);
|
|
if (r && r.width > 0 && r.x < 60 && r.y > 25 && r.y < 110 && r.width < 90) { await driver.tapElement(b).catch(() => {}); return; }
|
|
}
|
|
await driver.tap(33, 69); // 兜底
|
|
} else {
|
|
await driver.goBack().catch(() => {});
|
|
}
|
|
}
|
|
|
|
/** 进入 Profile → Preferences 页。返回是否到达。 */
|
|
export async function openPreferences(driver: DeviceDriver): Promise<boolean> {
|
|
// ① 已在 Preferences 列表 → 直接用。
|
|
if (await onPreferencesList(driver)) return true;
|
|
// ② 刚切完单位停在 Preferences 子页(Celsius/Relative Humidity 等)→ **点返回键**回到 Preferences 列表,
|
|
// 切勿回首页再重新找 Profile —— iOS 重新导航会滑动找 Profile/Preferences 入口而误点进消息/通知页(根因)。
|
|
{
|
|
const s = await driver.getSource().catch(() => '');
|
|
const inPrefSubpage = /Celsius|Fahrenheit|Relative Humidity|Absolute Humidity/i.test(s)
|
|
&& !/Sign Out|Manage Accounts/.test(s);
|
|
if (inPrefSubpage) {
|
|
for (let i = 0; i < 2; i++) {
|
|
await tapNavBack(driver); await sleep(1200);
|
|
if (await onPreferencesList(driver)) return true;
|
|
}
|
|
}
|
|
}
|
|
// ③ 正常路径:回首页 → Profile → Preferences(详情页没有底部导航,goToProfile 会失败,故先复位)。
|
|
try { await driver.goBackToHomepage(); } catch { /* ignore */ }
|
|
await sleep(800);
|
|
await goToProfile(driver);
|
|
await sleep(1000);
|
|
const pref = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Preferences")')
|
|
|| await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("Preference")');
|
|
if (!pref) { console.log('Profile 无 Preferences 入口'); return false; }
|
|
await driver.tapElement(pref);
|
|
await sleep(2000);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 切换全局温度单位。unit='C'(摄氏)/'F'(华氏)。
|
|
* Preferences 里温度单位可能是:① 行项 "Temperature Unit" 点进去选 Celsius/Fahrenheit;② 直接的 ℃/℉ 分段开关。
|
|
* 两种都兼容尝试。返回是否切换成功。
|
|
*/
|
|
export async function setTemperatureUnit(driver: DeviceDriver, unit: 'C' | 'F'): Promise<boolean> {
|
|
if (!(await openPreferences(driver))) return false;
|
|
|
|
const targets = unit === 'C'
|
|
? ['Celsius', '°C', '℃']
|
|
: ['Fahrenheit', '°F', '℉'];
|
|
|
|
// 情况①:先点 "Temperature Unit" 行进入子页
|
|
const row = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("Temperature Unit")')
|
|
|| await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("Temperature")');
|
|
if (row) { await driver.tapElement(row); await sleep(1500); }
|
|
|
|
// 选目标单位(子页或当页的分段开关)
|
|
for (const t of targets) {
|
|
const el = await driver.findElementRaw('-android uiautomator', `new UiSelector().textContains("${t}")`);
|
|
if (el) {
|
|
await driver.tapElement(el);
|
|
await sleep(1500);
|
|
console.log(`温度单位已切到 ${unit}(命中 "${t}")`);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// 没找到 → 打印 Preferences 当前文本,便于定位真实控件
|
|
const src = await driver.getSource();
|
|
const texts = (src.match(/text="[^"]+"/g) || []).filter(t => !/text=""/.test(t)).slice(0, 30).join(' | ');
|
|
console.log(`未找到温度单位控件(unit=${unit})。Preferences 可见文本: ${texts}`);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 切换全局湿度类型(Profile → Preferences → "Humidity Type")。
|
|
* type='relative'(相对湿度 %)/ 'absolute'(绝对湿度 g/m³)。
|
|
* 选项文本:"Relative Humidity %" / "Absolute Humidity g/m³"。
|
|
* 切换后**回读 Preferences 的 Humidity Type 行值**校验是否生效(功能页同时显示 %/g·m³ 无法区分)。
|
|
* 返回是否切换并校验成功。
|
|
*/
|
|
export async function setHumidityType(driver: DeviceDriver, type: 'relative' | 'absolute'): Promise<boolean> {
|
|
if (!(await openPreferences(driver))) return false;
|
|
const row = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Humidity Type")');
|
|
if (!row) { console.log('Preferences 无 Humidity Type 行'); return false; }
|
|
await driver.tapElement(row);
|
|
await sleep(1500);
|
|
|
|
const optText = type === 'relative' ? 'Relative Humidity' : 'Absolute Humidity';
|
|
const opt = await driver.findElementRaw('-android uiautomator', `new UiSelector().textContains("${optText}")`);
|
|
if (!opt) { console.log(`未找到湿度选项 "${optText}"`); return false; }
|
|
await driver.tapElement(opt);
|
|
await sleep(1500);
|
|
|
|
// 校验:回到 Preferences,Humidity Type 行现值应为目标单位符号
|
|
const src = await driver.getSource();
|
|
const wantSym = type === 'relative' ? '%' : 'g/m³';
|
|
const otherSym = type === 'relative' ? 'g/m³' : '%';
|
|
// 该页 Area Unit=square meters、Temperature=°C,湿度行的值才是 %/g·m³;absolute 时出现 g/m³ 即生效
|
|
const ok = type === 'absolute'
|
|
? src.includes('g/m³')
|
|
: (src.includes('%') && !src.includes('g/m³'));
|
|
console.log(`湿度类型切到 ${type}(期望含 "${wantSym}"): ${ok}`);
|
|
return ok;
|
|
}
|