diff --git a/frigate/http.py b/frigate/http.py index a4bc5ffc1..f2731ec2f 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -616,8 +616,8 @@ def event_preview(id: str, max_cache_age=2592000): ) start_ts = event.start_time - end_ts = ( - start_ts + min(event.end_time - event.start_time, 20) if event.end_time else 20 + end_ts = start_ts + ( + min(event.end_time - event.start_time, 20) if event.end_time else 20 ) if datetime.fromtimestamp(event.start_time) < datetime.now().replace( diff --git a/frigate/util/services.py b/frigate/util/services.py index fa8622210..b9b8ceace 100644 --- a/frigate/util/services.py +++ b/frigate/util/services.py @@ -103,8 +103,17 @@ def get_cpu_stats() -> dict[str, dict]: docker_memlimit = get_docker_memlimit_bytes() / 1024 total_mem = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") / 1024 + system_cpu = psutil.cpu_percent( + interval=None + ) # no interval as we don't want to be blocking + system_mem = psutil.virtual_memory() + usages["frigate.full_system"] = { + "cpu": str(system_cpu), + "mem": str(system_mem.percent), + } + for process in psutil.process_iter(["pid", "name", "cpu_percent", "cmdline"]): - pid = process.info["pid"] + pid = str(process.info["pid"]) try: cpu_percent = process.info["cpu_percent"] cmdline = process.info["cmdline"] diff --git a/web/src/App.tsx b/web/src/App.tsx index 2ad151c0f..af3483004 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -15,6 +15,8 @@ import NoMatch from "@/pages/NoMatch"; import Settings from "@/pages/Settings"; import UIPlayground from "./pages/UIPlayground"; import Events from "./pages/Events"; +import { isDesktop } from "react-device-detect"; +import Statusbar from "./components/Statusbar"; function App() { const [sheetOpen, setSheetOpen] = useState(false); @@ -30,9 +32,10 @@ function App() {
+ {isDesktop && }
} /> diff --git a/web/src/api/ws.tsx b/web/src/api/ws.tsx index 827ca4a13..a21ba4857 100644 --- a/web/src/api/ws.tsx +++ b/web/src/api/ws.tsx @@ -11,6 +11,7 @@ import { produce, Draft } from "immer"; import useWebSocket, { ReadyState } from "react-use-websocket"; import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateEvent, ToggleableSetting } from "@/types/ws"; +import { FrigateStats } from "@/types/stats"; type ReducerState = { [topic: string]: { @@ -221,6 +222,13 @@ export function useFrigateEvents(): { payload: FrigateEvent } { return { payload }; } +export function useFrigateStats(): { payload: FrigateStats } { + const { + value: { payload }, + } = useWs("stats", ""); + return { payload }; +} + export function useMotionActivity(camera: string): { payload: string } { const { value: { payload }, diff --git a/web/src/components/Sidebar.tsx b/web/src/components/Sidebar.tsx index 99fc585d1..92a44b520 100644 --- a/web/src/components/Sidebar.tsx +++ b/web/src/components/Sidebar.tsx @@ -34,7 +34,7 @@ function Sidebar({ /> ))}
- + ); diff --git a/web/src/components/Statusbar.tsx b/web/src/components/Statusbar.tsx new file mode 100644 index 000000000..d2a038209 --- /dev/null +++ b/web/src/components/Statusbar.tsx @@ -0,0 +1,84 @@ +import { useFrigateStats } from "@/api/ws"; +import { FrigateStats } from "@/types/stats"; +import { useMemo } from "react"; +import { MdCircle } from "react-icons/md"; +import useSWR from "swr"; + +export default function Statusbar({}) { + const { data: initialStats } = useSWR("stats", { + revalidateOnFocus: false, + }); + const { payload: latestStats } = useFrigateStats(); + const stats = useMemo(() => { + if (latestStats) { + return latestStats; + } + + return initialStats; + }, [initialStats, latestStats]); + + const cpuPercent = useMemo(() => { + const systemCpu = stats?.cpu_usages["frigate.full_system"]?.cpu; + + if (!systemCpu || systemCpu == "0.0") { + return null; + } + + return parseInt(systemCpu); + }, [stats]); + + return ( +
+ {cpuPercent && ( +
+ + CPU {cpuPercent}% +
+ )} + {Object.entries(stats?.gpu_usages || {}).map(([name, stats]) => { + if (name == "error-gpu") { + return; + } + + let gpuTitle; + switch (name) { + case "amd-vaapi": + gpuTitle = "AMD GPU"; + break; + case "intel-vaapi": + case "intel-qsv": + gpuTitle = "Intel GPU"; + break; + default: + gpuTitle = name; + break; + } + + const gpu = parseInt(stats.gpu); + + return ( +
+ + {gpuTitle} {gpu}% +
+ ); + })} +
+ ); +} diff --git a/web/src/pages/Live.tsx b/web/src/pages/Live.tsx index fd13a4a9c..ed66a8e38 100644 --- a/web/src/pages/Live.tsx +++ b/web/src/pages/Live.tsx @@ -79,7 +79,7 @@ function Live() { }, []); return ( -
+
{events && events.length > 0 && ( diff --git a/web/src/types/stats.ts b/web/src/types/stats.ts new file mode 100644 index 000000000..0ed34de14 --- /dev/null +++ b/web/src/types/stats.ts @@ -0,0 +1,60 @@ +export interface FrigateStats { + cameras: { [camera_name: string]: CameraStats }; + cpu_usages: { [pid: string]: CpuStats }; + detectors: { [detectorKey: string]: DetectorStats }; + gpu_usages?: { [gpuKey: string]: GpuStats }; + processes: { [processKey: string]: ExtraProcessStats }; + service: ServiceStats; + detection_fps: number; + } + + export type CameraStats = { + audio_dBFPS: number; + audio_rms: number; + camera_fps: number; + capture_pid: number; + detection_enabled: number; + detection_fps: number; + ffmpeg_pid: number; + pid: number; + process_fps: number; + skipped_fps: number; + }; + + export type CpuStats = { + cmdline: string; + cpu: string; + cpu_average: string; + mem: string; + }; + + export type DetectorStats = { + detection_start: number; + inference_speed: number; + pid: number; + }; + + export type ExtraProcessStats = { + pid: number; + }; + + export type GpuStats = { + gpu: string; + mem: string; + }; + + export type ServiceStats = { + last_updated: number; + storage: { [path: string]: StorageStats }; + temperatures: { [apex: string]: number }; + update: number; + latest_version: string; + version: string; + }; + + export type StorageStats = { + free: number; + total: number; + used: number; + mount_type: string; + }; \ No newline at end of file