35 KiB
设备相关接口完整说明
版本:2026-09-05
契约来源:internal/httpapi/router.go、实际处理器、openapi/openapi.yaml
生产 Base URL:https://121.41.46.238
适用对象:EchoEar/ESP32-S3 固件、Windows 模拟设备、用户 App/Web、平台运维端。
1. 文档范围
本文覆盖当前代码中全部直接或间接管理设备的接口:
| 接口面 | 方法数 | 鉴权 | 用途 |
|---|---|---|---|
设备直连接口 /device/v2、/device/v3 |
20 | 设备 HMAC | 启动、配置、命令、遥测、固件下载、会话、字幕、媒体、火山凭据 |
用户设备管理 /api/v1/devices* |
10 | OIDC Bearer | 绑定、设备列表、在线状态、影子、命令 |
运维设备管理 /api/v1/ops/devices* |
9 | OIDC Bearer + X-Tenant-Id |
登记、密钥轮换、绑定码、激活状态机 |
| 家庭端与运维端固件接口 | 31 | OIDC Bearer | 签名密钥、版本、上传、灰度、暂停、回滚、下载 |
| 设备诊断处置 | 2 | OIDC Bearer + X-Tenant-Id |
强制刷新配置、吊销设备 |
火山硬件智能体 WebSocket 不是平台 HTTP API,但设备聊天必须使用,故在第 10 节单独说明。
2. 通用约定
2.1 平台响应包络
成功:
{
"request_id": "req-01J...",
"data": {}
}
失败:
{
"request_id": "req-01J...",
"error": {
"code": "VALIDATION_FAILED",
"message": "请求参数不正确",
"retryable": false,
"field_errors": [{ "field": "hardware_id", "message": "不能为空" }],
"details": {}
}
}
2.2 ID、时间和分页
{id}、device_id、conversation_id、household_id、subject_id均为 UUID。- 时间均为 RFC3339 UTC,例如
2026-09-05T06:30:00Z。 Idempotency-Key长度为 16—128 字符;业务重试保留原正文和原幂等键。If-Match使用接口返回的强 ETag,例如"7"。- 密钥、Token、绑定码和下载地址不得写入普通日志。
2.3 设备 HMAC 请求头
所有 /device/v2/* 和 /device/v3/* 请求必须携带:
| Header | 输入说明 |
|---|---|
X-Device-Id |
平台设备公开 UUID |
X-Device-Timestamp |
RFC3339 时间,允许时钟偏差默认 ±5 分钟 |
X-Device-Nonce |
16—128 字符,一次请求只能使用一次 |
X-Device-Content-SHA256 |
原始请求正文 SHA-256 小写十六进制;空正文也计算 |
X-Device-Signature |
HMAC-SHA256 的 Base64URL 无填充编码 |
签名原文:
HTTP_METHOD大写\n
完整REQUEST_URI(含查询字符串)\n
X-Device-Timestamp\n
X-Device-Nonce\n
X-Device-Content-SHA256
3. 设备启动、配置、命令和遥测
3.1 GET /device/v2/bootstrap
介绍:设备上电后报告固件和硬件信息,取得平台权威设备状态、绑定信息、配置版本和服务器时间。
输入:
| 位置 | 字段 | 必填 | 说明 |
|---|---|---|---|
| Query | firmware_version |
是 | 当前固件版本,最长 80 |
| Query | device_time |
是 | RFC3339 设备时间 |
| Query | board_rev |
否 | 硬件版本 |
| Query | rtc_sdk_version |
否 | SDK 版本 |
| Query | codec |
否 | 编码,例如 pcm_s16le |
| Query | sample_rate |
否 | 采样率,例如 16000 |
| Query | network |
否 | 网络类型,例如 wifi |
输出:server_time、clock_skew_ms、设备状态、最低版本、兼容性、config_revision 和绑定对象。
示例:
GET /device/v2/bootstrap?firmware_version=1.0.0&board_rev=echoear-v1&codec=pcm_s16le&sample_rate=16000&device_time=2026-09-05T06%3A30%3A00Z&network=wifi
{
"request_id": "req-1",
"data": {
"server_time": "2026-09-05T06:30:01Z",
"clock_skew_ms": 1000,
"device": {
"device_id": "10000000-0000-0000-0000-000000000512",
"status": "registered",
"model": "echoear",
"firmware_compatible": true,
"config_revision": 7,
"binding": { "bound": true, "household_id": "11111111-1111-1111-1111-111111111111", "subject_id": "22222222-2222-2222-2222-222222222222", "subject_name": "张大妈" }
}
}
}
3.2 GET /device/v2/config
介绍:读取云端期望配置和当前绑定。设备保存响应 ETag,后续用 If-None-Match 增量检查。
输入:无正文;可选 Header If-None-Match: "7"。
输出:HTTP 200 返回 device_id、revision、desired_config、reported_revision、binding;配置未变化返回 304 且无正文。
{
"request_id": "req-2",
"data": {
"device_id": "10000000-0000-0000-0000-000000000512",
"revision": 8,
"desired_config": { "voice_runtime": "volc_hardware_agent", "volume": { "speaker": 70 } },
"reported_revision": 7,
"binding": { "bound": true, "subject_id": "22222222-2222-2222-2222-222222222222" }
}
}
3.3 GET /device/v2/commands
介绍:拉取未过期的待执行命令,并增加投递次数。
输入:Query limit,范围 1—20,默认 10。
输出:items[];每项包含 id、type、payload、status、expires_at、delivery_attempts、created_at。
{
"request_id": "req-3",
"data": {
"items": [{
"id": "33333333-3333-3333-3333-333333333333",
"type": "apply_config",
"payload": { "revision": 8 },
"status": "DELIVERED",
"expires_at": "2026-09-05T07:30:00Z",
"delivery_attempts": 1,
"created_at": "2026-09-05T06:30:00Z"
}]
}
}
3.4 POST /device/v2/commands/{id}/ack
介绍:设备回报命令已接收、成功或失败。
输入:status 为 ACKNOWLEDGED、SUCCEEDED、FAILED;result_summary 只能包含有限诊断字段,不得含 Secret/Token。
{
"status": "SUCCEEDED",
"result_summary": { "applied_revision": 8 }
}
输出:更新后的命令投递对象。
{
"request_id": "req-4",
"data": { "id": "33333333-3333-3333-3333-333333333333", "type": "apply_config", "status": "SUCCEEDED", "result_summary": { "applied_revision": 8 } }
}
3.5 POST /device/v2/telemetry:batch
介绍:批量上报状态、脱敏错误或命令结果;按 event_key 和 sequence_no 去重,并返回平台动作。
输入:events 为 1—500 项;event_type 为 status、error 或 command_result,且只携带对应同名对象。
{
"events": [{
"event_key": "boot.000008",
"sequence_no": 8,
"occurred_at": "2026-09-05T06:31:00Z",
"event_type": "status",
"status": {
"firmware_version": "1.0.0",
"reported_config_revision": 8,
"network": { "type": "wifi", "rssi": -51 },
"rtc": { "joined": false, "uplink": false, "downlink": false },
"recording_queue": { "pending_segments": 0, "failed_segments": 0 }
}
}]
}
输出:接收/重复数、最新期望配置版本和动作列表。
{
"request_id": "req-5",
"data": { "accepted_count": 1, "duplicate_count": 0, "desired_config_revision": 8, "actions": [{ "type": "pull_commands" }] }
}
4. 火山硬件智能体平台控制面
4.1 POST /device/v3/hardware-agent-credentials:provision
介绍:平台使用 Product Secret 代设备调用火山动态注册,只向当前设备返回它自己的一机一密。允许状态:BOUND、PROVISIONING、ACTIVE。
输入:Header Idempotency-Key;正文只包含稳定 hardware_id。
{ "hardware_id": "echoear-b43a451634a8" }
输出:instance_id、product_key、device_name、hardware_id、一次性敏感的 device_secret、版本和来源。响应必须 Cache-Control: no-store。
{
"request_id": "req-6",
"data": {
"instance_id": "6a92945cc0c4689234aef656",
"product_key": "6a9568047dd4b23eacab9b1a",
"device_name": "echoear-100000000512",
"hardware_id": "echoear-b43a451634a8",
"device_secret": "<仅保存到设备安全区>",
"credential_ref": "cred-01J...",
"credential_version": 1,
"credential_source": "platform_dynamic_registration",
"issued_at": "2026-09-05T06:32:00Z"
}
}
4.2 POST /device/v3/agent-sessions
介绍:每次聊天前创建一次平台会话,冻结本次 Bot、授权、人设、记忆、隐私策略和会话证明。仅 ACTIVE 设备可调用。
输入:
{
"device_time": "2026-09-05T06:35:00Z",
"monotonic_ms": 123456,
"capabilities": {
"hardware_agent": true,
"sdk_version": "1.1.0-echoear",
"transports": ["low_load"],
"input_audio_format": "pcm_s16le_16000_mono",
"output_audio_format": "pcm_s16le_16000_mono",
"server_event_callback": false,
"transcript_upload": true,
"dynamic_session_config": true,
"credential_rotation": true,
"near_pcm_recording": false,
"far_pcm_recording": false
},
"network": { "type": "wifi", "rssi": -52 },
"requested_goal": "open_conversation",
"config_revision": 8
}
输出:
{
"request_id": "req-7",
"data": {
"conversation_id": "44444444-4444-4444-4444-444444444444",
"status": "SESSION_READY",
"runtime_type": "volc_hardware_agent",
"runtime": {
"provider": "volcengine",
"bot_id": "botM4gWQ9A9y",
"transport": "low_load",
"provider_session_ref": "provider-ref",
"session_nonce": "<仅内存保存>",
"config_digest": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"wait_for_session_update": true
},
"session_config": {
"dialog_id": "55555555-5555-5555-5555-555555555555",
"system_prompt": "<本次人设>",
"persona_tone": "亲切、自然",
"authorized_context": "<本次授权背景与记忆>",
"prompt_bundle_version": "minimal-v2"
},
"privacy": { "retention_mode": "transcript_only", "record_near": false, "record_far": false, "upload_allowed": false },
"session": { "engine_mode": "s2s_hybrid", "heartbeat_interval_sec": 15, "allocation_expires_at": "2026-09-05T06:40:00Z" }
}
}
4.3 POST /device/v3/agent-sessions/{id}/events
介绍:设备回流 session_started、session_updated、session_ended、session_failed。sequence_no 严格递增。
开始事件输入:
{
"event_id": "evt-start-1",
"sequence_no": 1,
"event_type": "session_started",
"occurred_at": "2026-09-05T06:35:03Z",
"provider_session_ref": "provider-ref",
"session_nonce": "<创建会话返回值>",
"provider_connection_ref": "event_LUVjX_wvR",
"bot_id": "botM4gWQ9A9y",
"transport": "low_load",
"config_digest": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"dynamic_config_applied": true
}
输出:status、平台动作、是否重复和接收时间。
{
"request_id": "req-8",
"data": { "conversation_id": "44444444-4444-4444-4444-444444444444", "status": "ACTIVE", "action": "continue", "duplicate": false, "accepted_at": "2026-09-05T06:35:03Z" }
}
action=stop_session 时必须立即停麦、断开火山,再上报 session_ended 或 session_failed。
4.4 POST /device/v3/agent-sessions/{id}/turns:batch
介绍:可靠上传最终字幕;禁止上传增量字幕。单批 1—500 条。
输入:
{
"turns": [{
"turn_key": "user-final-1",
"sequence_no": 1,
"speaker": "subject",
"start_at": "2026-09-05T06:35:10Z",
"end_at": "2026-09-05T06:35:12Z",
"final_text": "你好。",
"interrupted": false
}]
}
输出:
{ "request_id": "req-9", "data": { "conversation_id": "44444444-4444-4444-4444-444444444444", "accepted_count": 1, "duplicate_count": 0 } }
5. 旧 RTC 会话兼容接口
新 EchoEar 使用硬件智能体 v3,不应调用本节的创建/激活接口。本节仅供存量 RTC 设备。
5.1 POST /device/v2/conversations
介绍:分配 RTC 房间和短期 Token,不启动智能体。
输入:
{
"device_time": "2026-09-05T06:40:00Z",
"monotonic_ms": 130000,
"capabilities": { "rtc": true, "audio_tracks": ["near", "far"], "upload_resume": true, "rtc_credential_renewal": true },
"network": { "type": "wifi", "rssi": -50 },
"requested_goal": "open_conversation"
}
输出:conversation_id、ROOM_ISSUED、RTC 房间/用户/Token、隐私策略和心跳配置。
{
"request_id": "req-10",
"data": {
"conversation_id": "66666666-6666-6666-6666-666666666666",
"status": "ROOM_ISSUED",
"rtc": { "app_id": "app", "room_id": "room", "user_id": "device-user", "agent_user_id": "agent-user", "token": "<仅内存>", "expires_at": "2026-09-05T06:45:00Z" },
"privacy": { "retention_mode": "none", "record_near": false, "record_far": false, "upload_allowed": false },
"session": { "engine_mode": "cascade", "heartbeat_interval_sec": 10, "allocation_expires_at": "2026-09-05T06:45:00Z" }
}
}
5.2 POST /device/v2/conversations/{id}/activate
介绍:设备确认已成功进入指定 RTC 房间后,触发平台启动旧语音任务。
输入:
{ "joined_at": "2026-09-05T06:40:02Z", "room_id": "room", "rtc_user_id": "device-user", "rtc_join_result": "success", "config_revision": 8, "connection_reference": "rtc-conn-1" }
输出:
{ "request_id": "req-11", "data": { "conversation_id": "66666666-6666-6666-6666-666666666666", "status": "STARTING_AGENT", "accepted_at": "2026-09-05T06:40:02Z" } }
5.3 GET /device/v2/conversations/{id}
介绍:读取平台权威会话状态和设备下一步动作。
输入:Path id。
输出示例:
{ "request_id": "req-12", "data": { "conversation_id": "66666666-6666-6666-6666-666666666666", "status": "ACTIVE", "action": "continue" } }
失败时可能返回 status=FAILED、action=leave_room、稳定脱敏的 failure_code。
5.4 POST /device/v2/conversations/{id}/heartbeat
介绍:刷新会话租约、设备在线状态和配置状态;返回停止、续签或配置动作。
输入:
{
"sequence_no": 2,
"device_time": "2026-09-05T06:40:12Z",
"monotonic_ms": 142000,
"conversation_status": "ACTIVE",
"rtc": { "joined": true, "uplink": true, "downlink": true },
"network": { "type": "wifi", "rssi": -49 },
"firmware_version": "1.0.0",
"reported_config_revision": 8,
"recording_queue": { "pending_segments": 0, "failed_segments": 0 },
"command_results": []
}
输出:
{ "request_id": "req-13", "data": { "lease_expires_at": "2026-09-05T06:40:42Z", "actions": [{ "type": "continue" }], "desired_config_revision": 8, "commands": [] } }
5.5 POST /device/v2/conversations/{id}/stop
介绍:设备挂断并可靠停止旧供应商任务;支持幂等重放。
输入:
{ "reason": "user_hangup", "device_time": "2026-09-05T06:42:00Z", "last_heartbeat_sequence": 5, "rtc_left": true, "recording": { "near_segments": 0, "far_segments": 0 } }
输出:
{ "request_id": "req-14", "data": { "conversation_id": "66666666-6666-6666-6666-666666666666", "status": "STOPPING", "accepted_at": "2026-09-05T06:42:00Z" } }
5.6 POST /device/v2/conversations/{id}/turns:batch
介绍:旧 RTC 回调缺少终稿时,由设备补传最终字幕。输入输出与 4.4 完全相同。
{ "turns": [{ "turn_key": "agent-final-2", "sequence_no": 2, "speaker": "agent", "start_at": "2026-09-05T06:41:00Z", "end_at": "2026-09-05T06:41:02Z", "final_text": "您好。", "interrupted": false }] }
6. 设备录音上传接口
仅当本次 privacy.upload_allowed=true 且对应轨道获授权时使用。
6.1 POST /device/v2/conversations/{id}/upload-batches
介绍:声明待上传录音分片,平台返回每个对象的短期 PUT 地址;业务 API 不接收音频字节。
输入:
{
"segments": [{
"track": "near",
"sequence_no": 0,
"codec": "wav_pcm_s16le",
"content_type": "audio/wav",
"duration_ms": 10000,
"expected_size": 320044,
"expected_digest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}]
}
输出:批次状态和分片;创建响应中的分片包含 upload_url、upload_until。
{
"request_id": "req-15",
"data": {
"id": "77777777-7777-7777-7777-777777777777",
"conversation_id": "66666666-6666-6666-6666-666666666666",
"status": "UPLOADING",
"segment_count": 1,
"expected_total_size": 320044,
"verified_count": 0,
"rejected_count": 0,
"verification_attempts": 0,
"expires_at": "2026-09-05T07:02:00Z",
"segments": [{ "id": "88888888-8888-8888-8888-888888888888", "track": "near", "sequence_no": 0, "status": "AUTHORIZED", "upload_url": "https://object.example/put?...", "upload_until": "2026-09-05T07:02:00Z" }]
}
}
6.2 POST /device/v2/upload-batches/{id}/complete
介绍:声明分片已上传,平台开始异步对象大小、摘要和容器校验。
输入:无正文;Path id 为上传批次 UUID。
输出:HTTP 202,返回批次,status 通常为 VERIFYING。
{ "request_id": "req-16", "data": { "id": "77777777-7777-7777-7777-777777777777", "status": "VERIFYING", "segment_count": 1, "verified_count": 0, "rejected_count": 0, "segments": [] } }
6.3 GET /device/v2/upload-batches/{id}
介绍:轮询后台校验结果。只有 VERIFIED 分片返回 local_delete_after。
输入:Path id。
输出示例:
{ "request_id": "req-17", "data": { "id": "77777777-7777-7777-7777-777777777777", "status": "COMPLETED", "segment_count": 1, "verified_count": 1, "rejected_count": 0, "segments": [{ "id": "88888888-8888-8888-8888-888888888888", "status": "VERIFIED", "local_delete_after": "2026-09-05T07:10:00Z" }] } }
6.4 POST /device/v2/media-segments/{id}/local-delete-ack
介绍:到达平台允许删除时间后,设备删除本地密文并回报;不会删除云端对象。
输入:
{ "status": "DELETED" }
失败示例:{ "status": "FAILED", "error_code": "FLASH_DELETE_FAILED" }。
输出:更新后的分片对象,包含 local_delete_status 和 local_delete_reported_at。
7. 设备固件下载接口
GET /device/v2/firmware-releases/{id}/download
介绍:仅向当前灰度批次中的目标设备签发 5 分钟私有下载地址。
输入:Path id 为固件版本 UUID,无正文。
输出:
{ "request_id": "req-18", "data": { "download_url": "https://object.example/firmware.bin?...", "download_until": "2026-09-05T07:05:00Z" } }
非目标设备或暂停/退役版本返回 403 FIRMWARE_DOWNLOAD_DENIED。
8. 用户 App/Web 设备管理接口
统一输入 Header:Authorization: Bearer <OIDC access token>。以下表中“示例输入/输出”均省略通用包络字段。
| 方法与路径 | 接口介绍 | 输入 | 输出 | 示例 |
|---|---|---|---|---|
POST /api/v1/device-claims |
消费一次性绑定码 | Body:claim_code、household_id、subject_id |
binding_id、device_id、归属、bound_at |
入:{"claim_code":"CLAIM-...","household_id":"<uuid>","subject_id":"<uuid>"};出:{"binding_id":"<uuid>","device_id":"<uuid>","bound_at":"2026-09-05T07:00:00Z"} |
GET /api/v1/devices |
列出当前账号可管理设备 | 无 | items: ManagedDevice[] |
{"items":[{"id":"<uuid>","model":"echoear","status":"registered","desired_revision":8}]} |
GET /api/v1/devices/{id} |
查询设备与绑定摘要 | Path:设备 UUID | ManagedDevice |
{"id":"<uuid>","binding_id":"<uuid>","subject_name":"张大妈","firmware_version":"1.0.0","version":3} |
GET /api/v1/devices/{id}/binding-impact |
解绑/转移前检查阻断项 | Query:action=unbind|transfer;转移时 target_subject_id |
影响、allowed、blockers、当前 binding_id |
{"action":"transfer","allowed":true,"pending_command_count":0,"active_firmware_task_count":0} |
POST /api/v1/devices/{id}/binding-change |
执行解绑或转移 | Body:action、expected_binding_id、reason、acknowledged=true;转移时 target_subject_id |
旧/新绑定和变更时间 | 入:{"action":"unbind","expected_binding_id":"<uuid>","reason":"用户解除绑定","acknowledged":true};出:{"device_id":"<uuid>","action":"unbind","changed_at":"..."} |
GET /api/v1/devices/{id}/presence |
查询实时在线状态 | Path:设备 UUID | online、status、last_seen_at、source |
{"device_id":"<uuid>","online":true,"status":"ACTIVE","last_seen_at":"...","source":"redis"} |
GET /api/v1/devices/{id}/twin |
查询设备影子 | Path:设备 UUID | 期望/上报配置、版本、能力、同步状态;Header ETag |
{"device_id":"<uuid>","desired_revision":8,"desired_config":{"volume":{"speaker":70}},"sync_state":"applied"} |
PATCH /api/v1/devices/{id}/twin |
修改非敏感期望配置 | Header:If-Match;Body:desired_config,顶层仅 volume/interaction/upload/rtc/policy_refs |
更新后的 DeviceTwin 和新 ETag |
{"desired_config":{"volume":{"speaker":60}}} |
GET /api/v1/devices/{id}/commands |
查询最近 100 条命令 | Path:设备 UUID | items: DeviceCommand[] |
{"items":[{"id":"<uuid>","type":"reboot","status":"SUCCEEDED"}]} |
POST /api/v1/devices/{id}/commands |
下发可靠命令 | Header:Idempotency-Key;Body:type、payload、expires_in_sec |
DeviceCommand,首次 202,重放 200 |
入:{"type":"reboot","payload":{"reason":"maintenance"},"expires_in_sec":300};出:{"id":"<uuid>","status":"QUEUED","delivery_attempts":0} |
ManagedDevice 主要输出字段:id、binding_id、household_id、subject_id、subject_name、model、hardware_revision、status、固件/配置版本、last_reported_at、bound_at、registered_at、version。
9. 平台运维设备管理接口
统一输入 Header:Authorization: Bearer <operator token>、X-Tenant-Id: <tenant UUID>。只有独立运维账号可调用。
| 方法与路径 | 接口介绍 | 输入 | 输出 | 示例 |
|---|---|---|---|---|
GET /api/v1/ops/devices |
查询租户全部设备 | 无 | items: OpsDevice[],不返回密钥 |
{"items":[{"id":"<uuid>","model":"echoear","activation_state":"ACTIVE","credential_status":"active","voice_runtime":"volc_hardware_agent"}]} |
POST /api/v1/ops/devices |
登记新设备并生成平台 HMAC 密钥 | Body:model 必填;可选硬件/最低版本/归属/运行时/初始激活状态 |
device、只显示一次的 credential;可能同时返回 claim |
入:{"model":"echoear","hardware_revision":"esp32-s3","voice_runtime":"volc_hardware_agent","activation_state":"FACTORY"};出:{"device":{"id":"<uuid>"},"credential":{"device_id":"<uuid>","secret":"<64位hex>","key_version":1}} |
GET /api/v1/ops/device-assignment-options |
查询可分配家庭和长者 | 无 | items[] 含家庭/长者 ID 和名称 |
{"items":[{"household_id":"<uuid>","household_name":"张家","subject_id":"<uuid>","subject_name":"张大妈"}]} |
PATCH /api/v1/ops/devices/{id} |
更新设备型号、版本策略、状态、归属或运行时 | Header:If-Match;Body 至少一个可修改字段 |
更新后的 OpsDevice |
{"minimum_firmware_version":"1.1.0","status":"registered"} |
DELETE /api/v1/ops/devices/{id} |
软删除并吊销设备/凭据/绑定 | Header:If-Match |
吊销后的 OpsDevice |
{"id":"<uuid>","status":"revoked","activation_state":"REVOKED","credential_status":"revoked"} |
POST /api/v1/ops/devices/{id}/credentials/rotate |
轮换平台 HMAC 密钥,旧密钥立即失效 | 无正文 | 只显示一次的新 secret、key_version、issued_at |
{"device_id":"<uuid>","secret":"<64位hex>","key_version":2,"issued_at":"..."} |
POST /api/v1/ops/devices/{id}/claims |
为未绑定设备生成 24 小时绑定码 | 无正文 | 只显示一次的 claim_code 和过期时间 |
{"device_id":"<uuid>","claim_code":"CLAIM-...","issued_at":"...","expires_at":"..."} |
POST /api/v1/ops/devices/{id}/activation-transitions |
执行受控激活状态迁移 | Body:target_state、reason、expected_revision |
更新后的 OpsDevice |
{"target_state":"CLAIMABLE","reason":"工厂质检通过","expected_revision":1} |
GET /api/v1/ops/devices/{id}/activation-transitions |
查询完整激活审计历史 | Path:设备 UUID | items: DeviceActivationTransition[] |
{"items":[{"from_state":"FACTORY","to_state":"CLAIMABLE","reason":"工厂质检通过","activation_revision":2,"created_at":"..."}]} |
激活状态:FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE → LOST/REVOKED。设备不能自行修改该状态。
10. 火山硬件智能体 WebSocket
10.1 建连
wss://ai-gateway.vei.volces.com/v1/realtime?bot=botM4gWQ9A9y&wait_for_session_update=true
输入 Header:
| Header | 说明 |
|---|---|
X-Auth-Type |
固定 1 |
X-Product-Key |
平台预配返回的 Product Key |
X-Device-Name |
平台预配返回的 Device Name |
X-Random-Num |
本次随机整数 |
X-Timestamp |
Unix 秒 |
X-Instance-Id |
平台预配返回的 Instance ID |
X-Signature |
使用火山 Device Secret 计算的 HMAC-SHA256 Base64 |
X-Hardware-Id |
稳定 Hardware ID |
签名正文顺序固定:
auth_type=1&device_name={name}&random_num={random}&product_key={key}×tamp={unix}&instance_id={instance}
10.2 动态配置输入
连接打开后立即发送:
{
"event_id": "windows-session-update-<uuid>",
"type": "session.update",
"session": {
"object": "realtime.session",
"model": "",
"input_audio_format": "pcm16",
"output_audio_format": "pcm16",
"config": {
"ASRConfig": { "TurnDetectionMode": 0 },
"LLMConfig": { "SystemMessages": ["<平台冻结的人设与授权上下文>"] }
},
"agent_config": { "EnableConversationStateCallback": false }
}
}
输出:session.created 或 session.updated;在收到确认前不得开启麦克风。
10.3 音频和文本输入
上行音频:PCM16 little-endian、16kHz、单声道,建议每 20ms 320 个采样。
{ "event_id": "audio-<uuid>", "type": "input_audio_buffer.append", "audio": "<PCM Base64>" }
文本测试:
{
"event_id": "text-<uuid>",
"type": "conversation.item.create",
"item": { "type": "message", "role": "user", "content": [{ "type": "input_text", "text": "你好" }], "interrupt_mode": 1 }
}
10.4 设备消费的输出事件
| 事件 | 输出内容 | 设备动作 |
|---|---|---|
input_audio_buffer.speech_started/stopped |
服务端 VAD 状态 | 更新交互状态 |
conversation.item.input_audio_transcription.completed |
用户最终字幕 | 上传平台 turns:batch |
response.audio.delta |
Base64 PCM16、16kHz、单声道 | 连续缓冲并按声卡采样率重采样播放 |
response.audio.done |
本轮音频结束 | 排空播放缓冲 |
response.audio_transcript.delta/done |
智能体增量/最终字幕 | 只把最终字幕上传平台 |
response.function_call_arguments.done |
工具名、参数和 call_id |
调用平台受控工具并回传结果 |
response.done |
本轮完成 | 继续监听 |
error |
供应商错误 | 脱敏后上报 session_failed |
注意:low-load WebSocket 下行是 16kHz。把它按 24kHz 播放会出现约 1.5 倍速、音调升高和明显电子感。
11. 固件管理接口
11.1 家庭/租户授权接口
| 方法与路径 | 介绍 | 输入 | 输出 |
|---|---|---|---|
GET /api/v1/households/{id}/firmware-signing-keys |
查询可信 Ed25519 公钥 | Path household id |
FirmwareSigningKey[] |
POST /api/v1/households/{id}/firmware-signing-keys |
登记签名公钥 | key_id、public_key_base64 |
201,创建结果 |
POST /api/v1/households/{id}/firmware-signing-keys/revoke |
吊销公钥 | key_id |
FirmwareSigningKey |
GET /api/v1/households/{id}/firmware-releases |
查询固件版本 | Path household id |
FirmwareRelease[] |
POST /api/v1/households/{id}/firmware-releases |
创建 UPLOADING 版本 |
PrepareFirmwareReleaseRequest |
版本和短期上传地址 |
GET /api/v1/firmware-releases/{id}/impact |
发布/暂停/退役前影响评估 | Path release id |
FirmwareReleaseImpact |
POST /api/v1/firmware-releases/{id}/upload-session |
重新签发上传地址 | 无正文 | FirmwareRelease |
POST /api/v1/firmware-releases/{id}/finalize |
校验大小、SHA-256、Ed25519 后发布 | 无正文 | FirmwareRelease |
POST /api/v1/firmware-releases/{id}/pause |
暂停版本和未投递命令 | 无正文 | FirmwareRelease |
POST /api/v1/firmware-releases/{id}/resume |
恢复版本分发 | 无正文 | FirmwareRelease |
POST /api/v1/firmware-releases/{id}/retire |
退役版本并保留审计 | 无正文 | FirmwareRelease |
GET /api/v1/households/{id}/firmware-rollouts |
查询 OTA 批次 | Path household id |
FirmwareRollout[] |
POST /api/v1/households/{id}/firmware-rollouts |
启动灰度升级 | StartFirmwareRolloutRequest |
202 FirmwareRollout |
GET /api/v1/firmware-rollouts/{id} |
查询逐设备升级进度 | Path rollout id |
FirmwareRollout |
POST /api/v1/firmware-rollouts/{id}/pause |
暂停批次 | 无正文 | FirmwareRollout |
POST /api/v1/firmware-rollouts/{id}/rollback |
创建回滚批次 | rollback_release_id |
新的 FirmwareRollout |
11.2 运维固件接口
以下接口全部额外要求 X-Tenant-Id:
| 方法与路径 | 介绍 | 输入 | 输出 |
|---|---|---|---|
GET /api/v1/ops/firmware-signing-keys |
查询租户可信公钥 | 无 | FirmwareSigningKey[] |
POST /api/v1/ops/firmware-signing-keys |
登记可信公钥 | key_id、public_key_base64 |
201 |
POST /api/v1/ops/firmware-signing-keys/revoke |
吊销公钥 | key_id |
FirmwareSigningKey |
GET /api/v1/ops/firmware-releases |
按 model/status 查询版本 |
Query model、status 可选 |
FirmwareRelease[] |
POST /api/v1/ops/firmware-releases |
创建租户级版本 | PrepareOpsFirmwareReleaseRequest |
201 FirmwareRelease |
POST /api/v1/ops/firmware-releases/{id}/upload-session |
续签上传地址 | 无正文 | FirmwareRelease |
POST /api/v1/ops/firmware-releases/{id}/finalize |
正式发布 | 无正文 | FirmwareRelease |
POST /api/v1/ops/firmware-releases/{id}/pause |
暂停 | 无正文 | FirmwareRelease |
POST /api/v1/ops/firmware-releases/{id}/resume |
恢复 | 无正文 | FirmwareRelease |
POST /api/v1/ops/firmware-releases/{id}/retire |
退役 | 无正文 | FirmwareRelease |
GET /api/v1/ops/firmware-releases/{id}/download |
运维下载制品 | Path release id |
下载地址、摘要、签名元数据 |
GET /api/v1/ops/firmware-rollouts |
查询 OTA 批次 | 无 | FirmwareRollout[] |
POST /api/v1/ops/firmware-rollouts |
启动 OTA | StartFirmwareRolloutRequest |
202 FirmwareRollout |
GET /api/v1/ops/firmware-rollouts/{id} |
查询逐设备进度 | Path rollout id |
FirmwareRollout |
POST /api/v1/ops/firmware-rollouts/{id}/pause |
暂停 OTA | 无正文 | FirmwareRollout |
固件请求示例:
{ "key_id": "release-key-2026-01", "public_key_base64": "<32字节Ed25519公钥的Base64>" }
{
"model": "echoear",
"version": "1.1.0",
"expected_size": 2097152,
"expected_digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"signing_key_id": "release-key-2026-01",
"signature": "<firmware-manifest-v1签名Base64>"
}
{ "release_id": "<uuid>", "name": "第一批灰度", "target_percentage": 10, "device_ids": ["<device-uuid>"] }
典型版本输出:
{ "request_id": "req-fw", "data": { "id": "<uuid>", "model": "echoear", "version": "1.1.0", "status": "READY", "sha256": "bbbb...", "created_at": "2026-09-05T07:00:00Z" } }
典型批次输出:
{ "request_id": "req-rollout", "data": { "id": "<uuid>", "release_id": "<uuid>", "name": "第一批灰度", "status": "RUNNING", "target_percentage": 10, "targeted": 1, "succeeded": 0, "failed": 0 } }
12. 运维诊断设备动作
| 方法与路径 | 介绍 | 输入 | 输出 | 示例 |
|---|---|---|---|---|
POST /api/v1/ops/diagnostics/devices/{id}/config-refresh |
强制设备重新拉取配置 | X-Tenant-Id、Idempotency-Key、设备 UUID;Body:reason |
action=refresh_config、命令 ID/状态、queued |
入:{"reason":"远端配置与影子不一致"};出:{"action":"refresh_config","device_id":"<uuid>","command_id":"<uuid>","command_status":"QUEUED","queued":true} |
POST /api/v1/ops/diagnostics/devices/{id}/revoke |
紧急吊销异常设备 | X-Tenant-Id、Idempotency-Key、设备 UUID;Body:reason |
action=revoke_device、设备 ID、queued |
入:{"reason":"设备密钥疑似泄露"};出:{"action":"revoke_device","device_id":"<uuid>","queued":false} |
13. 主要错误码
| HTTP | 错误码 | 含义与处理 |
|---|---|---|
| 400 | VALIDATION_FAILED |
字段、Header、时间或幂等键不符合契约 |
| 401 | DEVICE_AUTHENTICATION_FAILED |
检查平台 Device Secret、时钟、URI、正文摘要和 Base64URL |
| 403 | DEVICE_ACTIVATION_REQUIRED |
当前激活状态不允许该操作 |
| 403 | TRANSCRIPT_STORAGE_NOT_ALLOWED |
当前授权不允许保存字幕 |
| 403 | FIRMWARE_DOWNLOAD_DENIED |
设备不是灰度目标或版本不可下载 |
| 404 | RESOURCE_NOT_FOUND |
资源不存在或不属于当前主体/设备 |
| 409 | IDEMPOTENCY_KEY_REUSED |
同一幂等键对应不同正文,客户端必须换业务请求 |
| 409 | DEVICE_ACTIVATION_CONFLICT |
激活状态或修订号冲突,刷新后再处理 |
| 409 | HARDWARE_AGENT_HARDWARE_MISMATCH |
Hardware ID 与已注册记录不一致,禁止覆盖 |
| 409 | HARDWARE_SESSION_PROOF_INVALID |
session_nonce/配置摘要/Bot/传输不匹配,丢弃本次会话重新创建 |
| 412 | VERSION_CONFLICT |
If-Match 过期,先重新 GET |
| 426 | FIRMWARE_UPDATE_REQUIRED |
当前固件低于最低版本 |
| 429 | USAGE_QUOTA_EXCEEDED |
额度或并发限制达到上限 |
| 503 | HARDWARE_AGENT_PROVISIONING_UNAVAILABLE |
平台 Product Secret/产品配置不可用 |
| 503 | HARDWARE_AGENT_PROVISIONING_FAILED |
火山动态注册失败;按 request_id 排障,可退避重试 |
14. 设备端推荐调用顺序
上电
→ GET /device/v2/bootstrap
→ GET /device/v2/config
→ GET /device/v2/commands
→ POST /device/v2/telemetry:batch
首次绑定后
→ POST /device/v3/hardware-agent-credentials:provision
→ 安全保存火山 Device Secret
每次聊天
→ POST /device/v3/agent-sessions
→ 连接火山 WebSocket
→ session.update
→ 收到 session.created/session.updated
→ POST /device/v3/agent-sessions/{id}/events (session_started)
→ 16k PCM 双向音频
→ POST /device/v3/agent-sessions/{id}/turns:batch
→ 周期 events (session_updated)
→ events (session_ended/session_failed)
15. 机器可读契约
- 完整 OpenAPI:
../openapi/openapi.yaml - 设备实现与安全说明:
device-api-reference.md - 设备接入说明:
device-integration.md - 固件灰度说明:
firmware-rollout.md
若本文与 OpenAPI 或运行时代码不一致,以同一发布版本的 OpenAPI 和处理器为准,并应同步修正文档。