mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-26 13:47:03 +02:00
parent
add68b8860
commit
71df5ad058
@ -33,6 +33,8 @@ class OnvifCommandEnum(str, Enum):
|
||||
stop = "stop"
|
||||
zoom_in = "zoom_in"
|
||||
zoom_out = "zoom_out"
|
||||
focus_in = "focus_in"
|
||||
focus_out = "focus_out"
|
||||
|
||||
|
||||
class OnvifController:
|
||||
@ -185,6 +187,16 @@ class OnvifController:
|
||||
ptz: ONVIFService = await onvif.create_ptz_service()
|
||||
self.cams[camera_name]["ptz"] = ptz
|
||||
|
||||
imaging: ONVIFService = await onvif.create_imaging_service()
|
||||
self.cams[camera_name]["imaging"] = imaging
|
||||
try:
|
||||
video_sources = await media.GetVideoSources()
|
||||
if video_sources and len(video_sources) > 0:
|
||||
self.cams[camera_name]["video_source_token"] = video_sources[0].token
|
||||
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||
logger.debug(f"Unable to get video sources for {camera_name}: {e}")
|
||||
self.cams[camera_name]["video_source_token"] = None
|
||||
|
||||
# setup continuous moving request
|
||||
move_request = ptz.create_type("ContinuousMove")
|
||||
move_request.ProfileToken = profile.token
|
||||
@ -265,9 +277,15 @@ class OnvifController:
|
||||
"RelativeZoomTranslationSpace"
|
||||
][zoom_space_id]["URI"]
|
||||
else:
|
||||
if "Zoom" in move_request["Translation"]:
|
||||
if (
|
||||
move_request["Translation"] is not None
|
||||
and "Zoom" in move_request["Translation"]
|
||||
):
|
||||
del move_request["Translation"]["Zoom"]
|
||||
if "Zoom" in move_request["Speed"]:
|
||||
if (
|
||||
move_request["Speed"] is not None
|
||||
and "Zoom" in move_request["Speed"]
|
||||
):
|
||||
del move_request["Speed"]["Zoom"]
|
||||
logger.debug(
|
||||
f"{camera_name}: Relative move request after deleting zoom: {move_request}"
|
||||
@ -360,7 +378,19 @@ class OnvifController:
|
||||
f"Disabling autotracking zooming for {camera_name}: Absolute zoom not supported. Exception: {e}"
|
||||
)
|
||||
|
||||
# set relative pan/tilt space for autotracker
|
||||
if self.cams[camera_name]["video_source_token"] is not None:
|
||||
try:
|
||||
imaging_capabilities = await imaging.GetImagingSettings(
|
||||
{"VideoSourceToken": self.cams[camera_name]["video_source_token"]}
|
||||
)
|
||||
if (
|
||||
hasattr(imaging_capabilities, "Focus")
|
||||
and imaging_capabilities.Focus
|
||||
):
|
||||
supported_features.append("focus")
|
||||
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||
logger.debug(f"Focus not supported for {camera_name}: {e}")
|
||||
|
||||
if (
|
||||
self.config.cameras[camera_name].onvif.autotracking.enabled_in_config
|
||||
and self.config.cameras[camera_name].onvif.autotracking.enabled
|
||||
@ -385,6 +415,18 @@ class OnvifController:
|
||||
"Zoom": True,
|
||||
}
|
||||
)
|
||||
if (
|
||||
"focus" in self.cams[camera_name]["features"]
|
||||
and self.cams[camera_name]["video_source_token"]
|
||||
):
|
||||
try:
|
||||
stop_request = self.cams[camera_name]["imaging"].create_type("Stop")
|
||||
stop_request.VideoSourceToken = self.cams[camera_name][
|
||||
"video_source_token"
|
||||
]
|
||||
await self.cams[camera_name]["imaging"].Stop(stop_request)
|
||||
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||
logger.warning(f"Failed to stop focus for {camera_name}: {e}")
|
||||
self.cams[camera_name]["active"] = False
|
||||
|
||||
async def _move(self, camera_name: str, command: OnvifCommandEnum) -> None:
|
||||
@ -593,6 +635,35 @@ class OnvifController:
|
||||
|
||||
self.cams[camera_name]["active"] = False
|
||||
|
||||
async def _focus(self, camera_name: str, command: OnvifCommandEnum) -> None:
|
||||
if self.cams[camera_name]["active"]:
|
||||
logger.warning(
|
||||
f"{camera_name} is already performing an action, not moving..."
|
||||
)
|
||||
await self._stop(camera_name)
|
||||
|
||||
if (
|
||||
"focus" not in self.cams[camera_name]["features"]
|
||||
or not self.cams[camera_name]["video_source_token"]
|
||||
):
|
||||
logger.error(f"{camera_name} does not support ONVIF continuous focus.")
|
||||
return
|
||||
|
||||
self.cams[camera_name]["active"] = True
|
||||
move_request = self.cams[camera_name]["imaging"].create_type("Move")
|
||||
move_request.VideoSourceToken = self.cams[camera_name]["video_source_token"]
|
||||
move_request.Focus = {
|
||||
"Continuous": {
|
||||
"Speed": 0.5 if command == OnvifCommandEnum.focus_in else -0.5
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
await self.cams[camera_name]["imaging"].Move(move_request)
|
||||
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||
logger.warning(f"Onvif sending focus request to {camera_name} failed: {e}")
|
||||
self.cams[camera_name]["active"] = False
|
||||
|
||||
async def handle_command_async(
|
||||
self, camera_name: str, command: OnvifCommandEnum, param: str = ""
|
||||
) -> None:
|
||||
@ -616,11 +687,10 @@ class OnvifController:
|
||||
elif command == OnvifCommandEnum.move_relative:
|
||||
_, pan, tilt = param.split("_")
|
||||
await self._move_relative(camera_name, float(pan), float(tilt), 0, 1)
|
||||
elif (
|
||||
command == OnvifCommandEnum.zoom_in
|
||||
or command == OnvifCommandEnum.zoom_out
|
||||
):
|
||||
elif command in (OnvifCommandEnum.zoom_in, OnvifCommandEnum.zoom_out):
|
||||
await self._zoom(camera_name, command)
|
||||
elif command in (OnvifCommandEnum.focus_in, OnvifCommandEnum.focus_out):
|
||||
await self._focus(camera_name, command)
|
||||
else:
|
||||
await self._move(camera_name, command)
|
||||
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||
@ -631,7 +701,6 @@ class OnvifController:
|
||||
) -> None:
|
||||
"""
|
||||
Handle ONVIF commands by scheduling them in the event loop.
|
||||
This is the synchronous interface that schedules async work.
|
||||
"""
|
||||
future = asyncio.run_coroutine_threadsafe(
|
||||
self.handle_command_async(camera_name, command, param), self.loop
|
||||
|
@ -38,6 +38,14 @@
|
||||
"label": "Zoom PTZ camera out"
|
||||
}
|
||||
},
|
||||
"focus": {
|
||||
"in": {
|
||||
"label": "Focus PTZ camera in"
|
||||
},
|
||||
"out": {
|
||||
"label": "Focus PTZ camera out"
|
||||
}
|
||||
},
|
||||
"frame": {
|
||||
"center": {
|
||||
"label": "Click in the frame to center the PTZ camera"
|
||||
|
@ -1,4 +1,11 @@
|
||||
type PtzFeature = "pt" | "zoom" | "pt-r" | "zoom-r" | "zoom-a" | "pt-r-fov";
|
||||
type PtzFeature =
|
||||
| "pt"
|
||||
| "zoom"
|
||||
| "pt-r"
|
||||
| "zoom-r"
|
||||
| "zoom-a"
|
||||
| "pt-r-fov"
|
||||
| "focus";
|
||||
|
||||
export type CameraPtzInfo = {
|
||||
name: string;
|
||||
|
@ -92,6 +92,8 @@ import {
|
||||
LuX,
|
||||
} from "react-icons/lu";
|
||||
import {
|
||||
MdCenterFocusStrong,
|
||||
MdCenterFocusWeak,
|
||||
MdClosedCaption,
|
||||
MdClosedCaptionDisabled,
|
||||
MdNoPhotography,
|
||||
@ -808,10 +810,10 @@ function PtzControlPanel({
|
||||
sendPtz("MOVE_DOWN");
|
||||
break;
|
||||
case "+":
|
||||
sendPtz("ZOOM_IN");
|
||||
sendPtz(modifiers.shift ? "FOCUS_IN" : "ZOOM_IN");
|
||||
break;
|
||||
case "-":
|
||||
sendPtz("ZOOM_OUT");
|
||||
sendPtz(modifiers.shift ? "FOCUS_OUT" : "ZOOM_OUT");
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -922,6 +924,40 @@ function PtzControlPanel({
|
||||
</TooltipButton>
|
||||
</>
|
||||
)}
|
||||
{ptz?.features?.includes("focus") && (
|
||||
<>
|
||||
<TooltipButton
|
||||
label={t("ptz.focus.in.label")}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
sendPtz("FOCUS_IN");
|
||||
}}
|
||||
onTouchStart={(e) => {
|
||||
e.preventDefault();
|
||||
sendPtz("FOCUS_IN");
|
||||
}}
|
||||
onMouseUp={onStop}
|
||||
onTouchEnd={onStop}
|
||||
>
|
||||
<MdCenterFocusStrong />
|
||||
</TooltipButton>
|
||||
<TooltipButton
|
||||
label={t("ptz.focus.out.label")}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
sendPtz("FOCUS_OUT");
|
||||
}}
|
||||
onTouchStart={(e) => {
|
||||
e.preventDefault();
|
||||
sendPtz("FOCUS_OUT");
|
||||
}}
|
||||
onMouseUp={onStop}
|
||||
onTouchEnd={onStop}
|
||||
>
|
||||
<MdCenterFocusWeak />
|
||||
</TooltipButton>
|
||||
</>
|
||||
)}
|
||||
|
||||
{ptz?.features?.includes("pt-r-fov") && (
|
||||
<TooltipProvider>
|
||||
|
Loading…
Reference in New Issue
Block a user