Shelly MQTT Topic 结构
Shelly MQTT Topic 结构
本节详细介绍 Shelly 设备的 MQTT Topic 结构和数据格式。学习完成后,您将能够:
- 理解 Shelly Gen1 和 Gen2 的 Topic 差异
- 订阅正确的 Topic 获取设备状态和能耗数据
- 发布命令控制 Shelly 设备
- 设计统一的 Topic 抽象层
Gen1 Shelly Topic Structure
Section titled “Gen1 Shelly Topic Structure”自动发布的 Topic
Section titled “自动发布的 Topic”# 设备状态 (定期推送)shelly1pm-{deviceid}/status → {"relays":[{"ison":false}],"meters":[{"power":0.0,"total":123}]}
# 继电器状态变化 (事件触发)shelly1pm-{deviceid}/relay/0 → {"ison":true}
# 在线状态shelly1pm-{deviceid}/online → true / false控制命令 Topic
Section titled “控制命令 Topic”# 通过 HTTP API 控制 (Gen1 主要方式)curl http://shelly1pm-ip/relay/0?turn=oncurl http://shelly1pm-ip/relay/0?turn=off
# 通过 MQTT 控制 (需要额外配置)# 发布到:shelly1pm-{deviceid}/command → "on" / "off" / "toggle"能耗数据格式 (Gen1)
Section titled “能耗数据格式 (Gen1)”{ "relays": [ {"ison": false, "has_timer": false} ], "meters": [ { "power": 42.5, // 当前功率 (W) "overpower": 0.0, "is_valid": true, "timestamp": 1694321234, "counters": [0.0, 0.0], "total": 1452000.0 // 总电量 (Wh) } ]}Gen2 Shelly Topic Structure
Section titled “Gen2 Shelly Topic Structure”自动发布的 Topic
Section titled “自动发布的 Topic”# 开关状态shellyplus1pm-{deviceid}/status/switch:0 → {"id":0,"output":true,"apower":42.5,"voltage":223.1,"current":0.216}
# 功率数据shellyplus1pm-{deviceid}/status/devicepower:0 → {"id":0,"battery":{"V":3.2},"external":{"V":223.1}}
# 在线状态shellyplus1pm-{deviceid}/status/cloud → {"connected":true}能耗数据格式 (Gen2)
Section titled “能耗数据格式 (Gen2)”{ "id": 0, "source": "switch", "output": true, "apower": 42.5, // 当前功率 (W) "voltage": 223.1, // 电压 (V) "current": 0.216, // 电流 (A) "pf": 0.92, // 功率因数 "aenergy": { "total": 1452000, // 总电量 (mWh) "by_minute": [12.3, 15.1], // 每分钟电量 "minute_ts": 1694321234 }, "temperature": { "tC": 42.5 // 内部温度 }}RPC over MQTT (Gen2)
Section titled “RPC over MQTT (Gen2)”Gen2 的核心能力是通过 MQTT RPC 通道进行双向通信:
发送 RPC 命令
Section titled “发送 RPC 命令”Topic: shellyplus1pm-{deviceid}/rpc
// 获取状态{ "id": 1, "src": "nodered", "method": "Switch.GetStatus", "params": {"id": 0}}
// 控制开关{ "id": 2, "src": "nodered", "method": "Switch.Set", "params": { "id": 0, "on": true }}
// 设置自动关闭{ "id": 3, "src": "nodered", "method": "Switch.Set", "params": { "id": 0, "on": true, "toggle_after": 300 // 5 分钟后自动关闭 }}
// 获取设备信息{ "id": 4, "src": "nodered", "method": "Shelly.GetDeviceInfo"}接收 RPC 响应
Section titled “接收 RPC 响应”Topic: nodered/rpc (根据请求中的 src 字段)
{ "id": 1, "src": "shellyplus1pm-{deviceid}", "result": { "id": 0, "output": false, "apower": 0, "voltage": 223.1, "current": 0.0 }}Complete Topic Map
Section titled “Complete Topic Map”Shelly Plus 1PM (Gen2) Topic Structure:─────────────────────────────────────────
📂 shellyplus1pm-{deviceid}/ │ ├── 📂 status/ ← 自动状态推送 │ ├── 📄 switch:0 ← 开关状态 + 能耗数据 │ ├── 📄 devicepower:0 ← 电源状态 │ ├── 📄 cloud ← 云连接状态 │ ├── 📄 wifi ← Wi-Fi 状态 │ └── 📄 sys ← 系统信息 │ ├── 📄 rpc ← RPC 命令通道 │ └── 📂 events/ ← 事件通知 (RPC events) └── 📄 rpcNode-RED 统一抽象层
Section titled “Node-RED 统一抽象层”建议在 Node-RED 中创建统一的能耗数据处理 Flow:
// Function: 统一 Shelly 数据解析器// 支持 Gen1 和 Gen2 两种格式
var topic = msg.topic;var payload = msg.payload;
// 检测设备类型var deviceType = "unknown";if (topic.includes("shellyplus")) { deviceType = "shelly-gen2";} else if (topic.includes("shelly")) { deviceType = "shelly-gen1";}
// 标准化输出var normalized = { deviceId: topic.split('/')[0], deviceType: deviceType, timestamp: Date.now()};
if (deviceType === "shelly-gen2") { // Gen2: 直接从 status/switch 获取 normalized.power = payload.apower || 0; normalized.voltage = payload.voltage || 0; normalized.current = payload.current || 0; normalized.powerFactor = payload.pf || 0; normalized.output = payload.output || false; normalized.totalEnergy = (payload.aenergy?.total || 0) / 1000; normalized.temperature = payload.temperature?.tC || 0;} else if (deviceType === "shelly-gen1") { // Gen1: 从 status 中 extract var meter = payload.meters?.[0]; var relay = payload.relays?.[0]; normalized.power = meter?.power || 0; normalized.totalEnergy = (meter?.total || 0) / 1000; normalized.output = relay?.ison || false;}
msg.payload = normalized;return msg;Common Customer Questions
Section titled “Common Customer Questions”Q1: Gen1 和 Gen2 的 Topic 结构完全不兼容,如何统一管理?
Section titled “Q1: Gen1 和 Gen2 的 Topic 结构完全不兼容,如何统一管理?”建议在 Node-RED 中创建数据抽象层(如上面的 Function 代码),将 Gen1 和 Gen2 数据标准化为统一格式,下游的 InfluxDB 和 Grafana 无需关心设备类型。
Q2: RPC over MQTT 的优势是什么?
Section titled “Q2: RPC over MQTT 的优势是什么?”RPC 提供请求-响应模式,可以获取即时数据(不需要等 TelePeriod 推送),支持双向通信,且可以通过同一个 MQTT 连接管理所有 RPC 方法。
Q3: 如何批量管理多个 Shelly 设备?
Section titled “Q3: 如何批量管理多个 Shelly 设备?”在 Node-RED 中使用通配符订阅:
- 订阅
shelly+/+/status/#接收所有设备状态 - 按设备 ID 分流处理
- 使用 Flow/Global Context 管理设备注册表
✅ 推荐做法:
- Gen2 优先使用 RPC over MQTT 获取数据
- 在 Node-RED 中创建统一的数据抽象层
- 使用通配符订阅多设备时注意 Topic 过滤
- 为每个 RPC 请求生成唯一 ID 用于匹配响应
❌ 避免做法:
- Gen1 和 Gen2 的控制接口混用
- RPC 请求 ID 不唯一导致响应匹配混乱
- 订阅过于宽泛的 Topic(如
#) - 不处理 RPC 响应超时的情况
Summary
Section titled “Summary”- Gen1 Shelly 使用
shelly{id}/status和shelly{id}/relay/{n}Topic - Gen2 Shelly 使用
shellyplus{id}/status/switch:{n}结构化 Topic - RPC over MQTT 提供双向请求-响应通信
- RPC 命令通过
{deviceid}/rpc发送,响应路由到src指定的 Topic - Node-RED 抽象层可统一 Gen1/Gen2 的数据格式