使用

智能群组模型(SGM) 是在群组环境下运行的对 智能设备模型(SDM) 的扩展。设计目标是尽量保证与 SDM 的 API 兼容,从而减少业务层的改动。大多数 SDM 的 Hooks 与 API 在 SGM 下可直接使用,但部分能力在群组场景下无法实现或语义不同,需要开发者注意并进行兼容处理。

Warning

智能群组模型目前暂不支持以下能力:

  • 通用能力中的一键执行(tapToRun)
  • 通用能力中的告警推送(alarm)

在开始使用智能群组模型之前,建议先阅读 智能设备的使用文档,SDM 的大部分示例与模式在群组场景下仍然适用:

差异说明

总体上 SGM 与 SDM 保持兼容,但在群组场景存在若干不可对齐的能力与行为差异,需在业务层予以关注:

  • 功能限制:通用能力中的一键执行(tapToRun)与告警推送(alarm)在群组场景不可用,因此相关 Hooks(如 useBuiltInAlarm、useCustomAlarm)也不可用或返回空结果。
  • 语义/行为差异:群组的设备信息结构、状态同步在细节上可能与单设备不同。
  • 性能与并发:群组操作通常会触发对多个设备的并发请求,针对不同设备协议类型,需注意节流与容错策略。

最佳实践

下面给出兼容性处理建议、Hooks 兼容实践,便于确保在群组环境下的稳定运行:

功能限制的兼容处理

大部分 Hooks(例如 useProps、useActions、useStructuredProps、useStructuredActions)在 SGM 下仍然可用。针对受限能力的 API 或 Hooks,建议采用以下兼容策略:

Warning

重要:根据 React Hooks 规则,Hooks 不能在条件语句中调用。对于可能不可用的 Hooks(如 useBuiltInAlarm、useCustomAlarm),应始终调用但处理返回的数据兼容性。

正确的 Hooks 兼容方式:

import { getLaunchOptionsSync } from '@ray-js/ray';
import { useBuiltInAlarm, useCustomAlarm } from '@ray-js/panel-sdk';

const isGroupDevice = !!getLaunchOptionsSync()?.query?.groupId;

// ✅ 正确:始终调用 Hooks,处理返回数据的兼容性
const builtInAlarmData = useBuiltInAlarm();
const customAlarmData = useCustomAlarm();

// 在群组环境下,这些 Hooks 可能返回空或无效数据,需要做兼容处理
const safeBuiltInAlarms = isGroupDevice ? [] : (builtInAlarmData?.alarms || []);
const safeCustomAlarms = isGroupDevice ? [] : (customAlarmData?.alarms || []);

API 调用的兼容处理:

import { useActions } from '@ray-js/panel-sdk';

const actions = useActions();

// 对于可能不存在的 API 方法,使用容错调用
const handleTapToRunTrigger = async () => {
  try {
    await actions.tapToRun?.trigger?.();
  } catch (e) {
    console.warn('一键执行功能在群组环境下不可用:', e);
    // 提供降级方案或用户提示
  }
};

语义/行为差异的兼容处理

群组环境下的设备信息、状态同步可能与单设备有所不同,特别需要注意 useDevice 在群组环境下返回的是 GroupInfo 而非 DeviceInfo,因此可能缺少某些设备特有的字段。

import { getLaunchOptionsSync } from '@ray-js/ray';
import { useDevice, useProps } from '@ray-js/panel-sdk';

const isGroupDevice = !!getLaunchOptionsSync()?.query?.groupId;

// useDevice 在群组环境下返回 GroupInfo,需要做字段兼容处理
const entityInfo = useDevice(device => device.devInfo);

// ✅ 安全的字段访问方式
const id = isGroupDevice 
  ? entityInfo.groupId // 群组设备
  : entityInfo.devId // 单设备

// ✅ 在线状态判断差异 - 群组的在线状态语义不同
const isOnline = isGroupDevice
  ? entityInfo?.deviceList?.some(dev => dev.isOnline) // 群组:只要有一个设备在线就认为在线
  : entityInfo?.isCloudOnline === true; // 单设备:必须明确在线

性能与并发的兼容处理

在某些协议(如蓝牙本地群组)下,可能存在丢包现象,需要实现重试机制来保证操作的可靠性:

import { getLaunchOptionsSync } from '@ray-js/ray';
import { useActions } from '@ray-js/panel-sdk';

const isGroupDevice = !!getLaunchOptionsSync()?.query?.groupId;
const actions = useActions();

// 重试机制实现
const withRetry = async (actionFn, maxRetries = 3, delay = 1000) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await actionFn();
      
      // 对于群组操作,检查是否所有设备都响应成功
      if (isGroupDevice && result?.failedDevices?.length > 0) {
        console.warn(`第 ${attempt} 次尝试,${result.failedDevices.length} 个设备响应失败`);
        if (attempt === maxRetries) {
          throw new Error(`重试 ${maxRetries} 次后仍有设备失败`);
        }
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      return result;
    } catch (error) {
      console.warn(`第 ${attempt} 次尝试失败:`, error);
      if (attempt === maxRetries) {
        throw error;
      }
      // 指数退避策略
      await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));
    }
  }
};

// 使用重试机制的操作示例
const handlePowerToggle = async () => {
  try {
    await withRetry(async () => {
      return await actions.power.toggle();
    });
    console.log('电源状态切换成功');
  } catch (error) {
    console.error('电源状态切换失败:', error);
    // 提供用户友好的错误提示
    showToast('操作失败,请检查设备连接状态');
  }
};