Homepage 集成 OpenDTU 光伏逆变器 Widget:配置、数据字段与源码实现解析
【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage
导读
本文介绍如何在 Homepage(A highly customizable homepage / startpage / application dashboard)中集成 OpenDTU Widget,用于将基于 ESP32 的 OpenDTU 光伏微逆逆变器数据展示到个人首页仪表盘上。读完本文,你将掌握 OpenDTU Widget 的 YAML 配置方法、四个展示字段(今日发电量、实时功率、功率百分比、功率限制)的数据来源与计算逻辑,并通过源码与测试用例理解其底层 API 代理与数据校验机制。
OpenDTU Widget 是什么
OpenDTU 是一个开源项目(tbnobody/OpenDTU),它通过 ESP32 等硬件直连 Hoymiles(禾迈)系列微型逆变器,将原本封闭的逆变器协议以 Web 界面和 JSON API 的形式开放出来。Homepage 提供了对应的opendtu类型 Widget,让用户无需打开 OpenDTU 管理界面,就能在自建首页上实时看到光伏发电的核心指标。
官方文档(docs/widgets/services/opendtu.md)明确指出,该 Widget 允许展示的字段为["yieldDay", "relativePower", "absolutePower", "limit"],即今日发电量、相对功率、绝对功率和功率限制四项。
最小配置示例
在 Homepage 的services.yaml中,OpenDTU Widget 的配置非常简洁,只需指定类型与 OpenDTU 实例的访问地址:
widget: type: opendtu url: http://opendtu.host.or.iptype:固定为opendtu,用于在 src/widgets/widgets.js 中注册的 Widget 映射表中查找对应实现(第 255 行将opendtu模块注册进 Widget 列表)。url:OpenDTU 设备的 HTTP 访问地址,可以是主机名或 IP,例如http://opendtu.local或http://192.168.1.100。
该 Widget 不需要用户名密码(OpenDTU 的 livedata 接口默认无需鉴权),也不存在额外的端点参数,属于开箱即用的类型。
数据来源:livedata 状态接口
从 src/widgets/opendtu/widget.js 可以看到 Widget 的核心定义:
import genericProxyHandler from "utils/proxy/handlers/generic"; const widget = { api: "{url}/api/livedata/status", proxyHandler: genericProxyHandler, }; export default widget;它只做两件事:
- 声明 API 地址模板为
{url}/api/livedata/status,其中{url}会被运行时替换为你在配置中填写的url值; - 指定使用通用代理处理器
genericProxyHandler(src/utils/proxy/handlers/generic.js)完成服务端转发请求。
也就是说,Homepage 前端不会直接请求 OpenDTU,而是由后端代理统一发起 HTTP 请求。这样设计的好处包括:避免浏览器跨域(CORS)问题、可以在服务端统一注入请求头(如 Basic Auth 鉴权,genericProxyHandler会在配置了username/password时自动生成Authorization: Basic ...头),并能对响应数据做集中校验与日志记录。
四个展示字段的语义与计算逻辑
组件实现位于 src/widgets/opendtu/component.jsx,数据加载使用useWidgetAPIhook。组件在加载完成前会先渲染四个占位块(yieldDay、relativePower、absolutePower、limit),数据到达后按以下逻辑填充:
| 字段 key | 界面显示名(en / zh-Hans) | 数据来源 | 展示规则 |
|---|---|---|---|
yieldDay | Today / 今日 | data.total.YieldDay.v | 四舍五入取整,单位取data.total.YieldDay.u拼接,如12kWh |
absolutePower | Power / 功率 | data.total.Power.v | 四舍五入取整,单位取data.total.Power.u拼接,如250W |
relativePower | Power % / 功率 % | 计算得出:Power / totalLimit * 100 | 以百分比格式展示,如50(即 50%) |
limit | Limit / 限制 | 计算得出:所有逆变器limit_absolute之和 | 单位固定为W |
各字段的界面文案由国际化文件定义,例如 public/locales/en/common.json 中:
"opendtu": { "yieldDay": "Today", "absolutePower": "Power", "relativePower": "Power %", "limit": "Limit" }简体中文(public/locales/zh-Hans/common.json)对应为「今日 / 功率 / 功率 % / 限制」,Homepage 会依据界面语言自动切换。
relativePower 与 limit 的关键计算
组件中两个字段不是直接读取接口值,而是经过计算:
const totalLimit = opendtuData.inverters.map((inverter) => inverter.limit_absolute).reduce((a, b) => a + b); const totalLimitUnit = "W"; const powerPercentage = (power / totalLimit) * 100;limit(功率限制):对 OpenDTU 返回的inverters数组中每个逆变器的limit_absolute字段求和,即当前所有逆变器允许输出的总功率上限,单位固定为W;relativePower(功率百分比):用实时总功率除以该总限制再乘以 100,得到当前出力相对限制的百分比。
这一逻辑在 src/widgets/opendtu/component.test.jsx 中有明确验证:当接口返回total.YieldDay = { v: 12.4, u: "kWh" }、total.Power = { v: 250, u: "W" },且两台逆变器limit_absolute分别为 200 和 300 时,期望的渲染结果是:yieldDay = "12kWh"(12.4 四舍五入为 12)、relativePower = "50"(250 / 500 × 100)、absolutePower = "250W"、limit = "500W"。
数值格式化
所有数值在展示前都会通过t("common.number", { value: ..., style: "unit" })进行本地化格式化,其中relativePower额外指定unit: "percent"以百分比形式呈现。这意味着显示效果(如千分位分隔符)会跟随界面语言环境变化。
后端代理与数据校验流程
从源码结构看,一次 OpenDTU 数据请求的完整链路为:
- 前端组件
useWidgetAPI(src/utils/proxy/use-widget-api.js)向 Homepage 自身的代理 API 发起请求; - 后端
genericProxyHandler根据widget.type在 src/widgets/widgets.js 中查找api模板,将{url}替换为真实地址后通过httpProxy请求 OpenDTU 的/api/livedata/status; - 响应数据经过
validateWidgetData(src/utils/proxy/validate-widget-data.js)校验后返回给前端; - 组件解析
total与inverters字段并完成上述计算渲染。
其中validateWidgetData会尝试把响应解析为 JSON(Buffer 数据先JSON.parse,失败则去除空白字符后再解析一次),解析失败时返回{ error: "Invalid data" }并记录详细日志。这保证了 OpenDTU 返回异常数据(如设备离线、接口被篡改)时,前端组件能够通过错误分支渲染错误提示(widget.api_error),而不是白屏崩溃——这一点同样在 src/widgets/opendtu/component.test.jsx 的错误场景测试中得到覆盖。
实战:将 Widget 挂载到服务分组
OpenDTU Widget 可以像其他服务一样被组织进分组(group)与服务(service),一个完整的services.yaml示例:
- 能源监控: - OpenDTU 逆变器: icon: sh-solar-panel href: http://opendtu.host.or.ip description: 光伏微逆实时数据 widget: type: opendtu url: http://opendtu.host.or.ipicon、href、description是服务通用字段,与 Widget 无关,可按需配置;href通常指向 OpenDTU 的 Web 管理界面,方便点击跳转;widget块中的url是 Widget 数据请求的目标地址,与href可以相同,也可以指向内网可访问的其他地址。
注意事项
- OpenDTU 的
/api/livedata/status为只读状态接口,Widget 仅用于展示,不提供控制能力(如修改限功率),这与其「仪表盘」定位一致; - 若 OpenDTU 实例启用了访问密码,目前
genericProxyHandler支持在 Widget 配置中通过username/password字段自动附加 Basic Auth 头,但默认接口通常无需认证; - 组件将数值四舍五入取整后再拼接单位,因此小于 0.5 的今日发电量会显示为
0kWh之类的整数值,属预期行为; - 本文描述的接口路径、字段结构与计算逻辑均以当前仓库源码(widget.js、component.jsx 及其测试)为准,若 OpenDTU 侧接口结构发生变更,需要同步关注数据字段映射。
【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考