
# aispea / cyberbase 设备接入

*把现有 aispea/cyberbase 设备直接指向 Hearo——沿用其 MQTT 对话协议，由 mqtt-gateway 适配*

如果你已有跑 cyberbase MQTT 对话协议的设备（`/chat/<dev_id>/up|down`、`JSON||<opus>`、连续 cloud-VAD），无需改固件：把卡片配置里的对话 MQTT 指向 Hearo 的 `mqtt-gateway` 即可。`mqtt-gateway` 内的 aispea 适配器会把该协议翻译到 Hearo 的房间 + AI 智能体（ASR→LLM→TTS）。

> 💡 **先读统一模型**：客户端 Token / 设备接入密码的签发、API Key、房间分配（client→room）集中在 [客户端与接入模型](/docs/clients)。本页是 **aispea/cyberbase 兼容协议**（连续 cloud-VAD 对话、`JSON||opus` 帧）的具体接入；它正是统一模型在 MQTT 上的推荐形态（凭据鉴权 + client→room 实时切房）。

## 1. 把设备指向 Hearo（CONNECT 鉴权）

设备的卡片/产品配置里，把对话 MQTT 字段改为 Hearo 部署：

| 字段 | 说明 |
|---|---|
| mqtt_chat_broker | `124.174.6.145`（本部署的 mqtt-gateway） |
| mqtt_chat_port | `2883` |
| mqtt_chat_password | **推荐：设备接入密码**（`hkd_` 开头的短密码，控制台 → 设备 页生成/复制，可随时重置）；也可用客户端 Token（JWT，较长，注意固件密码字段长度） |
| mqtt_chat_username | 用设备接入密码时任意（建议 deviceId）；用 Token 时留空或与 Token 的 `sub`（clientId）一致 |
| chat_sequential | true（连续 cloud-VAD 对话） |

Client ID 沿用 `chat_<dev_id>`；topic 沿用 `/chat/<dev_id>/up` 与 `/chat/<dev_id>/down`——适配器按 **topic 里的 `<dev_id>`** 识别设备（与 MQTT Client ID 无关）。进入哪个房间由设备的房间绑定决定（见第 3 节）。协议逐字段细节见仓库 `docs/aispea-mqtt-protocol.md`。

## 2. 对话时序（适配器已实现）

```text
设备 -> 后端  session.update(cloud_vad=1, conversation_id=A)
后端 -> 设备  session.updated(A)                # 适配器进房（+ 房间默认 AI 自动就位）
设备 -> 后端  input_audio_buffer.append||opus    # 持续上行
后端 -> 设备  input_audio_buffer.speech_start(A)
后端 -> 设备  input_audio_buffer.speech_stopped(A)
后端 -> 设备  response.asr.result(text=..., A)
后端 -> 设备  response.text(..., A)              # 可多条
后端 -> 设备  response.audio||opus(A)             # 多条，16k 单声道 Opus（60ms/帧）
后端 -> 设备  response.audio.done(vad_off=false, A)
```

### 已映射的事件

| 设备事件 | | 适配器行为 |
|---|---|---|
| session.update | → | 进入绑定的房间（未绑定则私有房 `aispea-<dev_id>`）；回 session.updated。同房间重复发送只刷新会话，不重进房 |
| input_audio_buffer.append\|\|opus | → | 解 Opus → 进房（喂 AI 的 ASR/VAD） |
| （AI 识别中） | → | speech_start / speech_stopped / response.asr.result |
| （AI 回复） | → | response.text + response.audio\|\|opus + response.audio.done |
| input_audio_buffer.clear | → | 打断当前回复 + 回 input_audio_buffer.cleared |
| input_audio_buffer.commit | → | 回 input_audio_buffer.committed（PTT 兼容） |
| （空闲超时，可选） | → | session.clear（设了 `HEARO_AISPEA_IDLE_SEC` 时；默认关闭） |
| （设备未登记） | → | `{"type":"error","error":"device_not_registered"}` |

音频编码默认 **Opus 16k 单声道**（`input_audio_format`/`output_audio_format` 填 `pcm` 系列值可切 PCM）；下行按 60ms 一帧发送，匹配设备的播放缓冲。

## 3. 设备入库与房间（设备→房间）

设备必须先在**设备管理**入库（控制台 → 设备，或运营中心 → 设备管理）才允许接入，未入库一律拒绝（收到 `device_not_registered` 错误）。每台设备有一个**房间号**：连接时按 deviceId 解析到它的房间，改绑定**实时生效**（在线秒切，无需重连）。

| 房间号 | 效果 | 说明 |
|---|---|---|
| 留空 | 私有房 | 该设备独享房间 `aispea-<dev_id>`（一对一对话） |
| 多台填同一房间号 | 群聊 / 通话 | 这些设备进入同一房间、实时互通 |

要让 A、B 两台设备通话，把它们的**房间号都设成同一个值**（如 `call-1`）即可——无需配对接口，改设备的房间号就是路由。改绑定用控制台「房间 / 分组」页或 `PATCH /api/v1/clients/{id}`（见 [REST API](/docs/rest-api)）。

## 4. AI 配置

推荐给房间配置**默认智能体**（[房间与分组](/docs/rooms)）：任何设备进入该房间，AI 自动就位、空闲自动撤走；智能体的提示词 / Provider / 音色在「智能体管理」里配。私有房场景给设备的私有房间登记默认智能体即可。另有开关 `HEARO_AISPEA_IDLE_SEC`（静默 N 秒后自动结束会话，默认关闭）。请确保所选厂商可用，否则 AI 不会出声。

## 5. 联调

任何标准 MQTT 客户端都可模拟设备联调。先在设备管理登记 `testdev01` 并复制它的设备接入密码，然后用 mosquitto 观察下行：

```sh
# 订阅下行（观察 session.updated / response.* 事件）
mosquitto_sub -h 124.174.6.145 -p 2883 -u testdev01 -P 'hkd_...' \
  -i chat_testdev01 -t '/chat/testdev01/down' -v

# 发起会话（另开一个终端）
mosquitto_pub -h 124.174.6.145 -p 2883 -u testdev01 -P 'hkd_...' \
  -i chat_testdev01_pub -t '/chat/testdev01/up' \
  -m '{"type":"session.update","session":{"conversation_id":"test-1"}}'
```

音频上行是 `JSON头||Opus包` 的二进制拼接，脚本化联调建议用任意 MQTT 库照 `docs/aispea-mqtt-protocol.md` 组包；端到端验证最直接的方式是用一台真实设备，或在控制台房间的「演示」页与设备同房间对话。
