Files
HomeOS/docs/superpowers/specs/2026-03-29-mqtt-home-design.md

233 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MQTT 智能家居管理系统 - 设计文档
## 概述
一个轻量级的智能家居管理项目,通过自建 EMQX Broker 管理 MQTT 设备。提供 Web 管理页面和 CLI 命令行工具,支持 Home Assistant Discovery 协议和自定义协议。
## 技术选型
| 组件 | 选择 | 理由 |
|------|------|------|
| 后端框架 | FastAPI | 原生异步,适合 MQTT 长连接;自带 API 文档 |
| MQTT 客户端 | aiomqtt (paho-mqtt) | 异步兼容 FastAPI |
| 前端 | Vue 3 + Vite + Tailwind CSS + Pinia | 现代单页应用,响应式 |
| 数据库 | SQLite + aiosqlite + SQLAlchemy | 零配置,单文件,适合单进程 |
| CLI | click | 与 Web API 共享业务逻辑 |
| 配置管理 | pydantic-settings | 类型安全的环境变量加载 |
## 架构方案:单体 FastAPI
采用单体架构FastAPI 同时处理 REST API 和 MQTT 连接。单进程部署Web 和 CLI 共享同一套业务逻辑。
```
EMQX Broker <--MQTT--> FastAPI MQTT Client <---> SQLite
| ^
REST API CLI (click)
|
Vue SPA
```
**优势:** 最简部署(一个 Python 进程 + 一个 SQLite 文件),零逻辑重复。
**局限:** MQTT 客户端和 Web 服务共享进程,不适合多实例水平扩展。
## 项目结构
```
mqtt-home/
├── pyproject.toml # 依赖管理、入口点
├── .env # EMQX 凭据gitignore
├── .env.example # 配置模板
├── src/
│ └── mqtt_home/
│ ├── __init__.py
│ ├── config.py # 环境变量配置pydantic-settings
│ ├── main.py # FastAPI 应用 + 生命周期(启停 MQTT
│ ├── database.py # SQLite 引擎、会话、Base
│ ├── models.py # SQLAlchemy ORM 模型
│ ├── schemas.py # Pydantic 请求/响应模型
│ ├── mqtt_client.py # aiomqtt 客户端:订阅、发布、回调
│ ├── device_registry.py # 业务逻辑:添加/删除/控制设备
│ ├── discovery.py # HA Discovery 协议处理器
│ ├── emqx_api.py # EMQX REST API 封装
│ ├── cli.py # click CLI 命令
│ └── api/
│ ├── __init__.py
│ ├── devices.py # REST 路由:设备增删改查 + 控制
│ ├── broker.py # REST 路由Broker 状态、客户端、主题
│ └── dashboard.py # REST 路由:聚合统计
└── frontend/ # Vue SPA
└── ...
```
**模块职责:**
- `device_registry.py` — 设备业务逻辑的唯一入口API 路由和 CLI 都从此导入
- `mqtt_client.py` — MQTT 连接生命周期管理
- `discovery.py` — 在 mqtt_client 上注册 HA Discovery 主题回调
- `emqx_api.py` — EMQX REST API 的薄封装,处理认证和端点映射
- 模块间通过直接函数调用通信,无消息总线或事件队列
## 数据模型
### devices 设备表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | TEXT (UUID) | 主键 |
| name | TEXT | 设备名称 |
| type | TEXT | 设备类型switch, light, sensor, binary_sensor, climate 等 |
| protocol | TEXT | ha_discovery 或 custom |
| mqtt_topic | TEXT | 状态主题(如 homeassistant/light/living_room/status |
| command_topic | TEXT | 命令主题(发送控制指令) |
| discovery_topic | TEXT | 原始 HA 发现主题(可为空) |
| discovery_payload | TEXT (JSON) | 原始发现配置(可为空) |
| attributes | TEXT (JSON) | 设备属性 |
| state | TEXT | 当前状态on, off, 温度值等) |
| is_online | BOOLEAN | 在线状态(根据 last_seen 超时判断) |
| last_seen | DATETIME | 最后收到 MQTT 消息的时间 |
| created_at | DATETIME | 创建时间 |
| updated_at | DATETIME | 更新时间 |
### device_logs 设备日志表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INTEGER (自增) | 主键 |
| device_id | TEXT (外键) | 关联设备 |
| direction | TEXT | rx收到或 tx发送命令 |
| topic | TEXT | MQTT 主题 |
| payload | TEXT | 消息内容 |
| timestamp | DATETIME | 时间戳 |
每设备保留最近 100 条日志,超出自动清理。
### 设备状态流转
1. **HA Discovery 自动注册** — 收到 `homeassistant/<type>/<node_id>/config` 消息时自动创建设备记录
2. **手动添加** — 通过 API/CLI 指定主题、类型、命令主题
3. **状态更新** — 设备状态主题收到消息 → 更新 state + last_seen
4. **设备控制** — API/CLI 向命令主题发布消息 → 同时记录日志
5. **在线判断** — last_seen 超过 60 秒标记为离线
## MQTT 集成
### HA Discovery 协议(被动,自动)
- 启动时订阅 `homeassistant/#`
- 解析 `config` 主题 → 自动创建设备记录
- 自动订阅每个设备的 `state_topic`
- 处理 `availability` 主题判断设备上下线
- 支持标准 HA Discovery 所有字段device class, unit of measurement, state class 等
### 自定义协议(手动,用户定义)
- 用户通过 CLI 或 Web UI 添加设备,指定:
- 状态主题(监听状态更新的位置)
- 命令主题(发送控制指令的位置)
- JSON 负载格式(简单模板,如 `{"state": "{{value}}"}`
- 系统订阅状态主题并追踪变化
### 通用 MQTT 行为
- 使用 `aiomqtt`(基于 `paho-mqtt` 的异步封装)
- 所有订阅使用 QoS 1至少一次投递
- 使用 Retained 消息保持状态持久化(订阅时读取保留消息)
- 连接断开时指数退避重连(初始 1 秒,最大 60 秒)
- 客户端 ID`mqtt-home-{uuid}`
## API 设计
### REST APIFastAPI 路由)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/devices` | 获取所有设备列表 |
| POST | `/api/devices` | 手动添加设备 |
| GET | `/api/devices/{id}` | 获取设备详情 |
| PUT | `/api/devices/{id}` | 更新设备配置 |
| DELETE | `/api/devices/{id}` | 删除设备 |
| POST | `/api/devices/{id}/command` | 向设备发送控制命令 |
| GET | `/api/devices/{id}/logs` | 获取设备最近消息日志 |
| GET | `/api/broker/status` | EMQX Broker 统计信息 |
| GET | `/api/broker/clients` | 已连接 MQTT 客户端列表 |
| GET | `/api/broker/topics` | 活跃主题列表 |
| GET | `/api/dashboard` | 聚合统计(设备在线/离线数、最近活动) |
### WebSocket
- `/ws/devices` — 设备状态变化实时推送state, is_online 变更时推送)
### CLI 命令
```bash
# 设备管理
mqtt-home device list
mqtt-home device add --name "客厅灯" --type light --state-topic home/living/light --command-topic home/living/light/set
mqtt-home device info <device_id>
mqtt-home device remove <device_id>
mqtt-home device command <device_id> --payload '{"state":"on"}'
mqtt-home device logs <device_id> [--limit 20]
# Broker 管理
mqtt-home broker status
mqtt-home broker clients
mqtt-home broker topics
# 服务启动
python -m mqtt_home serve
```
## 前端设计
### 页面
- **仪表盘** — 设备数量统计、在线/离线状态、最近活动时间线
- **设备列表** — 网格视图,每个设备卡片显示类型图标、名称、当前状态、在线指示灯、快捷切换
- **设备详情** — 状态展示、控制面板(根据类型显示开关/滑块/数值输入)、消息日志
- **Broker 管理** — 已连接客户端列表、活跃主题、Broker 健康状态
### 关键交互
- WebSocket 实时推送设备状态变化
- 设备卡片根据设备类型显示对应图标和颜色
- 控制面板根据设备类型自适应switch → 切换按钮light → 开关+亮度滑块sensor → 只读数值显示)
- 响应式布局,支持移动端访问
## 配置
### 环境变量
```
MQTT_HOST=192.168.0.31
MQTT_PORT=1883
MQTT_USERNAME=
MQTT_PASSWORD=
EMQX_API_URL=http://192.168.0.31:18083/api/v5
EMQX_API_KEY=<your-api-key>
EMQX_API_SECRET=<your-secret>
DATABASE_URL=sqlite+aiosqlite:///./data/mqtt_home.db
WEB_HOST=0.0.0.0
WEB_PORT=8000
```
### EMQX 连接信息
- MQTT Broker`192.168.0.31:1883`
- EMQX Dashboard`http://192.168.0.31:18083`
- EMQX REST API`http://192.168.0.31:18083/api/v5`
- 认证方式API Key + Secret KeyHTTP Basic Auth
## 错误处理
- MQTT 连接失败记录日志自动重连Web 端显示连接状态
- EMQX API 请求失败:返回错误信息,不阻塞核心功能
- 设备命令发送失败:记录日志,返回错误给用户
- 数据库操作失败:事务回滚,返回 500 错误
## 部署
- Python 3.11+
- `pip install -e .` 安装
- `python -m mqtt_home serve` 启动Web + MQTT
- CLI 可独立使用,不需要启动 Web 服务
- 可选 docker-compose 部署