diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 76dd0d25e..cc7674f02 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -727,5 +727,19 @@
"stopped": "Stopped",
"passed": "Passed",
"failed": "Failed"
+ },
+ "uptimerobot": {
+ "status": "Status",
+ "uptime": "Uptime",
+ "lastDown": "Last Downtime",
+ "downDuration": "Downtime Duration",
+ "sitesUp": "Sites Up",
+ "sitesDown": "Sites Down",
+ "paused": "Paused",
+ "notyetchecked": "Not Yet Checked",
+ "up": "Up",
+ "seemsdown": "Seems Down",
+ "down": "Down",
+ "unknown": "Unknown"
}
}
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 3b6485a93..4ed79044b 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -97,6 +97,7 @@ const components = {
unifi: dynamic(() => import("./unifi/component")),
unmanic: dynamic(() => import("./unmanic/component")),
uptimekuma: dynamic(() => import("./uptimekuma/component")),
+ uptimerobot: dynamic(() => import("./uptimerobot/component")),
urbackup: dynamic(() => import("./urbackup/component")),
watchtower: dynamic(() => import("./watchtower/component")),
whatsupdocker: dynamic(() => import("./whatsupdocker/component")),
diff --git a/src/widgets/uptimerobot/component.jsx b/src/widgets/uptimerobot/component.jsx
new file mode 100644
index 000000000..3f04901c7
--- /dev/null
+++ b/src/widgets/uptimerobot/component.jsx
@@ -0,0 +1,99 @@
+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 secondsToDhms(seconds) {
+ const d = Math.floor(seconds / (3600*24));
+ const h = Math.floor(seconds % (3600*24) / 3600);
+ const m = Math.floor(seconds % 3600 / 60);
+ const s = Math.floor(seconds % 60);
+
+ const dDisplay = d > 0 ? d + (d === 1 ? " day, " : " days, ") : "";
+ const hDisplay = h > 0 ? h + (h === 1 ? " hr, " : " hrs, ") : "";
+ let mDisplay = m > 0 && d === 0 ? m + (m === 1 ? " min" : " mins") : "";
+ let sDisplay = "";
+
+ if (d === 0 && h === 0) {
+ mDisplay = m > 0 ? m + (m === 1 ? " min, " : " mins, ") : "";
+ sDisplay = s > 0 ? s + (s === 1 ? " sec" : " secs") : "";
+ }
+ return (dDisplay + hDisplay + mDisplay + sDisplay).replace(/,\s*$/, "");
+}
+
+export default function Component({ service }) {
+ const { widget } = service;
+ const { t } = useTranslation();
+
+ const { data: uptimerobotData, error: uptimerobotError } = useWidgetAPI(widget, "getmonitors");
+
+ if (uptimerobotError) {
+ return ;
+ }
+
+ if (!uptimerobotData) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ // multiple monitors
+ if (uptimerobotData.pagination?.total > 1) {
+ const sitesUp = uptimerobotData.monitors.filter(m => m.status === 2).length;
+
+ return (
+
+
+
+
+ );
+ }
+
+ // single monitor
+ const monitor = uptimerobotData.monitors[0];
+ let status;
+ let uptime = 0;
+ let logIndex = 0;
+
+ switch (monitor.status) {
+ case 0:
+ status = t("uptimerobot.paused");
+ break;
+ case 1:
+ status = t("uptimerobot.notyetchecked");
+ break;
+ case 2:
+ status = t("uptimerobot.up");
+ uptime = secondsToDhms(monitor.logs[0].duration);
+ logIndex = 1;
+ break;
+ case 8:
+ status = t("uptimerobot.seemsdown");
+ break;
+ case 9:
+ status = t("uptimerobot.down");
+ break;
+ default:
+ status = t("uptimerobot.unknown");
+ break;
+ }
+
+ const lastDown = new Date(monitor.logs[logIndex].datetime * 1000).toLocaleString();
+ const downDuration = secondsToDhms(monitor.logs[logIndex].duration);
+ const hideDown = logIndex === 1 && monitor.logs[logIndex].type !== 1;
+
+ return (
+
+
+
+ {!hideDown && }
+ {!hideDown && }
+
+ );
+}
diff --git a/src/widgets/uptimerobot/widget.js b/src/widgets/uptimerobot/widget.js
new file mode 100644
index 000000000..a56cdc63e
--- /dev/null
+++ b/src/widgets/uptimerobot/widget.js
@@ -0,0 +1,20 @@
+import genericProxyHandler from "utils/proxy/handlers/generic";
+
+const widget = {
+ api: "{url}/v2/{endpoint}?api_key={key}",
+ proxyHandler: genericProxyHandler,
+
+ mappings: {
+ getmonitors: {
+ method: "POST",
+ endpoint: "getMonitors",
+ body: 'format=json&logs=1',
+ headers: {
+ "content-type": "application/x-www-form-urlencoded",
+ "cache-control": "no-cache"
+ },
+ },
+ },
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index cbeeeaf30..a2b97dd37 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -91,6 +91,7 @@ import truenas from "./truenas/widget";
import unifi from "./unifi/widget";
import unmanic from "./unmanic/widget";
import uptimekuma from "./uptimekuma/widget";
+import uptimerobot from "./uptimerobot/widget";
import watchtower from "./watchtower/widget";
import whatsupdocker from "./whatsupdocker/widget";
import xteve from "./xteve/widget";
@@ -192,6 +193,7 @@ const widgets = {
unifi_console: unifi,
unmanic,
uptimekuma,
+ uptimerobot,
urbackup,
watchtower,
whatsupdocker,