Files
story/docs/device-api-complete-reference.md
Codex Local 0c2151ace9
ci / verify (push) Canceled after 0s
chore: checkpoint 第一版
2026-09-06 17:04:11 +08:00

35 KiB
Raw Permalink Blame History

设备相关接口完整说明

版本: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}&timestamp={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 和处理器为准,并应同步修正文档。