diff --git a/docs/widgets/services/myspeed.md b/docs/widgets/services/myspeed.md
new file mode 100644
index 000000000..1790c9c11
--- /dev/null
+++ b/docs/widgets/services/myspeed.md
@@ -0,0 +1,15 @@
+---
+title: MySpeed
+description: MySpeed Widget Configuration
+---
+
+Learn more about [MySpeed](https://myspeed.dev/).
+
+Allowed fields: `["ping", "download", "upload"]`.
+
+```yaml
+widget:
+ type: myspeed
+ url: http://myspeed.host.or.ip:port
+ password: password # only required if password is set
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index b71960d88..a6a1f3d56 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -88,6 +88,7 @@ nav:
- widgets/services/mjpeg.md
- widgets/services/moonraker.md
- widgets/services/mylar.md
+ - widgets/services/myspeed.md
- widgets/services/navidrome.md
- widgets/services/netdata.md
- widgets/services/netalertx.md
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 3e582a4da..469b98bf4 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -888,5 +888,10 @@
"auth": "With Auth",
"outdated": "Outdated",
"banned": "Banned"
+ },
+ "myspeed": {
+ "ping": "Ping",
+ "download": "Download",
+ "upload": "Upload"
}
}
diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js
index de2111b12..82e79550a 100644
--- a/src/utils/proxy/handlers/credentialed.js
+++ b/src/utils/proxy/handlers/credentialed.js
@@ -69,6 +69,8 @@ export default async function credentialedProxyHandler(req, res, map) {
headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`;
} else if (widget.type === "plantit") {
headers.Key = `${widget.key}`;
+ } else if (widget.type === "myspeed") {
+ headers.Password = `${widget.password}`;
} else {
headers["X-API-Key"] = `${widget.key}`;
}
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 36b9de076..6b31b0778 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -62,6 +62,7 @@ const components = {
mjpeg: dynamic(() => import("./mjpeg/component")),
moonraker: dynamic(() => import("./moonraker/component")),
mylar: dynamic(() => import("./mylar/component")),
+ myspeed: dynamic(() => import("./myspeed/component")),
navidrome: dynamic(() => import("./navidrome/component")),
netalertx: dynamic(() => import("./netalertx/component")),
netdata: dynamic(() => import("./netdata/component")),
diff --git a/src/widgets/myspeed/component.jsx b/src/widgets/myspeed/component.jsx
new file mode 100644
index 000000000..e4c481c7f
--- /dev/null
+++ b/src/widgets/myspeed/component.jsx
@@ -0,0 +1,60 @@
+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, error } = useWidgetAPI(widget, "info");
+
+ if (error || (data && data.message) || (data && data[0] && data[0].error)) {
+ let finalError = error ?? data;
+ if (data && data[0] && data[0].error) {
+ try {
+ finalError = JSON.parse(data[0].error);
+ } catch (e) {
+ finalError = data[0].error;
+ }
+ }
+ return ;
+ }
+
+ if (!data || (data && data.length === 0)) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/widgets/myspeed/widget.js b/src/widgets/myspeed/widget.js
new file mode 100644
index 000000000..c9cf38565
--- /dev/null
+++ b/src/widgets/myspeed/widget.js
@@ -0,0 +1,14 @@
+import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
+
+const widget = {
+ api: "{url}/api/{endpoint}",
+ proxyHandler: credentialedProxyHandler,
+
+ mappings: {
+ info: {
+ endpoint: "speedtests?limit=1",
+ },
+ },
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index 950954b03..da29dcc9e 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -54,6 +54,7 @@ import mikrotik from "./mikrotik/widget";
import mjpeg from "./mjpeg/widget";
import moonraker from "./moonraker/widget";
import mylar from "./mylar/widget";
+import myspeed from "./myspeed/widget";
import navidrome from "./navidrome/widget";
import netalertx from "./netalertx/widget";
import netdata from "./netdata/widget";
@@ -172,6 +173,7 @@ const widgets = {
mjpeg,
moonraker,
mylar,
+ myspeed,
navidrome,
netalertx,
netdata,