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, 中间人代理, 智能家居, 本地化控制, 物联网, 逆向工具