blakeblackshear.frigate/frigate/stats.py

169 lines
5.2 KiB
Python
Raw Normal View History

import json
import logging
import threading
import time
2021-02-03 13:36:13 +01:00
import psutil
import shutil
import os
import requests
from typing import Optional, Any
from paho.mqtt.client import Client
from multiprocessing.synchronize import Event
from frigate.config import FrigateConfig
2021-02-03 13:36:13 +01:00
from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR
from frigate.types import StatsTrackingTypes, CameraMetricsTypes
from frigate.version import VERSION
from frigate.util import get_cpu_stats
from frigate.object_detection import ObjectDetectProcess
logger = logging.getLogger(__name__)
2021-02-17 14:23:32 +01:00
def get_latest_version() -> str:
2022-05-26 17:04:33 +02:00
try:
request = requests.get(
"https://api.github.com/repos/blakeblackshear/frigate/releases/latest",
timeout=10,
2022-05-26 17:04:33 +02:00
)
except:
return "unknown"
response = request.json()
if request.ok and response and "tag_name" in response:
return str(response.get("tag_name").replace("v", ""))
else:
return "unknown"
def stats_init(
camera_metrics: dict[str, CameraMetricsTypes],
detectors: dict[str, ObjectDetectProcess],
) -> StatsTrackingTypes:
stats_tracking: StatsTrackingTypes = {
2021-02-17 14:23:32 +01:00
"camera_metrics": camera_metrics,
"detectors": detectors,
"started": int(time.time()),
"latest_frigate_version": get_latest_version(),
}
return stats_tracking
2021-02-17 14:23:32 +01:00
def get_fs_type(path: str) -> str:
2021-02-03 13:36:13 +01:00
bestMatch = ""
fsType = ""
for part in psutil.disk_partitions(all=True):
if path.startswith(part.mountpoint) and len(bestMatch) < len(part.mountpoint):
fsType = part.fstype
bestMatch = part.mountpoint
return fsType
2021-12-12 17:27:01 +01:00
def read_temperature(path: str) -> Optional[float]:
if os.path.isfile(path):
with open(path) as f:
2021-12-12 17:27:01 +01:00
line = f.readline().strip()
return int(line) / 1000
return None
2021-12-12 17:27:01 +01:00
def get_temperatures() -> dict[str, float]:
2021-12-12 17:27:01 +01:00
temps = {}
# Get temperatures for all attached Corals
2021-12-12 17:27:01 +01:00
base = "/sys/class/apex/"
if os.path.isdir(base):
for apex in os.listdir(base):
temp = read_temperature(os.path.join(base, apex, "temp"))
if temp is not None:
temps[apex] = temp
return temps
2021-02-17 14:23:32 +01:00
2021-12-12 17:27:01 +01:00
def stats_snapshot(stats_tracking: StatsTrackingTypes) -> dict[str, Any]:
2021-02-17 14:23:32 +01:00
camera_metrics = stats_tracking["camera_metrics"]
stats: dict[str, Any] = {}
total_detection_fps = 0
for name, camera_stats in camera_metrics.items():
2021-02-17 14:23:32 +01:00
total_detection_fps += camera_stats["detection_fps"].value
pid = camera_stats["process"].pid if camera_stats["process"] else None
ffmpeg_pid = (
camera_stats["ffmpeg_pid"].value if camera_stats["ffmpeg_pid"] else None
)
cpid = (
camera_stats["capture_process"].pid
if camera_stats["capture_process"]
else None
)
stats[name] = {
2021-02-17 14:23:32 +01:00
"camera_fps": round(camera_stats["camera_fps"].value, 2),
"process_fps": round(camera_stats["process_fps"].value, 2),
"skipped_fps": round(camera_stats["skipped_fps"].value, 2),
"detection_fps": round(camera_stats["detection_fps"].value, 2),
"pid": pid,
"capture_pid": cpid,
"ffmpeg_pid": ffmpeg_pid,
}
2021-02-17 14:23:32 +01:00
stats["detectors"] = {}
for name, detector in stats_tracking["detectors"].items():
pid = detector.detect_process.pid if detector.detect_process else None
2021-02-17 14:23:32 +01:00
stats["detectors"][name] = {
"inference_speed": round(detector.avg_inference_speed.value * 1000, 2),
"detection_start": detector.detection_start.value,
"pid": pid,
}
2021-02-17 14:23:32 +01:00
stats["detection_fps"] = round(total_detection_fps, 2)
stats["cpu_usages"] = get_cpu_stats()
2021-02-17 14:23:32 +01:00
stats["service"] = {
"uptime": (int(time.time()) - stats_tracking["started"]),
"version": VERSION,
"latest_version": stats_tracking["latest_frigate_version"],
2021-02-17 14:23:32 +01:00
"storage": {},
2021-12-12 17:27:01 +01:00
"temperatures": get_temperatures(),
}
2021-02-03 13:36:13 +01:00
for path in [RECORD_DIR, CLIPS_DIR, CACHE_DIR, "/dev/shm"]:
storage_stats = shutil.disk_usage(path)
2021-02-17 14:23:32 +01:00
stats["service"]["storage"][path] = {
"total": round(storage_stats.total / 1000000, 1),
"used": round(storage_stats.used / 1000000, 1),
"free": round(storage_stats.free / 1000000, 1),
"mount_type": get_fs_type(path),
2021-02-03 13:36:13 +01:00
}
return stats
2021-02-17 14:23:32 +01:00
class StatsEmitter(threading.Thread):
2021-02-17 14:23:32 +01:00
def __init__(
self,
config: FrigateConfig,
stats_tracking: StatsTrackingTypes,
mqtt_client: Client,
topic_prefix: str,
stop_event: Event,
2021-02-17 14:23:32 +01:00
):
threading.Thread.__init__(self)
2021-02-17 14:23:32 +01:00
self.name = "frigate_stats_emitter"
self.config = config
self.stats_tracking = stats_tracking
self.mqtt_client = mqtt_client
self.topic_prefix = topic_prefix
self.stop_event = stop_event
def run(self) -> None:
time.sleep(10)
while not self.stop_event.wait(self.config.mqtt.stats_interval):
stats = stats_snapshot(self.stats_tracking)
2021-02-17 14:23:32 +01:00
self.mqtt_client.publish(
f"{self.topic_prefix}/stats", json.dumps(stats), retain=False
)
logger.info(f"Exiting watchdog...")