diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js
index 4488277a..cabe386a 100644
--- a/src/utils/config/service-helpers.js
+++ b/src/utils/config/service-helpers.js
@@ -299,6 +299,8 @@ export function cleanServiceGroups(groups) {
stream, // mjpeg
fit,
method, // openmediavault widget
+ mappings, // customapi widget
+ refreshInterval,
} = cleanedService.widget;
let fieldsList = fields;
@@ -372,6 +374,10 @@ export function cleanServiceGroups(groups) {
if (type === "openmediavault") {
if (method) cleanedService.widget.method = method;
}
+ if (type === "customapi") {
+ if (mappings) cleanedService.widget.mappings = mappings;
+ if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval;
+ }
}
return cleanedService;
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 4662a8c3..3b6485a9 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -14,6 +14,7 @@ const components = {
channelsdvrserver: dynamic(() => import("./channelsdvrserver/component")),
cloudflared: dynamic(() => import("./cloudflared/component")),
coinmarketcap: dynamic(() => import("./coinmarketcap/component")),
+ customapi: dynamic(() => import("./customapi/component")),
deluge: dynamic(() => import("./deluge/component")),
diskstation: dynamic(() => import("./diskstation/component")),
downloadstation: dynamic(() => import("./downloadstation/component")),
diff --git a/src/widgets/customapi/component.jsx b/src/widgets/customapi/component.jsx
new file mode 100644
index 00000000..7de3cac2
--- /dev/null
+++ b/src/widgets/customapi/component.jsx
@@ -0,0 +1,75 @@
+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";
+
+function getValue(field, data) {
+ let value = data;
+ let lastField = field;
+ let key = '';
+
+ while (typeof lastField === "object") {
+ key = Object.keys(lastField)[0] ?? null;
+
+ if (key === null) {
+ break;
+ }
+
+ value = value[key];
+ lastField = lastField[key];
+ }
+
+ if (typeof value === 'undefined') {
+ return null;
+ }
+
+ return value[lastField] ?? null;
+}
+
+function formatValue(t, mapping, value) {
+ switch (mapping?.format) {
+ case 'number':
+ return t("common.number", { value: parseInt(value, 10) });
+ case 'float':
+ return t("common.number", { value });
+ case 'percent':
+ return t("common.percent", { value });
+ case 'text':
+ default:
+ return value;
+ }
+}
+
+export default function Component({ service }) {
+ const { t } = useTranslation();
+
+ const { widget } = service;
+
+ const { mappings = [], refreshInterval = 10000 } = widget;
+ const { data: customData, error: customError } = useWidgetAPI(widget, null, {
+ refreshInterval: Math.max(1000, refreshInterval),
+ });
+
+ if (customError) {
+ return ;
+ }
+
+ if (!customData) {
+ return (
+
+ { mappings.slice(0,4).map(item => ) }
+
+ );
+ }
+
+ return (
+
+ { mappings.slice(0,4).map(mapping => ) }
+
+ );
+}
diff --git a/src/widgets/customapi/widget.js b/src/widgets/customapi/widget.js
new file mode 100644
index 00000000..9fdc21d9
--- /dev/null
+++ b/src/widgets/customapi/widget.js
@@ -0,0 +1,8 @@
+import genericProxyHandler from "utils/proxy/handlers/generic";
+
+const widget = {
+ api: "{url}",
+ proxyHandler: genericProxyHandler,
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index f17a4767..cbeeeaf3 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -11,6 +11,7 @@ import changedetectionio from "./changedetectionio/widget";
import channelsdvrserver from "./channelsdvrserver/widget";
import cloudflared from "./cloudflared/widget";
import coinmarketcap from "./coinmarketcap/widget";
+import customapi from "./customapi/widget";
import deluge from "./deluge/widget";
import diskstation from "./diskstation/widget";
import downloadstation from "./downloadstation/widget";
@@ -109,6 +110,7 @@ const widgets = {
channelsdvrserver,
cloudflared,
coinmarketcap,
+ customapi,
deluge,
diskstation,
downloadstation,