Files
story/docs/设备相关外部接口表.md
2026-08-29 21:56:51 +08:00

27 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 设备相关外部接口表
范围仅包含设备终端以 HMAC 身份直接调用的 `/device/v2/*` 外部接口;不包含家庭/运营 Web API、内部工具和供应商回调。基于 `openapi/openapi.yaml`(OpenAPI 3.1,版本 0.70.0)生成,并与 `internal/httpapi/router.go` 的实际路由逐项核对。当前共 **16** 个已注册且纳入 OpenAPI 契约的接口。
说明:`*` 表示必填字段;`body` 表示请求体;响应栏只展开成功响应。全部接口都要求设备 HMAC 请求头:`X-Device-Id`、`X-Device-Timestamp`、`X-Device-Nonce`、`X-Device-Content-SHA256`、`X-Device-Signature`,表内不重复。通用错误响应为 `ErrorResponse`。
## 设备端 v2
| 接口名字 | 方法与路径 | 接口描述 | 输入参数 | 输出参数 |
|---|---|---|---|---|
| `getDeviceBootstrap` | `GET /device/v2/bootstrap` | 返回可信设备状态、服务器时间、绑定和最低安全版本 | query `firmware_version*`: string<br>query `board_rev`: string<br>query `rtc_sdk_version`: string<br>query `codec`: string<br>query `sample_rate`: integer<br>query `device_time*`: string(date-time)<br>query `network`: string | 200 DeviceBootstrapResponse{data*:DeviceBootstrapData{clock_skew_ms*:integer(int64), device*:DeviceBootstrap, server_time*:string(date-time)}, request_id*:string} — 返回可信设备状态、服务器时间、绑定和最低安全版本 |
| `pullDeviceCommands` | `GET /device/v2/commands` | 返回未过期的待执行命令并记录投递次数 | query `limit`: integer | 200 DeviceCommandDeliveryListResponse{data*:object{items*:array<DeviceCommandDelivery>}, request_id*:string} — 返回未过期的待执行命令并记录投递次数 |
| `acknowledgeDeviceCommand` | `POST /device/v2/commands/{id}/ack` | 回执已保存 | path `id*`: string(uuid)<br>body* `application/json` DeviceCommandAckRequest{result_summary*:object, status*:string[ACKNOWLEDGED/SUCCEEDED/FAILED]} | 200 DeviceCommandDeliveryResponse{data*:DeviceCommandDelivery{created_at*:string(date-time), delivery_attempts*:integer, expires_at*:string(date-time), id*:string(uuid), payload*:object, status*:DeviceCommandStatus, type*:string[apply_config/reboot/diagnostics/firmware_update]}, request_id*:string} — 回执已保存 |
| `getDeviceConfig` | `GET /device/v2/config` | 返回设备期望配置及当前绑定 | header `If-None-Match`: string | 200 DeviceConfigResponse{data*:DeviceConfiguration{binding*:DeviceBinding, desired_config*:object, device_id*:string(uuid), reported_revision:integer(int64), revision*:integer(int64)}, request_id*:string};headers{ETag:string} — 返回设备期望配置及当前绑定<br>304 无响应体 — 配置未变化 |
| `createDeviceConversation` | `POST /device/v2/conversations` | 分配房间和短期入房凭证;此步骤不启动语音智能体。 | header `Idempotency-Key*`: string<br>body* `application/json` CreateConversationRequest{capabilities*:DeviceCapabilities{audio_tracks*:array<string[near/far]>, rtc*:boolean, rtc_credential_renewal:boolean, upload_resume*:boolean}, device_time*:string(date-time), monotonic_ms:integer(int64), network*:DeviceNetwork{rssi:integer, type*:string}, requested_goal*:string} | 201 CreateConversationResponse{data*:CreatedConversation{conversation_id*:string(uuid), privacy*:ConversationPrivacy, rtc*:RTCGrant, session*:ConversationSession, status*:string}, request_id*:string};headers{Idempotency-Replayed:string[true]} — 房间资料已分配;重复请求返回相同通话和相同确定性短期Token |
| `getDeviceConversationState` | `GET /device/v2/conversations/{id}` | 设备确认入房后读取云端权威通话状态和下一步动作;供应商永久拒绝启动或异步启动失败时返回稳定failure_code及leave_room,响应不包含供应商原始错误。 | path `id*`: string(uuid) | 200 DeviceConversationStateResponse{data*:DeviceConversationState{action*:string[wait/continue/leave_room], conversation_id*:string(uuid), failure_code:string, status*:ConversationStatus}, request_id*:string} — 返回当前设备所属通话的权威状态和设备动作 |
| `activateDeviceConversation` | `POST /device/v2/conversations/{id}/activate` | 设备确认已进入指定RTC房间后,投递固定供应商语音任务。 | path `id*`: string(uuid)<br>header `Idempotency-Key*`: string<br>body* `application/json` ActivateConversationRequest{config_revision*:integer(int64), connection_reference:string, joined_at*:string(date-time), room_id*:string, rtc_join_result*:string, rtc_user_id*:string} | 202 ActivateConversationResponse{data*:ActivatedConversation{accepted_at*:string(date-time), conversation_id*:string(uuid), status*:string[STARTING_AGENT/ACTIVE]}, request_id*:string} — 入房确认已接受;语音智能体正在启动或已经启动 |
| `heartbeatDeviceConversation` | `POST /device/v2/conversations/{id}/heartbeat` | 按严格递增序号刷新设备在线状态和通话短期租约;重复或乱序心跳不会延长租约。 | path `id*`: string(uuid)<br>body* `application/json` HeartbeatConversationRequest{command_results*:array<object>, conversation_status*:string[PREPARING/ROOM_ISSUED/STARTING_AGENT/ACTIVE/STOPPING/COMPLETED/FAILED/EXPIRED], device_time*:string(date-time), firmware_version*:string, last_error:string, monotonic_ms*:integer(int64), network*:DeviceNetwork{rssi:integer, type*:string}, recording_queue*:RecordingQueue{failed_segments*:integer, last_failure_at:string(date-time), last_failure_code:string, pending_segments*:integer}, reported_config_revision*:integer(int64), rtc*:RTCHeartbeat{downlink*:boolean, joined*:boolean, uplink*:boolean}, rtc_credential_expires_at:string(date-time), sequence_no*:integer(int64)} | 200 HeartbeatConversationResponse{data*:HeartbeatResult{actions*:array<HeartbeatAction>, commands*:array<object>, desired_config_revision*:integer(int64), lease_expires_at:string(date-time)}, request_id*:string} — 心跳已接受,或已按序号判定为安全重放/乱序请求 |
| `stopDeviceConversation` | `POST /device/v2/conversations/{id}/stop` | 记录设备挂断并可靠停止固定供应商任务;重复执行不产生第二个外部副作用。 | path `id*`: string(uuid)<br>header `Idempotency-Key*`: string<br>body* `application/json` StopConversationRequest{device_time*:string(date-time), last_heartbeat_sequence*:integer(int64), reason*:string, recording*:StopRecording{far_segments*:integer, near_segments*:integer}, rtc_left*:boolean} | 202 StopConversationResponse{data*:StoppedConversation{accepted_at*:string(date-time), conversation_id*:string(uuid), status*:string[STOPPING/COMPLETED/FAILED/EXPIRED]}, request_id*:string};headers{Idempotency-Replayed:string[true]} — 停止请求已接受,或通话已经处于停止中/终态 |
| `submitDeviceConversationTurns` | `POST /device/v2/conversations/{id}/turns:batch` | 当供应商最终转写缺失时由当前通话所属终端批量补传终稿;平台按turn_key和sequence_no与已有供应商终稿原子去重合并,任一内容冲突会拒绝整批请求。 | path `id*`: string(uuid)<br>header `Idempotency-Key*`: string<br>body* `application/json` DeviceTranscriptBatchRequest{turns*:array<DeviceFinalTurn>} | 200 DeviceTranscriptBatchResponse{data*:DeviceTranscriptBatchResult{accepted_count*:integer, conversation_id*:string(uuid), duplicate_count*:integer}, request_id*:string};headers{Idempotency-Replayed:string[true]} — 整批终稿已合并;完全相同的已有终稿计入duplicate_count而不重复保存 |
| `prepareMediaUploadBatch` | `POST /device/v2/conversations/{id}/upload-batches` | 设备提交near/far录音分片清单;业务API仅返回单对象短期PUT地址,不接收录音字节。 | path `id*`: string(uuid)<br>header `Idempotency-Key*`: string<br>body* `application/json` PrepareMediaUploadBatchRequest{segments*:array<MediaSegmentDeclaration>} | 201 MediaUploadBatchResponse{data*:MediaUploadBatch{conversation_id*:string(uuid), expected_total_size*:integer(int64), expires_at*:string(date-time), id*:string(uuid), last_error_code:string, rejected_count*:integer, segment_count*:integer, segments*:array<MediaSegment>, status*:string[PENDING/UPLOADING/VERIFYING/COMPLETED/FAILED/EXPIRED], verification_attempts*:integer, verified_count*:integer}, request_id*:string} — 返回尚未验证分片的10至20分钟单对象上传地址 |
| `getDeviceFirmwareDownload` | `GET /device/v2/firmware-releases/{id}/download` | 仅向当前灰度目标签发 5 分钟有效的私有下载地址 | path `id*`: string(uuid) | 200 application/json `{download_url*:string(uri), download_until*:string(date-time)}`;header `Cache-Control: no-store` |
| `acknowledgeLocalMediaDelete` | `POST /device/v2/media-segments/{id}/local-delete-ack` | 设备在平台给出的local_delete_after之后删除本地密文并回报结果;该操作不删除云端已验证对象,DELETED重复回报幂等。 | path `id*`: string(uuid)<br>body* `application/json` LocalMediaDeleteReceiptRequest{error_code:string, status*:string[DELETED/FAILED]} | 200 MediaSegmentResponse{data*:MediaSegment{codec*:string, content_type*:string, duration_ms*:integer, expected_digest*:string, expected_size*:integer(int64), id*:string(uuid), last_error_code:string, local_delete_after:string(date-time), local_delete_error_code:string, local_delete_reported_at:string(date-time), local_delete_status:string[DELETED/FAILED], sequence_no*:integer, status*:string[DECLARED/AUTHORIZED/UPLOADED/VERIFYING/VERIFIED/REJECTED/DELETED], track*:string[near/far], upload_until:string(date-time), upload_url:string(uri), verified_at:string(date-time)}, request_id*:string} — 已保存本地删除回执 |
| `submitDeviceTelemetry` | `POST /device/v2/telemetry:batch` | 当前签名设备批量上报状态、有限错误码和命令执行结果;平台以设备内event_key和sequence_no原子去重,更新设备影子、命令状态和与当前设备通话关联的异常回收信号。 | header `Idempotency-Key*`: string<br>body* `application/json` DeviceTelemetryBatchRequest{events*:array<DeviceTelemetryEvent>} | 200 DeviceTelemetryBatchResponse{data*:DeviceTelemetryBatchResult{accepted_count*:integer, actions*:array<DeviceTelemetryAction>, desired_config_revision*:integer(int64), duplicate_count*:integer}, request_id*:string};headers{Idempotency-Replayed:string[true]} — 整批遥测已接收;完全相同的已有事件计入duplicate_count |
| `getDeviceMediaUploadBatch` | `GET /device/v2/upload-batches/{id}` | 返回分片校验结果;只有VERIFIED分片才包含local_delete_after | path `id*`: string(uuid) | 200 MediaUploadBatchResponse{data*:MediaUploadBatch{conversation_id*:string(uuid), expected_total_size*:integer(int64), expires_at*:string(date-time), id*:string(uuid), last_error_code:string, rejected_count*:integer, segment_count*:integer, segments*:array<MediaSegment>, status*:string[PENDING/UPLOADING/VERIFYING/COMPLETED/FAILED/EXPIRED], verification_attempts*:integer, verified_count*:integer}, request_id*:string} — 返回分片校验结果;只有VERIFIED分片才包含local_delete_after |
| `completeMediaUploadBatch` | `POST /device/v2/upload-batches/{id}/complete` | 设备声明本批上传完成,平台可靠投递后台对象校验任务;重复提交不会创建重复任务。 | path `id*`: string(uuid) | 202 MediaUploadBatchResponse{data*:MediaUploadBatch{conversation_id*:string(uuid), expected_total_size*:integer(int64), expires_at*:string(date-time), id*:string(uuid), last_error_code:string, rejected_count*:integer, segment_count*:integer, segments*:array<MediaSegment>, status*:string[PENDING/UPLOADING/VERIFYING/COMPLETED/FAILED/EXPIRED], verification_attempts*:integer, verified_count*:integer}, request_id*:string} — 批次已进入VERIFYING或已经完成 |