109 lines
4.7 KiB
TypeScript
109 lines
4.7 KiB
TypeScript
/**
|
|
* 继电器板通道映射 + 物理按键动作配置(参数化,勿在脚本里写死通道号/时长)。
|
|
*
|
|
* 每路继电器接到某设备的某个物理按键或电源。这里把「设备 → 按键名 → 通道号」以及
|
|
* 「进入配对模式要按哪些键、按多久」集中配置,脚本只引用语义名(如 'remote' 的 'pairing')。
|
|
*
|
|
* **按平台区分通道**:Android 与 iOS 是两套真机,同一设备的按键接在继电器板的不同通道上。
|
|
* 通道号写成 { android, ios };动作逻辑(按哪些键/按多久)两端共用。平台取 process.env.PLATFORM
|
|
* (与 config/app.config、设备名注册表一致,默认 ios)。
|
|
*
|
|
* 实测接线(Android,2026-06):ch33=meter 主键、ch34=remote 凸键、ch35=remote 凹键。
|
|
* iOS 通道待接线后填入(留空则在 iOS 下调用会报清晰错误)。
|
|
* 时长可被 env 覆盖:RELAY_HOLD_MS_<DEVICE>_<ACTION>(全大写)。
|
|
*/
|
|
|
|
/** 通道号:两端相同写数字;两端不同写 { android, ios }。 */
|
|
export type ChannelSpec = number | { android?: number; ios?: number };
|
|
|
|
export interface RelayAction {
|
|
buttons: string[]; // 同时按下的按键名(引用本设备 buttons 的 key);单键即数组里一个
|
|
holdMs: number; // 按住毫秒数
|
|
desc?: string;
|
|
}
|
|
|
|
export interface RelayDeviceConfig {
|
|
buttons: Record<string, ChannelSpec>; // 按键名 → 通道(可按平台)
|
|
actions?: Record<string, RelayAction>;
|
|
}
|
|
|
|
export const RELAY_MAP: Record<string, RelayDeviceConfig> = {
|
|
meter: {
|
|
buttons: { main: { android: 33 /*, ios: <填iOS通道> */ } },
|
|
actions: {
|
|
pairing: { buttons: ['main'], holdMs: 2000, desc: '按住主键进入添加模式' },
|
|
},
|
|
},
|
|
remote: {
|
|
buttons: {
|
|
convex: { android: 34 /*, ios: <填> */ }, // 凸键
|
|
concave: { android: 35 /*, ios: <填> */ }, // 凹键
|
|
},
|
|
actions: {
|
|
pairing: { buttons: ['convex', 'concave'], holdMs: 2000, desc: '同时按凸键+凹键 2s 进入添加模式(切换式:按一次进、再按退,勿重按)' },
|
|
},
|
|
},
|
|
outdoor_meter: {
|
|
buttons: { main: { android: 36 /*, ios: <填> */ } },
|
|
actions: {
|
|
pairing: { buttons: ['main'], holdMs: 2000, desc: '按住主键 2s 进入添加模式' },
|
|
},
|
|
},
|
|
curtain3: {
|
|
buttons: {
|
|
power: { android: 37 /*, ios: <填> */ }, // 开关按钮
|
|
track: { android: 38 /*, ios: <填> */ }, // 控制轨道按钮(控制用,不参与配对)
|
|
},
|
|
actions: {
|
|
pairing: { buttons: ['power'], holdMs: 2000, desc: '按住开关按钮 2s 进入添加模式' },
|
|
},
|
|
},
|
|
curtain: {
|
|
buttons: { power: { android: 39 /*, ios: <填> */ } }, // 开关按钮
|
|
actions: {
|
|
pairing: { buttons: ['power'], holdMs: 2000, desc: '按住开关按钮 2s 进入添加模式' },
|
|
},
|
|
},
|
|
hub: {
|
|
buttons: { main: { android: 43 /*, ios: <填> */ } }, // Hub Mini 主键
|
|
actions: {
|
|
pairing: { buttons: ['main'], holdMs: 4000, desc: '按住主键 4s 进入添加模式(Hub Mini;指引页写3s,实测需4s)' },
|
|
},
|
|
},
|
|
};
|
|
|
|
function currentPlatform(): 'android' | 'ios' {
|
|
return (process.env.PLATFORM || 'ios').toLowerCase() === 'android' ? 'android' : 'ios';
|
|
}
|
|
|
|
/** 解析某按键在当前平台的通道号。iOS 未单独配通道时,回退到 android(两端复用同一套设备/通道时用)。 */
|
|
function channelOf(device: string, button: string): number {
|
|
const spec = RELAY_MAP[device]?.buttons?.[button];
|
|
if (spec == null) throw new Error(`relay 配置无 '${device}.${button}'(config/relay.config.ts)`);
|
|
if (typeof spec === 'number') return spec;
|
|
const plat = currentPlatform();
|
|
const ch = spec[plat] ?? spec.android; // iOS 缺省回退 android(先复用 Android 配置)
|
|
if (ch == null) throw new Error(`'${device}.${button}' 未配置 ${plat}(且无 android 回退)的通道(config/relay.config.ts)`);
|
|
return ch;
|
|
}
|
|
|
|
/** 取设备某动作要触发的通道列表 + 时长(按当前平台解析通道;env 可覆盖时长)。 */
|
|
export function resolveRelayAction(
|
|
device: string,
|
|
action: string
|
|
): { channels: number[]; holdMs: number; desc?: string } {
|
|
const dev = RELAY_MAP[device];
|
|
if (!dev) throw new Error(`relay 配置无设备 '${device}'(config/relay.config.ts)`);
|
|
const act = dev.actions?.[action];
|
|
if (!act) throw new Error(`设备 '${device}' 无动作 '${action}'`);
|
|
const channels = act.buttons.map((b) => channelOf(device, b));
|
|
const envKey = `RELAY_HOLD_MS_${device.toUpperCase()}_${action.toUpperCase()}`;
|
|
const holdMs = process.env[envKey] ? parseInt(process.env[envKey] as string, 10) : act.holdMs;
|
|
return { channels, holdMs, desc: act.desc };
|
|
}
|
|
|
|
/** 取设备某按键在当前平台的通道号。 */
|
|
export function resolveRelayButton(device: string, button: string): number {
|
|
return channelOf(device, button);
|
|
}
|