# 电工定时 SDK

`@ray-js/electrician-timing-sdk` 用于统一管理电工设备定时能力，支持：

- 电工定时（循环 / 随机 / 延时关）
- 倒计时
- 云定时
- 冲突检测与冲突处理
- 在线状态判断

## 安装

```bash
npm i @ray-js/electrician-timing-sdk
```

## API 总览

### 生命周期

| API | 说明 |
| --- | --- |
| `init(options)` | 初始化 SDK，并加载运行时状态 |
| `changeConfig(options)` | 更新配置；设备/群组变化时会重建 |
| `destroy()` | 清理内部状态、事件监听和配置 |

### 云定时

| API | 说明 |
| --- | --- |
| `addCloudTimer(data, option?)` | 添加单个云定时 |
| `batchAddCloudTimer(dataList, option?)` | 批量添加云定时 |
| `updateCloudTimer(data, option?)` | 更新云定时内容 |
| `updateCloudTimerStatus(data, option?)` | 启用/禁用云定时 |
| `removeCloudTimer(id)` | 删除云定时 |
| `onCloudUpdate(cb)` | 监听云定时变化事件 |
| `offCloudUpdate(cb)` | 取消云定时变化监听 |

### 倒计时

| API | 说明 |
| --- | --- |
| `createCountdown(code, countdown, option?)` | 创建倒计时 |
| `cancelCountdown(code)` | 取消倒计时 |

### 自定义 DP 定时

| API | 说明 |
| --- | --- |
| `addDpTimer(code, data, option?)` | 添加自定义 DP 定时 |
| `updateDpTimer(code, data, option?)` | 更新自定义 DP 定时 |
| `updateDpTimerStatus(code, id, status, option?)` | 启用/禁用自定义 DP 定时 |
| `removeDpTimer(code, id)` | 删除自定义 DP 定时 |

### 电工快捷 API（`electri`）

| API | 说明 |
| --- | --- |
| `electri.cycle.add/update/updateStatus/remove` | 循环定时增删改与状态切换 |
| `electri.cycle.getConfig/validateMax/validateRepeat` | 循环定时配置与校验 |
| `electri.random.add/update/updateStatus/remove` | 随机定时增删改与状态切换 |
| `electri.random.getConfig/validateMax/validateRepeat` | 随机定时配置与校验 |
| `electri.inching.add/update/updateStatus/remove` | 延时关增删改与状态切换 |
| `electri.inching.getConfig/validateMax/validateRepeat` | 延时关配置与校验 |

### 在线状态

| API | 说明 |
| --- | --- |
| `isLANOnline()` | 判断是否局域网在线 |
| `isLocalOnline()` | 判断是否本地在线 |

## 初始化说明

### 基本规则

- `devId` 与 `groupId` 至少传一个。
- 若两者都传，优先使用 `devId`。
- `type` 支持 `ele` 与 `custom`。

### `init(options)` 通用参数

| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `devId` | 否 | 设备 ID |
| `groupId` | 否 | 群组 ID |
| `type` | 是 | `ele` 或 `custom` |
| `category` | 否 | 云定时分组，默认 `sdk_schedule`，**新项目不建议修改** |
| `conflictModallId` | 否 | 默认冲突弹窗 id |
| `is24Hour` | 否 | 是否使用 24 小时制 |
| `combineSameData` | 否 | 是否合并多通道相同定时，默认 `true` |

### `type: 'ele'` 额外参数

| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `switches` | 否 | 开关 code 列表，不传自动识别 |
| `countdowns` | 否 | 倒计时配置列表或倒计时 code 列表 |
| `dpTimerConfig` | 否 | 自定义 DP 定时配置映射 |
| `supportCycle/supportRandom/supportInching/supportCloud` | 否 | 能力开关：`auto` / `y` / `n` |
| `cycleCode/randomCode/inchingCode` | 否 | 显式指定定时 dp code |

### `type: 'custom'` 额外参数

| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `switches` | 是 | 开关 code 列表 |
| `countdowns` | 是 | 倒计时配置列表 |
| `dpTimerConfig` | 是 | 自定义定时 dp 配置映射 |
| `supportCloud` | 否 | 云定时能力开关 |

## 冲突弹窗（ConflictPopup）

当定时相关 API 传入 `{ useDefaultModal: true }` 时，SDK 在检测到冲突后会取**当前页面栈顶页**，通过 `selectComponent('#<id>')` 找到挂载的冲突弹窗组件并调用其 **`show(conflictData, validateData)`** 展示冲突列表；用户点击确认后由组件内部完成与主包一致的冲突解决（写 DP / 云定时等）。因此 **`init` 中的 `conflictModallId`（默认 `smart-conflict-popup`）必须与页面里弹窗节点的 `id` 完全一致**，否则会报错提示找不到对应节点。

```mermaid
flowchart TB
  A[定时 API + useDefaultModal: true] --> B[SDK 校验冲突]
  B -->|无冲突| C[继续下发 / pass]
  B -->|有冲突| D["selectComponent('#conflictModallId').show(...)"]
  D --> E[ConflictPopup 展示列表]
  E --> F{用户操作}
  F -->|确认| G[内部 resolve 解决冲突]
  F -->|取消/关闭| H[API 返回 cancel]
  G --> I[成功返回 success / 失败 toast + reject]
```

### React 中使用 `ConflictPopup`

```tsx
import { ConflictPopup } from '@ray-js/electrician-timing-sdk/lib/components';

// 挂在页面根或布局中；id 须与 init({ conflictModallId }) 一致（默认可省略 id 使用 smart-conflict-popup）
<ConflictPopup
  id="smart-conflict-popup"
  title="定时冲突"
  description="是否替换冲突定时？"
  locale={locale}
  cancelText="取消"
  okText="确认"
/>
```

列表数据由 SDK 在弹出时通过原生组件方法 `show` 注入，一般只需像上图一样配置标题、说明、`locale` 与按钮文案；具体以 Ray + React 工程的组件编译与注册方式为准。

### `ConflictPopup` 属性说明

| 属性 | 必填 | 说明 |
| --- | --- | --- |
| `title` | 是 | 弹窗标题 |
| `locale` | 是 | 多语言对象（`Locale`），含星期、类型标签、`conflictFailure`、DP 文案等 |
| `id` | 否 | 与 `init` 的 `conflictModallId` 一致，便于 `selectComponent` |
| `description` | 否 | 副标题 / 说明文案 |
| `cancelText` / `okText` | 否 | 取消、确认按钮文案（有内置默认值） |
| `themeColor` | 否 | 列表项图标背景主题色 |
| `hideMask` | 否 | 是否隐藏蒙层 |
| `visble` | 否 | 是否显示（与源码拼写一致；常态由 SDK `show` 驱动） |
| `usesolt` | 否 | 为 `true` 时使用插槽自定义列表区（与源码拼写一致） |
| `data` / `validateData` | 否 | 通常由 SDK 在 `show` 时传入，无需在模板中写死 |

## 完整 API 示例

### 1）初始化

```mermaid
flowchart LR
  A[传入 devId/groupId + type] --> B[init 加载运行时状态]
  B --> C[支持云定时/电工定时等能力]
  C --> D[可调用后续定时 API]
```

```ts
import { init } from '@ray-js/electrician-timing-sdk';

await init({
  devId: 'your-device-id',
  type: 'ele',
  supportCloud: 'auto',
});
```

### 2）循环定时

```mermaid
flowchart LR
  A[构造循环定时数据] --> B[electri.cycle.add]
  B --> C{冲突校验}
  C -->|通过| D[下发 DP / success]
  C -->|冲突| E[返回 conflict + validateData]
  C -->|取消| F[cancel]
```

```ts
import { electri } from '@ray-js/electrician-timing-sdk';

await electri.cycle.add(
  {
    id: -1,
    type: 'sdk_cycle',
    startTime: 8 * 60,
    endTime: 18 * 60,
    week: [1, 1, 1, 1, 1, 0, 0],
    status: true,
    actions: [{ code: 'switch_1' }],
    onHoldTime: 100,
    offHoldTime: 100,
  },
  { useDefaultModal: true }
);
```

### 3）随机定时

```mermaid
flowchart LR
  A[构造随机定时数据] --> B[electri.random.add]
  B --> C{冲突校验}
  C -->|通过| D[下发 DP / success]
  C -->|冲突| E[返回 conflict + validateData]
  C -->|取消| F[cancel]
```

```ts
import { electri } from '@ray-js/electrician-timing-sdk';

await electri.random.add(
  {
    id: -1,
    type: 'sdk_random',
    startTime: 10 * 60,
    endTime: 12 * 60,
    week: [0, 0, 1, 0, 0, 0, 0],
    status: true,
    actions: [{ code: 'switch_1' }],
  },
  { useDefaultModal: true }
);
```

### 4）延时关

```mermaid
flowchart LR
  A[构造延时关数据 time + actions] --> B[electri.inching.add]
  B --> C{冲突校验}
  C -->|通过| D[下发 DP / success]
  C -->|冲突| E[返回 conflict + validateData]
  C -->|取消| F[cancel]
```

```ts
import { electri } from '@ray-js/electrician-timing-sdk';

await electri.inching.add(
  {
    id: -1,
    type: 'sdk_inching',
    time: 300,
    status: true,
    actions: [{ code: 'switch_1' }],
  },
  { useDefaultModal: true }
);
```

### 5）云定时（单条 + 批量）

```mermaid
flowchart TB
  subgraph single["单条添加"]
    S1[addCloudTimer 单条数据] --> S2{冲突校验}
    S2 --> S3[success / cancel / conflict]
  end
  subgraph batch["批量添加"]
    B1[batchAddCloudTimer 数据列表] --> B2{冲突校验}
    B2 --> B3[success / cancel / conflict]
  end
```

```ts
import { addCloudTimer, batchAddCloudTimer } from '@ray-js/electrician-timing-sdk';

await addCloudTimer(
  {
    aliasName: 'morning',
    startTime: 9 * 60,
    week: [1, 1, 1, 1, 1, 1, 1],
    status: true,
    actions: [{ code: 'switch_1', value: true }],
    isAppPush: false,
  },
  { useDefaultModal: true }
);

await batchAddCloudTimer(
  [
    {
      aliasName: 'workday',
      startTime: 8 * 60,
      week: [1, 1, 1, 1, 1, 0, 0],
      status: true,
      actions: [{ code: 'switch_1', value: true }],
      isAppPush: false,
    },
  ],
  { useDefaultModal: true }
);
```

### 6）倒计时

```mermaid
flowchart LR
  A[createCountdown code + 时长] --> B{冲突校验}
  B -->|通过| C[下发倒计时 DP]
  B -->|冲突/取消| D[返回对应结果]
  C --> E[可选: cancelCountdown 取消]
```

```ts
import { createCountdown, cancelCountdown } from '@ray-js/electrician-timing-sdk';

await createCountdown('countdown_1', 1800, { useDefaultModal: true });
await cancelCountdown('countdown_1');
```

### 7）自定义 DP 定时

```mermaid
flowchart TB
  A[addDpTimer code + data] --> B{冲突校验}
  B --> C[success / conflict]
  C --> D[updateDpTimerStatus 启用/禁用]
  C --> E[removeDpTimer 删除]
  D --> F[维护生命周期]
  E --> F
```

```ts
import { addDpTimer, updateDpTimerStatus, removeDpTimer } from '@ray-js/electrician-timing-sdk';

await addDpTimer(
  'cycle_timing',
  {
    id: 1,
    type: 'sdk_cycle',
    startTime: 360,
    endTime: 480,
    week: [1, 1, 1, 1, 1, 1, 1],
    status: true,
    actions: [{ code: 'switch_1' }],
    onHoldTime: 60,
    offHoldTime: 60,
  },
  { useDefaultModal: true }
);

await updateDpTimerStatus('cycle_timing', 1, false);
await removeDpTimer('cycle_timing', 1);
```

## 正确的定时使用顺序

建议严格按以下顺序使用，以确保行为稳定：

1. **初始化**：调用 `init(options)`，再进行其他定时 API 调用。
2. **能力确认**：根据业务判断当前能力（云定时 / 电工定时 / 倒计时）是否可用。
3. **创建定时**：调用 `electri.*`、`addCloudTimer`、`createCountdown` 或 `addDpTimer`。
4. **处理冲突返回**：  
   - `success`：结束。  
   - `cancel`：用户取消。  
   - `{ conflict, validateData }`：进入你的冲突处理流程/弹窗确认流程。
5. **维护生命周期**：通过 `update*` 与 `update*Status` 管理后续变更。
6. **监听变化**：注册 `onConflictChange`、`onCloudUpdate` 同步 UI。
7. **退出清理**：离开页面/场景时，执行 `off*` + `destroy()`。

## 冲突相关返回值

- `success`：执行成功
- `cancel`：用户取消冲突处理
- `pass`：内部校验通过（通常 SDK 内部状态）
- `{ conflict, validateData }`：冲突详情结果

## 错误码

| errCode | errMsg |
| --- | --- |
| 1001 | 已达到最大支持的定时数量 |
| 1002 | 未找到相关的定时 dp 配置 |
| 1003 | 未找到对应 id 的定时 |
| 1004 | 设备不支持云定时 |
| 1005 | 未配置倒计时取消值 |
| 1006 | 未找到倒计时配置 |
| 1007 | 未找到 dp 定义 |
| 1008 | 无效的值 |
| 1009 | 循环定时开始时间与结束时间不能相等 |
| 1010 | 循环单元时长不能大于定时总时长 |
| 1011 | 随机定时持续时间不能小于 30 分钟 |

## 注意事项

- 群组场景默认不支持云定时。
- 启用路径操作（新增、更新为启用、启用状态切换）会触发冲突校验。
- 倒计时使用枚举总时长时，请正确配置 `totalRange` 与 `cancelValue`。
- `combineSameData` 会影响多通道相同内容定时的合并策略。