news 2026/9/11 18:02:28

Homepage 集成 OpenDTU 光伏逆变器 Widget:配置、数据字段与源码实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Homepage 集成 OpenDTU 光伏逆变器 Widget:配置、数据字段与源码实现解析

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.ip
  • type:固定为opendtu,用于在 src/widgets/widgets.js 中注册的 Widget 映射表中查找对应实现(第 255 行将opendtu模块注册进 Widget 列表)。
  • url:OpenDTU 设备的 HTTP 访问地址,可以是主机名或 IP,例如http://opendtu.localhttp://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;

它只做两件事:

  1. 声明 API 地址模板为{url}/api/livedata/status,其中{url}会被运行时替换为你在配置中填写的url值;
  2. 指定使用通用代理处理器genericProxyHandler(src/utils/proxy/handlers/generic.js)完成服务端转发请求。

也就是说,Homepage 前端不会直接请求 OpenDTU,而是由后端代理统一发起 HTTP 请求。这样设计的好处包括:避免浏览器跨域(CORS)问题、可以在服务端统一注入请求头(如 Basic Auth 鉴权,genericProxyHandler会在配置了username/password时自动生成Authorization: Basic ...头),并能对响应数据做集中校验与日志记录。

四个展示字段的语义与计算逻辑

组件实现位于 src/widgets/opendtu/component.jsx,数据加载使用useWidgetAPIhook。组件在加载完成前会先渲染四个占位块(yieldDayrelativePowerabsolutePowerlimit),数据到达后按以下逻辑填充:

字段 key界面显示名(en / zh-Hans)数据来源展示规则
yieldDayToday / 今日data.total.YieldDay.v四舍五入取整,单位取data.total.YieldDay.u拼接,如12kWh
absolutePowerPower / 功率data.total.Power.v四舍五入取整,单位取data.total.Power.u拼接,如250W
relativePowerPower % / 功率 %计算得出:Power / totalLimit * 100以百分比格式展示,如50(即 50%)
limitLimit / 限制计算得出:所有逆变器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 数据请求的完整链路为:

  1. 前端组件useWidgetAPI(src/utils/proxy/use-widget-api.js)向 Homepage 自身的代理 API 发起请求;
  2. 后端genericProxyHandler根据widget.type在 src/widgets/widgets.js 中查找api模板,将{url}替换为真实地址后通过httpProxy请求 OpenDTU 的/api/livedata/status
  3. 响应数据经过validateWidgetData(src/utils/proxy/validate-widget-data.js)校验后返回给前端;
  4. 组件解析totalinverters字段并完成上述计算渲染。

其中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.ip
  • iconhrefdescription是服务通用字段,与 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 17:59:23

yao-meta-skill - artifact-design-doctrine

工件设计原则 当技能产生面向用户的工件时使用此层:HTML 报告、Markdown 教程、审查查看器、仪表板、截图、表格、幻灯片式页面或生成的技能概览页。 原则 输出质量是技能质量的一部分。生成的技能不仅应该知道要做什么;还应该知道其最终工件应如何阅读、…

作者头像 李华
网站建设 2026/9/11 17:59:21

xvary-stock-research - nvda-analysis

示例:/analyze NVDA 示例性技能输出格式。以下指标由公开的 EDGAR 市场快照生成,应视为研究背景,而非投资建议。 裁决 建设性(信念度:74/100) NVDA 被筛选为具有卓越经营杠杆的高质量复利增长公司&#…

作者头像 李华
网站建设 2026/9/11 17:54:13

AI 代码占比 40% 之后,我把团队的 Code Review 规范推翻重写了

AI 代码占比 40% 之后,我把团队的 Code Review 规范推翻重写了 上个月组里出了个不大不小的线上事故。一个跑了半年的积分服务,某天凌晨开始线程池打满,接口大面积超时。接手的同事查了两天,日志、监控、heap dump 翻了个遍&…

作者头像 李华
网站建设 2026/9/11 17:53:42

Spark ALS协同过滤推荐系统毕设实战:从CSV数据到Java Web部署

简介:Java毕业设计基于Spark的餐饮平台菜品智能分析推荐系统源码与数据库,面向计算机相关专业毕业生、课程设计与期末大作业学生;系统围绕餐饮菜品数据,基于Spark进行智能分析与推荐,涵盖用户菜品评分数据、推荐算法逻…

作者头像 李华