cgstever/zafro-ha-local
GitHub: cgstever/zafro-ha-local
通过在本地局域网伪装厂商云端服务器,将无本地 API 的 Zafro/I4season Wi-Fi 空调桥接至 Home Assistant,实现完全离线、无云端的智能控制。
Stars: 0 | Forks: 0
# Zafro / I4season 空调 — 本地 Home Assistant 控制(无需云端)
完全通过**您自己局域网内的 Home Assistant** 来控制 **Zafro**(或其他 **I4season / nbrowan.com 平台**)Wi‑Fi 空调,彻底摆脱厂商云。
拔掉网线也能正常工作。
这些空调(例如 Zafro **A9092R**)出厂时配备了 "Zafro Smart" 应用,并且只会与位于 `zafro.nbrowan.com`(托管在 AWS 上)的厂商云进行通信。它们没有本地 API,不支持 Tuya/ESPHome,网络上也没有任何您可以直接与之通信的内容。本项目在您的局域网中运行一个小型服务器,**伪装成那个云端**:它响应空调的登录请求,使用空调期望的 MQTT‑over‑WebSocket 协议进行通信,并通过 MQTT Discovery 将空调桥接到 Home Assistant。
最终结果是获得一套完整的原生 HA 实体(模式、温度、湿度、风扇、摆风、节能/睡眠/强劲、显示屏灯、童锁、水箱传感器、实时温度和湿度)来驱动真实的设备 —— **不会有任何数据离开您的家。**
## 工作原理
```
AC ──TLS──► nginx (your server, :443) ──► zafro_cloud.py ──MQTT──► Mosquitto ──► Home Assistant
(thinks it's (terminates TLS, (fake cloud + (broker)
zafro.nbrowan.com) proxies REST + WS) HA bridge)
```
1. 您通过 DNS 将空调的云端主机名 (`zafro.nbrowan.com`) 指向您的服务器。
2. **nginx** 使用自签名证书终止空调的 TLS 连接,并将其代理到 `zafro_cloud.py`。
3. **`zafro_cloud.py`** 响应 REST 登录请求,并在空调的 WebSocket 上充当微型 MQTT broker,然后使用 MQTT Discovery 通过 **Mosquitto** 在 Home Assistant 之间镜像映射空调状态 —— 因此实体会自动出现。
空调的序列号会自动从其自身的流量中获取 —— 无需配置任何特定于设备的内容。
## 环境要求
- 同一局域网内的一台小型且常开的 Linux 设备(如树莓派、旧电脑、您的 HA 主机等)
- 安装了 **MQTT 集成**的 **Home Assistant**
- **Mosquitto**(或任何 MQTT broker) —— 可以是 HA 插件或系统软件包
- **nginx**
- **Python 3.9+**
- 一种在您的网络上覆盖空调主机名 DNS 解析的方法(见第 5 步) —— 例如支持 DNS 主机映射的路由器,或 Pi‑hole,或 dnsmasq。
## 设置
### 1. 安装桥接服务
```
sudo mkdir -p /opt/zafro-cloud
sudo chown $USER /opt/zafro-cloud
python3 -m venv /opt/zafro-cloud/venv
/opt/zafro-cloud/venv/bin/pip install -r requirements.txt # paho-mqtt aiohttp
cp zafro_cloud.py /opt/zafro-cloud/
```
### 2. Mosquitto + Home Assistant MQTT 集成
如果您还没有 MQTT broker:
```
sudo apt install mosquitto mosquitto-clients
sudo cp deploy/mosquitto-local.conf /etc/mosquitto/conf.d/local.conf
sudo systemctl restart mosquitto
```
(如果您运行的是 Home Assistant OS,**Mosquitto broker** 插件也可以 —— 从 **Settings → Add-ons** 安装它,然后创建一个 MQTT 用户。)
现在将 Home Assistant 连接到 broker:
1. **Settings → Devices & Services → Add Integration → MQTT**。
2. **Broker:** broker 的 IP(如果它位于 HA 主机上则为 `127.0.0.1`,或您的服务器局域网 IP / 插件则为 `core-mosquitto`)。 **Port:** `1883`。
3. 仅在您的 broker 需要时才填写用户名/密码 —— 如果需要,还需在 systemd 单元文件(第 4 步)中设置 `MQTT_USER` / `MQTT_PASS`,以便桥接服务也能登录。
4. 提交。这就是所有的 HA 配置 —— 一旦设备连接(第 7 步),空调的实体就会通过 MQTT Discovery **自动**出现;**无需编写任何 YAML**。
### 3. 自签名证书
空调**不**验证证书,因此使用自签名证书即可:
```
sudo bash deploy/gen-cert.sh # writes /etc/zafro-cloud/{cert,key}.pem
```
### 4. nginx 虚拟主机
```
sudo cp deploy/nginx-zafro.conf /etc/nginx/sites-available/zafro.conf
sudo ln -s /etc/nginx/sites-available/zafro.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```
该配置中有两点是**必不可少的**(参见 [注意事项](#gotchas)):
- `default_server` —— 空调**不发送 SNI**,因此该虚拟主机必须是默认主机。
- 旧版的 `ssl_ciphers` 配置行 —— 空调仅提供旧的 TLS 1.2 RSA 密码套件。
### 5. 将桥接服务作为服务运行
```
sudo cp deploy/zafro-cloud.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now zafro-cloud
journalctl -u zafro-cloud -f # watch it
```
### 6. 将空调的 DNS 重定向到您的服务器
让 `zafro.nbrowan.com` 解析为您服务器的 IP,**专供空调使用**。选择适合您网络的方式:
- **具有 "DNS Host Mapping" / 静态 DNS 主机功能的路由器**(许多 ISP 路由器,例如 Calix):添加 `zafro.nbrowan.com → <您的服务器 IP>`。这是最干净的方法 —— 因为只有空调会查询这个名称,所以不会影响其他任何设备。
- **Pi‑hole / AdGuard / dnsmasq:** 添加本地 DNS 记录 `zafro.nbrowan.com → <您的服务器 IP>`,并确保空调将其用作解析器。(dnsmasq 命令:`address=/zafro.nbrowan.com/<您的服务器 IP>`。)
从另一台机器上进行验证:`dig +short zafro.nbrowan.com` → 应显示您服务器的 IP。
### 7. 重启空调电源
空调会缓存其云端连接,因此请拔下电源插头约 10 秒钟,然后再重新插上。开机时,它会执行一次全新的 DNS 查找,连接到您的服务器,不到一分钟,Home Assistant 中的实体就会被激活。观察 `journalctl -u zafro-cloud -f` —— 您应该会看到 `AC websocket connected`,`identified AC: ...` 以及状态发布信息。
## 在 Home Assistant 中使用
一旦空调连接(第 7 步),实体会自动创建并归组在同一台 **Zafro AC** 设备下:
| 实体 | 类型 | 说明 |
|---|---|---|
| 模式 | select | off / cool / dry / fan |
| 目标温度 | number | °F,在 cool 模式下使用 |
| 目标湿度 | number | %,在 dry 模式下使用 |
| 风扇转速 | select | auto / 1 / 2 / 3 |
| 当前温度 | sensor | 实时室温 |
| 当前湿度 | sensor | 实时湿度 |
| 显示屏灯、童锁、静音提示音、节能、睡眠、强劲、垂直摆风、水平摆风 | switch | |
| 水箱已满 | binary_sensor | `problem` 类别 |
**查找设备:** **Settings → Devices & Services → Devices → Zafro AC**。所有内容都在该页面上,并且可以立即使用。
**将其添加到仪表板:** 打开设备页面并点击 **Add to dashboard**,或者编辑仪表板 → **Add Card** → **By device** → *Zafro AC*。要获得干净的手动布局,请粘贴此卡片(Edit dashboard → **Add Card** → **Manual**) —— 如果您的 HA 对它们的前缀命名不同,请调整实体 ID(请在设备页面上查看):
```
type: entities
title: Zafro AC
entities:
- entity: select.zafro_ac_mode
- entity: number.zafro_ac_target_temperature
- entity: number.zafro_ac_target_humidity
- entity: select.zafro_ac_fan_speed
- type: section
label: Status
- entity: sensor.zafro_ac_current_temperature
- entity: sensor.zafro_ac_current_humidity
- entity: binary_sensor.zafro_ac_water_tank_full
- type: section
label: Options
- entity: switch.zafro_ac_vertical_swing
- entity: switch.zafro_ac_horizontal_swing
- entity: switch.zafro_ac_eco_mode
- entity: switch.zafro_ac_sleep_mode
- entity: switch.zafro_ac_turbo
- entity: switch.zafro_ac_display_light
- entity: switch.zafro_ac_child_lock
- entity: switch.zafro_ac_mute_beep
```
**自动化和语音**像任何 HA 实体一样工作 —— 例如,当所有人都离开时关闭空调(`select.zafro_ac_mode` → `off`),在 **Water Tank Full**(水箱已满)开启时发送通知,或者说“将空调设置为制冷”。一个实用的入门自动化示例:
```
alias: AC off when nobody home
triggers:
- trigger: state
entity_id: zone.home
to: "0"
actions:
- action: select.select_option
target: { entity_id: select.zafro_ac_mode }
data: { option: "off" }
```
## 注意事项
这些是耗费了大量调试时间的经验;它们已经被内置于提供的配置中:
- **无 SNI。** 空调的 TLS ClientHello 不发送服务器名称,因此 nginx 必须将 zafro 虚拟主机作为 `listen 443 ssl default_server;` 提供服务,否则它会穿透到另一个具有严格密码套件的虚拟主机,导致握手失败(一个 7 字节的 TLS 警报)。
- **过时的密码套件。** 空调仅提供 TLS 1.2 RSA‑CBC 密码套件(`AES256-SHA256`, `AES256-SHA`, …),而现代 OpenSSL 会隐藏它们。虚拟主机通过 `ssl_ciphers "DEFAULT:@SECLEVEL=0";` 重新启用它们,**且不会**丢弃现代 TLS —— 这一点很重要,因为作为 `default_server`,此配置块也会为任何带有不匹配 SNI(例如,前端代理您其他站点的 Cloudflare Tunnel / 反向代理)到达此服务器的客户端终止 TLS。如果您将其固定为仅使用旧版密码套件,那些其他站点将会出现 502 错误。请保留 `ssl_protocols TLSv1.2 TLSv1.3;`。
- **MQTT 3.1 + 无 WebSocket 压缩。** 空调使用 MQTT **3.1**("MQIsdp",级别 3,30 秒 keepalive)通信,并请求 `permessage-deflate`,但无法读取压缩的服务器帧 —— 它会先 CONNECT,然后在 30 秒 keepalive 时静默失效。因此,桥接服务使用了 `WebSocketResponse(compress=False)`。
## 协议
完整的逆向工程协议和详尽的字段映射位于 [`docs/PROTOCOL.md`](docs/PROTOCOL.md)。
## 查找您设备的云端主机名(其他品牌)
本项目针对的是 `zafro.nbrowan.com`。其他 I4season 贴牌品牌可能使用不同的 `.nbrowan.com` 主机。要查找您的主机,请监视空调的 DNS 查询(例如在您的路由器/Pi‑hole 查询日志中,或通过 `tcpdump -ni 'host and port 53'`),并寻找 `*.nbrowan.com` 的查找记录。在本 README 中出现 `zafro.nbrowan.com` 的地方,请使用该主机名进行替换(证书 CN 和 nginx 的 `server_name` 可以保持通用,因为空调会忽略它们,但 DNS 覆盖必须与真实主机名匹配)。
## 许可证
MIT — 详见 [LICENSE](LICENSE)。
与 Zafro、I4season 或 Browan 无任何隶属关系。"Zafro" 及其他名称是其各自所有者的商标。在您拥有的硬件上使用,风险自负。
标签:Home Assistant, 中间人代理, 智能家居, 本地化控制, 物联网, 逆向工具