mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-07 20:41:13 +03:00
restore runtime state in dispatcher
This commit is contained in:
parent
5e7db736d2
commit
57280a1d9e
@ -3,11 +3,13 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from collections.abc import Iterable
|
||||||
from typing import Any, Callable, Optional, cast
|
from typing import Any, Callable, Optional, cast
|
||||||
|
|
||||||
from frigate.camera import PTZMetrics
|
from frigate.camera import PTZMetrics
|
||||||
from frigate.camera.activity_manager import AudioActivityManager, CameraActivityManager
|
from frigate.camera.activity_manager import AudioActivityManager, CameraActivityManager
|
||||||
from frigate.comms.base_communicator import Communicator
|
from frigate.comms.base_communicator import Communicator
|
||||||
|
from frigate.comms.runtime_state import RuntimeStatePersistence
|
||||||
from frigate.comms.webpush import WebPushClient
|
from frigate.comms.webpush import WebPushClient
|
||||||
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
||||||
from frigate.config.camera.updater import (
|
from frigate.config.camera.updater import (
|
||||||
@ -67,6 +69,7 @@ class Dispatcher:
|
|||||||
self.embeddings_reindex: dict[str, Any] = {}
|
self.embeddings_reindex: dict[str, Any] = {}
|
||||||
self.birdseye_layout: dict[str, Any] = {}
|
self.birdseye_layout: dict[str, Any] = {}
|
||||||
self.audio_transcription_state: str = "idle"
|
self.audio_transcription_state: str = "idle"
|
||||||
|
self._runtime_state = RuntimeStatePersistence()
|
||||||
self._camera_settings_handlers: dict[str, Callable] = {
|
self._camera_settings_handlers: dict[str, Callable] = {
|
||||||
"audio": self._on_audio_command,
|
"audio": self._on_audio_command,
|
||||||
"audio_transcription": self._on_audio_transcription_command,
|
"audio_transcription": self._on_audio_transcription_command,
|
||||||
@ -397,6 +400,62 @@ class Dispatcher:
|
|||||||
for comm in self.comms:
|
for comm in self.comms:
|
||||||
comm.stop()
|
comm.stop()
|
||||||
|
|
||||||
|
def restore_runtime_state(self) -> None:
|
||||||
|
"""Replay persisted runtime overrides through the camera settings handlers.
|
||||||
|
|
||||||
|
Called once after Frigate startup completes so processing threads can
|
||||||
|
receive the resulting ``config_updater`` broadcasts. Unknown cameras
|
||||||
|
and topics are skipped; handler exceptions are logged and replay
|
||||||
|
continues for remaining entries.
|
||||||
|
"""
|
||||||
|
state = self._runtime_state.load()
|
||||||
|
for camera_name, features in state.items():
|
||||||
|
if camera_name not in self.config.cameras:
|
||||||
|
continue
|
||||||
|
if not isinstance(features, dict):
|
||||||
|
continue
|
||||||
|
for topic, value in features.items():
|
||||||
|
handler = self._camera_settings_handlers.get(topic)
|
||||||
|
if handler is None:
|
||||||
|
continue
|
||||||
|
payload = "ON" if value else "OFF"
|
||||||
|
try:
|
||||||
|
handler(camera_name, payload)
|
||||||
|
except Exception:
|
||||||
|
logger.exception(
|
||||||
|
"Failed to restore runtime state %s.%s=%s",
|
||||||
|
camera_name,
|
||||||
|
topic,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
logger.info(
|
||||||
|
"Restored runtime state: %s.%s=%s",
|
||||||
|
camera_name,
|
||||||
|
topic,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
def clear_runtime_state_for_yaml_keys(self, dotted_keys: Iterable[str]) -> None:
|
||||||
|
"""Clear stored runtime overrides for YAML keys that were just rewritten.
|
||||||
|
|
||||||
|
Called by ``/api/config/set`` after a successful YAML save so an
|
||||||
|
explicit settings-UI save isn't silently overridden by an older
|
||||||
|
runtime toggle on the next restart.
|
||||||
|
"""
|
||||||
|
self._runtime_state.clear_for_yaml_keys(dotted_keys)
|
||||||
|
|
||||||
|
def clear_runtime_state(self) -> None:
|
||||||
|
"""Wipe every stored runtime override.
|
||||||
|
|
||||||
|
Called when a profile is activated or deactivated. A profile switch
|
||||||
|
changes the layer below the runtime overrides, so the stored
|
||||||
|
"steady state" is no longer valid and must be reset; otherwise a
|
||||||
|
subsequent restart would replay stale overrides on top of the new
|
||||||
|
profile-derived in-memory state.
|
||||||
|
"""
|
||||||
|
self._runtime_state.clear_all()
|
||||||
|
|
||||||
def _on_detect_command(self, camera_name: str, payload: str) -> None:
|
def _on_detect_command(self, camera_name: str, payload: str) -> None:
|
||||||
"""Callback for detect topic."""
|
"""Callback for detect topic."""
|
||||||
detect_settings = self.config.cameras[camera_name].detect
|
detect_settings = self.config.cameras[camera_name].detect
|
||||||
@ -428,6 +487,7 @@ class Dispatcher:
|
|||||||
CameraConfigUpdateTopic(CameraConfigUpdateEnum.detect, camera_name),
|
CameraConfigUpdateTopic(CameraConfigUpdateEnum.detect, camera_name),
|
||||||
detect_settings,
|
detect_settings,
|
||||||
)
|
)
|
||||||
|
self._runtime_state.set(camera_name, "detect", detect_settings.enabled)
|
||||||
self.publish(f"{camera_name}/detect/state", payload, retain=True)
|
self.publish(f"{camera_name}/detect/state", payload, retain=True)
|
||||||
|
|
||||||
def _on_enabled_command(self, camera_name: str, payload: str) -> None:
|
def _on_enabled_command(self, camera_name: str, payload: str) -> None:
|
||||||
@ -452,6 +512,7 @@ class Dispatcher:
|
|||||||
CameraConfigUpdateTopic(CameraConfigUpdateEnum.enabled, camera_name),
|
CameraConfigUpdateTopic(CameraConfigUpdateEnum.enabled, camera_name),
|
||||||
camera_settings.enabled,
|
camera_settings.enabled,
|
||||||
)
|
)
|
||||||
|
self._runtime_state.set(camera_name, "enabled", camera_settings.enabled)
|
||||||
self.publish(f"{camera_name}/enabled/state", payload, retain=True)
|
self.publish(f"{camera_name}/enabled/state", payload, retain=True)
|
||||||
|
|
||||||
def _on_motion_command(self, camera_name: str, payload: str) -> None:
|
def _on_motion_command(self, camera_name: str, payload: str) -> None:
|
||||||
@ -614,6 +675,7 @@ class Dispatcher:
|
|||||||
CameraConfigUpdateTopic(CameraConfigUpdateEnum.audio, camera_name),
|
CameraConfigUpdateTopic(CameraConfigUpdateEnum.audio, camera_name),
|
||||||
audio_settings,
|
audio_settings,
|
||||||
)
|
)
|
||||||
|
self._runtime_state.set(camera_name, "audio", audio_settings.enabled)
|
||||||
self.publish(f"{camera_name}/audio/state", payload, retain=True)
|
self.publish(f"{camera_name}/audio/state", payload, retain=True)
|
||||||
|
|
||||||
def _on_audio_transcription_command(self, camera_name: str, payload: str) -> None:
|
def _on_audio_transcription_command(self, camera_name: str, payload: str) -> None:
|
||||||
@ -670,6 +732,7 @@ class Dispatcher:
|
|||||||
CameraConfigUpdateTopic(CameraConfigUpdateEnum.record, camera_name),
|
CameraConfigUpdateTopic(CameraConfigUpdateEnum.record, camera_name),
|
||||||
record_settings,
|
record_settings,
|
||||||
)
|
)
|
||||||
|
self._runtime_state.set(camera_name, "recordings", record_settings.enabled)
|
||||||
self.publish(f"{camera_name}/recordings/state", payload, retain=True)
|
self.publish(f"{camera_name}/recordings/state", payload, retain=True)
|
||||||
|
|
||||||
def _on_snapshots_command(self, camera_name: str, payload: str) -> None:
|
def _on_snapshots_command(self, camera_name: str, payload: str) -> None:
|
||||||
@ -689,6 +752,7 @@ class Dispatcher:
|
|||||||
CameraConfigUpdateTopic(CameraConfigUpdateEnum.snapshots, camera_name),
|
CameraConfigUpdateTopic(CameraConfigUpdateEnum.snapshots, camera_name),
|
||||||
snapshots_settings,
|
snapshots_settings,
|
||||||
)
|
)
|
||||||
|
self._runtime_state.set(camera_name, "snapshots", snapshots_settings.enabled)
|
||||||
self.publish(f"{camera_name}/snapshots/state", payload, retain=True)
|
self.publish(f"{camera_name}/snapshots/state", payload, retain=True)
|
||||||
|
|
||||||
def _on_ptz_command(self, camera_name: str, payload: str | bytes) -> None:
|
def _on_ptz_command(self, camera_name: str, payload: str | bytes) -> None:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user