import { describe, it, beforeAll, afterAll, expect } from 'vitest'; import { DeviceDriver } from '../../drivers/types'; import { createDriver } from '../../drivers/factory'; import { TestReporter } from '../../utils/test-reporter'; import { sleep, addDeviceWithSerialPairing, findDeviceNameOnHomepage, saveAddedDevice, onesAdd, curtain3PostConnect, curtainPostConnect, } from '../../utils/common'; import { getDeviceName, getModelName } from '../../config/device.config'; import * as dotenv from 'dotenv'; import * as path from 'path'; dotenv.config({ path: path.resolve(__dirname, '../../.env') }); const deviceName = getDeviceName('curtain', 'CURTAIN_DEVICE'); const model = getModelName('curtain', 'CURTAIN_DEVICE'); // 型号名(锚点用) // curtain 与 curtain3 校准流程不同:按型号区分 relay 设备 + 连接后流程 const isCurtain3 = /curtain\s*3|curtain3/i.test(model); const RELAY_DEVICE = isCurtain3 ? 'curtain3' : 'curtain'; const ADD_ANCHOR = onesAdd('curtain', model); // 首页实际设备名:curtain/curtain3/2025 首页**都叫 "Curtain XX"**(名字不带型号)→ 型号区分靠**添加向导内联改名**(RENAME_TO); // skip / home 校验统一按重命名名,curtain 与 curtain3 互不串台。可用 env 覆盖。 // 是否做行程校验(需设备装在真实导轨上);默认跳过(Install and calibrate later),CURTAIN_CALIBRATE=1 开启 const CALIBRATE = process.env.CURTAIN_CALIBRATE === '1'; // 添加完成后按**类型重命名**(便于区分 Curtain/Curtain3,后续 skip/校验按稳定名精确识别,互不串台)。 // "Curtain Auto" 与 "Curtain3 Auto" 互不匹配(Curtain+空格 vs Curtain3)。 const RENAME_TO = process.env.CURTAIN_RENAME || (isCurtain3 ? 'Curtain3 Auto' : 'Curtain Auto'); describe('Curtain Connect - 添加窗帘设备(串口配对 + 行程校验)', () => { let driver: DeviceDriver; let reporter: TestReporter; beforeAll(async () => { driver = createDriver(); await driver.createSession(); reporter = new TestReporter(`Curtain_Connect_${deviceName}`, driver.platform.toUpperCase()); }); afterAll(async () => { reporter.generate(); await driver.destroySession(); }); it(`${ADD_ANCHOR} 通过BLE添加窗帘设备`, { timeout: 360000 }, async () => { const start = Date.now(); try { // 已按类型重命名过(如 "Curtain3 Auto")→ 跳过。按稳定重命名名识别,Curtain/Curtain3 互不串台。 const renamed = await findDeviceNameOnHomepage(driver, RENAME_TO); if (renamed) { console.log(`${renamed}已在首页(已重命名),跳过重新添加`); saveAddedDevice('curtain', renamed); reporter.record(`${ADD_ANCHOR} 添加窗帘设备`, 'SKIP', Date.now() - start, `${renamed}已存在, 无需重新添加`); return; } const result = await addDeviceWithSerialPairing(driver, { categoryName: 'Curtain', // curtain / curtain3 同一添加入口 deviceKeyword: deviceName, relayDevice: RELAY_DEVICE, // 按开关键 2s(curtain ch39 / curtain3 ch37) registryCategory: 'curtain', // curtain/curtain3 均在**添加向导内联改名**为 RENAME_TO(curtain=命名页 / curtain3=Install 页)→ 落首页即该名,故 home 校验统一用 RENAME_TO namePattern: RENAME_TO, preferNamePattern: true, // 改名后名("Curtain3 Auto")含默认名子串("Curtain")→ 必须优先按 namePattern 回写真实名,否则注册表塌成通用 "Curtain" // 连接后专属流程:选模式 + 内联改名 +(curtain3:Install→行程校验 / curtain:命名页→稍后校准) postConnectSteps: (d) => isCurtain3 ? curtain3PostConnect(d, { calibrate: CALIBRATE, renameTo: RENAME_TO }) : curtainPostConnect(d, { calibrate: CALIBRATE, renameTo: RENAME_TO }), }); expect(result).toBe(true); const elapsed = ((Date.now() - start) / 1000).toFixed(1); reporter.record(`${ADD_ANCHOR} 添加窗帘设备`, 'PASS', Date.now() - start, `${model}添加成功并重命名为"${RENAME_TO}"${CALIBRATE ? '(含行程校验)' : ''}, 耗时${elapsed}s`); } catch (e: any) { const ss = await driver.screenshot().catch(() => ''); reporter.record(`${ADD_ANCHOR} 添加窗帘设备`, 'FAIL', Date.now() - start, e.message, ss); throw e; } }); });