diff --git a/frigate/config/camera/camera.py b/frigate/config/camera/camera.py index 967a69427..0f2b1c8be 100644 --- a/frigate/config/camera/camera.py +++ b/frigate/config/camera/camera.py @@ -177,6 +177,12 @@ class CameraConfig(FrigateBaseModel): def ffmpeg_cmds(self) -> list[dict[str, list[str]]]: return self._ffmpeg_cmds + def get_formatted_name(self) -> str: + """Return the friendly name if set, otherwise return a formatted version of the camera name.""" + if self.friendly_name: + return self.friendly_name + return self.name.replace("_", " ").title() if self.name else "" + def create_ffmpeg_cmds(self): if "_ffmpeg_cmds" in self: return diff --git a/frigate/config/camera/zone.py b/frigate/config/camera/zone.py index 530ba1cf9..7df1a1f25 100644 --- a/frigate/config/camera/zone.py +++ b/frigate/config/camera/zone.py @@ -56,6 +56,12 @@ class ZoneConfig(BaseModel): def contour(self) -> np.ndarray: return self._contour + def get_formatted_name(self, zone_name: str) -> str: + """Return the friendly name if set, otherwise return a formatted version of the zone name.""" + if self.friendly_name: + return self.friendly_name + return zone_name.replace("_", " ").title() + @field_validator("objects", mode="before") @classmethod def validate_objects(cls, v): diff --git a/frigate/data_processing/post/review_descriptions.py b/frigate/data_processing/post/review_descriptions.py index a7594a46d..9691ac8fd 100644 --- a/frigate/data_processing/post/review_descriptions.py +++ b/frigate/data_processing/post/review_descriptions.py @@ -16,6 +16,7 @@ from peewee import DoesNotExist from frigate.comms.embeddings_updater import EmbeddingsRequestEnum from frigate.comms.inter_process import InterProcessRequestor from frigate.config import FrigateConfig +from frigate.config.camera import CameraConfig from frigate.config.camera.review import GenAIReviewConfig, ImageSourceEnum from frigate.const import CACHE_DIR, CLIPS_DIR, UPDATE_REVIEW_DESCRIPTION from frigate.data_processing.types import PostProcessDataEnum @@ -193,7 +194,7 @@ class ReviewDescriptionProcessor(PostProcessorApi): self.requestor, self.genai_client, self.review_desc_speed, - camera, + camera_config, final_data, thumbs, camera_config.review.genai, @@ -422,7 +423,7 @@ def run_analysis( requestor: InterProcessRequestor, genai_client: GenAIClient, review_inference_speed: InferenceSpeed, - camera: str, + camera_config: CameraConfig, final_data: dict[str, str], thumbs: list[bytes], genai_config: GenAIReviewConfig, @@ -430,10 +431,19 @@ def run_analysis( attribute_labels: list[str], ) -> None: start = datetime.datetime.now().timestamp() + + # Format zone names using zone config friendly names if available + formatted_zones = [] + for zone_name in final_data["data"]["zones"]: + if zone_name in camera_config.zones: + formatted_zones.append( + camera_config.zones[zone_name].get_formatted_name(zone_name) + ) + analytics_data = { "id": final_data["id"], - "camera": camera, - "zones": final_data["data"]["zones"], + "camera": camera_config.get_formatted_name(), + "zones": formatted_zones, "start": datetime.datetime.fromtimestamp(final_data["start_time"]).strftime( "%A, %I:%M %p" ), diff --git a/frigate/genai/__init__.py b/frigate/genai/__init__.py index 881e63b97..dd42fc6dd 100644 --- a/frigate/genai/__init__.py +++ b/frigate/genai/__init__.py @@ -51,8 +51,7 @@ class GenAIClient: def get_concern_prompt() -> str: if concerns: concern_list = "\n - ".join(concerns) - return f""" -- `other_concerns` (list of strings): Include a list of any of the following concerns that are occurring: + return f"""- `other_concerns` (list of strings): Include a list of any of the following concerns that are occurring: - {concern_list}""" else: return "" @@ -70,7 +69,7 @@ class GenAIClient: return "\n- (No objects detected)" context_prompt = f""" -Your task is to analyze the sequence of images ({len(thumbnails)} total) taken in chronological order from the perspective of the {review_data["camera"].replace("_", " ")} security camera. +Your task is to analyze the sequence of images ({len(thumbnails)} total) taken in chronological order from the perspective of the {review_data["camera"]} security camera. ## Normal Activity Patterns for This Property @@ -110,7 +109,7 @@ Your response MUST be a flat JSON object with: - Frame 1 = earliest, Frame {len(thumbnails)} = latest - Activity started at {review_data["start"]} and lasted {review_data["duration"]} seconds -- Zones involved: {", ".join(z.replace("_", " ").title() for z in review_data["zones"]) or "None"} +- Zones involved: {", ".join(review_data["zones"]) if review_data["zones"] else "None"} ## Objects in Scene