diff --git a/frigate/api/app.py b/frigate/api/app.py index 35eed2b9ce..8ed365fce8 100644 --- a/frigate/api/app.py +++ b/frigate/api/app.py @@ -908,6 +908,11 @@ def config_set(request: Request, body: AppConfigSetBody): status_code=500, ) + # drop runtime overrides for any fields the user just rewrote in + # yaml so a stale override doesn't silently win after restart + if request.app.dispatcher is not None: + request.app.dispatcher.clear_runtime_state_for_yaml_keys(updates.keys()) + if body.requires_restart == 0 or body.update_topic: old_config: FrigateConfig = request.app.frigate_config request.app.frigate_config = config diff --git a/frigate/app.py b/frigate/app.py index 8b5766148b..b2402d2326 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -348,7 +348,11 @@ class FrigateApp: persisted in cam.profiles for cam in self.config.cameras.values() ): logger.info("Restoring persisted profile '%s'", persisted) - self.profile_manager.activate_profile(persisted) + # don't clear runtime overrides here, restore_runtime_state() later + # in startup replays it on top of the activated profile + self.profile_manager.activate_profile( + persisted, clear_runtime_overrides=False + ) def start_detectors(self) -> None: for name in self.config.cameras.keys(): @@ -612,6 +616,9 @@ class FrigateApp: self.start_record_cleanup() self.start_watchdog() + # restore persisted runtime overrides on top of config + self.dispatcher.restore_runtime_state() + self.init_auth() try: diff --git a/frigate/config/profile_manager.py b/frigate/config/profile_manager.py index d109bdecbc..6aba8f1942 100644 --- a/frigate/config/profile_manager.py +++ b/frigate/config/profile_manager.py @@ -124,11 +124,24 @@ class ProfileManager: self.config.active_profile = None self._persist_active_profile(None) - def activate_profile(self, profile_name: Optional[str]) -> Optional[str]: + # drop all runtime overrides so they don't replay stale values on restart + if self.dispatcher is not None: + self.dispatcher.clear_runtime_state() + + def activate_profile( + self, + profile_name: Optional[str], + clear_runtime_overrides: bool = True, + ) -> Optional[str]: """Activate a profile by name, or deactivate if None. Args: profile_name: Profile name to activate, or None to deactivate. + clear_runtime_overrides: When True (the default, for user-initiated + activations) drop the dispatcher's runtime override file because + the layer below changed. Startup callers that are replaying a + persisted profile pass False so the runtime state stays + available for the subsequent replay step. Returns: None on success, or an error message string on failure. @@ -156,6 +169,11 @@ class ProfileManager: self.config.active_profile = profile_name self._persist_active_profile(profile_name) + + # a profile switch invalidates the steady-state runtime overrides + if clear_runtime_overrides and self.dispatcher is not None: + self.dispatcher.clear_runtime_state() + logger.info( "Profile %s", f"'{profile_name}' activated" if profile_name else "deactivated",