mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Typing: mypy fixes for
* __main__.py * app.py * models.py * plus.py * stats.py In addition a new module was introduced: types There all TypedDicts are included. Bitte geben Sie eine Commit-Beschreibung für Ihre Änderungen ein. Zeilen,
This commit is contained in:
parent
ebf4e43ced
commit
cafe0917c7
@ -1,14 +1,13 @@
|
|||||||
import faulthandler
|
import faulthandler
|
||||||
|
from flask import cli
|
||||||
|
|
||||||
faulthandler.enable()
|
faulthandler.enable()
|
||||||
import sys
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
threading.current_thread().name = "frigate"
|
threading.current_thread().name = "frigate"
|
||||||
|
|
||||||
from frigate.app import FrigateApp
|
from frigate.app import FrigateApp
|
||||||
|
|
||||||
cli = sys.modules["flask.cli"]
|
|
||||||
cli.show_server_banner = lambda *x: None
|
cli.show_server_banner = lambda *x: None
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
|
from multiprocessing.queues import Queue
|
||||||
|
from multiprocessing.synchronize import Event
|
||||||
|
from multiprocessing.context import Process
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from logging.handlers import QueueHandler
|
from logging.handlers import QueueHandler
|
||||||
from typing import Dict, List
|
from typing import Optional
|
||||||
|
from types import FrameType
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
import yaml
|
import yaml
|
||||||
@ -16,7 +20,7 @@ from playhouse.sqliteq import SqliteQueueDatabase
|
|||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from frigate.config import DetectorTypeEnum, FrigateConfig
|
from frigate.config import DetectorTypeEnum, FrigateConfig
|
||||||
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR, PLUS_ENV_VAR, PLUS_API_HOST
|
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
|
||||||
from frigate.edgetpu import EdgeTPUProcess
|
from frigate.edgetpu import EdgeTPUProcess
|
||||||
from frigate.events import EventCleanup, EventProcessor
|
from frigate.events import EventCleanup, EventProcessor
|
||||||
from frigate.http import create_app
|
from frigate.http import create_app
|
||||||
@ -31,32 +35,27 @@ from frigate.stats import StatsEmitter, stats_init
|
|||||||
from frigate.version import VERSION
|
from frigate.version import VERSION
|
||||||
from frigate.video import capture_camera, track_camera
|
from frigate.video import capture_camera, track_camera
|
||||||
from frigate.watchdog import FrigateWatchdog
|
from frigate.watchdog import FrigateWatchdog
|
||||||
|
from frigate.types import CameraMetricsTypes
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FrigateApp:
|
class FrigateApp:
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self.stop_event = mp.Event()
|
self.stop_event: Event = mp.Event()
|
||||||
self.base_config: FrigateConfig = None
|
self.detection_queue: Queue = mp.Queue()
|
||||||
self.config: FrigateConfig = None
|
self.detectors: dict[str, EdgeTPUProcess] = {}
|
||||||
self.detection_queue = mp.Queue()
|
self.detection_out_events: dict[str, Event] = {}
|
||||||
self.detectors: Dict[str, EdgeTPUProcess] = {}
|
self.detection_shms: list[mp.shared_memory.SharedMemory] = []
|
||||||
self.detection_out_events: Dict[str, mp.Event] = {}
|
self.log_queue: Queue = mp.Queue()
|
||||||
self.detection_shms: List[mp.shared_memory.SharedMemory] = []
|
self.plus_api = PlusApi()
|
||||||
self.log_queue = mp.Queue()
|
self.camera_metrics: dict[str, CameraMetricsTypes] = {}
|
||||||
self.plus_api = (
|
|
||||||
PlusApi(PLUS_API_HOST, os.environ.get(PLUS_ENV_VAR))
|
|
||||||
if PLUS_ENV_VAR in os.environ
|
|
||||||
else None
|
|
||||||
)
|
|
||||||
self.camera_metrics = {}
|
|
||||||
|
|
||||||
def set_environment_vars(self):
|
def set_environment_vars(self) -> None:
|
||||||
for key, value in self.config.environment_vars.items():
|
for key, value in self.config.environment_vars.items():
|
||||||
os.environ[key] = value
|
os.environ[key] = value
|
||||||
|
|
||||||
def ensure_dirs(self):
|
def ensure_dirs(self) -> None:
|
||||||
for d in [RECORD_DIR, CLIPS_DIR, CACHE_DIR]:
|
for d in [RECORD_DIR, CLIPS_DIR, CACHE_DIR]:
|
||||||
if not os.path.exists(d) and not os.path.islink(d):
|
if not os.path.exists(d) and not os.path.islink(d):
|
||||||
logger.info(f"Creating directory: {d}")
|
logger.info(f"Creating directory: {d}")
|
||||||
@ -64,7 +63,7 @@ class FrigateApp:
|
|||||||
else:
|
else:
|
||||||
logger.debug(f"Skipping directory: {d}")
|
logger.debug(f"Skipping directory: {d}")
|
||||||
|
|
||||||
def init_logger(self):
|
def init_logger(self) -> None:
|
||||||
self.log_process = mp.Process(
|
self.log_process = mp.Process(
|
||||||
target=log_process, args=(self.log_queue,), name="log_process"
|
target=log_process, args=(self.log_queue,), name="log_process"
|
||||||
)
|
)
|
||||||
@ -72,7 +71,7 @@ class FrigateApp:
|
|||||||
self.log_process.start()
|
self.log_process.start()
|
||||||
root_configurer(self.log_queue)
|
root_configurer(self.log_queue)
|
||||||
|
|
||||||
def init_config(self):
|
def init_config(self) -> None:
|
||||||
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
|
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
|
||||||
|
|
||||||
# Check if we can use .yaml instead of .yml
|
# Check if we can use .yaml instead of .yml
|
||||||
@ -100,9 +99,11 @@ class FrigateApp:
|
|||||||
"read_start": mp.Value("d", 0.0),
|
"read_start": mp.Value("d", 0.0),
|
||||||
"ffmpeg_pid": mp.Value("i", 0),
|
"ffmpeg_pid": mp.Value("i", 0),
|
||||||
"frame_queue": mp.Queue(maxsize=2),
|
"frame_queue": mp.Queue(maxsize=2),
|
||||||
|
"capture_process": None,
|
||||||
|
"process": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
def set_log_levels(self):
|
def set_log_levels(self) -> None:
|
||||||
logging.getLogger().setLevel(self.config.logger.default.value.upper())
|
logging.getLogger().setLevel(self.config.logger.default.value.upper())
|
||||||
for log, level in self.config.logger.logs.items():
|
for log, level in self.config.logger.logs.items():
|
||||||
logging.getLogger(log).setLevel(level.value.upper())
|
logging.getLogger(log).setLevel(level.value.upper())
|
||||||
@ -110,21 +111,23 @@ class FrigateApp:
|
|||||||
if not "werkzeug" in self.config.logger.logs:
|
if not "werkzeug" in self.config.logger.logs:
|
||||||
logging.getLogger("werkzeug").setLevel("ERROR")
|
logging.getLogger("werkzeug").setLevel("ERROR")
|
||||||
|
|
||||||
def init_queues(self):
|
def init_queues(self) -> None:
|
||||||
# Queues for clip processing
|
# Queues for clip processing
|
||||||
self.event_queue = mp.Queue()
|
self.event_queue: Queue = mp.Queue()
|
||||||
self.event_processed_queue = mp.Queue()
|
self.event_processed_queue: Queue = mp.Queue()
|
||||||
self.video_output_queue = mp.Queue(maxsize=len(self.config.cameras.keys()) * 2)
|
self.video_output_queue: Queue = mp.Queue(
|
||||||
|
maxsize=len(self.config.cameras.keys()) * 2
|
||||||
|
)
|
||||||
|
|
||||||
# Queue for cameras to push tracked objects to
|
# Queue for cameras to push tracked objects to
|
||||||
self.detected_frames_queue = mp.Queue(
|
self.detected_frames_queue: Queue = mp.Queue(
|
||||||
maxsize=len(self.config.cameras.keys()) * 2
|
maxsize=len(self.config.cameras.keys()) * 2
|
||||||
)
|
)
|
||||||
|
|
||||||
# Queue for recordings info
|
# Queue for recordings info
|
||||||
self.recordings_info_queue = mp.Queue()
|
self.recordings_info_queue: Queue = mp.Queue()
|
||||||
|
|
||||||
def init_database(self):
|
def init_database(self) -> None:
|
||||||
# Migrate DB location
|
# Migrate DB location
|
||||||
old_db_path = os.path.join(CLIPS_DIR, "frigate.db")
|
old_db_path = os.path.join(CLIPS_DIR, "frigate.db")
|
||||||
if not os.path.isfile(self.config.database.path) and os.path.isfile(
|
if not os.path.isfile(self.config.database.path) and os.path.isfile(
|
||||||
@ -146,10 +149,10 @@ class FrigateApp:
|
|||||||
models = [Event, Recordings]
|
models = [Event, Recordings]
|
||||||
self.db.bind(models)
|
self.db.bind(models)
|
||||||
|
|
||||||
def init_stats(self):
|
def init_stats(self) -> None:
|
||||||
self.stats_tracking = stats_init(self.camera_metrics, self.detectors)
|
self.stats_tracking = stats_init(self.camera_metrics, self.detectors)
|
||||||
|
|
||||||
def init_web_server(self):
|
def init_web_server(self) -> None:
|
||||||
self.flask_app = create_app(
|
self.flask_app = create_app(
|
||||||
self.config,
|
self.config,
|
||||||
self.db,
|
self.db,
|
||||||
@ -158,16 +161,16 @@ class FrigateApp:
|
|||||||
self.plus_api,
|
self.plus_api,
|
||||||
)
|
)
|
||||||
|
|
||||||
def init_mqtt(self):
|
def init_mqtt(self) -> None:
|
||||||
self.mqtt_client = create_mqtt_client(self.config, self.camera_metrics)
|
self.mqtt_client = create_mqtt_client(self.config, self.camera_metrics)
|
||||||
|
|
||||||
def start_mqtt_relay(self):
|
def start_mqtt_relay(self) -> None:
|
||||||
self.mqtt_relay = MqttSocketRelay(
|
self.mqtt_relay = MqttSocketRelay(
|
||||||
self.mqtt_client, self.config.mqtt.topic_prefix
|
self.mqtt_client, self.config.mqtt.topic_prefix
|
||||||
)
|
)
|
||||||
self.mqtt_relay.start()
|
self.mqtt_relay.start()
|
||||||
|
|
||||||
def start_detectors(self):
|
def start_detectors(self) -> None:
|
||||||
model_path = self.config.model.path
|
model_path = self.config.model.path
|
||||||
model_shape = (self.config.model.height, self.config.model.width)
|
model_shape = (self.config.model.height, self.config.model.width)
|
||||||
for name in self.config.cameras.keys():
|
for name in self.config.cameras.keys():
|
||||||
@ -214,7 +217,7 @@ class FrigateApp:
|
|||||||
detector.num_threads,
|
detector.num_threads,
|
||||||
)
|
)
|
||||||
|
|
||||||
def start_detected_frames_processor(self):
|
def start_detected_frames_processor(self) -> None:
|
||||||
self.detected_frames_processor = TrackedObjectProcessor(
|
self.detected_frames_processor = TrackedObjectProcessor(
|
||||||
self.config,
|
self.config,
|
||||||
self.mqtt_client,
|
self.mqtt_client,
|
||||||
@ -228,7 +231,7 @@ class FrigateApp:
|
|||||||
)
|
)
|
||||||
self.detected_frames_processor.start()
|
self.detected_frames_processor.start()
|
||||||
|
|
||||||
def start_video_output_processor(self):
|
def start_video_output_processor(self) -> None:
|
||||||
output_processor = mp.Process(
|
output_processor = mp.Process(
|
||||||
target=output_frames,
|
target=output_frames,
|
||||||
name=f"output_processor",
|
name=f"output_processor",
|
||||||
@ -242,7 +245,7 @@ class FrigateApp:
|
|||||||
output_processor.start()
|
output_processor.start()
|
||||||
logger.info(f"Output process started: {output_processor.pid}")
|
logger.info(f"Output process started: {output_processor.pid}")
|
||||||
|
|
||||||
def start_camera_processors(self):
|
def start_camera_processors(self) -> None:
|
||||||
model_shape = (self.config.model.height, self.config.model.width)
|
model_shape = (self.config.model.height, self.config.model.width)
|
||||||
for name, config in self.config.cameras.items():
|
for name, config in self.config.cameras.items():
|
||||||
camera_process = mp.Process(
|
camera_process = mp.Process(
|
||||||
@ -264,7 +267,7 @@ class FrigateApp:
|
|||||||
camera_process.start()
|
camera_process.start()
|
||||||
logger.info(f"Camera processor started for {name}: {camera_process.pid}")
|
logger.info(f"Camera processor started for {name}: {camera_process.pid}")
|
||||||
|
|
||||||
def start_camera_capture_processes(self):
|
def start_camera_capture_processes(self) -> None:
|
||||||
for name, config in self.config.cameras.items():
|
for name, config in self.config.cameras.items():
|
||||||
capture_process = mp.Process(
|
capture_process = mp.Process(
|
||||||
target=capture_camera,
|
target=capture_camera,
|
||||||
@ -276,7 +279,7 @@ class FrigateApp:
|
|||||||
capture_process.start()
|
capture_process.start()
|
||||||
logger.info(f"Capture process started for {name}: {capture_process.pid}")
|
logger.info(f"Capture process started for {name}: {capture_process.pid}")
|
||||||
|
|
||||||
def start_event_processor(self):
|
def start_event_processor(self) -> None:
|
||||||
self.event_processor = EventProcessor(
|
self.event_processor = EventProcessor(
|
||||||
self.config,
|
self.config,
|
||||||
self.camera_metrics,
|
self.camera_metrics,
|
||||||
@ -286,21 +289,21 @@ class FrigateApp:
|
|||||||
)
|
)
|
||||||
self.event_processor.start()
|
self.event_processor.start()
|
||||||
|
|
||||||
def start_event_cleanup(self):
|
def start_event_cleanup(self) -> None:
|
||||||
self.event_cleanup = EventCleanup(self.config, self.stop_event)
|
self.event_cleanup = EventCleanup(self.config, self.stop_event)
|
||||||
self.event_cleanup.start()
|
self.event_cleanup.start()
|
||||||
|
|
||||||
def start_recording_maintainer(self):
|
def start_recording_maintainer(self) -> None:
|
||||||
self.recording_maintainer = RecordingMaintainer(
|
self.recording_maintainer = RecordingMaintainer(
|
||||||
self.config, self.recordings_info_queue, self.stop_event
|
self.config, self.recordings_info_queue, self.stop_event
|
||||||
)
|
)
|
||||||
self.recording_maintainer.start()
|
self.recording_maintainer.start()
|
||||||
|
|
||||||
def start_recording_cleanup(self):
|
def start_recording_cleanup(self) -> None:
|
||||||
self.recording_cleanup = RecordingCleanup(self.config, self.stop_event)
|
self.recording_cleanup = RecordingCleanup(self.config, self.stop_event)
|
||||||
self.recording_cleanup.start()
|
self.recording_cleanup.start()
|
||||||
|
|
||||||
def start_stats_emitter(self):
|
def start_stats_emitter(self) -> None:
|
||||||
self.stats_emitter = StatsEmitter(
|
self.stats_emitter = StatsEmitter(
|
||||||
self.config,
|
self.config,
|
||||||
self.stats_tracking,
|
self.stats_tracking,
|
||||||
@ -310,11 +313,11 @@ class FrigateApp:
|
|||||||
)
|
)
|
||||||
self.stats_emitter.start()
|
self.stats_emitter.start()
|
||||||
|
|
||||||
def start_watchdog(self):
|
def start_watchdog(self) -> None:
|
||||||
self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
|
self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
|
||||||
self.frigate_watchdog.start()
|
self.frigate_watchdog.start()
|
||||||
|
|
||||||
def start(self):
|
def start(self) -> None:
|
||||||
self.init_logger()
|
self.init_logger()
|
||||||
logger.info(f"Starting Frigate ({VERSION})")
|
logger.info(f"Starting Frigate ({VERSION})")
|
||||||
try:
|
try:
|
||||||
@ -363,7 +366,7 @@ class FrigateApp:
|
|||||||
self.start_watchdog()
|
self.start_watchdog()
|
||||||
# self.zeroconf = broadcast_zeroconf(self.config.mqtt.client_id)
|
# self.zeroconf = broadcast_zeroconf(self.config.mqtt.client_id)
|
||||||
|
|
||||||
def receiveSignal(signalNumber, frame):
|
def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None:
|
||||||
self.stop()
|
self.stop()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
@ -376,7 +379,7 @@ class FrigateApp:
|
|||||||
|
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self) -> None:
|
||||||
logger.info(f"Stopping...")
|
logger.info(f"Stopping...")
|
||||||
self.stop_event.set()
|
self.stop_event.set()
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ def set_retain(id):
|
|||||||
|
|
||||||
@bp.route("/events/<id>/plus", methods=("POST",))
|
@bp.route("/events/<id>/plus", methods=("POST",))
|
||||||
def send_to_plus(id):
|
def send_to_plus(id):
|
||||||
if current_app.plus_api is None:
|
if current_app.plus_api.is_active():
|
||||||
return make_response(
|
return make_response(
|
||||||
jsonify(
|
jsonify(
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
from numpy import unique
|
from numpy import unique
|
||||||
from peewee import *
|
from peewee import (
|
||||||
|
Model,
|
||||||
|
CharField,
|
||||||
|
DateTimeField,
|
||||||
|
FloatField,
|
||||||
|
BooleanField,
|
||||||
|
JSONField,
|
||||||
|
TextField,
|
||||||
|
IntegerField,
|
||||||
|
)
|
||||||
from playhouse.sqlite_ext import *
|
from playhouse.sqlite_ext import *
|
||||||
|
|
||||||
|
|
||||||
class Event(Model):
|
class Event(Model): # type: ignore[misc]
|
||||||
id = CharField(null=False, primary_key=True, max_length=30)
|
id = CharField(null=False, primary_key=True, max_length=30)
|
||||||
label = CharField(index=True, max_length=20)
|
label = CharField(index=True, max_length=20)
|
||||||
sub_label = CharField(max_length=20, null=True)
|
sub_label = CharField(max_length=20, null=True)
|
||||||
@ -24,7 +33,7 @@ class Event(Model):
|
|||||||
plus_id = CharField(max_length=30)
|
plus_id = CharField(max_length=30)
|
||||||
|
|
||||||
|
|
||||||
class Recordings(Model):
|
class Recordings(Model): # type: ignore[misc]
|
||||||
id = CharField(null=False, primary_key=True, max_length=30)
|
id = CharField(null=False, primary_key=True, max_length=30)
|
||||||
camera = CharField(index=True, max_length=20)
|
camera = CharField(index=True, max_length=20)
|
||||||
path = CharField(unique=True)
|
path = CharField(unique=True)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[mypy]
|
[mypy]
|
||||||
python_version = 3.9
|
python_version = 3.9
|
||||||
show_error_codes = true
|
show_error_codes = true
|
||||||
follow_imports = silent
|
follow_imports = normal
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
strict_equality = true
|
strict_equality = true
|
||||||
warn_incomplete_stub = true
|
warn_incomplete_stub = true
|
||||||
@ -23,12 +23,32 @@ no_implicit_reexport = true
|
|||||||
[mypy-frigate.*]
|
[mypy-frigate.*]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
|
[mypy-frigate.__main__]
|
||||||
|
ignore_errors = false
|
||||||
|
disallow_untyped_calls = false
|
||||||
|
|
||||||
|
[mypy-frigate.app]
|
||||||
|
ignore_errors = false
|
||||||
|
disallow_untyped_calls = false
|
||||||
|
|
||||||
[mypy-frigate.const]
|
[mypy-frigate.const]
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
|
|
||||||
[mypy-frigate.log]
|
[mypy-frigate.log]
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
|
|
||||||
|
[mypy-frigate.models]
|
||||||
|
ignore_errors = false
|
||||||
|
|
||||||
|
[mypy-frigate.plus]
|
||||||
|
ignore_errors = false
|
||||||
|
|
||||||
|
[mypy-frigate.stats]
|
||||||
|
ignore_errors = false
|
||||||
|
|
||||||
|
[mypy-frigate.types]
|
||||||
|
ignore_errors = false
|
||||||
|
|
||||||
[mypy-frigate.version]
|
[mypy-frigate.version]
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
|
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import requests
|
import requests
|
||||||
|
from frigate.const import PLUS_ENV_VAR, PLUS_API_HOST
|
||||||
|
from requests.models import Response
|
||||||
import cv2
|
import cv2
|
||||||
|
from numpy import ndarray
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_jpg_bytes(image, max_dim, quality):
|
def get_jpg_bytes(image: ndarray, max_dim: int, quality: int) -> bytes:
|
||||||
if image.shape[1] >= image.shape[0]:
|
if image.shape[1] >= image.shape[0]:
|
||||||
width = min(max_dim, image.shape[1])
|
width = min(max_dim, image.shape[1])
|
||||||
height = int(width * image.shape[0] / image.shape[1])
|
height = int(width * image.shape[0] / image.shape[1])
|
||||||
@ -17,45 +21,54 @@ def get_jpg_bytes(image, max_dim, quality):
|
|||||||
original = cv2.resize(image, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
original = cv2.resize(image, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
||||||
|
|
||||||
ret, jpg = cv2.imencode(".jpg", original, [int(cv2.IMWRITE_JPEG_QUALITY), quality])
|
ret, jpg = cv2.imencode(".jpg", original, [int(cv2.IMWRITE_JPEG_QUALITY), quality])
|
||||||
return jpg.tobytes()
|
jpg_bytes = jpg.tobytes()
|
||||||
|
return jpg_bytes if type(jpg_bytes) is bytes else b""
|
||||||
|
|
||||||
|
|
||||||
class PlusApi:
|
class PlusApi:
|
||||||
def __init__(self, host, key: str):
|
def __init__(self) -> None:
|
||||||
self.host = host
|
self.host = PLUS_API_HOST
|
||||||
self.key = key
|
if PLUS_ENV_VAR in os.environ:
|
||||||
self._token_data = None
|
self.key = os.environ.get(PLUS_ENV_VAR)
|
||||||
|
else:
|
||||||
|
self.key = None
|
||||||
|
self._is_active: bool = self.key is not None
|
||||||
|
self._token_data: dict = {}
|
||||||
|
|
||||||
def _refresh_token_if_needed(self):
|
def _refresh_token_if_needed(self) -> None:
|
||||||
if (
|
if (
|
||||||
self._token_data is None
|
self._token_data.get("expires") is None
|
||||||
or self._token_data["expires"] - datetime.datetime.now().timestamp() < 60
|
or self._token_data["expires"] - datetime.datetime.now().timestamp() < 60
|
||||||
):
|
):
|
||||||
|
if self.key is None:
|
||||||
|
raise Exception("Plus API not activated")
|
||||||
parts = self.key.split(":")
|
parts = self.key.split(":")
|
||||||
r = requests.get(f"{self.host}/v1/auth/token", auth=(parts[0], parts[1]))
|
r = requests.get(f"{self.host}/v1/auth/token", auth=(parts[0], parts[1]))
|
||||||
self._token_data = r.json()
|
self._token_data = r.json()
|
||||||
|
|
||||||
def _get_authorization_header(self):
|
def _get_authorization_header(self) -> dict:
|
||||||
self._refresh_token_if_needed()
|
self._refresh_token_if_needed()
|
||||||
return {"authorization": f"Bearer {self._token_data['accessToken']}"}
|
return {"authorization": f"Bearer {self._token_data.get('accessToken')}"}
|
||||||
|
|
||||||
def _get(self, path):
|
def _get(self, path: str) -> Response:
|
||||||
return requests.get(
|
return requests.get(
|
||||||
f"{self.host}/v1/{path}", headers=self._get_authorization_header()
|
f"{self.host}/v1/{path}", headers=self._get_authorization_header()
|
||||||
)
|
)
|
||||||
|
|
||||||
def _post(self, path, data):
|
def _post(self, path: str, data: dict) -> Response:
|
||||||
return requests.post(
|
return requests.post(
|
||||||
f"{self.host}/v1/{path}",
|
f"{self.host}/v1/{path}",
|
||||||
headers=self._get_authorization_header(),
|
headers=self._get_authorization_header(),
|
||||||
json=data,
|
json=data,
|
||||||
)
|
)
|
||||||
|
|
||||||
def upload_image(self, image, camera: str):
|
def is_active(self) -> bool:
|
||||||
|
return self._is_active
|
||||||
|
|
||||||
|
def upload_image(self, image: ndarray, camera: str) -> str:
|
||||||
r = self._get("image/signed_urls")
|
r = self._get("image/signed_urls")
|
||||||
presigned_urls = r.json()
|
presigned_urls = r.json()
|
||||||
if not r.ok:
|
if not r.ok:
|
||||||
logger.exception(ex)
|
|
||||||
raise Exception("Unable to get signed urls")
|
raise Exception("Unable to get signed urls")
|
||||||
|
|
||||||
# resize and submit original
|
# resize and submit original
|
||||||
@ -93,4 +106,4 @@ class PlusApi:
|
|||||||
raise Exception(r.text)
|
raise Exception(r.text)
|
||||||
|
|
||||||
# return image id
|
# return image id
|
||||||
return presigned_urls["imageId"]
|
return str(presigned_urls.get("imageId"))
|
||||||
|
@ -6,10 +6,15 @@ import psutil
|
|||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
|
from typing import Optional, Any
|
||||||
|
from paho.mqtt.client import Client
|
||||||
|
from multiprocessing.synchronize import Event
|
||||||
|
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR
|
from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR
|
||||||
|
from frigate.types import StatsTrackingTypes, CameraMetricsTypes
|
||||||
from frigate.version import VERSION
|
from frigate.version import VERSION
|
||||||
|
from frigate.edgetpu import EdgeTPUProcess
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -20,14 +25,16 @@ def get_latest_version() -> str:
|
|||||||
)
|
)
|
||||||
response = request.json()
|
response = request.json()
|
||||||
|
|
||||||
if request.ok and response:
|
if request.ok and response and "tag_name" in response:
|
||||||
return response.get("tag_name", "unknown").replace("v", "")
|
return str(response.get("tag_name").replace("v", ""))
|
||||||
else:
|
else:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
def stats_init(camera_metrics, detectors):
|
def stats_init(
|
||||||
stats_tracking = {
|
camera_metrics: dict[str, CameraMetricsTypes], detectors: dict[str, EdgeTPUProcess]
|
||||||
|
) -> StatsTrackingTypes:
|
||||||
|
stats_tracking: StatsTrackingTypes = {
|
||||||
"camera_metrics": camera_metrics,
|
"camera_metrics": camera_metrics,
|
||||||
"detectors": detectors,
|
"detectors": detectors,
|
||||||
"started": int(time.time()),
|
"started": int(time.time()),
|
||||||
@ -36,7 +43,7 @@ def stats_init(camera_metrics, detectors):
|
|||||||
return stats_tracking
|
return stats_tracking
|
||||||
|
|
||||||
|
|
||||||
def get_fs_type(path):
|
def get_fs_type(path: str) -> str:
|
||||||
bestMatch = ""
|
bestMatch = ""
|
||||||
fsType = ""
|
fsType = ""
|
||||||
for part in psutil.disk_partitions(all=True):
|
for part in psutil.disk_partitions(all=True):
|
||||||
@ -46,7 +53,7 @@ def get_fs_type(path):
|
|||||||
return fsType
|
return fsType
|
||||||
|
|
||||||
|
|
||||||
def read_temperature(path):
|
def read_temperature(path: str) -> Optional[float]:
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
line = f.readline().strip()
|
line = f.readline().strip()
|
||||||
@ -54,7 +61,7 @@ def read_temperature(path):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_temperatures():
|
def get_temperatures() -> dict[str, float]:
|
||||||
temps = {}
|
temps = {}
|
||||||
|
|
||||||
# Get temperatures for all attached Corals
|
# Get temperatures for all attached Corals
|
||||||
@ -68,29 +75,36 @@ def get_temperatures():
|
|||||||
return temps
|
return temps
|
||||||
|
|
||||||
|
|
||||||
def stats_snapshot(stats_tracking):
|
def stats_snapshot(stats_tracking: StatsTrackingTypes) -> dict[str, Any]:
|
||||||
camera_metrics = stats_tracking["camera_metrics"]
|
camera_metrics = stats_tracking["camera_metrics"]
|
||||||
stats = {}
|
stats: dict[str, Any] = {}
|
||||||
|
|
||||||
total_detection_fps = 0
|
total_detection_fps = 0
|
||||||
|
|
||||||
for name, camera_stats in camera_metrics.items():
|
for name, camera_stats in camera_metrics.items():
|
||||||
total_detection_fps += camera_stats["detection_fps"].value
|
total_detection_fps += camera_stats["detection_fps"].value
|
||||||
|
pid = camera_stats["process"].pid if camera_stats["process"] else None
|
||||||
|
cpid = (
|
||||||
|
camera_stats["capture_process"].pid
|
||||||
|
if camera_stats["capture_process"]
|
||||||
|
else None
|
||||||
|
)
|
||||||
stats[name] = {
|
stats[name] = {
|
||||||
"camera_fps": round(camera_stats["camera_fps"].value, 2),
|
"camera_fps": round(camera_stats["camera_fps"].value, 2),
|
||||||
"process_fps": round(camera_stats["process_fps"].value, 2),
|
"process_fps": round(camera_stats["process_fps"].value, 2),
|
||||||
"skipped_fps": round(camera_stats["skipped_fps"].value, 2),
|
"skipped_fps": round(camera_stats["skipped_fps"].value, 2),
|
||||||
"detection_fps": round(camera_stats["detection_fps"].value, 2),
|
"detection_fps": round(camera_stats["detection_fps"].value, 2),
|
||||||
"pid": camera_stats["process"].pid,
|
"pid": pid,
|
||||||
"capture_pid": camera_stats["capture_process"].pid,
|
"capture_pid": cpid,
|
||||||
}
|
}
|
||||||
|
|
||||||
stats["detectors"] = {}
|
stats["detectors"] = {}
|
||||||
for name, detector in stats_tracking["detectors"].items():
|
for name, detector in stats_tracking["detectors"].items():
|
||||||
|
pid = detector.detect_process.pid if detector.detect_process else None
|
||||||
stats["detectors"][name] = {
|
stats["detectors"][name] = {
|
||||||
"inference_speed": round(detector.avg_inference_speed.value * 1000, 2),
|
"inference_speed": round(detector.avg_inference_speed.value * 1000, 2),
|
||||||
"detection_start": detector.detection_start.value,
|
"detection_start": detector.detection_start.value,
|
||||||
"pid": detector.detect_process.pid,
|
"pid": pid,
|
||||||
}
|
}
|
||||||
stats["detection_fps"] = round(total_detection_fps, 2)
|
stats["detection_fps"] = round(total_detection_fps, 2)
|
||||||
|
|
||||||
@ -118,10 +132,10 @@ class StatsEmitter(threading.Thread):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
stats_tracking,
|
stats_tracking: StatsTrackingTypes,
|
||||||
mqtt_client,
|
mqtt_client: Client,
|
||||||
topic_prefix,
|
topic_prefix: str,
|
||||||
stop_event,
|
stop_event: Event,
|
||||||
):
|
):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.name = "frigate_stats_emitter"
|
self.name = "frigate_stats_emitter"
|
||||||
@ -131,7 +145,7 @@ class StatsEmitter(threading.Thread):
|
|||||||
self.topic_prefix = topic_prefix
|
self.topic_prefix = topic_prefix
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
while not self.stop_event.wait(self.config.mqtt.stats_interval):
|
while not self.stop_event.wait(self.config.mqtt.stats_interval):
|
||||||
stats = stats_snapshot(self.stats_tracking)
|
stats = stats_snapshot(self.stats_tracking)
|
||||||
|
28
frigate/types.py
Normal file
28
frigate/types.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from typing import Optional, TypedDict
|
||||||
|
from multiprocessing.queues import Queue
|
||||||
|
from multiprocessing.sharedctypes import Synchronized
|
||||||
|
from multiprocessing.context import Process
|
||||||
|
|
||||||
|
from frigate.edgetpu import EdgeTPUProcess
|
||||||
|
|
||||||
|
|
||||||
|
class CameraMetricsTypes(TypedDict):
|
||||||
|
camera_fps: Synchronized
|
||||||
|
capture_process: Optional[Process]
|
||||||
|
detection_enabled: Synchronized
|
||||||
|
detection_fps: Synchronized
|
||||||
|
detection_frame: Synchronized
|
||||||
|
ffmpeg_pid: Synchronized
|
||||||
|
frame_queue: Queue
|
||||||
|
improve_contrast_enabled: Synchronized
|
||||||
|
process: Optional[Process]
|
||||||
|
process_fps: Synchronized
|
||||||
|
read_start: Synchronized
|
||||||
|
skipped_fps: Synchronized
|
||||||
|
|
||||||
|
|
||||||
|
class StatsTrackingTypes(TypedDict):
|
||||||
|
camera_metrics: dict[str, CameraMetricsTypes]
|
||||||
|
detectors: dict[str, EdgeTPUProcess]
|
||||||
|
started: int
|
||||||
|
latest_frigate_version: str
|
@ -8,7 +8,6 @@ import signal
|
|||||||
from frigate.edgetpu import EdgeTPUProcess
|
from frigate.edgetpu import EdgeTPUProcess
|
||||||
from frigate.util import restart_frigate
|
from frigate.util import restart_frigate
|
||||||
from multiprocessing.synchronize import Event
|
from multiprocessing.synchronize import Event
|
||||||
from typing import dict
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ peewee_migrate == 1.4.*
|
|||||||
psutil == 5.9.*
|
psutil == 5.9.*
|
||||||
pydantic == 1.9.*
|
pydantic == 1.9.*
|
||||||
PyYAML == 6.0.*
|
PyYAML == 6.0.*
|
||||||
|
types-PyYAML == 6.0.*
|
||||||
requests == 2.27.*
|
requests == 2.27.*
|
||||||
|
types-requests == 2.27.*
|
||||||
scipy == 1.8.*
|
scipy == 1.8.*
|
||||||
setproctitle == 1.2.*
|
setproctitle == 1.2.*
|
||||||
ws4py == 0.5.*
|
ws4py == 0.5.*
|
||||||
|
Loading…
Reference in New Issue
Block a user