diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 9a6da7a4..16648977 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -529,6 +529,12 @@
"streams_active": "Active Streams",
"streams_xepg": "XEPG Channels"
},
+ "opendtu": {
+ "yieldDay": "Today",
+ "absolutePower": "Power",
+ "relativePower": "Power %",
+ "limit": "Limit"
+ },
"opnsense": {
"cpu": "CPU Load",
"memory": "Active Memory",
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 4ed79044..66044406 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -61,6 +61,7 @@ const components = {
octoprint: dynamic(() => import("./octoprint/component")),
omada: dynamic(() => import("./omada/component")),
ombi: dynamic(() => import("./ombi/component")),
+ opendtu: dynamic(() => import("./opendtu/component")),
opnsense: dynamic(() => import("./opnsense/component")),
overseerr: dynamic(() => import("./overseerr/component")),
openmediavault: dynamic(() => import("./openmediavault/component")),
diff --git a/src/widgets/opendtu/component.jsx b/src/widgets/opendtu/component.jsx
new file mode 100644
index 00000000..bd7c9fd8
--- /dev/null
+++ b/src/widgets/opendtu/component.jsx
@@ -0,0 +1,48 @@
+import { useTranslation } from "next-i18next";
+
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
+import useWidgetAPI from "utils/proxy/use-widget-api";
+
+export default function Component({ service }) {
+ const { t } = useTranslation();
+
+ const { widget } = service;
+
+ const { data: opendtuData, error: opendtuError } = useWidgetAPI(widget);
+
+ if (opendtuError) {
+ return ;
+ }
+
+ if (!opendtuData) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ const yieldDay = opendtuData.total.YieldDay.v
+ const yieldDayUnit = opendtuData.total.YieldDay.u
+
+ const power = opendtuData.total.Power.v
+ const powerUnit = opendtuData.total.Power.u
+
+ const totalLimit = opendtuData.inverters.map(inverter => inverter.limit_absolute).reduce((a,b) => a+b);
+ const totalLimitUnit = "W"
+
+ const powerPercentage = (power / totalLimit) * 100
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/widgets/opendtu/widget.js b/src/widgets/opendtu/widget.js
new file mode 100644
index 00000000..f0ac2cc6
--- /dev/null
+++ b/src/widgets/opendtu/widget.js
@@ -0,0 +1,8 @@
+import genericProxyHandler from "utils/proxy/handlers/generic";
+
+const widget = {
+ api: "{url}/api/livedata/status",
+ proxyHandler: genericProxyHandler,
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index a2b97dd3..6bc682da 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -55,6 +55,7 @@ import nzbget from "./nzbget/widget";
import octoprint from "./octoprint/widget";
import omada from "./omada/widget";
import ombi from "./ombi/widget";
+import opendtu from "./opendtu/widget";
import opnsense from "./opnsense/widget";
import overseerr from "./overseerr/widget";
import openmediavault from "./openmediavault/widget";
@@ -156,6 +157,7 @@ const widgets = {
octoprint,
omada,
ombi,
+ opendtu,
opnsense,
overseerr,
openmediavault,