///
import EventEmitter from 'events';
import SmartDeviceModel, { SmartDeviceModelInterceptors } from './SmartDeviceModel';
import { LogConfig } from './utils';
import type { DpDataChangeParams, DeviceOnlineStatusUpdateParams, DeviceInfoUpdatedParams, NetworkStatusChangeParams, BluetoothAdapterStatusChangeParams } from './types';
type FailedDeviceInfo = {
/** 设备实例在管理器中的唯一键名 */
key: string;
/** 设备 ID */
deviceId: string;
/** 初始化错误 */
error: Error;
};
type AddConfig = {
/** 设备实例在管理器中的唯一键名 */
key: string;
/** 设备 ID */
deviceId: string;
/** 该设备实例的拦截器配置,常见用于搭配 dp-kit 实现结构化 DP 转换,选填,默认不配置 */
interceptors?: SmartDeviceModelInterceptors;
/** 该设备实例的日志配置,选填,默认不配置 */
logConfig?: LogConfig;
};
type BatchAddOptions = {
/** 并发初始化数量,默认 5(避免一次性批量调用过多 API 导致异常) */
concurrency?: number;
};
export type SmartDevicesManagerChangeEvent = {
type: 'add';
key: string;
instance: SmartDeviceModel;
} | {
type: 'init';
key: string;
instance: SmartDeviceModel;
} | {
type: 'initComplete';
} | {
type: 'initProgress';
progress: {
total: number;
initialized: number;
keys: string[];
};
} | {
type: 'delete';
key: string;
} | {
type: 'dpDataChange';
key: string;
instance: SmartDeviceModel;
data: DpDataChangeParams;
} | {
type: 'deviceOnlineStatusUpdate';
key: string;
instance: SmartDeviceModel;
data: DeviceOnlineStatusUpdateParams;
} | {
type: 'deviceInfoUpdated';
key: string;
instance: SmartDeviceModel;
data: DeviceInfoUpdatedParams;
} | {
type: 'networkStatusChange';
data: NetworkStatusChangeParams;
} | {
type: 'bluetoothAdapterStateChange';
data: BluetoothAdapterStatusChangeParams;
};
export type BatchAddResult = {
/**
* 成功初始化的设备实例映射
* @typeOverride success Record
*/
success: Record>;
/**
* 初始化失败的设备列表
*/
failed: FailedDeviceInfo[];
};
/**
* @public
* 多设备管理器:统一管理多个设备实例,负责批量初始化、全局事件路由、全局状态缓存,以及 add / batchAdd / delete 等标准化操作。
* 构造器本身零副作用,不会注册任何全局监听。
* @since @ray-js/panel-sdk 1.16.0
* @example 创建多设备管理器
* ```ts
* const manager = new SmartDevicesManager();
* manager.init();
* manager.add({ key: 'lamp1', deviceId: '1234567890' });
* manager.add({ key: 'lamp2', deviceId: '1234567891' });
* ```
*/
export declare class SmartDevicesManager extends EventEmitter {
private _instances;
/**
* deviceId -> key 的映射,用于 O(1) 路由事件到具体设备实例
*/
private _deviceIdMap;
/**
* 全局事件监听器是否已注册
*/
private _globalListenersRegistered;
private _globalStateCache;
private _globalStateFetching;
private _logConfig?;
/**
* 构造器零副作用:仅初始化内部状态,不注册任何全局监听。
* 使用前需调用 init() 开始监听;卸载时调用 destroy(),再次使用可重新 init()。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @param options - 配置选项
* @typeOverride options { logConfig?: LogConfig }
* @example 实例化设备管理器
* ```ts
* import { SmartDevicesManager, setSdmLogScopeMuted } from '@ray-js/panel-sdk';
*
* // 默认配置
* const deviceManager = new SmartDevicesManager();
*
* // 开启详细日志,并设置 scope 以便在必要时静音
* const deviceManager2 = new SmartDevicesManager({
* logConfig: { level: 'VERBOSE', scope: 'multi-device' },
* });
*
* // 在某些场景下(如进入单设备页)静音多设备日志
* setSdmLogScopeMuted('multi-device', true);
* // 返回多设备页时恢复
* setSdmLogScopeMuted('multi-device', false);
* ```
*/
constructor(options?: {
logConfig?: LogConfig;
});
/**
* 开始监听全局事件(Ray 全局 DP/上下线等),幂等调用。
* @remarks `add` / `batchAdd` 会自动调用 `init()`,因此如需在添加设备前预先注册全局监听(如监听 `change` 事件),可先手动调用一次 `init()`。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @returns void
* @example 预先注册全局监听
* ```ts
* import React, { useEffect } from 'react';
* import { SmartDevicesManager, SdmProvider } from '@ray-js/panel-sdk';
*
* const deviceManager = new SmartDevicesManager();
*
* export default function App({ children }) {
* useEffect(() => {
* deviceManager.init(); // 开始监听全局事件
* return () => {
* deviceManager.destroy(); // 组件卸载时清理
* };
* }, []);
*
* return (
*
* {children}
*
* );
* }
* ```
*/
init(): void;
/**
* 订阅多设备管理器变更事件。
* `SdmProvider` 内部已通过 `change` 事件驱动多设备 Hooks 刷新;常规业务通常无需手动监听。
* 仅在 Provider 外部监听设备状态变化(如全局日志、非 React 组件等场景)时使用。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @param event - 事件名,固定为 `change`
* @param handler - 事件回调函数
* @typeOverride handler (event: { type: string; [key: string]: any }) => void
* @returns 当前 Manager 实例,支持链式调用
* @remarks 常见事件类型包括:`add`、`init`、`initComplete`、`initProgress`、`delete`、`dpDataChange`、`deviceOnlineStatusUpdate` / `deviceInfoUpdated`、`networkStatusChange`、`bluetoothAdapterStateChange`。
* @example 监听不同事件类型并读取返回结构
* ```ts
* manager.on('change', event => {
* switch (event.type) {
* case 'add':
* case 'init':
* console.log(event.key, event.instance);
* break;
* case 'initProgress':
* console.log(event.progress.total, event.progress.initialized, event.progress.keys);
* break;
* case 'dpDataChange':
* case 'deviceOnlineStatusUpdate':
* case 'deviceInfoUpdated':
* console.log(event.key, event.instance, event.data);
* break;
* case 'networkStatusChange':
* case 'bluetoothAdapterStateChange':
* console.log(event.data);
* break;
* case 'initComplete':
* console.log('all initialized');
* break;
* case 'delete':
* console.log(event.key);
* break;
* }
* });
* ```
*/
on(event: 'change', handler: (event: SmartDevicesManagerChangeEvent) => void): this;
/**
* 取消订阅多设备管理器变更事件。
* 需传入与 `on` 时相同的事件名和回调引用。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @param event - 事件名,固定为 `change`
* @param handler - 事件回调函数
* @typeOverride handler (event: { type: string; [key: string]: any }) => void
* @returns 当前 Manager 实例,支持链式调用
* @remarks 注意回调引用保持一致,否则无法取消监听。
* @example 取消监听设备变更事件
* ```ts
* const handleChange = event => {
* console.log(event.type);
* };
*
* manager.on('change', handleChange);
* manager.off('change', handleChange);
* ```
*/
off(event: 'change', handler: (event: SmartDevicesManagerChangeEvent) => void): this;
/**
* 统一日志:监听 change 事件,以与 SmartDeviceModel 一致的格式输出一条带 key/type 的日志
* 使用 try/catch 避免日志逻辑抛错影响其他 change 监听器(如 SdmProvider)
*/
private __changeLogHandler__;
/**
* 注入 Manager 级 publishDps request interceptor(避免包实例方法导致递归)
* 同时兼容两类路径:
* - instance.publishDps(...)
* - instance.model.actions.xxx.set/toggle(...)
*/
private __injectManagerPublishDpsInterceptor__;
/**
* 注册全局事件监听器(仅一次)
* 多设备场景下,统一监听事件并路由到具体设备,避免每个设备独立注册导致的性能问题
*/
private _registerGlobalEventListeners;
/**
* 全局 DP 数据变化处理器(路由到具体设备实例)
*/
private __globalDpDataChangeHandler__;
/**
* 全局设备上下线状态处理器
*/
private __globalDeviceOnlineStatusHandler__;
/**
* 全局设备信息更新处理器
*/
private __globalDeviceInfoUpdatedHandler__;
/**
* 全局网络状态变化处理器
*/
private __globalNetworkStatusChangeHandler__;
/**
* 全局蓝牙适配器状态变化处理器
*/
private __globalBluetoothAdapterStateChangeHandler__;
/**
* 获取全局网络状态(懒加载 + 缓存)
* 首次调用时异步获取,后续设备直接使用缓存
*/
private getNetworkStatusShared;
/**
* 获取全局蓝牙状态(懒加载 + 缓存)
*/
private getBluetoothStatusShared;
/**
* 添加单个设备实例并完成初始化。
* 若尚未 init() 会先自动 init(),保证向后兼容。
* 常见场景是动态添加单台关联设备;若 `deviceId` 已存在则抛出错误。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @param config - 设备配置
* @returns 初始化完成的设备实例
* @typeOverride returns Promise
* @remarks 若 deviceId 已存在则会抛出错误
* @example 动态添加单个设备
* ```ts
* import { SmartDevicesManager, createDpKit } from '@ray-js/panel-sdk';
*
* const deviceManager = new SmartDevicesManager();
* const dpKit = createDpKit({ protocols });
*
* const lamp = await deviceManager.add({
* key: 'lamp1',
* deviceId: 'xxx_device_id',
* interceptors: dpKit.interceptors,
* logConfig: { level: 'VERBOSE' },
* });
*
* lamp.getDevInfo();
* ```
*/
add(config: AddConfig): Promise>;
/**
* 批量向多设备管理器中添加设备实例。内部会先批量拉取设备信息(单次网络请求),再按 `concurrency` 并发初始化,支持部分失败并在返回结果中区分成功与失败列表。
* 每台设备都可独立配置 `interceptors` 与 `logConfig`。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @param configs 设备配置列表
* @param options 配置选项
* @returns 包含 success 与 failed 的结果对象;其中 success 为成功初始化的设备实例映射,failed 为初始化失败的设备列表
* @example 批量添加设备
* ```ts
* import { SmartDevicesManager, createDpKit } from '@ray-js/panel-sdk';
*
* const deviceManager = new SmartDevicesManager();
* const dpKit = createDpKit({ protocols });
*
* const result = await deviceManager.batchAdd([
* {
* key: 'lamp1',
* deviceId: 'xxx_device_id_1',
* interceptors: dpKit.interceptors, // 搭配 dp-kit 使用结构化 DP
* },
* {
* key: 'lamp2',
* deviceId: 'xxx_device_id_2',
* interceptors: dpKit.interceptors,
* logConfig: { level: 'VERBOSE' }, // 单独开启此设备的详细日志
* },
* {
* key: 'failedDevice',
* deviceId: 'invalid_device_id',
* },
* ], { concurrency: 5 });
*
* if (result.failed.length > 0) {
* console.warn('部分设备初始化失败:', result.failed);
* }
* // batchAdd 返回示例:
* {
* "success": {
* "lamp1": "",
* "lamp2": ""
* },
* "failed": [
* {
* "key": "failedDevice",
* "deviceId": "invalid_device_id",
* "error": "Device info not found for invalid_device_id"
* }
* ]
* }
* ```
*/
batchAdd(configs: AddConfig[], options?: BatchAddOptions): Promise;
/**
* 删除设备实例。
* 从多设备管理器中删除指定 key 对应的设备实例,自动调用该实例的 `destroy()` 并清理内部映射。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @param params - 包含要删除的设备 key 的对象
* @returns void
* @example 删除单个设备
* ```ts
* manager.delete({ key: 'lamp1' });
* ```
*/
delete(params: {
key: string;
}): void;
/**
* 批量删除多设备管理器中指定 key 列表对应的设备实例,内部对每个 key 依次调用 `delete({ key })`,自动触发实例销毁与内部映射清理。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @param keys 设备实例 key 列表
* @returns void
* @example 批量删除设备
* ```ts
* deviceManager.batchDelete(['lamp1', 'lamp2']);
* ```
*/
batchDelete(keys: string[]): void;
/**
* 获取所有已注册设备实例的浅拷贝,以 key 为索引。
* 常用于需要直接操作设备实例而非通过 Hooks 的场景,例如在事件回调中遍历所有设备。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @returns 所有设备实例的记录对象
* @typeOverride returns Record
* @example 获取设备实例集合
* ```ts
* const instances = manager.getDevices();
* Object.values(instances).forEach(device => {
* device.publishDps({ switch_led: true });
* });
* ```
*/
getDevices(): {
[x: string]: SmartDeviceModel>;
};
/**
* 检查所有已添加设备是否已初始化完成。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @returns 所有的设备实例是否已初始化
* @example 检查所有设备是否已初始化
* ```ts
* const allInitialized = manager.isAllInitialized();
* console.log(allInitialized ? '全部初始化完成' : '仍在初始化中');
* ```
*/
isAllInitialized(): boolean;
/**
* 获取初始化进度(内部使用)
*/
private getInitProgress;
/**
* 检查指定设备是否已初始化(内部使用)
*/
private isInitialized;
/**
* 销毁多设备管理器:注销所有全局事件监听器、移除全部 `change` 事件订阅、销毁并清理所有设备实例与内部映射。
* @public
* @since @ray-js/panel-sdk 1.16.0
* @returns void
* @remarks 在 useEffect return 或组件 componentWillUnmount 中务必调用 destroy(),避免全局事件监听残留或内存泄漏。
* @example 销毁设备管理器
* ```ts
* // 页面卸载或清空设备时调用
* manager.destroy();
* ```
*/
destroy(): void;
}
export {};