---
title: Usage
---

# Usage

The **Smart Group Model (SGM)** is an extension of the **Smart Device Model (SDM)** designed to run in group environments. The design goal is to maintain API compatibility with SDM as much as possible, thereby reducing business layer modifications. Most SDM Hooks and APIs can be used directly under SGM, but some capabilities cannot be implemented or have different semantics in group scenarios, requiring developers to pay attention and implement compatibility handling.

<Alert type="warning">
  Smart Group Model currently does not support the following capabilities:
  - Tap-to-run functionality in general capabilities
  - Alarm push notifications in general capabilities
</Alert>

Before starting to use the Smart Group Model, it is recommended to first read the [Smart Device usage documentation](/en/miniapp/solution-panel/ability/common/sdm/usage), as most SDM examples and patterns are still applicable in group scenarios:

## Difference Description

Overall, SGM maintains compatibility with SDM, but there are several capabilities and behavioral differences that cannot be aligned in group scenarios, which need attention at the business layer:

- **Functional Limitations**: Tap-to-run and alarm push capabilities in general functions are not available in group scenarios, so related Hooks (such as useBuiltInAlarm, useCustomAlarm) are also unavailable or return empty results.
- **Semantic/Behavioral Differences**: Group device information structure and status synchronization may differ from single devices in details.
- **Performance and Concurrency**: Group operations usually trigger concurrent requests to multiple devices. For different device protocol types, attention should be paid to throttling and fault tolerance strategies.

## Best Practices

Below are compatibility handling suggestions and Hooks compatibility practices to ensure stable operation in group environments:

### Compatibility Handling for Functional Limitations

Most Hooks (such as `useProps`, `useActions`, `useStructuredProps`, `useStructuredActions`) are still available under SGM. For restricted capability APIs or Hooks, the following compatibility strategies are recommended:

<Callout type="warning" emoji="⚠️">
  Important: According to [React Hooks rules](https://react.dev/reference/rules/rules-of-hooks), Hooks cannot be called within conditional statements. For Hooks that may not be available (such as useBuiltInAlarm, useCustomAlarm), they should always be called but handle the compatibility of returned data.
</Callout>

**Correct Hooks compatibility approach**:

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

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

// ✅ Correct: Always call Hooks, handle compatibility of returned data
const builtInAlarmData = useBuiltInAlarm();
const customAlarmData = useCustomAlarm();

// In group environments, these Hooks may return empty or invalid data, requiring compatibility handling
const safeBuiltInAlarms = isGroupDevice ? [] : (builtInAlarmData?.alarms || []);
const safeCustomAlarms = isGroupDevice ? [] : (customAlarmData?.alarms || []);
```

**API call compatibility handling**:

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

const actions = useActions();

// For API methods that may not exist, use fault-tolerant calls
const handleTapToRunTrigger = async () => {
  try {
    await actions.tapToRun?.trigger?.();
  } catch (e) {
    console.warn('Tap-to-run functionality is not available in group environment:', e);
    // Provide fallback solution or user notification
  }
};
```

### Compatibility Handling for Semantic/Behavioral Differences

Device information and status synchronization in group environments may differ from single devices. Special attention should be paid to the fact that `useDevice` returns `GroupInfo` instead of `DeviceInfo` in group environments, so some device-specific fields may be missing.

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

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

// useDevice returns GroupInfo in group environment, requiring field compatibility handling
const entityInfo = useDevice(device => device.devInfo);

// ✅ Safe field access approach
const id = isGroupDevice 
  ? entityInfo.groupId // Group device
  : entityInfo.devId // Single device

// ✅ Online status judgment difference - group online status has different semantics
const isOnline = isGroupDevice
  ? entityInfo?.deviceList?.some(dev => dev.isOnline) // Group: considered online if at least one device is online
  : entityInfo?.isCloudOnline === true; // Single device: must be explicitly online
```

### Performance and Concurrency Compatibility Handling

Under certain protocols (such as Bluetooth local groups), packet loss may occur, requiring the implementation of retry mechanisms to ensure operation reliability:

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

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

// Retry mechanism implementation
const withRetry = async (actionFn, maxRetries = 3, delay = 1000) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await actionFn();
      
      // For group operations, check if all devices responded successfully
      if (isGroupDevice && result?.failedDevices?.length > 0) {
        console.warn(`Attempt ${attempt}, ${result.failedDevices.length} devices failed to respond`);
        if (attempt === maxRetries) {
          throw new Error(`Still have failed devices after ${maxRetries} retries`);
        }
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      return result;
    } catch (error) {
      console.warn(`Attempt ${attempt} failed:`, error);
      if (attempt === maxRetries) {
        throw error;
      }
      // Exponential backoff strategy
      await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));
    }
  }
};

// Example of operation using retry mechanism
const handlePowerToggle = async () => {
  try {
    await withRetry(async () => {
      return await actions.power.toggle();
    });
    console.log('Power status toggle successful');
  } catch (error) {
    console.error('Power status toggle failed:', error);
    // Provide user-friendly error notification
    showToast('Operation failed, please check device connection status');
  }
};
```
