xiaodu bridge 使用说明:把 Home Assistant 设备接进小度

xiaodu_bridge 是一个 Home Assistant 自定义集成。它把 HA 里已经接好的设备重新组织成 DuerOS 语义设备,再交给小度音箱和小度 App 发现、查询和控制。商店里的名字是 xiaodu bridge,集成 domain 是 xiaodu_bridge。

它不是把 HA 实体一对一转成小度设备,而是有一个桥接层:一台浴霸可以把照明、取暖、换气等多个 HA 实体聚合成一台浴霸;一个房间也能从 HA 区域同步成小度房间分组。小度发来的控制请求先进入这个桥接层,再由它调用 HA 的实体服务。

整体链路是:

flowchart TB
  subgraph home["家里"]
    direction TB
    devices["米家 / Zigbee / 蓝牙 / Matter"]
    ha["Home Assistant<br/>实体 + 区域 + 能力"]
    bridge["xiaodu bridge<br/>DuerOS 语义设备模型"]
    entry["公网入口<br/>域名 / DDNS / NGINX"]
  end
  cloud["小度云端"]
  speaker["小度音箱"]
  app["小度 App"]

  devices -->|"标准化实体"| ha
  ha -->|"选中设备"| bridge
  cloud -->|"OAuth / 控制请求"| entry
  entry --> bridge
  bridge -->|"发现 / 控制 / 查询"| ha
  bridge -->|"Change Report"| cloud
  cloud --> speaker
  cloud --> app

整套配置从 HA 侧往小度侧推,一次走完大致是:

flowchart TD
  a["设备接进 HA<br/>改名字 / 分区域"]
  b["安装 xiaodu bridge<br/>HACS 或手动"]
  c["配置公网 HTTPS 入口<br/>DDNS + 证书 + 反向代理"]
  d["小度后台创建智能家居技能"]
  e["HA 里配置 Client / botId / Callback"]
  f["选择暴露设备和能力<br/>开启 sync_areas"]
  g["小度发现设备<br/>模拟测试 + 语音测试"]

  a --> b --> c --> d --> e --> f --> g

仓库地址:https://github.com/Jackroyal/xiaodu-bridge

准备设备

装桥接之前,先把设备名字和房间理好。

小度侧的设备名就是 HA 实体名,房间就是 HA 区域。switch.switch_1 这种名字说不出来,小度也就没法控制了。改名字只做两件事:

1. 名字改成适合说话的:客厅吸顶灯、卧室空调、书房插座。
2. 房间分对:客厅就是客厅,卧室就是卧室。

改名字最省事的位置是源头:小米的设备名和房间从米家 App 直接同步到 HA,HA 再同步给小度,一路下来不用重复修改,源头设置好后续链路可以自动同步。

米家、蓝牙、美的空调 等设备都有各自的接入方式,Xiaomi Home、Sonoff、Midea Smart Home 这些集成我都在用,大家根据各自设备按需选择自己对应的集成,善用大模型或搜索。

安装集成

要求 Home Assistant 2025.1.0 或更高版本。本文按 xiaodu bridge v0.9.17 写。

HACS 安装(推荐)

xiaodu bridge 已经进了 HACS 默认商店,不用再加自定义仓库:

  1. HACS → Integrations,搜索 xiaodu bridge,下载。
  2. 重启 Home Assistant。

HACS 下载只是把文件装到位。重启后还要去 设置 → 设备与服务 → 添加集成,搜 xiaodu bridge 添加一次——填 OAuth 凭证、botId、公网地址就在这一步。只下载不添加,集成不会生效。

手动安装

不想用 HACS 就手动复制:

git clone https://github.com/Jackroyal/xiaodu-bridge.git
cp -R xiaodu-bridge/custom_components/xiaodu_bridge /config/custom_components/

复制完检查路径,别多套一层:

正确:/config/custom_components/xiaodu_bridge/manifest.json
错误:/config/custom_components/xiaodu_bridge/xiaodu_bridge/manifest.json

确认没套错后重启:

ha core restart

手动和 HACS 两条路到这里都只是「文件装好了」,最后一步是一样的:去 设置 → 设备与服务 → 添加集成里搜 xiaodu bridge 添加。

添加完成后,集成列表里就能看到它:

集成列表里的 Xiaodu Bridge

点进详情页,会看到版本号、一个「小度中枢」设备,以及右上角的齿轮(配置)和三个点(菜单)按钮(截图停在 0.9.13,之后的版本界面没变):

Xiaodu Bridge 详情页

配置公网访问

正式接入时,小度云端会回调 xiaodu bridge 提供的三个 HTTPS 接口(必须按这个填写,写死的path):

/api/xiaodu/oauth/authorize
/api/xiaodu/oauth/token
/api/xiaodu/service

所以纯内网地址完成不了正式接入,小度云端必须能从外面访问到 HA 的这几个路径。推荐结构:

小度云端
  → 公网域名
  → 路由器端口转发
  → HAOS 反向代理
  → HA Core 8123
  → xiaodu bridge

我家里是电信光纤,有公网 IPv4,主路由是华硕路由器。华硕自带 DDNS,但它内置的服务商里只有花生壳这类要实名的,我常用的 cloudflare / dnspod 都不在里面。

所以我在局域网的软路由上装了 iStoreOS,用它来做域名解析更新:软路由是长期开机的,在上面跑一个 ddns 插件,公网 IP 一变就调 cloudflare / dnspod 的 API 把 A 记录改掉。华硕路由器该干嘛干嘛,DDNS 这件事交给软路由,和 HAOS 在哪台机器上没关系。

1. 确认真的有公网 IPv4

路由器 WAN IP 和手机流量看到的出口 IP 一致才算数。WAN IP 是 100.x.x.x、10.x.x.x、172.16.x.x、192.168.x.x 这些,就是运营商套了一层,说明你没有公网ip,别折腾端口转发了。

2. 域名和 DDNS

先准备一个域名,比如 ha.example.com,在你使用的域名解析后台(cloudflare/dnspod/aliyun之类的)加一条 A 记录指向公网 IP。IP 会变就配 DDNS,公网 IP 一变,域名跟着变。

我用的是 ddns-go。在 iStoreOS 的应用商店里搜「ddns-go」装上,装完它会起一个 Web 配置页,浏览器打开 软路由IP:9876 就能看到面板,填完配置它会以服务形式常驻,公网 IP 一变就自动更新解析。

面板上要填的就几项:

配置项 填什么
DNS服务商 顶部单选,支持阿里云 / DnsPod / Cloudflare / 华为云 等二十来家
Token 对应服务商的 API 凭据(截图里 Cloudflare 就一个 Token 输入框,面板上还带生成令牌的指引链接)
TTL 域名解析的缓存时间,默认「自动」即可;测试阶段可以设短一点,便于改动快速生效
域名 要更新的那条记录,比如 ha.example.com
IP 获取方式 默认「通过接口获取」就行——软路由自己不持有公网 IP 时,靠本地网卡读不到真实出口地址,让它去外部接口查更准

ddns-go 配置面板:DNS 服务商单选、Token、TTL

配完在面板上点「测试」或等一个更新周期,然后在电脑上用 nslookup ha.example.com 或 dig 看一眼解析结果对不对。DNS 记录用“仅 DNS”模式。

平常用多个服务商的话,面板顶部有「配置切换」,可以建多套配置(比如一套 cloudflare、一套 dnspod),点「添加」新建、切换下拉选择,互不影响。 大致的原理,其实就是起个脚本监听ip变化,如果发生变化则调用cloudflare或者dnspod等的api接口更新一下域名对应的ip,变更肯定会有一定延迟,但够用了。

这里的 API Token 后面配证书还要用——HAOS 的 Let’s Encrypt add-on 走 DNS 校验,连的就是同一个 provider。

3. 端口转发

家宽对 80 和 443 的入站管控挺常见,可以用 8443 这种非标端口:

外部协议:TCP
外部端口:8443
内部目标:HAOS 主机 IP,例如 192.168.1.10
内部端口:443

里面的 443 给 HAOS 反向代理监听。HA 的 8123 端口不要直接暴露,公网统一走 HTTPS,由反向代理转 8123。

为了安全起见不建议使用DMZ开放过多内容,使用端口转发只开放一个端口,相对更安全。

华硕路由器自身远程控制用的是8443(使用华硕的子域名,我要使用自定义域名,所以不复用8443),所以我使用的是8663端口。

4. 证书和反向代理

HAOS 可以直接用 Let’s Encrypt 和 NGINX SSL proxy 这两个 add-on。跑 Docker / Core 或其他安装方式的,这两个 add-on 用不了,需要在宿主机或容器旁边准备等价方案(Caddy、NGINX 或带 DNS 模块的 Caddy)。

HAOS 上这两个 add-on 的证书走 DNS 校验,不依赖 80 端口,证书目录和文件名默认就是配套的,装完不用手动挪证书。

Cloudflare DNS 的例子:

challenge: dns
dns:
  provider: dns-cloudflare
  cloudflare_api_token: <你的Cloudflare API Token>
domains:
  - ha.example.com
email: <你的邮箱>

NGINX SSL proxy 只需要配域名和其他选项:

domain: ha.example.com
hsts: max-age=31536000; includeSubDomains
client_max_body_size_megabytes: 1
cloudflare: false

反向代理只放行小度需要的那三个路径(前面列过),其他公网请求返回 404。

别为了省事把整个 HA 管理页面开放到公网。

然后到 HA 的 设置 → 系统 → 网络,外部地址填:

https://ha.example.com:8443

外网请求返回 400、日志里出现 forwarded 或 trusted proxy 相关内容,多半是反向代理网段没进 trusted_proxies。把 NGINX 所在网段加进去,并开启 use_x_forwarded_for。

HA 网络设置:信任 X-Forwarded-For 与受信任的代理

在小度后台建技能

登录小度开发者平台,创建智能家居技能。技能的「配置服务」里要填的东西,来自两个地方:

小度后台基础信息页:
  botId          技能 ID,后面要填回 HA 集成里

HA 集成确认页给出的三个地址(这里用它们):
  授权地址        Authorize,HA 公网地址 + /api/xiaodu/oauth/authorize
  Token地址       HA 公网地址 + /api/xiaodu/oauth/token
  WebService      HA 公网地址 + /api/xiaodu/service

自己定义、两边配一致:
  Client_Id
  ClientSecret

假设公网地址是 https://ha.example.com:8443,三个地址就是把上面的路径拼在它后面,比如 https://ha.example.com:8443/api/xiaodu/oauth/authorize。

后台「配置服务」页填起来就长这样(授权地址 / Client_Id / 回调地址 / Token 地址 / ClientSecret / WebService):

小度后台配置服务:授权地址、Token 地址、WebService 的填法

授权和设备发现的过程是:

sequenceDiagram
  autonumber
  participant U as 用户 / 浏览器
  participant D as 小度云端
  participant X as 公网入口 / xiaodu bridge
  participant H as Home Assistant

  U->>D: 开始绑定账号
  D->>U: 打开 Authorize URL
  U->>X: 访问授权页并同意
  X-->>D: 返回到小度 Callback
  D->>X: 请求 Token URL
  X-->>D: 下发私有 Access / Refresh Token
  D->>X: 调用 WebService 发现设备
  X->>H: 读取已选实体、区域和能力
  H-->>X: 返回实体状态
  X-->>D: 返回 DuerOS 语义设备
  D-->>U: 展示可控制设备

两个容易搞混的点:

1. Client_Id 和 ClientSecret 是自己定义的,不是百度分配的,两边配一致就行。
   Secret 用长随机串,别用 123456 这种。
2. 回调地址不是你的 HA 地址,是小度平台生成的那个,填回 HA 集成里时要原样照抄。

填写 OAuth 配置

重启 HA 后打开:

设置 → 设备与服务 → 添加集成 → xiaodu bridge

按下面填(界面是中文标签,括号里是它的含义):

界面字段 填什么 示例
Client_Id 自定义客户端 ID dueros_xxx
ClientSecret 自定义随机密钥 一段长随机串
小度技能ID(botId) 小度后台技能 ID 12345678
回调地址 小度平台生成的地址,不可修改 https://xiaodu.baidu.com/...
公网访问地址(HTTPS) HA 公网基础地址 https://ha.example.com:8443

保存后确认页会把授权地址、Token地址、WebService 三个地址直接列出来,照着填到小度后台就行,不用自己拼。Client_Id / ClientSecret 两边保持一致即可——它们是你自己定义的,不是百度分配的。

回调地址不用操心调试环境:集成自动兼容小度的正式环境(xiaodu.baidu.com)与调试环境(xiaodu-dbp.baidu.com),后台只用填平台生成的那一个。

填错了想改,打开集成条目右上角 ⋮ →「重新配置」(OAuth 配置)就能重填,不用删掉集成。

OAuth 配置

OAuth token 这块集成自己管:Access Token 有效期 7 天,Refresh Token 30 天,到期用 refresh_token 续。这个 token 是桥接层自己的私有不透明串,只能打小度桥接的接口,碰不到 HA 的 API。

选择要暴露给小度的设备

添加成功后,打开集成条目 →「配置」,进入「小度中枢」页:

小度中枢:配置菜单

注意:从 v0.9.15 起,设备暴露改成按需选择。全新安装、或者从没保存过设备选项的实例,默认不会向小度发送任何设备,必须在这里选好并保存。

建议别一次全选,先拿两三台练手:

  1. 点「添加 / 移除设备」。
  2. 选择要暴露的 HA 设备(列表按「房间 · 设备名」展示,重名的实体也能分清)。

添加 / 移除设备:设备按房间分组

  1. 点进某台设备,先选「设备能力」勾选能力;需要时再进「高级配置」限制能力、隐藏实体、绑定语义角色或改小度侧的展示名。
  2. 保存后让小度重新发现。

已选设备可以在「已选设备」列表里逐台检查:

已选设备列表

设备默认暴露全部可用能力,想省事可以直接用默认;想精细控制再一台台进能力编辑。控制类设备的开关能力是强制的,温湿度这种只读能力按需勾。底部那个「同步 HA 房间到小度」开关打开后,HA 的区域会同步成小度的房间分组。

暴露得越精准,后面误触发越少,不需要把 HA 里每个实体都丢给小度。

测试

按这个顺序来:

  1. 小度开发者后台触发设备发现。
  2. 看设备有没有出现在小度侧。
  3. 查询一个温湿度传感器。
  4. 打开一个灯或插座,再关掉。
  5. 手动动一下设备状态,看小度 App 是否同步。
  6. 用音箱和 App 试真实语音。

HAOS 上看日志:

ha core logs -f | grep --line-buffered "Xiaodu access"

成功的发现请求里能看到这样一段(实际是一行):

kind=dueros namespace=DuerOS.ConnectedHome.Discovery \
name=DiscoverAppliancesRequest result=DiscoverAppliancesResponse status=200

这里连请求都没有,就别急着改 HA 配置,先查域名、端口转发、证书和反向代理。

语音可以试这些:

小度小度,打开客厅灯。
小度小度,把卧室空调调到 26 度。
小度小度,客厅温度是多少?
小度小度,明天早上八点打开书房插座。

几个值得说的特性

这几个特性单独拎出来说。

主动状态上报

可控设备状态一变,集成会主动推 Change Report 给小度,小度 App 里的状态会跟着刷新。纯只读传感器不上报,这类本来也没有“被控制”的状态可推。小度云端偶尔会拒绝一次(开机那波最常见),集成会在 60 秒窗口后补发一次,不会无限重试。

房间分组

HA 区域同步成小度房间分组后,“打开客厅的灯”这类指令小度能自己圈范围,不用每台设备都指名道姓。前提还是第一步:房间得分对。

定时开关

支持小度侧的 timingTurnOn 和 timingTurnOff。定时信息存在 HA Storage 里,HA 重启后任务仍然保留。

语义设备模型

这是桥接层最核心的设计。它不是把 HA 实体一对一递给小度,而是先在桥接层把设备组装成 DuerOS 语义设备:浴霸就是一台浴霸,照明取暖换气都在它身上;洗衣机、晾衣杆、扫地机同理。小度协议定义了六十多种设备类型,这个集成实现了常用的十几种:灯、开关、插座、空调、窗帘、风扇、传感器、媒体播放器,外加浴霸、晾衣杆、扫地机、洗衣机四个专用档案。

晾衣杆的语义按小度的实际模型处理:开关控制的是照明,升降走上升 / 下降动作;没有灯实体的机型回退为控制杆子本身。

常见排查

集成搜索不到

先看目录有没有多套一层,再看 manifest.json 是否存在,然后重启 HA。HACS 里搜不到的话,先把 HACS 更新到最新版并刷新商店列表;还是没有就改用手动安装。

HA 里正常,小度发现不到

按顺序查:

1. 小度后台 WebService 地址对不对。
2. 地址里的端口有没有少。
3. 公网能不能访问 HA。
4. 反向代理有没有放行 /api/xiaodu。
5. HA 集成里有没有选中设备。
6. HA 日志里有没有 Xiaodu access。

如果小度云端访问不到 HA,音箱和后台都会持续失败,先把公网链路查完。

授权页打不开也是同一类问题,从外面测一下入口通不通:

curl -I https://ha.example.com:8443/api/xiaodu/service

能拿到 HTTP 状态码说明入口至少通了;TLS 都失败的话,先解决证书、DNS 和端口转发。

米家设备状态不准

问题多半不在桥接层。先回 Xiaomi Home 或米家集成里看:HA 页面能不能控制,状态会不会变,实体有没有被禁用。HA 侧不稳,桥接写得再好也没办法。

控制失败但发现成功

能力没勾全,或设备类型映射不对。空调要有温度、模式、风速这些能力;窗帘要有打开、关闭能力。回集成选项里改完,再让小度重新发现。

状态不同步

先确认这台设备是可控设备,只读传感器本来就不主动上报。再手动动一下设备,看日志里有没有状态推送的记录。

最后

这套方案的核心是让 HA 当统一中间层。桥接层把 HA 设备按 DuerOS 的语义组装好交给小度,状态变化也主动往小度推。小度 App 能拿到较新的状态,浴霸这类多实体设备也能当成一台设备控制。

设备命名和房间分好,语音体验会顺很多。先暴露少量设备测试,稳定后再慢慢加。