diff --git a/docs/widgets/services/frigate.md b/docs/widgets/services/frigate.md new file mode 100644 index 000000000..411c3cc79 --- /dev/null +++ b/docs/widgets/services/frigate.md @@ -0,0 +1,17 @@ +--- +title: Frigate +description: Frigate Widget Configuration +--- + +Learn more about [Frigate](https://frigate.video/). + +Allowed fields: `["cameras", "uptime", "version"]`. + +A recent event listing is disabled by default, but can be enabled with the `enableRecentEvents` option. + +```yaml +widget: + type: frigate + url: http://frigate.host.or.ip:port + enableRecentEvents: true # Optional, defaults to false +``` diff --git a/docs/widgets/services/index.md b/docs/widgets/services/index.md index dd2db6de5..e0502488b 100644 --- a/docs/widgets/services/index.md +++ b/docs/widgets/services/index.md @@ -30,6 +30,7 @@ You can also find a list of all available service widgets in the sidebar navigat - [Fileflows](fileflows.md) - [Flood](flood.md) - [FreshRSS](freshrss.md) +- [Frigate](frigate.md) - [Fritz!Box](fritzbox.md) - [GameDig](gamedig.md) - [Gatus](gatus.md) diff --git a/mkdocs.yml b/mkdocs.yml index a539db6e4..4d6524b31 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -55,6 +55,7 @@ nav: - widgets/services/fileflows.md - widgets/services/flood.md - widgets/services/freshrss.md + - widgets/services/frigate.md - widgets/services/fritzbox.md - widgets/services/gamedig.md - widgets/services/gatus.md diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c0314d4e5..28c548953 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -900,5 +900,10 @@ "open": "Open - US Market", "closed": "Closed - US Market", "invalidConfiguration": "Invalid Configuration" + }, + "frigate": { + "cameras": "Cameras", + "uptime": "Uptime", + "version": "Version" } } diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index 75d31add0..03b4507ea 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -399,6 +399,9 @@ export function cleanServiceGroups(groups) { expandOneStreamToTwoRows, showEpisodeNumber, + // frigate + enableRecentEvents, + // glances, pihole, pfsense version, @@ -614,6 +617,9 @@ export function cleanServiceGroups(groups) { if (type === "wgeasy") { if (threshold !== undefined) cleanedService.widget.threshold = parseInt(threshold, 10); } + if (type === "frigate") { + if (enableRecentEvents !== undefined) cleanedService.widget.enableRecentEvents = enableRecentEvents; + } } return cleanedService; diff --git a/src/widgets/components.js b/src/widgets/components.js index 215de0421..341f5211d 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -29,6 +29,7 @@ const components = { fileflows: dynamic(() => import("./fileflows/component")), flood: dynamic(() => import("./flood/component")), freshrss: dynamic(() => import("./freshrss/component")), + frigate: dynamic(() => import("./frigate/component")), fritzbox: dynamic(() => import("./fritzbox/component")), gamedig: dynamic(() => import("./gamedig/component")), gatus: dynamic(() => import("./gatus/component")), diff --git a/src/widgets/frigate/component.jsx b/src/widgets/frigate/component.jsx new file mode 100644 index 000000000..43b566e8e --- /dev/null +++ b/src/widgets/frigate/component.jsx @@ -0,0 +1,70 @@ +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, "stats"); + const { data: eventsData, error: eventsError } = useWidgetAPI(widget, "events"); + + if (error) { + return ; + } + + if (eventsError) { + return ; + } + + if (!data || !eventsData) { + return ( + + + + + + ); + } + + return ( + <> + + + + + + {widget.enableRecentEvents && + eventsData?.map((event) => ( +
+
+
+ {event.camera} ({event.label} {t("common.percent", { value: event.score * 100 })}) +
+
+
+ {t("common.date", { + value: event.start_time, + formatParams: { value: { timeStyle: "short", dateStyle: "medium" } }, + })} +
+
+ ))} + + ); +} diff --git a/src/widgets/frigate/widget.js b/src/widgets/frigate/widget.js new file mode 100644 index 000000000..62327c4e9 --- /dev/null +++ b/src/widgets/frigate/widget.js @@ -0,0 +1,38 @@ +import { asJson } from "utils/proxy/api-helpers"; +import genericProxyHandler from "utils/proxy/handlers/generic"; + +const widget = { + api: "{url}/api/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + stats: { + endpoint: "stats", + map: (data) => { + const jsonData = asJson(data); + return { + num_cameras: jsonData?.cameras !== undefined ? Object.keys(jsonData?.cameras).length : 0, + uptime: jsonData?.service?.uptime, + version: jsonData?.service.version, + }; + }, + }, + events: { + endpoint: "events", + map: (data) => + asJson(data) + .slice(0, 5) + .map((event) => ({ + id: event.id, + camera: event.camera, + label: event.label, + start_time: new Date(event.start_time * 1000), + thumbnail: event.thumbnail, + score: event.data.score, + type: event.data.type, + })), + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index c053e6c1b..a72a4126d 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -23,6 +23,7 @@ import evcc from "./evcc/widget"; import fileflows from "./fileflows/widget"; import flood from "./flood/widget"; import freshrss from "./freshrss/widget"; +import frigate from "./frigate/widget"; import fritzbox from "./fritzbox/widget"; import gamedig from "./gamedig/widget"; import gatus from "./gatus/widget"; @@ -141,6 +142,7 @@ const widgets = { fileflows, flood, freshrss, + frigate, fritzbox, gamedig, gatus,