chore: 初始化仓库基线(AGENTS.md、git 规范、敏感文件排除)

This commit is contained in:
weijuesen
2026-08-10 22:30:53 +08:00
commit 84abf4454c
358 changed files with 75993 additions and 0 deletions
+547
View File
@@ -0,0 +1,547 @@
# 物联网设备接口和数据格式
## 一、MQTT Topic 结构
后端同时订阅 `silk/+/+/+/up/telemetry``silk/+/+/+/down/cmd`(部分设备 publish/subscribe 方向反配)。下行命令主题根据设备实际上报主题自动推导:
- 设备上报 `/up/telemetry` -> 命令发送到 `/down/cmd`
- 设备上报 `/down/cmd` -> 命令发送到 `/up/telemetry`(反配设备)
| 方向 | 主题格式 | 示例 |
|------|----------|------|
| **上行**(设备 -> 后端) | `silk/{appId}/{roomKey}/{deviceKey}/up/telemetry` | `silk/default/room-001/sensor-001/up/telemetry` |
| **下行**(后端 -> 设备) | `silk/{appId}/{roomKey}/{deviceKey}/down/cmd` | `silk/default/room-001/sensor-001/down/cmd` |
### 路径段说明
| 段 | 说明 | 示例值 |
|----|------|--------|
| `silk` | 系统前缀(固定) | `silk` |
| `{appId}` | 应用标识 | `default` |
| `{roomKey}` | 房间标识 | `room-001` |
| `{deviceKey}` | 设备标识(MAC 或自定义 ID) | `sensor-001``28562f8c35e8` |
> **注意**GSTMB1 设备的 `deviceKey` 在数据库中使用 MAC 地址(如 `28562f8c35e8`),但 topic 路径中仍使用配网时配置的名称(如 `sensor-001`)。后端通过消息体中的 `mac` 字段识别设备。
---
## 二、GSTMB1 温湿度传感器
### 设备信息
| 项目 | 值 |
|------|-----|
| 型号 | GSTMB1 |
| 厂商 | GeekOpen / smart-bird.cn |
| 通信协议 | MQTT |
| 设备标识 | MAC 地址(如 `28562f8c35e8` |
| 上报指标 | temperature、humidity、signal 等 |
### 1. 设备定时上报(遥测数据)
设备直接在 body 顶层放置数值字段,后端自动提取 `temperature``humidity``signal``lux``co2` 等字段。
```json
{
"type": "GSTMB1",
"mac": "28562f8c35e8",
"temperature": 28.24,
"humidity": 57.83,
"signal": -75,
"ip": "192.168.1.56",
"ssid": "GeekOpen",
"version": "1.0.5"
}
```
### 2. 命令响应(设备 -> 后端)
`source` 字段为 `"command"` 表示是对下行命令的回复。
**成功响应(info-all):**
```json
{
"commandName": "info-all",
"mac": "28562f8c35e8",
"source": "command",
"success": true,
"temperature": 28.24,
"humidity": 57.83,
"signal": "-75",
"ip": "192.168.1.56",
"ssid": "GeekOpen",
"version": "1.0.5",
"dhcpEnable": 1,
"h_compensate": 0,
"t_compensate": 0,
"timerEnable": 1,
"timerInterval": 15
}
```
**错误响应:**
```json
{
"commandName": "error",
"mac": "28562f8c35e8",
"source": "command",
"success": false,
"message": "MQTT消息缺少必要的type字段"
}
```
### 3. 下行命令(后端 -> 设备)
| 功能 | API 端点 | 命令 JSON |
|------|----------|-----------|
| 获取设备信息 | `POST /api/v1/devices/:id/gstmb1/info` | `{"type":"info","messageId":"..."}` |
| 重启设备 | `POST /api/v1/devices/:id/gstmb1/restart` | `{"type":"setting","system":"restart"}` |
| 恢复出厂设置 | `POST /api/v1/devices/:id/gstmb1/command` | `{"type":"setting","system":"reset"}` |
| 设置上报间隔 | `POST /api/v1/devices/:id/gstmb1/interval` | `{"type":"setting","messageId":"...","timerEnable":1,"timerInterval":60}` |
| 自定义命令 | `POST /api/v1/devices/:id/gstmb1/command` | 任意 JSON 原样转发 |
**参数说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `type` | String | **必填**。命令类型:`info`(查询)、`setting`(设置)、`event`(控制) |
| `messageId` | String | 消息 ID,设备响应时原样返回 |
| `system` | String | `restart`(重启)、`reset`(恢复出厂) |
| `timerEnable` | int | 定时上报开关:`0`(关闭)、`1`(开启) |
| `timerInterval` | int | 上报间隔(秒),范围 5-86400 |
> API 端点中的 `:id` 是数据库中设备的 UUID,不是 MAC 地址。需 JWT 认证。
### 4. 命令响应字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `commandName` | String | 命令名称:`info-all``device-timer-interval``controller-restart``controller-reset``error` |
| `mac` | String | 设备 MAC 地址 |
| `source` | String | 固定 `"command"`,表示命令响应 |
| `success` | boolean | 执行结果 |
| `message` | String | 执行返回结果或错误信息 |
| `temperature` | Float | 温度(摄氏度) |
| `humidity` | Float | 湿度(% |
| `signal` | String | WiFi 信号强度(dBm),0~-50 最好,<-100 无信号 |
| `ip` | String | 设备内网 IP |
| `ssid` | String | WiFi 名称 |
| `version` | String | 固件版本 |
| `dhcpEnable` | int | DHCP 开关:`1`(开启)、`0`(关闭) |
| `t_compensate` | Float | 温度补偿(范围 -20 至 +20) |
| `h_compensate` | Float | 湿度补偿(范围 -20 至 +20) |
| `timerEnable` | int | 定时上报开关 |
| `timerInterval` | int | 上报间隔(秒) |
---
## 三、GSPE1B 智能插座
### 设备信息
| 项目 | 值 |
|------|-----|
| 型号 | GSPE1B |
| 厂商 | GeekOpen / smart-bird.cn |
| 通信协议 | MQTT |
| 设备类型 | 控制器(actuator |
| 设备标识 | MAC 地址(如 `e8f60a2f01e8` |
| 上报指标 | voltage、current、power、energy、key(通断状态) |
> **注意**:该设备的 MQTT 主题方向与常规配置相反——设备发布到 `/down/cmd` 主题,订阅 `/up/telemetry` 主题接收命令。后端已适配此情况。
### 1. 设备定时上报(遥测数据)
设备每 15 秒自动上报电量数据,`source` 字段为 `"auto"`
```json
{
"mac": "e8f60a2f01e8",
"source": "auto",
"voltage": 226.234,
"current": 0.327,
"power": 66.795,
"energy": 0.025,
"key": 1
}
```
### 2. 命令响应(设备 -> 后端)
`source` 字段为 `"command"` 表示是对下行命令的回复。
**info-all 响应(设备信息):**
```json
{
"commandName": "info-all",
"mac": "e8f60a2f01e8",
"source": "command",
"success": true,
"key": 1,
"onState": 2,
"wifiLock": 0,
"keyLock": 0,
"timerEnable": 1,
"timerInterval": 15,
"signal": "-75",
"ip": "192.168.1.59",
"version": "1.0.5"
}
```
**info-statistic 响应(电量信息):**
```json
{
"commandName": "info-statistic",
"mac": "e8f60a2f01e8",
"source": "command",
"success": true,
"voltage": 226.234,
"current": 0.327,
"power": 66.795,
"energy": 0.025,
"key": 1
}
```
**controller-event 响应(通断控制后回复):**
与 info-all 格式相同,包含设备状态信息。
### 3. 下行命令(后端 -> 设备)
| 功能 | API 端点 | 命令 JSON |
|------|----------|-----------|
| 通电 | `POST /api/v1/devices/:id/plug/on` | `{"type":"event","key":1}` |
| 断电 | `POST /api/v1/devices/:id/plug/off` | `{"type":"event","key":0}` |
| 查询设备信息 | `POST /api/v1/devices/:id/plug/info` | `{"type":"info","messageId":"..."}` |
| 查询电量 | `POST /api/v1/devices/:id/plug/statistic` | `{"type":"statistic","messageId":"..."}` |
| 重启设备 | `POST /api/v1/devices/:id/gstmb1/restart` | `{"type":"setting","system":"restart"}` |
| 设置上报间隔 | `POST /api/v1/devices/:id/gstmb1/interval` | `{"type":"setting","timerEnable":1,"timerInterval":60}` |
> API 端点中的 `:id` 是数据库中设备的 UUID,不是 MAC 地址。需 JWT 认证。
### 4. 指标字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `voltage` | Float | 电压(V),如 `226.234` |
| `current` | Float | 电流(A),如 `0.327` |
| `power` | Float | 功率(W),如 `66.795` |
| `energy` | Float | 累计电量(kWh),如 `0.025` |
| `key` | int | 通断状态:`1`(通电)、`0`(断电) |
| `onState` | int | 上电状态:`0`(保持断电)、`1`(保持通电)、`2`(记忆上次状态) |
| `wifiLock` | int | WiFi 锁:`0`(关闭)、`1`(开启) |
| `keyLock` | int | 按键锁:`0`(关闭)、`1`(开启) |
### 5. 命令响应字段说明
| commandName | 说明 |
|-------------|------|
| `info-all` | 设备信息查询响应,包含 key、onState、wifiLock、timerEnable 等 |
| `info-statistic` | 电量查询响应,包含 voltage、current、power、energy、key |
| `controller-event` | 通断控制响应,格式同 info-all |
| `error` | 错误响应,`success: false``message` 字段描述错误原因 |
---
## 四、GSCW1M-4G 4G 智能断路器
### 设备信息
| 项目 | 值 |
|------|-----|
| 型号 | GSCW1M-4G |
| 厂商 | GeekOpen / smart-bird.cn |
| 通信协议 | MQTT over 4G LTE(合宙780ex4G全网通) |
| 设备类型 | 控制器(actuator |
| 设备标识 | IMEI 号(如 `862323080741518`),消息体中使用 `imei` 字段 |
| 上报指标 | voltage、current、power、energy、key(通断状态)、signal0-31 |
> **与 GSPE1B 的区别**:4G 通信(非 WiFi);设备标识用 `imei`(非 `mac`);信号强度为 0-31 整数(非 dBm);`timerInterval` 单位为毫秒(非秒);响应消息**无** `source` 和 `commandName` 字段。
>
> **固件 Bug**:部分设备在 info 响应中 `imei` 的第二位 `6` 误发为 `G`(如 `8G2323080741518`),statistic 响应中则为正确的 `862323080741518`。后端已做规范化处理(`G` -> `6`)。
>
> **主题方向**:与 GSPE1B 智能插座相同,设备响应消息出现在订阅主题(`/down/cmd`)上,而非发布主题(`/up/telemetry`)。后端已适配。
### 1. 设备响应(设备 -> 后端)
断路器的响应消息**没有** `source: "command"` 字段,所有响应均作为遥测数据处理,后端自动提取 body 中的数值字段。
**info 响应(设备信息):**
```json
{
"key": 0,
"iccid": "898604E6192390306804",
"imei": "862323080741518",
"signal": 30,
"version": "1.0.1",
"timerEnable": 0,
"keyLock": 0,
"type": "Breaker-plus-1-4G",
"timerInterval": 0,
"resetLock": 0,
"onState": 2
}
```
**statistic 响应(电量信息):**
```json
{
"voltage": "230.547",
"current": "0.035",
"power": "4.547",
"energy": "0.001",
"key": 1,
"imei": "862323080741518",
"messageId": "auto"
}
```
> **注意**:断路器的 `voltage`、`current`、`power`、`energy` 字段为**字符串类型**(如 `"230.547"`),后端 `toFloat` 函数自动转换为数值入库。`messageId` 为 `"auto"` 时表示定时上报数据。
**event 响应(通断控制后回复):**
```json
{
"iccid": "898604E6192390306804",
"timerEnable": 1,
"onState": 2,
"timerInterval": 60,
"resetLock": 0,
"imei": "862323080741518",
"messageId": "0875c300-a0eb-475b-83ea-607eb27d19a0",
"keyLock": 0,
"type": "Breaker-plus-1-4G",
"signal": 30,
"version": "1.0.1",
"key": 0
}
```
### 2. 下行命令(后端 -> 设备)
命令格式与 GSPE1B 智能插座完全相同,复用同一套 API:
| 功能 | API 端点 | 命令 JSON |
|------|----------|-----------|
| 通电 | `POST /api/v1/devices/:id/plug/on` | `{"type":"event","key":1}` |
| 断电 | `POST /api/v1/devices/:id/plug/off` | `{"type":"event","key":0}` |
| 查询设备信息 | `POST /api/v1/devices/:id/plug/info` | `{"type":"info","messageId":"..."}` |
| 查询电量 | `POST /api/v1/devices/:id/plug/statistic` | `{"type":"statistic","messageId":"..."}` |
| 设置按键锁 | `POST /api/v1/devices/:id/gstmb1/command` | `{"type":"setting","keyLock":0}` |
| 设置定时上报 | `POST /api/v1/devices/:id/gstmb1/interval` | `{"type":"setting","timerEnable":1,"timerInterval":10000}` |
| 重启设备 | `POST /api/v1/devices/:id/gstmb1/restart` | `{"type":"setting","system":"restart"}` |
| 恢复出厂设置 | `POST /api/v1/devices/:id/gstmb1/command` | `{"type":"setting","system":"reset"}` |
> `timerInterval` 单位为**毫秒**(如 `10000` = 10 秒),取值范围 5000-86400000。
### 3. 指标字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `voltage` | Float | 电压(V),如 `226.024` |
| `current` | Float | 电流(A),如 `15.027` |
| `power` | Float | 功率(W),如 `2921.511` |
| `energy` | Float | 累计电量(kWh),如 `25.047`,断电不归零,重置后归零 |
| `key` | int | 通断状态:`1`(通电)、`0`(断电) |
| `signal` | int | 4G 信号强度,0-31,值越大越好 |
| `imei` | String | 设备 IMEI(唯一标识,非数值指标) |
| `iccid` | String | 物联网卡 ICCID(非数值指标) |
| `onState` | int | 上电默认状态:`0`(记忆)、`1`(断电)、`2`(通电) |
| `keyLock` | int | 按键锁:`0`(关闭)、`1`(开启) |
| `resetLock` | int | 重置锁:`0`(关闭)、`1`(开启) |
| `timerEnable` | int | 定时上报:`0`(关闭)、`1`(开启) |
| `timerInterval` | int | 上报间隔(毫秒),如 `10000` = 10 秒 |
---
## 五、GSCU1B-4G 智能红外控制器
### 设备信息
| 项目 | 值 |
|------|-----|
| 型号 | GSCU1B-4G |
| 厂商 | GeekOpen / smart-bird.cn |
| 通信协议 | MQTT over 4G LTE(合宙780ex4G全网通) |
| 设备类型 | 控制器(actuator |
| 设备标识 | IMEI 号(如 `864269069911747`),消息体中使用 `imei` 字段 |
| 设备代号 | `Infrared-Controller-4G` |
| 功能 | 红外码学习、发射、擦除,遥控空调/电视/风扇等家电 |
| 红外码编号 | 1-248,可同时存储多个遥控器的红外码 |
> 与断路器共用 `imei` 标识和 IMEI 规范化逻辑。无电压/电流/功率等电量指标,仅有 `signal`(4G 信号强度 0-31)。
### 1. 设备响应(设备 -> 后端)
**info 响应(设备信息):**
```json
{
"signal": 25,
"code": "Infrared-Controller-4G",
"version": "1.0.0",
"iccid": "89860323442024493202",
"type": "info",
"imei": "864269069911747"
}
```
**learn 响应(学习红外码结果):**
```json
{
"type": "infrared",
"action": "learn",
"imei": "864269069911747",
"success": true
}
```
**emit 响应(发射红外码结果):**
```json
{
"type": "infrared",
"action": "emit",
"imei": "864269069911747",
"success": true
}
```
> `success: false` 表示发射失败(红外编号对应的红外码为空)。学习成功蜂鸣器响 1 声,失败无声。
### 2. 下行命令(后端 -> 设备)
| 功能 | API 端点 | 命令 JSON |
|------|----------|-----------|
| 查询设备信息 | `POST /api/v1/devices/:id/plug/info` | `{"type":"info","messageId":"..."}` |
| 学习红外码 | `POST /api/v1/devices/:id/ir/learn` | `{"type":"infrared","action":"learn","data":{"no":100}}` |
| 发射红外码 | `POST /api/v1/devices/:id/ir/emit` | `{"type":"infrared","action":"emit","data":{"no":100}}` |
| 取消学习 | `POST /api/v1/devices/:id/ir/cancel` | `{"type":"infrared","action":"learnCancel"}` |
| 擦除全部红外码 | `POST /api/v1/devices/:id/ir/erase` | `{"type":"infrared","action":"erase"}` |
| 重启设备 | `POST /api/v1/devices/:id/gstmb1/restart` | `{"type":"setting","system":"restart"}` |
> 学习和发射命令的 body 需包含 `{"no": N}`N 为 1-248 的红外码编号。
### 3. 推荐红外码编号规划
| 编号 | 用途 | 学习方法 |
|------|------|---------|
| 100 | 关机 | 遥控器对准控制器,按关机键 |
| 101 | 标准制冷 | 遥控器设为制冷 25°C 风量自动,按开机键 |
| 102 | 标准制热 | 遥控器设为制热 30°C 风量自动,按开机键 |
| 103+ | 自定义 | 按需扩展 |
### 4. 红外码学习流程
1. 后端发送 `{"type":"infrared","action":"learn","data":{"no":100}}`
2. 设备进入学习状态(不响应其他指令)
3. 遥控器对准设备,按一下对应按键
4. 设备"嘀"一声,返回 `{"success":true}`
5. 后续发送 `{"type":"infrared","action":"emit","data":{"no":100}}` 即可控制
---
## 六、通用 MQTT 数据格式(非 GSTMB1 设备)
后端同时兼容以下消息格式,按优先级依次匹配:
### 1. metrics 对象格式
```json
{
"msgId": "msg-test-001",
"ts": 1751900000000,
"metrics": {
"temperature": 24.8,
"humidity": 78.2,
"lux": 360,
"co2": 890
}
}
```
### 2. data 数组格式
```json
{
"data": [
{"metric": "temperature", "value": 24.8},
{"metric": "humidity", "value": 78.2}
]
}
```
### 3. 单值格式
```json
{
"metric": "temperature",
"value": 24.8
}
```
### 4. GSTMB1 顶层字段格式
```json
{
"type": "GSTMB1",
"mac": "28562f8c35e8",
"temperature": 28.24,
"humidity": 57.83
}
```
### 设备标识优先级
1. `body.deviceKey`(显式指定)
2. `body.mac`GSTMB1 传感器 / GSPE1B 智能插座)
3. `body.imei`GSCW1M-4G 断路器,自动将 `G` 规范化为 `6`
4. topic 路径第 4 段(如 `sensor-001`
---
## 七、后端处理逻辑
### 消息处理流程
```
MQTT 消息到达
├── JSON 解析失败 → 记录警告,丢弃
├── 提取 deviceKeymac > deviceKey > topic[3]
├── 记录 deviceKey → 上行主题映射(用于下行命令)
├── 更新设备 online_status = "online", last_seen = now
├── source == "command"?
│ ├── success == false -> 记录警告,结束
│ ├── commandName == "info-all" / "device-timer-interval" -> 提取温湿度入库
│ ├── commandName == "info-statistic" / "controller-event" -> 提取电压/电流/功率/电量入库
│ └── 其他 -> 记录日志,结束
└── 提取遥测指标(按优先级)
├── body.metrics(对象)
├── body.data(数组)
├── body.value(单值)
└── body 顶层字段(温度/湿度/电压/电流/功率/电量/通断状态等)
├── 持久化(IoTDB 优先,降级 PostgreSQL
├── WebSocket 推送前端
└── 阈值检测
```
### 离线检测
- 每 60 秒检查一次
- 超过 5 分钟未上报的设备标记为 `offline`
- 通过 WebSocket 广播状态变更