智能插座如何接入Home Assistant,从零开始完整工程实现(Python+React)
GemeOpen GSPM1B × Home Assistant × Python/React Demo
把 GemeOpen GSPM1B 智能转换器插座 接入 Home Assistant,实现 通断电控制 与
电压 / 电流 / 功率 / 累计用电量 采集;并附一套 Python(FastAPI) + React(Vite) 的完整自研 Demo。
面向程序员初学者:文档从零讲起,每一步都可直接复制执行。
工程源码下载地址
https://smart-bird-oss.smart-bird.cn/document/2026/09/27/1107261215687071.zip
✨ 能力一览
| 能力 | Home Assistant | 自研 Demo |
|---|---|---|
| 通断电开关 | ✅ switch | ✅ Web 开关 |
| 实时电压/电流/功率 | ✅ sensor | ✅ 实时卡片 |
| 累计用电量 | ✅ sensor(能源面板) | ✅ 卡片 |
| 历史曲线 | ✅ History graph | ✅ recharts 折线 |
| 实时推送 | — | ✅ WebSocket |
| 需要写代码 | ❌ 仅 YAML | ✅ Python + React |
🏗 架构
GSPM1B ──MQTT──► Mosquitto Broker ◄──MQTT── Home Assistant (路径 A, 零代码)
▲
└──MQTT── Python 后端 ──REST/WS── React 前端 (路径 B, 自研 Demo)
📁 目录结构
gemeopen-ha-demo/
├── README.md # 本文件
├── docker-compose.yml # 一键启动 Mosquitto + 后端 + 前端
├── docs/ # 全部中文文档
│ ├── 00-architecture.md # 架构与接口契约(基准)
│ ├── 01-overview.md # 方案总览(先读)
│ ├── 02-device-setup.md # 设备端 MQTT 配置
│ ├── 03-homeassistant.md # Home Assistant 接入
│ ├── 04-backend.md # Python 后端说明
│ ├── 05-frontend.md # React 前端说明
│ ├── 06-deploy-ops.md # 部署与运维
│ └── 07-troubleshooting.md # 排错手册
├── homeassistant/ # HA 配置片段
│ ├── mqtt-gspm1b.yaml # switch + sensor 实体定义
│ └── dashboard.yaml # Lovelace 仪表盘
├── backend/ # Python FastAPI 后端
├── frontend/ # React 前端
├── scripts/ # 设备配置脚本
│ ├── configure_device.py # 把设备切到自建 Broker
│ └── device_switch.example.json
└── mosquitto/ # MQTT Broker 配置
└── mosquitto.conf
🚀 快速开始
# 1) 启动 Broker + 后端 + 前端
docker compose up -d --build
# 2) 把设备切换到你的 Broker(编辑 scripts/device_switch.json 后执行)
cd scripts && cp device_switch.example.json device_switch.json
python3 configure_device.py --config device_switch.json
# 成功后给设备断电重启
# 3) 开启设备定时上报(示例 15 秒)
mosquitto_pub -h <你的IP> -p 1883 \
-t 'gemeopen/gspm1b/<MAC>/command' \
-m '{"messageId":"1","timerEnable":1,"timerInterval":15,"type":"setting"}'
# 4) 打开自研 Demo
# http://<你的IP>:8080
# 5) 接入 Home Assistant:见 docs/03-homeassistant.md
📖 文档路线(建议顺序)
- [01 · 方案总览] docs/01-overview.md
- [02 · 设备端配置] docs/02-device-setup.md
- 按需选择:
- Home Assistant 用户 → [03 · HA 接入] docs/03-homeassistant.md
- 开发者 → [04 · Python 后端] docs/04-backend.md)、[05 · React 前端] 浏览工程包 docs/05-frontend.md
- 上线 → [06 · 部署与运维] docs/06-deploy-ops.md
- 遇问题 → [07 · 排错手册] docs/07-troubleshooting.md
⚠️ 三个最重要的提醒
- 主题语义反直觉:设备
publish主题是你要订阅的;设备subcribe主题是你要发布指令的。 - Broker 地址设备要能访问:设备端填的必须是你服务器的局域网 IP / 公网域名,不能是
localhost。 - 切换 MQTT 后需重启设备生效。
🔧 环境要求
| 组件 | 版本 |
|---|---|
| Docker / Docker Compose | 24+ |
| Python(跑后端/脚本) | 3.11+ |
| Node(跑前端) | 18+ / 20+ |
| Home Assistant | 任意近期版本 |
📄 说明
- 本工程为教学示例,生产使用请参考 [06 · 部署与运维] docs/06-deploy-ops.md 的安全清单(密码认证、HTTPS、最小权限等)。
- 设备型号协议以 GemeOpen 官方开发文档为准。
工程包说明
00-architecture.md
00 · 架构与接口契约(基准文档)
本文是整个工程的基准契约。所有子模块(Home Assistant 配置、Python 后端、React 前端)都必须严格遵守本文件定义的主题名、消息格式与 API 字段名。
1. 总体架构
┌─────────────┐ MQTT ┌──────────────────┐ MQTT ┌──────────────────────┐
│ GSPM1B │ ────────► │ Mosquitto │ ◄──────── │ Home Assistant │
│ 智能插座 │ ◄──────── │ (自建 Broker) │ ────────► │ MQTT 集成 │
└─────────────┘ │ │ │ switch + sensor │
│ │ └──────────────────────┘
│ │ MQTT ┌──────────────────────┐
│ │ ◄──────── │ Python 后端 (FastAPI)│
│ │ ────────► │ MQTT ⇄ REST/WebSocket│
└──────────────────┘ └──────────┬───────────┘
│ HTTP / WS
┌──────────▼───────────┐
│ React 前端 (Vite) │
└──────────────────────┘
两条并行的消费路径(互不影响):
- 路径 A(面向最终用户):Home Assistant 直接通过 MQTT 集成接入,无需自研代码。
- 路径 B(面向二次开发):Python + React Demo,演示如何自建一套 Web 控制台。
2. 设备通信配置(出厂默认 → 自建 Broker)
设备出厂默认连接 GemeOpen 厂家 MQTT 服务器。接入自建 Broker 需在原厂通道下发 setting-mqtt 指令(详见 docs/02-device-setup.md)。
setting-mqtt 指令:
{
"clientId": "gspm1b-28372fcbbbb8",
"messageId": "20260520120000001",
"password": "device-pass",
"port": "1883",
"protocol": "mqtt",
"publish": "gemeopen/gspm1b/28372fcbbbb8/report",
"server": "192.168.1.10",
"subcribe": "gemeopen/gspm1b/28372fcbbbb8/command",
"type": "custom",
"username": "device"
}
⚠️ 语义(易错,务必按此理解)
publish= 设备发布数据的主题 → 服务端订阅它来接收上报(本项目记作 reportTopic)。subcribe= 设备订阅的主题 → 服务端发布指令到它来控制设备(本项目记作 commandTopic)。
3. MQTT 主题约定
| 名称 | 默认值 | 方向 |
|---|---|---|
| reportTopic | gemeopen/gspm1b/{mac}/report | 设备 → 服务端(上行,服务端订阅) |
| commandTopic | gemeopen/gspm1b/{mac}/command | 服务端 → 设备(下行,服务端发布) |
HA 与 Python 后端使用同一套主题;两者各自用独立 clientId 连接 Broker。
4. 指令与消息契约(JSON)
4.1 下行指令(服务端 → 设备,发到 commandTopic)
| 功能 | payload | 说明 |
|---|---|---|
| 通断电控制 | {"key":1,"messageId":"<id>","type":"event"} | key=1 通电,key=0 断电 |
| 查询实时电量 | {"messageId":"<id>","type":"statistic"} | 触发一次实时数据回传 |
| 设置上报频率 | {"messageId":"<id>","timerEnable":1,"timerInterval":15,"type":"setting"} | 自动上报开关与周期(5~86400 秒) |
| 获取设备信息 | {"messageId":"<id>","type":"info"} | 返回固件、IP、信号等 |
| 恢复出厂 | {"messageId":"<id>","system":"reset","type":"setting"} | 谨慎 |
4.2 上行消息(设备 → 服务端,来自 reportTopic)
A. 定时自动上报(device-timer-task)
{
"commandName": "device-timer-task",
"mac": "28372fcbbbb8",
"messageId": "",
"source": "auto",
"key": 1,
"voltage": 226.024,
"current": 0.501,
"power": 113.4,
"energy": 25.047
}
B. 指令响应(controller-event / info-statistic / info-all)
{
"commandName": "info-statistic",
"mac": "28372fcbbbb8",
"messageId": "20260520120000001",
"source": "command",
"key": 1,
"voltage": 226.024,
"current": 0.501,
"power": 113.4,
"energy": 25.047
}
字段语义与单位
| 设备字段 | 含义 | 单位 | 归一化字段(本项目统一命名) |
|---|---|---|---|
| key | 通断电状态(1 通电 / 0 断电) | — | relayOn (boolean) |
| voltage | 电压 | V | voltageV |
| current | 电流 | A | currentA |
| power | 有功功率 | W | activePowerW |
| energy | 累计用电量(断电不归零) | kW·h | energyKwh |
| signal | 信号强度 | dBm | signalDbm |
| commandName | 指令名 | — | commandName |
| source | 来源 command/auto/button | — | source |
5. Python 后端 API 契约(供 React 前端调用)
Base URL:http://<host>:8000
5.1 GET /api/health
{ "status": "ok", "mqttConnected": true, "deviceOnline": true }
5.2 GET /api/device
返回当前设备状态快照 DeviceState:
{
"mac": "28372fcbbbb8",
"online": true,
"relayOn": true,
"voltageV": 226.024,
"currentA": 0.501,
"activePowerW": 113.4,
"energyKwh": 25.047,
"signalDbm": -75,
"source": "auto",
"lastUpdate": 1787992225
}
lastUpdate为秒级 Unix 时间戳;无数据时各数值字段为null。
5.3 POST /api/device/switch
请求:{ "on": true } → 响应:{ "success": true, "state": <DeviceState> }
5.4 POST /api/device/refresh
触发一轮 info + statistic 查询,响应:{ "success": true }
5.5 GET /api/device/history?limit=200
返回采样历史(时间升序):
[ { "ts": 1787992225, "voltageV": 226.024, "currentA": 0.501, "activePowerW": 113.4, "energyKwh": 25.047, "relayOn": true } ]
5.6 GET /api/config
返回非敏感运行配置:{ "reportTopic": "...", "commandTopic": "...", "mac": "..." }
5.7 WS /ws/telemetry
WebSocket,每次设备状态更新时推送一帧 DeviceState(与 5.2 同结构)。
6. 后端环境变量(.env)
| 变量 | 默认值 | 说明 |
|---|---|---|
MQTT_HOST | 127.0.0.1 | Broker 地址 |
MQTT_PORT | 1883 | Broker 端口 |
MQTT_USERNAME | backend | 后端连接 Broker 的用户名 |
MQTT_PASSWORD | backend-pass | 后端密码 |
MQTT_CLIENT_ID | gemeopen-backend | 后端 clientId(唯一) |
MQTT_REPORT_TOPIC | gemeopen/gspm1b/+/report | 订阅(+ 通配单设备) |
MQTT_COMMAND_TOPIC | gemeopen/gspm1b/{mac}/command | 发布指令 |
DEVICE_MAC | 28372fcbbbb8 | 目标设备 MAC |
HISTORY_SIZE | 2000 | 内存中保留的采样条数 |
CORS_ORIGINS | * | 允许的前端来源 |
7. 前端约定
- 构建工具:Vite + React 18
- 状态来源:REST 轮询(
/api/device)+ WebSocket(/ws/telemetry)实时推送 - API Base:环境变量
VITE_API_BASE(默认http://localhost:8000) - 展示内容:通断电开关、电压/电流/功率实时值、累计用电量、在线状态、简易历史曲线
8. 目录结构
gemeopen-ha-demo/
├── README.md # 总览与快速开始
├── docker-compose.yml # Mosquitto + 后端 + 前端
├── docs/
│ ├── 00-architecture.md # 本文件(契约)
│ ├── 01-overview.md # 方案总览(初学者向)
│ ├── 02-device-setup.md # 设备端 MQTT 配置
│ ├── 03-homeassistant.md # Home Assistant 接入
│ ├── 04-backend.md # Python 后端说明
│ ├── 05-frontend.md # React 前端说明
│ ├── 06-deploy-ops.md # 部署与运维
│ └── 07-troubleshooting.md # 排错手册
├── homeassistant/ # HA 配置片段
├── backend/ # Python FastAPI 后端
├── frontend/ # React 前端
├── scripts/ # 设备配置脚本
└── mosquitto/ # Broker 配置
01-overview.md
01 · 方案总览(初学者先读这篇)
1. 这个项目要解决什么?
GemeOpen GSPM1B 智能转换器插座支持二次开发(MQTT/TCP)。本工程教你把它接入 Home Assistant,
实现:
- ✅ 通断电控制(开 / 关)
- ✅ 实时电压、电流、功率采集
- ✅ 累计用电量查询
- ✅(加分)一套 Python 后端 + React 前端的自研 Demo,演示如何自己写控制台
2. 两条接入路径(任选,也可都做)
┌──────────────────────────────────────────┐
GSPM1B ──MQTT─┤ Mosquitto Broker │
(设备) └───────┬───────────────────────┬───────────┘
│ MQTT │ MQTT
┌────────▼─────────┐ ┌────────▼──────────────┐
│ 路径 A │ │ 路径 B │
│ Home Assistant │ │ Python 后端 (FastAPI) │
│ MQTT 集成 │ │ ⇅ REST / WebSocket │
│ switch + sensor │ │ React 前端 (Vite) │
└──────────────────┘ └────────────────────────┘
无需写代码 适合学习/二次开发
- 路径 A(Home Assistant):零代码,靠 YAML 配置。日常使用推荐。
- 路径 B(Python + React):完整可运行 Demo,适合程序员学习 MQTT、FastAPI、React 全栈。
3. 你需要准备什么
| 准备项 | 说明 |
|---|---|
| 硬件 | 1 台 GSPM1B 插座;一个能连 2.4GHz WiFi 的网络 |
| 服务器/电脑 | 跑 Mosquitto(和 Home Assistant)的机器,建议 Linux / 树莓派 / NAS |
| 软件 | Docker(推荐);Node 18+、Python 3.11+(跑 Demo 用) |
| 账号信息 | 设备当前 Broker 的接入信息(见 02 文档) |
4. 快速开始(5 步)
# ① 启动 Broker + 后端 + 前端(一条命令)
cd gemeopen-ha-demo
docker compose up -d --build
# ② 把设备切到你的 Broker(改成你的实际参数后执行)
cd scripts && cp device_switch.example.json device_switch.json
python3 configure_device.py --config device_switch.json
# 成功后给设备断电重启
# ③ 让设备定时上报(示例 15 秒)
mosquitto_pub -h <你的IP> -p 1883 -t 'gemeopen/gspm1b/<MAC>/command' \
-m '{"messageId":"1","timerEnable":1,"timerInterval":15,"type":"setting"}'
# ④ 打开自研 Demo 前端
# 浏览器访问 http://<你的IP>:8080 → 能看数据、能开关
# ⑤ 接入 Home Assistant(详见 docs/03)
# - MQTT 集成连接同一 Broker
# - 引入 homeassistant/mqtt-gspm1b.yaml
5. 文档导航
| 文档 | 内容 | 适合 |
|---|---|---|
| [02 · 设备端配置] 02-device-setup.md | 把设备指向自建 Broker | 必读 |
| [03 · Home Assistant 接入] 03-homeassistant.md | HA 集成 + 实体 + 仪表盘 + 自动化 | 路径 A |
| [04 · Python 后端] 04-backend.md | FastAPI + MQTT 代码说明 | 路径 B |
| [05 · React 前端] 05-frontend.md | 前端组件与运行 | 路径 B |
| [06 · 部署与运维] 06-deploy-ops.md | 三种部署方式、备份、监控 | 上线 |
| [07 · 排错手册] 07-troubleshooting.md | 按症状排查 | 遇问题 |
| [00 · 架构与契约] 00-architecture.md | 主题/消息/API 契约(基准) | 开发 |
6. 关键概念 30 秒速通
- MQTT:一种轻量发布/订阅消息协议。设备把数据“发布”到某个“主题”,其他程序“订阅”该主题即可收到。
- Broker(Mosquitto):消息中转站。设备、HA、后端都连它,彼此不直接通信。
- Topic(主题):类似“频道名”。本项目用
gemeopen/gspm1b/<mac>/report(设备发)与.../command(设备收)。 - Home Assistant:开源智能家居平台,通过 MQTT 集成把设备变成可控制的“实体”。
⚠️ 最容易踩的坑:GemeOpen 文档里的
publish/subcribe与直觉相反——
设备的publish主题是你要订阅的,subcribe主题是你要发布指令的。详见 [02] 02-device-setup.md
02-device-setup.md
02 · 设备端 MQTT 配置(把 GSPM1B 指向自建 Broker)
目标:让 GSPM1B 把数据发到你自己的 MQTT Broker(Mosquitto),这样 Home Assistant 和 Python 后端才能读到数据、发出控制指令。
1. 为什么需要这一步?
GSPM1B 出厂时默认连接 GemeOpen 厂家测试服务器(mqtt.smart-bird.cn:1883),免费供调试。要让 HA 接管,就需要把设备改为连接你自己的 Broker。
设备提供了专门的指令 setting-mqtt(自定义 MQTT 参数)来完成这件事。
2. 准备工作
你需要先知道设备当前所在 Broker 的连接信息,通过设备指令 info-protocol 获取:
| 字段 | 含义 | 用途 |
|---|---|---|
server / port | 当前 Broker 地址/端口 | 连接它来下发切换指令 |
username | 当前 Broker 用户名 | 连接凭据 |
publish | 设备发布主题(服务端订阅它收数据) | 记为 report_topic |
subcribe | 设备订阅主题(服务端向它发指令) | 记为 command_topic |
⚠️ 注意
publish/subcribe(原文拼写)的语义与直觉相反:
- 设备把数据 publish 到
publish主题 → 所以你要订阅它。- 设备 subscribe 了
subcribe主题 → 所以你要往它发布指令。
获取方式:
- GemeOpen 控制台:登录后在设备详情页查看;
- 设备指令:往设备发
{"type":"info","messageId":"..."}(info-protocol需按其文档说明触发),返回里含上述字段; - 向厂家/销售索取:测试服务器的连接凭据(username/password)通常需要厂家提供。
3. 一键切换脚本
工程提供了脚本 scripts/configure_device.py:
cd scripts
cp device_switch.example.json device_switch.json
# 编辑 device_switch.json,填入 current(当前 Broker)与 target(你的自建 Broker)
pip install paho-mqtt==2.1.0
python3 configure_device.py --config device_switch.json
配置模板关键字段:
{
"current": { // 设备“现在”所在的 Broker
"server": "mqtt.smart-bird.cn",
"port": 1883,
"username": "<平台用户名>",
"password": "<平台密码>",
"report_topic": "<info-protocol 的 publish>",
"command_topic": "<info-protocol 的 subcribe>"
},
"target": { // 要切换到的“自建” Broker
"server": "192.168.1.10", // 必须设备网络可达
"port": 1883,
"username": "device",
"password": "device-pass",
"client_id": "gspm1b-28372fcbbbb8",
"publish_topic": "gemeopen/gspm1b/28372fcbbbb8/report",
"subcribe_topic": "gemeopen/gspm1b/28372fcbbbb8/command"
}
}
脚本会:连接当前 Broker → 向 command_topic 发布 setting-mqtt → 等待设备回执。
成功后会提示:
✅ 设备已接受新的 MQTT 配置。
请给设备【断电重启】,或发送 controller-restart 指令,使配置生效。
4. 使配置生效
setting-mqtt 需要重启才生效:
- 断电重启:拔掉设备再上电(最简单);
- 软重启:向设备发
{"messageId":"...","system":"restart","type":"setting"}(controller-restart)。
5. 验证设备已切换
在你的 Broker 上用任意 MQTT 客户端订阅:
mosquitto_sub -h 192.168.1.10 -p 1883 -u device -P device-pass \
-t 'gemeopen/gspm1b/28372fcbbbb8/report' -v
若设备按上报周期持续吐出 JSON(含 voltage/current/power/energy/key),说明切换成功 🎉。
6. 没有厂家凭据怎么办?
- 方案 A(推荐):联系 GemeOpen 获取测试服务器凭据,用脚本切换;
- 方案 B:设备支持内网 HTTP 控制(固件 2.0.0+),可在设备所在局域网内直接配置(具体接口以设备文档为准);
- 方案 C:先用厂家默认服务器调试,跑通
scripts/与后端逻辑后,再切换。
7. 主题与设备 MAC 的对应关系
建议按设备 MAC 规划主题,便于多设备管理:
gemeopen/gspm1b/<mac>/report # 设备上报(你订阅)
gemeopen/gspm1b/<mac>/command # 设备指令(你发布)
后端的 MQTT_REPORT_TOPIC 默认用通配符 gemeopen/gspm1b/+/report,可同时接收多台设备。
03-homeassistant.md
03 · Home Assistant 接入
面向初学者,从零把 GSPM1B 接入 Home Assistant,实现通断电控制 + 实时电流/电压/功率采集 + 累计用电量。
1. 整体思路(先看这张图)
GSPM1B ──MQTT──► Mosquitto Broker ◄──MQTT── Home Assistant
(设备) (你的服务器) (MQTT 集成)
Home Assistant 通过 MQTT 集成连接你的 Mosquitto,再用一份 YAML 把设备消息映射成实体:
- switch(开关)→ 控制通断电
- sensor(传感器)→ 电压 / 电流 / 功率 / 累计用电量 / 信号
2. 前置条件
| 项 | 要求 |
|---|---|
| Home Assistant | 任意安装方式(HAOS / Docker / Supervised / Core) |
| MQTT Broker | Mosquitto(本工程 mosquitto/ + docker-compose.yml 已提供) |
| 设备 | GSPM1B 已按 [02 · 设备端配置] 02-device-setup.md 指向你的 Broker |
| 设备 MAC | 后面配置中所有 <MAC> 都要替换成它 |
3. 步骤一:准备 Mosquitto Broker
3.1 用本工程一键启动(推荐)
cd gemeopen-ha-demo
docker compose up -d mosquitto
3.2 或使用 Home Assistant OS 的官方 Add-on
设置 → 加载项 → 搜索 Mosquitto broker → 安装 → 启动。
用哪种都行,只要设备、HA 都连同一个 Broker。
4. 步骤二:在 HA 中添加 MQTT 集成
- 设置 → 设备与服务 → 添加集成 → 搜索 MQTT;
- Broker 填
mosquitto的地址(Docker 场景填宿主机 IP,如192.168.1.10)、端口1883; - 填入 Mosquitto 的用户名/密码(见
mosquitto/mosquitto.conf与docker-compose.yml); - 提交,集成显示“已连接”。
5. 步骤三:添加实体配置
- 把本工程的
homeassistant/mqtt-gspm1b.yaml复制到 HA 配置目录(与configuration.yaml同级); - 编辑
mqtt-gspm1b.yaml,把 所有<MAC>替换为你的设备 MAC(小写,例如28372fcbbbb8); - 在
configuration.yaml中加入一行:
mqtt: !include mqtt-gspm1b.yaml
- 开发者工具 → YAML → 检查配置 → 重启 HA。
如果你更熟悉 UI:也可在设置 → 设备与服务 → MQTT → 通过“添加实体”逐个添加,参数与 YAML 一致。
6. 步骤四:验证实体
重启后,设置 → 设备与服务 → 实体,应能看到:
| 实体 ID | 类型 | 说明 |
|---|---|---|
switch.gspm1b_switch | switch | 通断电开关 |
sensor.gspm1b_voltage | sensor | 电压 (V) |
sensor.gspm1b_current | sensor | 电流 (A) |
sensor.gspm1b_power | sensor | 功率 (W) |
sensor.gspm1b_energy | sensor | 累计用电量 (kWh) |
sensor.gspm1b_signal | sensor | 信号强度 (dBm) |
测试:
- 点
switch.gspm1b_switch开/关,观察实物插座继电器是否动作; - 若传感器数值为“未知”,说明设备还没上报——检查:
- 设备是否已切换到该 Broker(见 02 文档);
- 是否已开启定时上报(见下节)。
7. 步骤五:开启设备定时上报(让传感器持续更新)
设备默认可能不主动汇报。给它下发一次“设置上报频率”指令即可(示例间隔 15 秒):
mosquitto_pub -h 192.168.1.10 -p 1883 -u device -P device-pass \
-t 'gemeopen/gspm1b/28372fcbbbb8/command' \
-m '{"messageId":"1","timerEnable":1,"timerInterval":15,"type":"setting"}'
之后 report 主题会每 15 秒收到一条 device-timer-task,HA 传感器随之更新。
8. 步骤六:添加仪表盘
- 设置 → 仪表盘 → 新建仪表盘(标题如“智能插座”);
- 打开它 → 右上角编辑 → 添加卡片,按
homeassistant/dashboard.yaml的内容添加:- 开关:Entities 卡片 →
switch.gspm1b_switch - 电压/电流:Gauge 卡片
- 功率/电量:Sensor 卡片(带迷你曲线)
- 历史:History graph 卡片
- 开关:Entities 卡片 →
也可直接把
dashboard.yaml的内容粘贴到“原始配置编辑器”。
9. 步骤七(加分项):能源面板与自动化
能源面板:累计用电量已设 device_class: energy + state_class: total_increasing,会自动出现在
设置 → 仪表盘 → 能源 → 添加用电设备。
自动化示例:功率超过 2000W 时通知(configuration.yaml 或 UI 自动化):
automation:
- alias: "GSPM1B 功率过高提醒"
trigger:
- platform: numeric_state
entity_id: sensor.gspm1b_power
above: 2000
for: "00:01:00"
action:
- service: persistent_notification.create
data:
title: "插座功率过高"
message: "当前功率 {{ states('sensor.gspm1b_power') }} W"
定时断电示例:
- alias: "每天 23:30 自动断电"
trigger:
- platform: time
at: "23:30:00"
action:
- service: switch.turn_off
target:
entity_id: switch.gspm1b_switch
10. 常见问题速查
| 现象 | 排查 |
|---|---|
| switch 显示不可用 | command_topic/state_topic 里的 <MAC> 是否替换正确;MQTT 集成是否连接 |
| 开关能点但实物无反应 | 设备是否已切到该 Broker;command_topic 是否为设备的 subcribe |
| 传感器一直“未知” | 未开启定时上报;或设备未上报;用 mosquitto_sub 抓包确认 |
| 数值不变 | 上报间隔过大;或本地缓存未触发上报 |
| 能源面板不显示 | 确认 state_class: total_increasing 已生效并已产生数据 |
更多排错见 [07 · 排错手册] 07-troubleshooting.md
04-backend.md
04 · Python 后端(FastAPI + MQTT)
路径 B 的后端:订阅设备 MQTT 消息 → 归一化 → 通过 REST / WebSocket 提供给 React 前端。
1. 文件结构
backend/
├── requirements.txt # 依赖
├── .env.example # 环境变量模板
├── Dockerfile # 镜像构建
├── README.md # 简版运行说明
└── app/
├── __init__.py
├── config.py # 读取环境变量(pydantic-settings)
├── models.py # DeviceState / Sample / SwitchRequest 数据模型
├── store.py # 线程安全状态 + 历史环形缓冲
├── mqtt_client.py # MQTT 连接、订阅、解析、下发指令
└── main.py # FastAPI 应用与路由(含 WebSocket)
2. 运行
本地
cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # 修改 MQTT_HOST、DEVICE_MAC 等
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
Docker
docker build -t gemeopen-backend ./backend
docker run -d --name gemeopen-backend -p 8000:8000 \
-e MQTT_HOST=192.168.1.10 -e DEVICE_MAC=28372fcbbbb8 \
gemeopen-backend
会自动生成交互式 API 文档:http://localhost:8000/docs
3. API 一览
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/health | 健康检查:服务/Broker/设备在线状态 |
| GET | /api/device | 当前设备状态快照 |
| POST | /api/device/switch | 通断电控制,body {"on": true} |
| POST | /api/device/refresh | 主动触发一次 info + statistic 查询 |
| GET | /api/device/history?limit=200 | 采样历史(用于绘图) |
| GET | /api/config | 非敏感运行配置(主题、MAC) |
| WS | /ws/telemetry | 实时状态推送(每帧一个 DeviceState) |
响应字段定义见 [00 · 架构与契约] 00-architecture.md 第 5 节。
4. 代码导读(重点看这三处)
4.1 mqtt_client.py:怎么把回调桥接到 Web 服务
MQTT 的回调运行在独立线程,而 FastAPI 是 asyncio 事件循环。若直接在回调里操作 WebSocket 会出错。
正确做法是把事件“递交”到事件循环:
# 伪代码
loop = asyncio.get_event_loop() # 主线程里保存
...
def on_message(client, userdata, msg): # 运行在 MQTT 线程
payload = json.loads(msg.payload)
state = normalize(payload) # key→relayOn, voltage→voltageV ...
store.update(state)
asyncio.run_coroutine_threadsafe(broadcast(state), loop) # 线程安全地广播
4.2 store.py:为什么用 deque
历史采样用 collections.deque(maxlen=HISTORY_SIZE) 做环形缓冲——满了自动丢最旧的一条,
不会无限占内存,非常适合“只看最近 N 条”的场景。
4.3 main.py:启动/关闭生命周期
@asynccontextmanager
async def lifespan(app):
client.connect() # 启动时连 Broker 并订阅
yield
client.disconnect() # 关闭时释放
5. 字段归一化对照
| 设备原始字段 | 归一化字段 | 类型 |
|---|---|---|
| key (0/1) | relayOn | boolean |
| voltage | voltageV | float |
| current | currentA | float |
| power | activePowerW | float |
| energy | energyKwh | float |
| signal | signalDbm | int |
6. 二次开发建议
- 接入数据库:把
store.py的内存历史换成 InfluxDB/PostgreSQL; - 多设备:
MQTT_REPORT_TOPIC已用+通配,可扩展为按 MAC 管理多台; - 鉴权:生产环境下在前端与后端之间加 Token 鉴权;
- 告警:复用前面温湿度工程的阈值引擎思路,对功率/电量做越限告警。
05-frontend.md
05 · React 前端(Vite)
路径 B 的前端:展示实时电压/电流/功率/累计电量,并提供通断电开关。
1. 文件结构
frontend/
├── package.json # 依赖与脚本(vite / react / recharts)
├── vite.config.js # 开发代理 /api、/ws → localhost:8000
├── index.html
├── .env.example # VITE_API_BASE
├── nginx.conf # 生产用 Nginx(托管 + 反向代理)
├── Dockerfile # 多阶段构建 → Nginx
├── README.md
└── src/
├── main.jsx # 入口
├── App.jsx # 根组件(布局 + 状态汇总)
├── api.js # REST 封装
├── useTelemetry.js # 数据 Hook:REST 初拉 + WS 实时 + 降级轮询
├── styles.css
└── components/
├── SwitchControl.jsx # 通断电开关
├── RealtimeMetrics.jsx # 电压/电流/功率/信号 卡片
├── EnergyTotal.jsx # 累计用电量
└── HistoryChart.jsx # 历史曲线(recharts)
2. 运行
cd frontend
npm install
npm run dev # http://localhost:5173
npm run build # 产物在 dist/
npm run preview # 本地预览构建产物
3. 数据来源与更新机制
useTelemetry.js 是核心:
- 首屏
GET /api/device拿一次快照; - 建立
WebSocket /ws/telemetry,后端每次状态更新即推一帧,界面实时刷新; - 断线后指数退避自动重连;
- 若 WebSocket 不可用,自动降级为 5 秒轮询,保证界面仍可用。
4. 配置
前端通过环境变量 VITE_API_BASE 指定后端地址(默认 http://localhost:8000):
cp .env.example .env
# .env
VITE_API_BASE=http://192.168.1.10:8000
- 开发模式:Vite 已配置代理,保持默认即可;
- 生产模式(Docker/Nginx):容器内
nginx.conf会把/api、/ws反代到后端,可保持默认值。
5. 界面构成
| 区域 | 组件 | 内容 |
|---|---|---|
| 顶部 | App | 标题、在线/连接状态徽标、刷新、最后更新时间 |
| 控制 | SwitchControl | 通电/断电开关(带请求中状态、离线禁用) |
| 指标 | RealtimeMetrics | 电压 V / 电流 A / 功率 W / 信号 dBm |
| 电量 | EnergyTotal | 累计用电量 kWh |
| 趋势 | HistoryChart | 电压/功率历史折线(双 Y 轴) |
6. 构建与部署
# 本地构建
npm run build
# 或 Docker(由仓库根 docker-compose.yml 统一编排)
docker build -t gemeopen-frontend ./frontend
docker run -d -p 8080:80 gemeopen-frontend
7. 二次开发建议
- 换 UI 库:可平滑替换为 Ant Design / MUI;
- 加认证:登录页 + Token 注入
api.js请求头; - 多设备切换:顶部加设备选择器,按 MAC 切换 API 参数;
- 告警展示:接入后端告警接口,做 Toast/声音提醒。
06-deploy-ops.md
06 · 部署与运维
面向初学者,覆盖 本地开发 → Docker 部署 → 生产建议,以及日常运维操作。
1. 部署方式总览
| 方式 | 适用场景 | 难度 | 说明 |
|---|---|---|---|
| A. 本地开发 | 学习/调试 | ★ | 手动起 Mosquitto + 后端 + 前端 |
| B. Docker Compose | 快速上线 | ★★ | 一条命令起全套(推荐) |
| C. 生产部署 | 长期运行 | ★★★ | 密码认证 + HTTPS + 自动启动 + 备份 |
2. 方式 A:本地开发
2.1 启动 MQTT Broker
# 有 Docker:直接用仓库内的配置
docker compose up -d mosquitto
# 或本机安装 mosquitto 后
mosquitto -c mosquitto/mosquitto.conf -v
2.2 启动 Python 后端
cd backend
python3 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # 按需修改(尤其 MQTT_HOST、DEVICE_MAC)
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
打开 http://localhost:8000/docs 可看到自动生成的 API 文档。
2.3 启动 React 前端
cd frontend
npm install
npm run dev # 默认 http://localhost:5173
开发时 Vite 已把
/api、/ws代理到localhost:8000,一般无需额外配置。
3. 方式 B:Docker Compose(推荐)
3.1 前置
- 安装 Docker 与 Docker Compose 插件;
- 修改
docker-compose.yml中DEVICE_MAC为你的设备 MAC。
3.2 启动
cd gemeopen-ha-demo
docker compose up -d --build
docker compose ps
启动后:
| 服务 | 地址 |
|---|---|
| Mosquitto | mqtt://<宿主机IP>:1883 |
| 后端 API | http://localhost:8000/docs |
| 前端界面 | http://localhost:8080 |
3.3 关键:让设备能访问到 Broker
docker-compose.yml 里 devices 的目标 Broker 地址应填 宿主机局域网 IP(如 192.168.1.10),
不能用 localhost / mosquitto(那是容器内部的地址,设备访问不到)。
同时确认防火墙放行 1883 端口。
4. 方式 C:生产环境建议
4.1 启用 Broker 密码认证
按 mosquitto/mosquitto.conf 文件末尾的说明生成 passwd 并开启认证,然后:
- 设备
setting-mqtt里填username/password; - HA 的 MQTT 集成里填同样的凭据;
- 后端
.env里填MQTT_USERNAME/MQTT_PASSWORD。
4.2 安全清单
- 关闭匿名访问;为设备/HA/后端分配独立账号
- MQTT 端口不直接暴露公网;远程访问用 VPN 或 TLS(8883)
- 前端对外访问用 HTTPS(Nginx/Caddy 反代 + 证书)
- 后端
CORS_ORIGINS由*收紧为你的域名 - 敏感信息用环境变量/密钥管理,不写进代码
4.3 开机自启
- Docker:
restart: unless-stopped已配置,Docker 服务开机自启即可; - 裸机后端可用 systemd:
# /etc/systemd/system/gemeopen-backend.service
[Unit]
Description=GemeOpen Backend
After=network.target mosquitto.service
[Service]
WorkingDirectory=/opt/gemeopen-ha-demo/backend
ExecStart=/opt/gemeopen-ha-demo/backend/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload && sudo systemctl enable --now gemeopen-backend
5. 日常运维
5.1 看日志
docker compose logs -f backend # 后端日志
docker compose logs -f mosquitto # Broker 日志
docker compose logs -f frontend # 前端日志
5.2 健康检查
curl http://localhost:8000/api/health
# {"status":"ok","mqttConnected":true,"deviceOnline":true}
5.3 更新
git pull # 或替换为新版本文件
docker compose up -d --build
5.4 备份
- Broker 数据:
mosquitto-data卷(docker compose down后备份该卷即可); - 历史数据:当前 Demo 存于内存,重启即清空。生产建议改接 InfluxDB / TimescaleDB / PostgreSQL 持久化。
5.5 监控建议
| 指标 | 方式 |
|---|---|
| 后端存活 | /api/health 加 Uptime Kuma / 定时 curl |
| MQTT 连接数 | mosquitto_sub -t '$SYS/broker/clients/connected' |
| 设备在线 | 后端 online 字段(可加 HA 自动化告警) |
| 磁盘/内存 | cAdvisor / node_exporter + Prometheus + Grafana |
5.6 容量与性能
- 单设备 15 秒上报 → 每天约 5760 条,压力很小;
- 多设备/高频上报时,
HISTORY_SIZE与前端刷新频率需相应调整; - 历史落库后再做长期趋势与统计。
6. 配置速查(后端环境变量)
见 [00 · 架构与接口契约] 00-architecture.md 第 6 节,或 backend/.env.example。
07-troubleshooting.md
07 · 排错手册
按“症状”查“原因/解决”。建议随身备好两条抓包命令:
# 看设备是否在发数据(订阅设备上报主题)
mosquitto_sub -h 192.168.1.10 -p 1883 -u device -P device-pass -t 'gemeopen/gspm1b/+/report' -v
# 看是否有人给设备发指令(订阅设备指令主题)
mosquitto_sub -h 192.168.1.10 -p 1883 -u device -P device-pass -t 'gemeopen/gspm1b/+/command' -v
一、设备侧
| 症状 | 可能原因 | 解决 |
|---|---|---|
| 设备不上报 | 未切换到自建 Broker | 用 scripts/configure_device.py 执行 setting-mqtt,再断电重启 |
| 切换后彻底没数据 | 目标 Broker 地址设备不可达 | 目标地址必须是局域网 IP/公网域名,且防火墙放行 1883 |
| 上报间隔很长 | 电池供电 + 缓存条数大 | 用 device-timer-interval 把 timerInterval 调小(如 15s) |
| 通电后数据不刷新 | 未开启定时上报 | 发 {"timerEnable":1,"timerInterval":15,"type":"setting"} |
| 想恢复出厂 | 配置错了 | 发 {"system":"reset","type":"setting"}(会清空配网,谨慎) |
| 改了参数不生效 | 部分设置需重启 | 断电重启或发 {"system":"restart","type":"setting"} |
二、MQTT Broker
| 症状 | 可能原因 | 解决 |
|---|---|---|
| 客户端连不上 | 端口未放行 / 地址错 | 检查防火墙、docker compose ps、mosquitto -v 日志 |
Connection Refused: not authorised | 开了密码认证但凭据错 | 核对 passwd 中的账号,或临时改回 allow_anonymous true 验证 |
| 订阅收不到消息 | 主题拼写错误 | 用 + 通配符测试:gemeopen/gspm1b/+/report |
| 容器起不来 | 配置文件语法错 | docker compose logs mosquitto,检查 mosquitto.conf |
三、Home Assistant
| 症状 | 可能原因 | 解决 |
|---|---|---|
| MQTT 集成添加失败 | Broker 地址/凭据错 | Docker 场景用宿主机 IP;核对账号密码 |
| switch 实体“不可用” | state_topic 无数据 | 先确保设备在报数据;检查 <MAC> 是否替换 |
| 点开关没反应 | command_topic 错(填成了 report) | 必须是设备的 subcribe 主题(.../command) |
| 开关状态不同步 | 上报里没有 key | 确认上报 JSON 含 key;模板为 {{ value_json.key }} |
| 传感器一直“未知” | 未开启定时上报 / 未收到该字段 | 开启上报;抓包确认字段名 voltage/current/power/energy |
| 能源面板无数据 | 缺 state_class | 确认 energy 传感器 state_class: total_increasing |
| 改了 YAML 没生效 | 未重载/重启 | 开发者工具 → YAML → 检查配置 → 重启 HA |
四、Python 后端
| 症状 | 可能原因 | 解决 |
|---|---|---|
启动报 ImportError | 依赖没装 | pip install -r requirements.txt |
/api/health 里 mqttConnected:false | 连不上 Broker | 检查 MQTT_HOST/PORT/USERNAME/PASSWORD |
/api/device 全为 null | 没收到设备上报 | 用 mosquitto_sub 验证设备是否在发 |
| 开关接口返回失败 | MQTT_COMMAND_TOPIC 的 {mac} 未替换 | 检查 .env 的 DEVICE_MAC 与 topic 模板 |
| 端口被占用 | 8000 已占用 | 换端口:uvicorn ... --port 8001,并同步前端 VITE_API_BASE |
五、React 前端
| 症状 | 可能原因 | 解决 |
|---|---|---|
| 页面一直“离线” | 后端未启动 / 地址错 | 打开 http://localhost:8000/docs 确认后端在跑;检查 VITE_API_BASE |
| 有数据但不刷新 | WebSocket 被代理拦截 | 开发用 Vite 代理;生产用 Nginx 的 /ws 段(已提供 frontend/nginx.conf) |
| 跨域报错(CORS) | 后端未允许前端来源 | 后端 CORS_ORIGINS 设为前端地址 |
| 图表为空 | /api/device/history 无数据 | 需先有若干条上报采样 |
| 构建失败 | Node 版本过低 | 使用 Node 18+/20+ |
六、网络连通性自查(万能四连)
# 1) 设备 → Broker 是否通(在能访问设备的网络内)
mosquitto_pub -h 192.168.1.10 -p 1883 -t test -m hello
# 2) 后端 → Broker
docker compose exec backend python -c "import os;print(os.getenv('MQTT_HOST'))"
# 3) 浏览器 → 后端
curl http://192.168.1.10:8000/api/health
# 4) HA → Broker(HA 的 MQTT 集成页面看“已连接”)
绝大多数问题都能通过“确认数据是否到达 Broker”这一步快速定位到是设备侧、Broker 侧还是应用侧。
更多推荐



所有评论(0)