diff --git a/frigate/comms/dispatcher.py b/frigate/comms/dispatcher.py index e5e9e8996..1ce99f7e3 100644 --- a/frigate/comms/dispatcher.py +++ b/frigate/comms/dispatcher.py @@ -900,6 +900,13 @@ class Dispatcher: logger.error(f"Motion mask {mask_name} is None") return + if payload == "ON": + if not mask.enabled_in_config: + logger.error( + f"Motion mask {mask_name} must be enabled in the config to be turned on via MQTT." + ) + return + mask.enabled = payload == "ON" # Recreate RuntimeMotionConfig to update rasterized_mask @@ -934,6 +941,12 @@ class Dispatcher: if mask_name in object_settings.mask: mask = object_settings.mask[mask_name] if mask: + if payload == "ON": + if not mask.enabled_in_config: + logger.error( + f"Object mask {mask_name} must be enabled in the config to be turned on via MQTT." + ) + return mask.enabled = payload == "ON" mask_found = True @@ -942,6 +955,12 @@ class Dispatcher: if mask_name in filter_config.mask: mask = filter_config.mask[mask_name] if mask: + if payload == "ON": + if not mask.enabled_in_config: + logger.error( + f"Object mask {mask_name} must be enabled in the config to be turned on via MQTT." + ) + return mask.enabled = payload == "ON" mask_found = True @@ -992,6 +1011,13 @@ class Dispatcher: logger.error(f"Unknown zone: {zone_name}") return + if payload == "ON": + if not camera_config.zones[zone_name].enabled_in_config: + logger.error( + f"Zone {zone_name} must be enabled in the config to be turned on via MQTT." + ) + return + camera_config.zones[zone_name].enabled = payload == "ON" self.config_updater.publish_update( diff --git a/frigate/config/camera/mask.py b/frigate/config/camera/mask.py index 939957578..98031f043 100644 --- a/frigate/config/camera/mask.py +++ b/frigate/config/camera/mask.py @@ -25,6 +25,9 @@ class MotionMaskConfig(FrigateBaseModel): title="Coordinates polygon for the motion mask.", ) raw_coordinates: Union[str, list[str]] = "" + enabled_in_config: Optional[bool] = Field( + default=None, title="Keep track of original state of motion mask." + ) def get_formatted_name(self, mask_id: str) -> str: """Return the friendly name if set, otherwise return a formatted version of the mask ID.""" @@ -57,6 +60,9 @@ class ObjectMaskConfig(FrigateBaseModel): title="Coordinates polygon for the object mask.", ) raw_coordinates: Union[str, list[str]] = "" + enabled_in_config: Optional[bool] = Field( + default=None, title="Keep track of original state of object mask." + ) @field_serializer("coordinates", when_used="json") def serialize_coordinates(self, value: Any, info): diff --git a/frigate/config/camera/zone.py b/frigate/config/camera/zone.py index fdcac76af..85abbcf2d 100644 --- a/frigate/config/camera/zone.py +++ b/frigate/config/camera/zone.py @@ -22,6 +22,9 @@ class ZoneConfig(BaseModel): default=True, title="Whether this zone is active. Disabled zones are ignored at runtime.", ) + enabled_in_config: Optional[bool] = Field( + default=None, title="Keep track of original state of zone." + ) filters: dict[str, FilterConfig] = Field( default_factory=dict, title="Zone filters", diff --git a/frigate/config/config.py b/frigate/config/config.py index 5bc2518d5..399b8542f 100644 --- a/frigate/config/config.py +++ b/frigate/config/config.py @@ -807,6 +807,7 @@ class FrigateConfig(FrigateBaseModel): raw_coordinates=relative_coords if relative_coords else coords, + enabled_in_config=mask_config.enabled, ) else: processed_global_masks[mask_id] = mask_config @@ -815,6 +816,11 @@ class FrigateConfig(FrigateBaseModel): # Apply global object masks and convert masks to numpy array for object, filter in camera_config.objects.filters.items(): + # Set enabled_in_config for per-object masks before processing + for mask_config in filter.mask.values(): + if mask_config: + mask_config.enabled_in_config = mask_config.enabled + # Merge global object masks with per-object filter masks merged_mask = dict(filter.mask) # Copy filter-specific masks @@ -834,6 +840,13 @@ class FrigateConfig(FrigateBaseModel): ), ) + # Set enabled_in_config for motion masks to match config file state BEFORE creating RuntimeMotionConfig + if camera_config.motion: + camera_config.motion.enabled_in_config = camera_config.motion.enabled + for mask_config in camera_config.motion.mask.values(): + if mask_config: + mask_config.enabled_in_config = mask_config.enabled + # Convert motion configuration if camera_config.motion is None: camera_config.motion = RuntimeMotionConfig( @@ -844,7 +857,6 @@ class FrigateConfig(FrigateBaseModel): frame_shape=camera_config.frame_shape, **camera_config.motion.model_dump(exclude_unset=True), ) - camera_config.motion.enabled_in_config = camera_config.motion.enabled # generate zone contours if len(camera_config.zones) > 0: @@ -858,6 +870,10 @@ class FrigateConfig(FrigateBaseModel): zone.generate_contour(camera_config.frame_shape) + # Set enabled_in_config for zones to match config file state + for zone in camera_config.zones.values(): + zone.enabled_in_config = zone.enabled + # Set live view stream if none is set if not camera_config.live.streams: camera_config.live.streams = {name: name}