turn on switch by default if pan and/or tilt capability is available

This commit is contained in:
Josh Hawkins 2026-06-09 16:37:07 -05:00
parent 470abbab9d
commit 3aadc88a29
3 changed files with 41 additions and 3 deletions

View File

@ -591,6 +591,32 @@ async def _connect_onvif_camera(
raise first_error
def _supports_continuous_pan_tilt(nodes) -> bool:
"""Whether any PTZ node advertises continuous pan/tilt velocity.
The web UI's directional controls issue ContinuousMove with a PanTilt
velocity, so continuous pan/tilt is what makes those controls usable. This
is intentionally narrower than ptz_supported, which is true for any device
exposing the ONVIF PTZ service - including zoom/focus-only varifocal lenses.
"""
for node in nodes or []:
spaces = getattr(node, "SupportedPTZSpaces", None) or (
node.get("SupportedPTZSpaces") if isinstance(node, dict) else None
)
if spaces is None:
continue
continuous = getattr(spaces, "ContinuousPanTiltVelocitySpace", None) or (
spaces.get("ContinuousPanTiltVelocitySpace")
if isinstance(spaces, dict)
else None
)
if continuous:
return True
return False
@router.get(
"/onvif/probe",
dependencies=[Depends(require_role(["admin"]))],
@ -748,6 +774,7 @@ async def onvif_probe(
# Check PTZ support and capabilities
ptz_supported = False
pan_tilt_supported = False
presets_count = 0
autotrack_supported = False
@ -781,6 +808,15 @@ async def onvif_probe(
logger.debug(f"Failed to get presets: {e}")
presets_count = 0
# Check for real (continuous) pan/tilt, which the UI controls need
if ptz_supported:
try:
nodes = await ptz_service.GetNodes()
pan_tilt_supported = _supports_continuous_pan_tilt(nodes)
logger.debug(f"Continuous pan/tilt supported: {pan_tilt_supported}")
except Exception as e:
logger.debug(f"Failed to read PTZ nodes for pan/tilt support: {e}")
# Check for autotracking support - requires both FOV relative movement and MoveStatus
if ptz_supported and first_profile_token and ptz_config_token:
# First check for FOV relative movement support
@ -900,6 +936,7 @@ async def onvif_probe(
"firmware_version": device_info["firmware_version"],
"profiles_count": profiles_count,
"ptz_supported": ptz_supported,
"pan_tilt_supported": pan_tilt_supported,
"presets_count": presets_count,
"autotrack_supported": autotrack_supported,
}

View File

@ -78,7 +78,7 @@ export default function Step3StreamConfig({
const onvif = wizardData.onvif;
const ptzSupported = wizardData.probeResult?.ptz_supported === true;
const hasPresets = (wizardData.probeResult?.presets_count ?? 0) > 0;
const panTiltSupported = wizardData.probeResult?.pan_tilt_supported === true;
const onvifInvalid = !!onvif?.enabled && (!onvif.host?.trim() || !onvif.port);
// Seed the PTZ pane once from the successful ONVIF probe
@ -87,7 +87,7 @@ export default function Step3StreamConfig({
if (ptzSupported && wizardData.onvif === undefined) {
onUpdate({
onvif: {
enabled: hasPresets,
enabled: panTiltSupported,
host: wizardData.host ?? "",
port: wizardData.onvifPort ?? 8000,
user: wizardData.username ?? "",
@ -97,7 +97,7 @@ export default function Step3StreamConfig({
}
}, [
ptzSupported,
hasPresets,
panTiltSupported,
wizardData.onvif,
wizardData.host,
wizardData.onvifPort,

View File

@ -212,6 +212,7 @@ export type OnvifProbeResponse = {
firmware_version?: string;
profiles_count?: number;
ptz_supported?: boolean;
pan_tilt_supported?: boolean;
presets_count?: number;
autotrack_supported?: boolean;
move_status_supported?: boolean;