Files
frigate/frigate/config/camera/updater.py
T

178 lines
6.4 KiB
Python
Raw Normal View History

2025-05-22 12:16:51 -06:00
"""Convenience classes for updating configurations dynamically."""
from dataclasses import dataclass
from enum import Enum
from typing import Any
from frigate.comms.config_updater import ConfigPublisher, ConfigSubscriber
2025-06-11 11:25:30 -06:00
from frigate.config import CameraConfig, FrigateConfig
2025-05-22 12:16:51 -06:00
class CameraConfigUpdateEnum(str, Enum):
"""Supported camera config update types."""
2025-06-11 11:25:30 -06:00
add = "add" # for adding a camera
2025-05-22 12:16:51 -06:00
audio = "audio"
2025-05-27 10:26:00 -05:00
audio_transcription = "audio_transcription"
2025-05-22 12:16:51 -06:00
birdseye = "birdseye"
detect = "detect"
enabled = "enabled"
ffmpeg = "ffmpeg"
2026-03-19 11:33:42 -05:00
live = "live"
2025-05-22 12:16:51 -06:00
motion = "motion" # includes motion and motion masks
2026-04-28 09:54:09 -05:00
mqtt = "mqtt"
2025-05-22 12:16:51 -06:00
notifications = "notifications"
objects = "objects"
2025-08-10 07:38:04 -06:00
object_genai = "object_genai"
2026-03-25 08:57:47 -05:00
onvif = "onvif"
2025-05-22 12:16:51 -06:00
record = "record"
2026-05-22 09:39:52 -05:00
refresh = "refresh" # signals the camera maintainer to recycle the camera process
2025-06-11 11:25:30 -06:00
remove = "remove" # for removing a camera
2025-05-22 12:16:51 -06:00
review = "review"
2025-08-10 07:38:04 -06:00
review_genai = "review_genai"
2025-07-07 09:03:57 -05:00
semantic_search = "semantic_search" # for semantic search triggers
2026-03-19 09:47:57 -05:00
face_recognition = "face_recognition"
lpr = "lpr"
2025-05-22 12:16:51 -06:00
snapshots = "snapshots"
2026-03-25 14:14:32 -05:00
timestamp_style = "timestamp_style"
2026-04-28 09:54:09 -05:00
ui = "ui"
2025-05-22 12:16:51 -06:00
zones = "zones"
@dataclass
class CameraConfigUpdateTopic:
update_type: CameraConfigUpdateEnum
camera: str
@property
def topic(self) -> str:
return f"config/cameras/{self.camera}/{self.update_type.name}"
class CameraConfigUpdatePublisher:
def __init__(self):
self.publisher = ConfigPublisher()
def publish_update(self, topic: CameraConfigUpdateTopic, config: Any) -> None:
self.publisher.publish(topic.topic, config)
def stop(self) -> None:
self.publisher.stop()
class CameraConfigUpdateSubscriber:
def __init__(
self,
2025-06-11 11:25:30 -06:00
config: FrigateConfig | None,
2025-05-22 12:16:51 -06:00
camera_configs: dict[str, CameraConfig],
topics: list[CameraConfigUpdateEnum],
):
2025-06-11 11:25:30 -06:00
self.config = config
2025-05-22 12:16:51 -06:00
self.camera_configs = camera_configs
self.topics = topics
base_topic = "config/cameras"
2026-06-11 06:36:30 -05:00
# global subscribers must hear every camera; only narrow per-camera workers
is_global_subscriber = (
CameraConfigUpdateEnum.add in self.topics
or CameraConfigUpdateEnum.remove in self.topics
)
if not is_global_subscriber and len(self.camera_configs) == 1:
2025-05-22 12:16:51 -06:00
base_topic += f"/{list(self.camera_configs.keys())[0]}"
self.subscriber = ConfigSubscriber(
base_topic,
exact=False,
)
def __update_config(
self, camera: str, update_type: CameraConfigUpdateEnum, updated_config: Any
) -> None:
2025-06-11 11:25:30 -06:00
if update_type == CameraConfigUpdateEnum.add:
2026-05-22 09:39:52 -05:00
shared = self.config.cameras.setdefault(camera, updated_config)
self.camera_configs[camera] = shared
2025-06-11 11:25:30 -06:00
return
elif update_type == CameraConfigUpdateEnum.remove:
2026-03-04 10:07:34 -06:00
self.config.cameras.pop(camera, None)
self.camera_configs.pop(camera, None)
2025-06-11 11:25:30 -06:00
return
config = self.camera_configs.get(camera)
2025-05-22 12:16:51 -06:00
if not config:
return
if update_type == CameraConfigUpdateEnum.audio:
config.audio = updated_config
elif update_type == CameraConfigUpdateEnum.ffmpeg:
config.ffmpeg = updated_config
config.recreate_ffmpeg_cmds()
2025-06-11 11:25:30 -06:00
elif update_type == CameraConfigUpdateEnum.audio_transcription:
2025-05-27 10:26:00 -05:00
config.audio_transcription = updated_config
2025-05-22 12:16:51 -06:00
elif update_type == CameraConfigUpdateEnum.birdseye:
config.birdseye = updated_config
elif update_type == CameraConfigUpdateEnum.detect:
config.detect = updated_config
elif update_type == CameraConfigUpdateEnum.enabled:
config.enabled = updated_config
2025-08-10 07:38:04 -06:00
elif update_type == CameraConfigUpdateEnum.object_genai:
2025-08-08 16:33:11 -06:00
config.objects.genai = updated_config
2026-03-19 11:33:42 -05:00
elif update_type == CameraConfigUpdateEnum.live:
config.live = updated_config
2025-05-22 12:16:51 -06:00
elif update_type == CameraConfigUpdateEnum.motion:
config.motion = updated_config
elif update_type == CameraConfigUpdateEnum.notifications:
config.notifications = updated_config
elif update_type == CameraConfigUpdateEnum.objects:
config.objects = updated_config
2025-05-22 12:16:51 -06:00
elif update_type == CameraConfigUpdateEnum.record:
2026-05-07 08:53:07 -05:00
old_enabled_in_config = config.record.enabled_in_config
2025-05-22 12:16:51 -06:00
config.record = updated_config
2026-05-07 08:53:07 -05:00
if old_enabled_in_config != updated_config.enabled_in_config:
config.recreate_ffmpeg_cmds()
2025-05-22 12:16:51 -06:00
elif update_type == CameraConfigUpdateEnum.review:
config.review = updated_config
2025-08-10 07:38:04 -06:00
elif update_type == CameraConfigUpdateEnum.review_genai:
config.review.genai = updated_config
2025-07-07 09:03:57 -05:00
elif update_type == CameraConfigUpdateEnum.semantic_search:
config.semantic_search = updated_config
2026-03-19 09:47:57 -05:00
elif update_type == CameraConfigUpdateEnum.face_recognition:
config.face_recognition = updated_config
elif update_type == CameraConfigUpdateEnum.lpr:
config.lpr = updated_config
2025-05-22 12:16:51 -06:00
elif update_type == CameraConfigUpdateEnum.snapshots:
config.snapshots = updated_config
2026-03-25 08:57:47 -05:00
elif update_type == CameraConfigUpdateEnum.onvif:
config.onvif = updated_config
2026-03-25 14:14:32 -05:00
elif update_type == CameraConfigUpdateEnum.timestamp_style:
config.timestamp_style = updated_config
2025-05-22 12:16:51 -06:00
elif update_type == CameraConfigUpdateEnum.zones:
config.zones = updated_config
def check_for_updates(self) -> dict[str, list[str]]:
updated_topics: dict[str, list[str]] = {}
# get all updates available
while True:
update_topic, update_config = self.subscriber.check_for_update()
if update_topic is None or update_config is None:
break
_, _, camera, raw_type = update_topic.split("/")
update_type = CameraConfigUpdateEnum[raw_type]
if update_type in self.topics:
if update_type.name in updated_topics:
updated_topics[update_type.name].append(camera)
else:
updated_topics[update_type.name] = [camera]
self.__update_config(camera, update_type, update_config)
return updated_topics
def stop(self) -> None:
self.subscriber.stop()