设备 API¶
所有设备接口需要认证,位于 /api/devices 路径前缀下。这些接口管理电子墨水屏设备的完整生命周期:列表查询、绑定、解绑、命令下发和状态读取。
设备模型¶
{
"id": "01912345-6789-7abc-def0-123456789abc",
"hwId": "a1b2c3d4-5678-9012-abcd-ef0123456789",
"thingName": "inklet-a1b2c3d4",
"firmware": "1.2.0",
"battery": 85,
"online": true,
"lastSeenAt": "2026-01-16T08:30:00Z",
"ownerId": "01912345-0000-7abc-def0-000000000001",
"boundAt": "2026-01-15T14:00:00Z",
"claimCode": null,
"state": "{\"screen\":\"text\",\"lastCmd\":\"abc123\"}",
"stateUpdatedAt": "2026-01-16T08:25:00Z"
}
| 字段 | 类型 | 描述 |
|---|---|---|
id |
UUID v7 | 数据库主键 |
hwId |
string | 出厂时烧录到设备中的硬件 UUID |
thingName |
string | AWS IoT Core Thing 名称(在配网时分配) |
firmware |
string 或 null | 设备上报的固件版本 |
battery |
integer 或 null | 电池百分比(0--100) |
online |
boolean | 设备当前是否连接到 IoT Core |
lastSeenAt |
timestamp 或 null | 最后一次心跳的时间戳 |
ownerId |
UUID 或 null | 拥有此设备的用户(未绑定时为 null) |
boundAt |
timestamp 或 null | 设备绑定到当前所有者的时间 |
claimCode |
string 或 null | 6 位配对码(仅在设备未绑定时存在) |
state |
JSON string 或 null | 通过 MQTT 上报的任意设备状态 |
stateUpdatedAt |
timestamp 或 null | 状态最后更新的时间 |
说明
claimCode 仅在设备未绑定时存在。设备绑定到用户后,配对码会被清除。
接口列表¶
GET /api/devices¶
:material-lock: 需要认证
列出当前认证用户绑定的所有设备。
请求头:
响应: 200 OK
[
{
"id": "01912345-6789-7abc-def0-123456789abc",
"hwId": "a1b2c3d4-5678-9012-abcd-ef0123456789",
"thingName": "inklet-a1b2c3d4",
"firmware": "1.2.0",
"battery": 85,
"online": true,
"lastSeenAt": "2026-01-16T08:30:00Z",
"ownerId": "01912345-0000-7abc-def0-000000000001",
"boundAt": "2026-01-15T14:00:00Z",
"claimCode": null,
"state": null,
"stateUpdatedAt": null
}
]
如果用户没有绑定任何设备,返回空数组 []。
GET /api/devices/{thing}¶
:material-lock: 需要认证 --- 仅设备所有者
通过 Thing 名称获取指定设备的详细信息。
路径参数:
| 参数 | 描述 |
|---|---|
thing |
设备的 thingName(例如 inklet-a1b2c3d4) |
响应: 200 OK
{
"id": "01912345-6789-7abc-def0-123456789abc",
"hwId": "a1b2c3d4-5678-9012-abcd-ef0123456789",
"thingName": "inklet-a1b2c3d4",
"firmware": "1.2.0",
"battery": 85,
"online": true,
"lastSeenAt": "2026-01-16T08:30:00Z",
"ownerId": "01912345-0000-7abc-def0-000000000001",
"boundAt": "2026-01-15T14:00:00Z",
"claimCode": null,
"state": "{\"screen\":\"text\",\"lastCmd\":\"abc123\"}",
"stateUpdatedAt": "2026-01-16T08:25:00Z"
}
错误:
| 状态码 | 原因 |
|---|---|
401 |
访问令牌缺失或无效 |
403 |
当前认证用户不是设备所有者 |
404 |
设备未找到 |
GET /api/devices/{thing}/state¶
:material-lock: 需要认证 --- 仅设备所有者
获取设备上报的原始 JSON 状态。此接口返回解析后的 JSON 对象,而非字符串形式的 state 字段。
路径参数:
| 参数 | 描述 |
|---|---|
thing |
设备的 thingName |
响应: 200 OK
提示
当需要以结构化 JSON 对象形式读取设备状态时,请使用此接口。GET /api/devices/{thing} 接口在设备对象中以转义 JSON 字符串的形式返回状态。
错误:
| 状态码 | 原因 |
|---|---|
401 |
访问令牌缺失或无效 |
403 |
当前认证用户不是设备所有者 |
404 |
设备未找到或尚未上报状态 |
POST /api/devices/bind/nfc¶
:material-lock: 需要认证
使用 NFC 载荷将设备绑定到当前认证用户。后端在绑定前会根据出厂密钥验证 HMAC 签名。
请求体:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
hwId |
string | 是 | 从 NFC 标签读取的硬件 UUID |
signature |
string | 是 | HMAC-SHA256(hwId, FACTORY_SECRET) 的前 16 个十六进制字符 |
响应: 200 OK
返回完整的设备对象,ownerId 设置为当前认证用户。
错误:
| 状态码 | 原因 |
|---|---|
400 |
字段缺失或签名无效 |
404 |
未找到对应 hwId 的设备 |
409 |
设备已被其他用户绑定 |
POST /api/devices/bind/code¶
:material-lock: 需要认证
使用设备屏幕上显示的 6 位配对码将设备绑定到当前认证用户。
请求体:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
code |
string | 是 | 6 位配对码(不区分大小写) |
响应: 200 OK
返回完整的设备对象,ownerId 设置为当前认证用户。
错误:
| 状态码 | 原因 |
|---|---|
400 |
配对码缺失或格式无效 |
404 |
未找到对应配对码的设备 |
409 |
设备已被其他用户绑定 |
410 |
配对码已过期 |
配对码从何而来?
当设备处于未绑定状态并开机时,会通过 MQTT 发布 request_claim 消息。后端生成一个 6 位配对码,存储到数据库,并通过 claim_code 命令发送回设备。设备将其渲染到电子墨水屏上。
POST /api/devices/{thing}/unbind¶
:material-lock: 需要认证 --- 仅设备所有者
将设备从当前认证用户解绑。设备将恢复为未绑定状态,并重新请求配对码。
路径参数:
| 参数 | 描述 |
|---|---|
thing |
设备的 thingName |
响应: 200 OK
解绑后,后端通过 MQTT 向设备发送 unbound 命令。设备清除屏幕内容并重新请求配对码。
错误:
| 状态码 | 原因 |
|---|---|
401 |
访问令牌缺失或无效 |
403 |
当前认证用户不是设备所有者 |
404 |
设备未找到 |
POST /api/devices/{thing}/cmd¶
:material-lock: 需要认证 --- 仅设备所有者
向设备发送命令。命令通过 MQTT 发送到设备的 down/cmd 主题。
路径参数:
| 参数 | 描述 |
|---|---|
thing |
设备的 thingName |
请求体:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
kind |
string | 是 | 命令类型(目前用户命令仅支持 text) |
text |
string | 条件必填 | 当 kind 为 text 时必须提供 |
响应: 200 OK
{
"id": "01912345-9999-7abc-def0-aaaaaaaaaaaa",
"deviceId": "01912345-6789-7abc-def0-123456789abc",
"kind": "text",
"text": "Hello from Inklet!",
"createdAt": "2026-01-16T09:00:00Z"
}
响应包含 DeviceCommand 记录,用于跟踪命令下发状态。
错误:
| 状态码 | 原因 |
|---|---|
400 |
命令字段缺失或无效 |
401 |
访问令牌缺失或无效 |
403 |
当前认证用户不是设备所有者 |
404 |
设备未找到 |
POST /api/devices/{thing}/refresh-code¶
:material-lock: 需要认证
重新生成设备的配对码。当当前配对码已过期或丢失时使用。
路径参数:
| 参数 | 描述 |
|---|---|
thing |
设备的 thingName |
响应: 200 OK
返回完整的设备对象,claimCode 已更新为新的配对码。
错误:
| 状态码 | 原因 |
|---|---|
401 |
访问令牌缺失或无效 |
404 |
设备未找到 |
错误响应¶
所有错误响应遵循统一的格式:
| 状态码 | 描述 |
|---|---|
400 |
请求无效 --- 请求体格式错误、缺少必填字段或格式无效 |
401 |
未授权 --- 访问令牌缺失、已过期或无效 |
403 |
禁止访问 --- 当前认证用户不是目标设备的所有者 |
404 |
未找到 --- 设备或资源不存在 |
409 |
冲突 --- 设备已被其他用户绑定 |
410 |
已失效 --- 配对码已过期,需要重新生成 |