diff --git a/docs/docs/configuration/birdseye.md b/docs/docs/configuration/birdseye.md index 0b54a5f5f..6471bf4e3 100644 --- a/docs/docs/configuration/birdseye.md +++ b/docs/docs/configuration/birdseye.md @@ -33,3 +33,25 @@ cameras: birdseye: enabled: False ``` + +### Sorting cameras in the Birdseye view + +It is possible to override the order of cameras that are being shown in the Birdseye view. +The order needs to be set at the camera level. + +```yaml +# Include all cameras by default in Birdseye view +birdseye: + enabled: True + mode: continuous + +cameras: + front: + birdseye: + order: 1 + back: + birdseye: + order: 2 +``` + +*Note*: Cameras are sorted by default using their name to ensure a constant view inside Birdseye. diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index 93a266b56..5a1284ebb 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -518,6 +518,12 @@ cameras: # Optional: password for login. password: admin + # Optional: Configuration for how to sort the cameras in the Birdseye view. + birdseye: + # Optional: Adjust sort order of cameras in the Birdseye view. Larger numbers come later (default: shown below) + # By default the cameras are sorted alphabetically. + order: 0 + # Optional ui: # Optional: Set the default live mode for cameras in the UI (default: shown below) diff --git a/frigate/config.py b/frigate/config.py index b7d965282..f3e760b6a 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -396,6 +396,7 @@ class BirdseyeConfig(FrigateBaseModel): # uses BaseModel because some global attributes are not available at the camera level class BirdseyeCameraConfig(BaseModel): enabled: bool = Field(default=True, title="Enable birdseye view for camera.") + order: int = Field(default=0, title="Position of the camera in the birdseye view.") mode: BirdseyeModeEnum = Field( default=BirdseyeModeEnum.objects, title="Tracking mode for camera." ) diff --git a/frigate/output.py b/frigate/output.py index b59d56a62..79a081837 100644 --- a/frigate/output.py +++ b/frigate/output.py @@ -4,6 +4,7 @@ import logging import math import multiprocessing as mp import os +import operator import queue import signal import subprocess as sp @@ -292,8 +293,16 @@ class BirdsEyeFrameManager: # calculate layout dimensions layout_dim = math.ceil(math.sqrt(len(active_cameras))) + # check if we need to reset the layout because there are new cameras to add + reset_layout = ( + True if len(active_cameras.difference(self.active_cameras)) > 0 else False + ) + # reset the layout if it needs to be different - if layout_dim != self.layout_dim: + if layout_dim != self.layout_dim or reset_layout: + if reset_layout: + logger.debug(f"Added new cameras, resetting layout...") + logger.debug(f"Changing layout size from {self.layout_dim} to {layout_dim}") self.layout_dim = layout_dim @@ -327,6 +336,20 @@ class BirdsEyeFrameManager: self.active_cameras = active_cameras + # this also converts added_cameras from a set to a list since we need + # to pop elements in order + added_cameras = sorted( + added_cameras, + # sort cameras by order and by name if the order is the same + key=lambda added_camera: ( + self.config.cameras[added_camera].birdseye.order, + added_camera, + ), + # we're popping out elements from the end, so this needs to be reverse + # as we want the last element to be the first + reverse=True, + ) + # update each position in the layout for position, camera in enumerate(self.camera_layout, start=0): # if this camera was removed, replace it or clear it