Files
frigate/web/src/hooks/use-stats.ts
T

175 lines
4.9 KiB
TypeScript
Raw Normal View History

import { FrigateConfig } from "@/types/frigateConfig";
import {
CameraDetectThreshold,
CameraFfmpegThreshold,
InferenceThreshold,
} from "@/types/graph";
import { FrigateStats, PotentialProblem } from "@/types/stats";
import { useMemo } from "react";
import useSWR from "swr";
2024-04-22 09:20:23 -05:00
import useDeepMemo from "./use-deep-memo";
2025-08-27 01:15:01 +08:00
import { capitalizeAll, capitalizeFirstLetter } from "@/utils/stringUtil";
2026-03-04 10:07:34 -06:00
import { isReplayCamera } from "@/utils/cameraUtil";
2024-05-26 15:49:12 -06:00
import { useFrigateStats } from "@/api/ws";
2026-04-09 21:53:17 -05:00
import { useIsAdmin } from "./use-is-admin";
import { useTranslation } from "react-i18next";
export default function useStats(stats: FrigateStats | undefined) {
const { t } = useTranslation(["views/system"]);
const { data: config } = useSWR<FrigateConfig>("config");
2026-04-09 21:53:17 -05:00
const isAdmin = useIsAdmin();
const { data: debugReplayStatus } = useSWR(
isAdmin ? "debug_replay/status" : null,
{
revalidateOnFocus: false,
},
);
2024-04-22 09:20:23 -05:00
const memoizedStats = useDeepMemo(stats);
const potentialProblems = useMemo<PotentialProblem[]>(() => {
const problems: PotentialProblem[] = [];
2024-04-22 09:20:23 -05:00
if (!memoizedStats) {
return problems;
}
2024-04-08 17:19:00 -06:00
// if frigate has just started
// don't look for issues
2024-04-22 09:20:23 -05:00
if (memoizedStats.service.uptime < 120) {
2024-04-08 17:19:00 -06:00
return problems;
}
2025-08-28 14:32:05 -05:00
// check shm level
const shm = memoizedStats.service.storage["/dev/shm"];
if (shm?.total && shm?.min_shm && shm.total < shm.min_shm) {
problems.push({
text: t("stats.shmTooLow", {
total: shm.total,
min: shm.min_shm,
}),
color: "text-danger",
relevantLink: "/system#storage",
});
}
// check detectors for high inference speeds
2024-04-22 09:20:23 -05:00
Object.entries(memoizedStats["detectors"]).forEach(([key, det]) => {
if (det["inference_speed"] > InferenceThreshold.error) {
problems.push({
2025-04-19 22:02:15 +08:00
text: t("stats.detectIsVerySlow", {
detect: capitalizeFirstLetter(key),
speed: det["inference_speed"],
}),
color: "text-danger",
relevantLink: "/system#general",
});
} else if (det["inference_speed"] > InferenceThreshold.warning) {
problems.push({
2025-04-19 22:02:15 +08:00
text: t("stats.detectIsSlow", {
detect: capitalizeFirstLetter(key),
speed: det["inference_speed"],
}),
color: "text-orange-400",
relevantLink: "/system#general",
});
}
});
// check for offline cameras
2024-04-22 09:20:23 -05:00
Object.entries(memoizedStats["cameras"]).forEach(([name, cam]) => {
if (!config) {
return;
}
2026-03-04 10:07:34 -06:00
// Skip replay cameras
if (isReplayCamera(name)) {
return;
}
2025-08-26 15:29:52 -05:00
const cameraName = config.cameras?.[name]?.friendly_name ?? name;
2024-05-18 14:19:32 -06:00
if (config.cameras[name].enabled && cam["camera_fps"] == 0) {
problems.push({
2025-04-19 22:02:15 +08:00
text: t("stats.cameraIsOffline", {
2025-08-27 01:15:01 +08:00
camera: capitalizeFirstLetter(capitalizeAll(cameraName)),
2025-04-19 22:02:15 +08:00
}),
color: "text-danger",
relevantLink: "logs",
});
}
});
// check camera cpu usages
2024-04-22 09:20:23 -05:00
Object.entries(memoizedStats["cameras"]).forEach(([name, cam]) => {
const ffmpegAvg = parseFloat(
2024-04-22 09:20:23 -05:00
memoizedStats["cpu_usages"][cam["ffmpeg_pid"]]?.cpu_average,
2024-04-01 09:23:57 -05:00
);
const detectAvg = parseFloat(
2024-04-22 09:20:23 -05:00
memoizedStats["cpu_usages"][cam["pid"]]?.cpu_average,
);
2025-08-26 15:29:52 -05:00
const cameraName = config?.cameras?.[name]?.friendly_name ?? name;
2026-03-04 10:07:34 -06:00
2026-03-15 07:26:23 -05:00
// Skip ffmpeg warnings for replay cameras
2026-03-04 10:07:34 -06:00
if (
!isNaN(ffmpegAvg) &&
ffmpegAvg >= CameraFfmpegThreshold.error &&
2026-03-15 07:26:23 -05:00
!isReplayCamera(name)
2026-03-04 10:07:34 -06:00
) {
problems.push({
text: t("stats.ffmpegHighCpuUsage", {
2025-08-27 01:15:01 +08:00
camera: capitalizeFirstLetter(capitalizeAll(cameraName)),
ffmpegAvg,
}),
color: "text-danger",
relevantLink: "/system#cameras",
});
}
if (!isNaN(detectAvg) && detectAvg >= CameraDetectThreshold.error) {
problems.push({
text: t("stats.detectHighCpuUsage", {
2025-08-27 01:15:01 +08:00
camera: capitalizeFirstLetter(capitalizeAll(cameraName)),
detectAvg,
}),
color: "text-danger",
relevantLink: "/system#cameras",
});
}
});
2026-03-04 10:07:34 -06:00
// Add message if debug replay is active
if (debugReplayStatus?.active) {
problems.push({
text: t("stats.debugReplayActive", {
defaultValue: "Debug replay session is active",
}),
color: "text-selected",
relevantLink: "/replay",
});
}
return problems;
2026-03-04 10:07:34 -06:00
}, [config, memoizedStats, t, debugReplayStatus]);
return { potentialProblems };
}
2024-05-26 15:49:12 -06:00
export function useAutoFrigateStats() {
const { data: initialStats } = useSWR<FrigateStats>("stats", {
revalidateOnFocus: false,
});
2024-07-11 09:25:33 -06:00
const latestStats = useFrigateStats();
2024-05-26 15:49:12 -06:00
const stats = useMemo(() => {
if (latestStats) {
return latestStats;
}
return initialStats;
}, [initialStats, latestStats]);
return stats;
}