diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index d53d480c7..fb5566c07 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -1,216 +1,220 @@
{
- "common": {
- "bytes": "{{value, bytes}}",
- "bits": "{{value, bytes(bits: true)}}",
- "bbytes": "{{value, bytes(binary: true)}}",
- "bbits": "{{value, bytes(bits: true, binary: true)}}",
- "byterate": "{{value, rate}}",
- "bitrate": "{{value, rate(bits: true)}}",
- "percent": "{{value, percent}}",
- "number": "{{value, number}}",
- "ms": "{{value, number}}"
- },
- "widget": {
- "missing_type": "Missing Widget Type: {{type}}",
- "api_error": "API Error",
- "status": "Status"
- },
- "weather": {
- "current": "Current Location",
- "allow": "Click to allow",
- "updating": "Updating",
- "wait": "Please wait"
- },
- "search": {
- "placeholder": "Search…"
- },
- "resources": {
- "cpu": "CPU",
- "total": "Total",
- "free": "Free",
- "used": "Used",
- "load": "Load"
- },
- "unifi": {
- "users": "Users",
- "uptime": "System Uptime",
- "days": "Days",
- "wan": "WAN",
- "lan_users": "LAN Users",
- "wlan_users": "WLAN Users",
- "up": "UP",
- "down": "DOWN",
- "wait": "Please wait"
- },
- "docker": {
- "rx": "RX",
- "tx": "TX",
- "mem": "MEM",
- "cpu": "CPU",
- "offline": "Offline"
- },
- "emby": {
- "playing": "Playing",
- "transcoding": "Transcoding",
- "bitrate": "Bitrate",
- "no_active": "No Active Streams"
- },
- "tautulli": {
- "playing": "Playing",
- "transcoding": "Transcoding",
- "bitrate": "Bitrate",
- "no_active": "No Active Streams"
- },
- "nzbget": {
- "rate": "Rate",
- "remaining": "Remaining",
- "downloaded": "Downloaded"
- },
- "plex": {
- "streams": "Active Streams",
- "movies": "Movies",
- "tv": "TV Shows"
- },
- "sabnzbd": {
- "rate": "Rate",
- "queue": "Queue",
- "timeleft": "Time Left"
- },
- "rutorrent": {
- "active": "Active",
- "upload": "Upload",
- "download": "Download"
- },
- "transmission": {
- "download": "Download",
- "upload": "Upload",
- "leech": "Leech",
- "seed": "Seed"
- },
- "qbittorrent": {
- "download": "Download",
- "upload": "Upload",
- "leech": "Leech",
- "seed": "Seed"
- },
- "sonarr": {
- "wanted": "Wanted",
- "queued": "Queued",
- "series": "Series"
- },
- "radarr": {
- "wanted": "Wanted",
- "queued": "Queued",
- "movies": "Movies"
- },
- "lidarr": {
- "wanted": "Wanted",
- "queued": "Queued",
- "albums": "Albums"
- },
- "readarr": {
- "wanted": "Wanted",
- "queued": "Queued",
- "books": "Books"
- },
- "bazarr": {
- "missingEpisodes": "Missing Episodes",
- "missingMovies": "Missing Movies"
- },
- "ombi": {
- "pending": "Pending",
- "approved": "Approved",
- "available": "Available"
- },
- "jellyseerr": {
- "pending": "Pending",
- "approved": "Approved",
- "available": "Available"
- },
- "overseerr": {
- "pending": "Pending",
- "approved": "Approved",
- "available": "Available"
- },
- "pihole": {
- "queries": "Queries",
- "blocked": "Blocked",
- "gravity": "Gravity"
- },
- "adguard": {
- "queries": "Queries",
- "blocked": "Blocked",
- "filtered": "Filtered",
- "latency": "Latency"
- },
- "speedtest": {
- "upload": "Upload",
- "download": "Download",
- "ping": "Ping"
- },
- "portainer": {
- "running": "Running",
- "stopped": "Stopped",
- "total": "Total"
- },
- "traefik": {
- "routers": "Routers",
- "services": "Services",
- "middleware": "Middleware"
- },
- "npm": {
- "enabled": "Enabled",
- "disabled": "Disabled",
- "total": "Total"
- },
- "coinmarketcap": {
- "configure": "Configure one or more crypto currencies to track",
- "1hour": "1 Hour",
- "1day": "1 Day",
- "7days": "7 Days",
- "30days": "30 Days"
- },
- "gotify": {
- "apps": "Applications",
- "clients": "Clients",
- "messages": "Messages"
- },
- "prowlarr":{
- "enableIndexers": "Indexers",
- "numberOfGrabs": "Grabs",
- "numberOfQueries": "Queries",
- "numberOfFailGrabs": "Fail Grabs",
- "numberOfFailQueries": "Fail Queries"
- },
- "jackett": {
- "configured": "Configured",
- "errored": "Errored"
- },
- "strelaysrv": {
- "numActiveSessions": "Sessions",
- "numConnections": "Connections",
- "dataRelayed": "Relayed",
- "transferRate": "Rate"
- },
- "mastodon": {
- "user_count": "Users",
- "status_count": "Posts",
- "domain_count": "Domains"
- },
- "authentik": {
- "users": "Users",
- "loginsLast24H": "Logins (24h)",
- "failedLoginsLast24H": "Failed Logins (24h)"
- },
- "proxmox": {
- "mem": "MEM",
- "cpu": "CPU",
- "lxc": "LXC",
- "vms": "VMs"
- },
- "glances": {
- "cpu": "CPU",
- "mem": "MEM",
- "wait": "Please wait"
- }
+ "common": {
+ "bytes": "{{value, bytes}}",
+ "bits": "{{value, bytes(bits: true)}}",
+ "bbytes": "{{value, bytes(binary: true)}}",
+ "bbits": "{{value, bytes(bits: true, binary: true)}}",
+ "byterate": "{{value, rate}}",
+ "bitrate": "{{value, rate(bits: true)}}",
+ "percent": "{{value, percent}}",
+ "number": "{{value, number}}",
+ "ms": "{{value, number}}"
+ },
+ "widget": {
+ "missing_type": "Missing Widget Type: {{type}}",
+ "api_error": "API Error",
+ "status": "Status"
+ },
+ "weather": {
+ "current": "Current Location",
+ "allow": "Click to allow",
+ "updating": "Updating",
+ "wait": "Please wait"
+ },
+ "search": {
+ "placeholder": "Search…"
+ },
+ "resources": {
+ "cpu": "CPU",
+ "total": "Total",
+ "free": "Free",
+ "used": "Used",
+ "load": "Load"
+ },
+ "unifi": {
+ "users": "Users",
+ "uptime": "System Uptime",
+ "days": "Days",
+ "wan": "WAN",
+ "lan_users": "LAN Users",
+ "wlan_users": "WLAN Users",
+ "up": "UP",
+ "down": "DOWN",
+ "wait": "Please wait"
+ },
+ "docker": {
+ "rx": "RX",
+ "tx": "TX",
+ "mem": "MEM",
+ "cpu": "CPU",
+ "offline": "Offline"
+ },
+ "emby": {
+ "playing": "Playing",
+ "transcoding": "Transcoding",
+ "bitrate": "Bitrate",
+ "no_active": "No Active Streams"
+ },
+ "changedetectionio": {
+ "totalObserved": "Total Observed",
+ "diffsDetected": "Diffs Detected"
+ },
+ "tautulli": {
+ "playing": "Playing",
+ "transcoding": "Transcoding",
+ "bitrate": "Bitrate",
+ "no_active": "No Active Streams"
+ },
+ "nzbget": {
+ "rate": "Rate",
+ "remaining": "Remaining",
+ "downloaded": "Downloaded"
+ },
+ "plex": {
+ "streams": "Active Streams",
+ "movies": "Movies",
+ "tv": "TV Shows"
+ },
+ "sabnzbd": {
+ "rate": "Rate",
+ "queue": "Queue",
+ "timeleft": "Time Left"
+ },
+ "rutorrent": {
+ "active": "Active",
+ "upload": "Upload",
+ "download": "Download"
+ },
+ "transmission": {
+ "download": "Download",
+ "upload": "Upload",
+ "leech": "Leech",
+ "seed": "Seed"
+ },
+ "qbittorrent": {
+ "download": "Download",
+ "upload": "Upload",
+ "leech": "Leech",
+ "seed": "Seed"
+ },
+ "sonarr": {
+ "wanted": "Wanted",
+ "queued": "Queued",
+ "series": "Series"
+ },
+ "radarr": {
+ "wanted": "Wanted",
+ "queued": "Queued",
+ "movies": "Movies"
+ },
+ "lidarr": {
+ "wanted": "Wanted",
+ "queued": "Queued",
+ "albums": "Albums"
+ },
+ "readarr": {
+ "wanted": "Wanted",
+ "queued": "Queued",
+ "books": "Books"
+ },
+ "bazarr": {
+ "missingEpisodes": "Missing Episodes",
+ "missingMovies": "Missing Movies"
+ },
+ "ombi": {
+ "pending": "Pending",
+ "approved": "Approved",
+ "available": "Available"
+ },
+ "jellyseerr": {
+ "pending": "Pending",
+ "approved": "Approved",
+ "available": "Available"
+ },
+ "overseerr": {
+ "pending": "Pending",
+ "approved": "Approved",
+ "available": "Available"
+ },
+ "pihole": {
+ "queries": "Queries",
+ "blocked": "Blocked",
+ "gravity": "Gravity"
+ },
+ "adguard": {
+ "queries": "Queries",
+ "blocked": "Blocked",
+ "filtered": "Filtered",
+ "latency": "Latency"
+ },
+ "speedtest": {
+ "upload": "Upload",
+ "download": "Download",
+ "ping": "Ping"
+ },
+ "portainer": {
+ "running": "Running",
+ "stopped": "Stopped",
+ "total": "Total"
+ },
+ "traefik": {
+ "routers": "Routers",
+ "services": "Services",
+ "middleware": "Middleware"
+ },
+ "npm": {
+ "enabled": "Enabled",
+ "disabled": "Disabled",
+ "total": "Total"
+ },
+ "coinmarketcap": {
+ "configure": "Configure one or more crypto currencies to track",
+ "1hour": "1 Hour",
+ "1day": "1 Day",
+ "7days": "7 Days",
+ "30days": "30 Days"
+ },
+ "gotify": {
+ "apps": "Applications",
+ "clients": "Clients",
+ "messages": "Messages"
+ },
+ "prowlarr": {
+ "enableIndexers": "Indexers",
+ "numberOfGrabs": "Grabs",
+ "numberOfQueries": "Queries",
+ "numberOfFailGrabs": "Fail Grabs",
+ "numberOfFailQueries": "Fail Queries"
+ },
+ "jackett": {
+ "configured": "Configured",
+ "errored": "Errored"
+ },
+ "strelaysrv": {
+ "numActiveSessions": "Sessions",
+ "numConnections": "Connections",
+ "dataRelayed": "Relayed",
+ "transferRate": "Rate"
+ },
+ "mastodon": {
+ "user_count": "Users",
+ "status_count": "Posts",
+ "domain_count": "Domains"
+ },
+ "authentik": {
+ "users": "Users",
+ "loginsLast24H": "Logins (24h)",
+ "failedLoginsLast24H": "Failed Logins (24h)"
+ },
+ "proxmox": {
+ "mem": "MEM",
+ "cpu": "CPU",
+ "lxc": "LXC",
+ "vms": "VMs"
+ },
+ "glances": {
+ "cpu": "CPU",
+ "mem": "MEM",
+ "wait": "Please wait"
+ }
}
diff --git a/src/widgets/changedetectionio/component.jsx b/src/widgets/changedetectionio/component.jsx
new file mode 100644
index 000000000..70936489f
--- /dev/null
+++ b/src/widgets/changedetectionio/component.jsx
@@ -0,0 +1,33 @@
+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 } = useWidgetAPI(widget, "info");
+
+ if (!data) {
+ return ;
+ }
+
+ const totalObserved = Object.keys(data).length;
+ let diffsDetected = 0;
+
+ Object.keys(data).forEach((key) => {
+ if (data[key].last_checked === data[key].last_changed) {
+ diffsDetected += 1;
+ }
+ });
+
+ return (
+
+
+
+
+ );
+}
diff --git a/src/widgets/changedetectionio/widget.js b/src/widgets/changedetectionio/widget.js
new file mode 100644
index 000000000..ed1153527
--- /dev/null
+++ b/src/widgets/changedetectionio/widget.js
@@ -0,0 +1,15 @@
+import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
+
+const widget = {
+ api: "{url}/api/v1/{endpoint}",
+ proxyHandler: credentialedProxyHandler,
+
+ mappings: {
+ info: {
+ method: "GET",
+ endpoint: "watch",
+ },
+ },
+};
+
+export default widget;
diff --git a/src/widgets/components.js b/src/widgets/components.js
index f6e075d8a..da6ce362d 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -4,6 +4,7 @@ const components = {
adguard: dynamic(() => import("./adguard/component")),
authentik: dynamic(() => import("./authentik/component")),
bazarr: dynamic(() => import("./bazarr/component")),
+ changedetectionio: dynamic(() => import("./changedetectionio/component")),
coinmarketcap: dynamic(() => import("./coinmarketcap/component")),
docker: dynamic(() => import("./docker/component")),
emby: dynamic(() => import("./emby/component")),
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index cd7f5b19a..a90bd6202 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -1,6 +1,7 @@
import adguard from "./adguard/widget";
import authentik from "./authentik/widget";
import bazarr from "./bazarr/widget";
+import changedetectionio from "./changedetectionio/widget";
import coinmarketcap from "./coinmarketcap/widget";
import emby from "./emby/widget";
import gotify from "./gotify/widget";
@@ -34,6 +35,7 @@ const widgets = {
adguard,
authentik,
bazarr,
+ changedetectionio,
coinmarketcap,
emby,
gotify,
@@ -63,7 +65,7 @@ const widgets = {
traefik,
transmission,
unifi,
- unifi_console: unifi
+ unifi_console: unifi,
};
export default widgets;