Miscellaneous fixes (#24878)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s

* don't calibrate or skip capability checks when autotracking is enabled at runtime

Enabling autotracking from the UI on a camera that had been disabled for missing FOV relative movement or MoveStatus skipped both checks because ONVIF was already initialized, then ran calibration on the object processing thread and hung waiting for a motor stop that never came. Setup now runs the capability checks every time, only calibrates at startup, stops tracking the triggering object when setup disables the camera, and publishes the OFF state so the toggle reflects it.

* only show settings reset buttons when the reset can succeed

Reset to Global and Reset to Default delete the section from config.yml, and the delete raised a KeyError (HTTP 500) when the key wasn't in the file. Camera live sections always read as overridden because the backend fills in live.streams per camera, and the global Reset to Default button showed for every section, including ones the user never wrote. Global reset now only shows when the section differs from its schema defaults, ignoring fields the backend resolves at runtime (ffmpeg and record export hwaccel_args, audio filters). Camera live overrides skip streams, ffmpeg overrides skip inputs, and reset is hidden for camera ffmpeg and detection models, where deleting the section can't produce a valid config. Birdseye modes uses a plain default so the schema carries it.

* Added an e2e spec covering reset visibility for default and customized global sections, the runtime-resolved fields, camera live overrides, and camera ffmpeg
This commit is contained in:
Josh Hawkins
2026-11-04 16:23:27 -07:00
committed by GitHub
parent 18a2eccab1
commit 1cb5ec4107
11 changed files with 196 additions and 37 deletions
+2 -7
View File
@@ -57,11 +57,6 @@ def birdseye_modes_to_mqtt_payload(modes: list[BirdseyeModeEnum]) -> str:
return payload or MQTT_NO_MODES
def default_birdseye_modes() -> list[BirdseyeModeEnum]:
"""Return the default Birdseye activity modes."""
return [BirdseyeModeEnum.all_objects]
class BirdseyeLayoutConfig(FrigateBaseModel):
scaling_factor: float = Field(
default=2.0,
@@ -84,7 +79,7 @@ class BirdseyeConfig(FrigateBaseModel):
description="Enable or disable the Birdseye view feature.",
)
modes: list[BirdseyeModeEnum] = Field(
default_factory=default_birdseye_modes,
default=[BirdseyeModeEnum.all_objects],
title="Activity types",
description="Activity types that include cameras in Birdseye.",
)
@@ -139,7 +134,7 @@ class BirdseyeCameraConfig(BaseModel):
description="Enable or disable the Birdseye view feature.",
)
modes: list[BirdseyeModeEnum] = Field(
default_factory=default_birdseye_modes,
default=[BirdseyeModeEnum.all_objects],
title="Activity types",
description="Activity types that include cameras in Birdseye.",
)
+31 -25
View File
@@ -233,7 +233,8 @@ class PtzAutoTracker(threading.Thread):
and camera_config.onvif.autotracking.enabled_in_config
):
future = asyncio.run_coroutine_threadsafe(
self._autotracker_setup(camera_config, camera), self.onvif.loop
self._autotracker_setup(camera_config, camera, startup=True),
self.onvif.loop,
)
# Wait for the coroutine to complete
future.result()
@@ -261,7 +262,9 @@ class PtzAutoTracker(threading.Thread):
self.config_subscriber.stop()
logger.info("Exiting autotracker...")
async def _autotracker_setup(self, camera_config: CameraConfig, camera: str):
async def _autotracker_setup(
self, camera_config: CameraConfig, camera: str, startup: bool = False
):
logger.debug(f"{camera}: Autotracker init")
self.tracked_object[camera] = None
@@ -288,35 +291,34 @@ class PtzAutoTracker(threading.Thread):
self._disable(camera, "Unable to initialize onvif")
return
if "pt-r-fov" not in self.onvif.cams[camera]["features"]:
self._disable(camera, "FOV relative movement not supported")
return
if "pt-r-fov" not in self.onvif.cams[camera]["features"]:
self._disable(camera, "FOV relative movement not supported")
return
move_status_supported = await self.onvif.get_service_capabilities(camera)
move_status_supported = await self.onvif.get_service_capabilities(camera)
if str(move_status_supported).lower() != "true":
self._disable(camera, "ONVIF MoveStatus not supported")
return
if str(move_status_supported).lower() != "true":
self._disable(camera, "ONVIF MoveStatus not supported")
return
if self.onvif.cams[camera]["init"]:
await self.onvif.get_camera_status(camera)
await self.onvif.get_camera_status(camera)
# movement queue with asyncio on OnvifController loop
asyncio.run_coroutine_threadsafe(
self._process_move_queue(camera), self.onvif.loop
)
# movement queue with asyncio on OnvifController loop
asyncio.run_coroutine_threadsafe(
self._process_move_queue(camera), self.onvif.loop
)
if camera_config.onvif.autotracking.movement_weights:
(
self.ptz_metrics[camera].min_zoom.value,
self.ptz_metrics[camera].max_zoom.value,
self.intercept[camera],
*self.move_coefficients[camera],
self.zoom_time[camera],
) = map(float, camera_config.onvif.autotracking.movement_weights)
if camera_config.onvif.autotracking.movement_weights:
(
self.ptz_metrics[camera].min_zoom.value,
self.ptz_metrics[camera].max_zoom.value,
self.intercept[camera],
*self.move_coefficients[camera],
self.zoom_time[camera],
) = map(float, camera_config.onvif.autotracking.movement_weights)
if camera_config.onvif.autotracking.calibrate_on_startup:
await self._calibrate_camera(camera)
if startup and camera_config.onvif.autotracking.calibrate_on_startup:
await self._calibrate_camera(camera)
self.dispatcher.publish(f"{camera}/ptz_autotracker/active", "OFF", retain=False)
self.autotracker_init[camera] = True
@@ -331,6 +333,7 @@ class PtzAutoTracker(threading.Thread):
CameraConfigUpdateTopic(CameraConfigUpdateEnum.autotracking, camera),
autotracking_config,
)
self.dispatcher.publish(f"{camera}/ptz_autotracker/state", "OFF", retain=True)
def _reset_tracked_object_metrics(self, camera: str) -> None:
self.tracked_object_metrics[camera] = {}
@@ -1269,6 +1272,9 @@ class PtzAutoTracker(threading.Thread):
# Wait for the coroutine to complete
future.result()
if not camera_config.onvif.autotracking.enabled:
return
if self.calibrating[camera]:
logger.debug(f"{camera}: Calibrating camera")
return