-
-
Save hawkeye217/152a1d4ba80760dac95d46e143d37112 to your computer and use it in GitHub Desktop.
# This script can help you determine if your PTZ is capable of | |
# working with Frigate NVR's autotracker. | |
# | |
# Cameras with a "YES" printed for each parameter at the end of | |
# the output will likely be supported by Frigate. | |
# | |
# Make sure you're using python3 with the onvif-zeep package | |
# Update the values for your camera below, then run: | |
# pip3 install onvif-zeep | |
# python3 ./fovtest.py | |
from onvif import ONVIFCamera | |
# UPDATE the IP address, ONVIF port, "admin" and "password" with your camera's details. | |
mycam = ONVIFCamera('192.168.1.100', 80, 'admin', 'password', '/etc/onvif/wsdl/') | |
print('Connected to ONVIF camera') | |
# Create media service object | |
media = mycam.create_media_service() | |
print('Created media service object') | |
# Get target profile | |
media_profiles = media.GetProfiles() | |
print('Media profiles') | |
print(media_profiles) | |
for key, onvif_profile in enumerate(media_profiles): | |
if ( | |
not onvif_profile.VideoEncoderConfiguration | |
or onvif_profile.VideoEncoderConfiguration.Encoding != "H264" | |
): | |
continue | |
# Configure PTZ options | |
if onvif_profile.PTZConfiguration: | |
if onvif_profile.PTZConfiguration.DefaultContinuousPanTiltVelocitySpace is not None: | |
media_profile = onvif_profile | |
token = media_profile.token | |
print('Chosen token') | |
print(token) | |
# Create ptz service object | |
print('Creating PTZ object') | |
ptz = mycam.create_ptz_service() | |
print('Created PTZ service object') | |
# Get PTZ configuration options for getting option ranges | |
request = ptz.create_type("GetConfigurations") | |
configs = ptz.GetConfigurations(request)[0] | |
print('PTZ configurations:') | |
print(configs) | |
print() | |
request = ptz.create_type('GetConfigurationOptions') | |
request.ConfigurationToken = media_profile.PTZConfiguration.token | |
ptz_configuration_options = ptz.GetConfigurationOptions(request) | |
print('PTZ configuration options:') | |
print(ptz_configuration_options) | |
print() | |
print('PTZ service capabilities:') | |
request = ptz.create_type('GetServiceCapabilities') | |
service_capabilities = ptz.GetServiceCapabilities(request) | |
print(service_capabilities) | |
print() | |
print('PTZ status:') | |
request = ptz.create_type("GetStatus") | |
request.ProfileToken = token | |
status = ptz.GetStatus(request) | |
print(status) | |
pantilt_space_id = next( | |
( | |
i | |
for i, space in enumerate( | |
ptz_configuration_options.Spaces.RelativePanTiltTranslationSpace | |
) | |
if "TranslationSpaceFov" in space["URI"] | |
), | |
None, | |
) | |
zoom_space_id = next( | |
( | |
i | |
for i, space in enumerate( | |
ptz_configuration_options.Spaces.RelativeZoomTranslationSpace | |
) | |
if "TranslationGenericSpace" in space["URI"] | |
), | |
None, | |
) | |
def find_by_key(dictionary, target_key): | |
if target_key in dictionary: | |
return dictionary[target_key] | |
else: | |
for value in dictionary.values(): | |
if isinstance(value, dict): | |
result = find_by_key(value, target_key) | |
if result is not None: | |
return result | |
return None | |
if find_by_key(vars(service_capabilities), "MoveStatus"): | |
print("YES - GetServiceCapabilities shows that the camera supports MoveStatus.") | |
else: | |
print("NO - GetServiceCapabilities shows that the camera does not support MoveStatus.") | |
# there doesn't seem to be an onvif standard with this optional parameter | |
# some cameras can report MoveStatus with or without PanTilt or Zoom attributes | |
pan_tilt_status = getattr(status.MoveStatus, "PanTilt", None) | |
zoom_status = getattr(status.MoveStatus, "Zoom", None) | |
if pan_tilt_status is not None and pan_tilt_status == "IDLE" and ( | |
zoom_status is None or zoom_status == "IDLE" | |
): | |
print("YES - MoveStatus is reporting IDLE.") | |
# if it's not an attribute, see if MoveStatus even exists in the status result | |
if pan_tilt_status is None: | |
pan_tilt_status = getattr(status.MoveStatus, "MoveStatus", None) | |
# we're unsupported | |
if pan_tilt_status is None or (isinstance(pan_tilt_status, str) and pan_tilt_status not in [ | |
"IDLE", | |
"MOVING", | |
]): | |
print("NO - MoveStatus not reporting IDLE or MOVING.") | |
if pantilt_space_id is not None and configs.DefaultRelativePanTiltTranslationSpace is not None: | |
print("YES - RelativeMove Pan/Tilt (FOV) is supported.") | |
else: | |
print("NO - RelativeMove Pan/Tilt is unsupported.") | |
if zoom_space_id is not None: | |
print("YES - RelativeMove Zoom is supported.") | |
else: | |
print("NO - RelativeMove Zoom is unsupported.") |
I'm getting the following for all my cameras:
Connected to ONVIF camera
Created media service object
Media profiles
[{
'Name': 'MainStream',
'VideoSourceConfiguration': {
'Name': 'VideoSourceMain',
'UseCount': 2,
'SourceToken': 'VideoSourceMain',
'Bounds': {
'x': 0,
'y': 0,
'width': 3840,
'height': 2160
},
'_value_1': None,
'Extension': None,
'token': 'VideoSourceMain',
'_attr_1': {
}
},
'AudioSourceConfiguration': {
'Name': 'AudioMainName',
'UseCount': 2,
'SourceToken': 'AudioMainSrcToken',
'_value_1': None,
'token': 'AudioMainToken',
'_attr_1': {
}
},
'VideoEncoderConfiguration': {
'Name': 'VideoEncodeMain',
'UseCount': 1,
'Encoding': 'H264',
'Resolution': {
'Width': 3840,
'Height': 2160
},
'Quality': 100.0,
'RateControl': {
'FrameRateLimit': 20,
'EncodingInterval': 1,
'BitrateLimit': 0
},
'MPEG4': {
'GovLength': 0,
'Mpeg4Profile': 'SP'
},
'H264': {
'GovLength': 20,
'H264Profile': 'High'
},
'Multicast': {
'Address': {
'Type': 'IPv4',
'IPv4Address': '192.168.10.61',
'IPv6Address': None
},
'Port': 0,
'TTL': 0,
'AutoStart': False,
'_value_1': None,
'_attr_1': None
},
'SessionTimeout': datetime.timedelta(seconds=720),
'_value_1': None,
'token': 'VideoEncodeMain',
'_attr_1': {
}
},
'AudioEncoderConfiguration': {
'Name': 'AudioMain',
'UseCount': 2,
'Encoding': 'G711',
'Bitrate': 64000,
'SampleRate': 16000,
'Multicast': {
'Address': {
'Type': 'IPv4',
'IPv4Address': '192.168.10.61',
'IPv6Address': None
},
'Port': 80,
'TTL': 1,
'AutoStart': False,
'_value_1': None,
'_attr_1': None
},
'SessionTimeout': datetime.timedelta(microseconds=60000),
'_value_1': None,
'token': 'G711',
'_attr_1': {
}
},
'VideoAnalyticsConfiguration': {
'Name': 'VideoAnalyticsName',
'UseCount': 3,
'AnalyticsEngineConfiguration': {
'AnalyticsModule': [
{
'Parameters': {
'SimpleItem': [
{
'Name': 'Sensitivity',
'Value': '80'
}
],
'ElementItem': [
{
'_value_1': <Element {http://www.onvif.org/ver10/schema}CellLayout at 0x7f02b19844c0>,
'Name': 'Layout'
}
],
'Extension': None,
'_attr_1': None
},
'Name': 'MyCellMotionModule',
'Type': 'tt:CellMotionEngine'
}
],
'Extension': None,
'_attr_1': None
},
'RuleEngineConfiguration': {
'Rule': [
{
'Parameters': {
'SimpleItem': [
{
'Name': 'MinCount',
'Value': '5'
},
{
'Name': 'AlarmOnDelay',
'Value': '100'
},
{
'Name': 'AlarmOffDelay',
'Value': '100'
},
{
'Name': 'ActiveCells',
'Value': '0P8A8A=='
}
],
'ElementItem': [],
'Extension': None,
'_attr_1': None
},
'Name': 'MyMotionDetectorRule',
'Type': 'tt:CellMotionDetector'
}
],
'Extension': None,
'_attr_1': None
},
'_value_1': None,
'token': 'VideoAnalyticsToken',
'_attr_1': {
}
},
'PTZConfiguration': {
'Name': 'ptz0',
'UseCount': 2,
'NodeToken': 'ptz0',
'DefaultAbsolutePantTiltPositionSpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
'DefaultAbsoluteZoomPositionSpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace',
'DefaultRelativePanTiltTranslationSpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace',
'DefaultRelativeZoomTranslationSpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/TranslationGenericSpace',
'DefaultContinuousPanTiltVelocitySpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace',
'DefaultContinuousZoomVelocitySpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace',
'DefaultPTZSpeed': {
'PanTilt': {
'x': 1.0,
'y': 1.0,
'space': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace'
},
'Zoom': {
'x': 1.0,
'space': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace'
}
},
'DefaultPTZTimeout': datetime.timedelta(seconds=60),
'PanTiltLimits': {
'Range': {
'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
},
'YRange': {
'Min': -1.0,
'Max': 1.0
}
}
},
'ZoomLimits': {
'Range': {
'URI': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
},
'Extension': None,
'token': 'ptz0',
'_attr_1': {
}
},
'MetadataConfiguration': None,
'Extension': None,
'token': 'MainStream',
'fixed': False,
'_attr_1': {
}
}, {
'Name': 'SubStream',
'VideoSourceConfiguration': {
'Name': 'VideoSourceMain',
'UseCount': 2,
'SourceToken': 'VideoSourceMain',
'Bounds': {
'x': 0,
'y': 0,
'width': 720,
'height': 480
},
'_value_1': None,
'Extension': None,
'token': 'VideoSourceMain',
'_attr_1': {
}
},
'AudioSourceConfiguration': {
'Name': 'AudioMainName',
'UseCount': 2,
'SourceToken': 'AudioMainSrcToken',
'_value_1': None,
'token': 'AudioMainToken',
'_attr_1': {
}
},
'VideoEncoderConfiguration': {
'Name': 'VideoEncodeSub',
'UseCount': 1,
'Encoding': 'H264',
'Resolution': {
'Width': 720,
'Height': 480
},
'Quality': 100.0,
'RateControl': {
'FrameRateLimit': 25,
'EncodingInterval': 1,
'BitrateLimit': 2048
},
'MPEG4': {
'GovLength': 0,
'Mpeg4Profile': 'SP'
},
'H264': {
'GovLength': 20,
'H264Profile': 'High'
},
'Multicast': {
'Address': {
'Type': 'IPv4',
'IPv4Address': '192.168.10.61',
'IPv6Address': None
},
'Port': 0,
'TTL': 0,
'AutoStart': False,
'_value_1': None,
'_attr_1': None
},
'SessionTimeout': datetime.timedelta(seconds=720),
'_value_1': None,
'token': 'VideoEncodeSub',
'_attr_1': {
}
},
'AudioEncoderConfiguration': {
'Name': 'AudioMain',
'UseCount': 2,
'Encoding': 'G711',
'Bitrate': 64000,
'SampleRate': 16000,
'Multicast': {
'Address': {
'Type': 'IPv4',
'IPv4Address': '192.168.10.61',
'IPv6Address': None
},
'Port': 80,
'TTL': 1,
'AutoStart': False,
'_value_1': None,
'_attr_1': None
},
'SessionTimeout': datetime.timedelta(microseconds=60000),
'_value_1': None,
'token': 'G711',
'_attr_1': {
}
},
'VideoAnalyticsConfiguration': {
'Name': 'VideoAnalyticsName',
'UseCount': 3,
'AnalyticsEngineConfiguration': {
'AnalyticsModule': [
{
'Parameters': {
'SimpleItem': [
{
'Name': 'Sensitivity',
'Value': '80'
}
],
'ElementItem': [
{
'_value_1': <Element {http://www.onvif.org/ver10/schema}CellLayout at 0x7f02b1989c40>,
'Name': 'Layout'
}
],
'Extension': None,
'_attr_1': None
},
'Name': 'MyCellMotionModule',
'Type': 'tt:CellMotionEngine'
}
],
'Extension': None,
'_attr_1': None
},
'RuleEngineConfiguration': {
'Rule': [
{
'Parameters': {
'SimpleItem': [
{
'Name': 'MinCount',
'Value': '5'
},
{
'Name': 'AlarmOnDelay',
'Value': '100'
},
{
'Name': 'AlarmOffDelay',
'Value': '100'
},
{
'Name': 'ActiveCells',
'Value': '0P8A8A=='
}
],
'ElementItem': [],
'Extension': None,
'_attr_1': None
},
'Name': 'MyMotionDetectorRule',
'Type': 'tt:CellMotionDetector'
}
],
'Extension': None,
'_attr_1': None
},
'_value_1': None,
'token': 'VideoAnalyticsToken',
'_attr_1': {
}
},
'PTZConfiguration': {
'Name': 'ptz0',
'UseCount': 2,
'NodeToken': 'ptz0',
'DefaultAbsolutePantTiltPositionSpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
'DefaultAbsoluteZoomPositionSpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace',
'DefaultRelativePanTiltTranslationSpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace',
'DefaultRelativeZoomTranslationSpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/TranslationGenericSpace',
'DefaultContinuousPanTiltVelocitySpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace',
'DefaultContinuousZoomVelocitySpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace',
'DefaultPTZSpeed': {
'PanTilt': {
'x': 1.0,
'y': 1.0,
'space': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace'
},
'Zoom': {
'x': 1.0,
'space': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace'
}
},
'DefaultPTZTimeout': datetime.timedelta(seconds=60),
'PanTiltLimits': {
'Range': {
'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
},
'YRange': {
'Min': -1.0,
'Max': 1.0
}
}
},
'ZoomLimits': {
'Range': {
'URI': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
},
'Extension': None,
'token': 'ptz0',
'_attr_1': {
}
},
'MetadataConfiguration': None,
'Extension': None,
'token': 'SubStream',
'fixed': False,
'_attr_1': {
}
}]
Chosen token
SubStream
Creating PTZ object
Created PTZ service object
PTZ configurations:
{
'Name': 'ptz0',
'UseCount': 2,
'NodeToken': 'ptz0',
'DefaultAbsolutePantTiltPositionSpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
'DefaultAbsoluteZoomPositionSpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace',
'DefaultRelativePanTiltTranslationSpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace',
'DefaultRelativeZoomTranslationSpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/TranslationGenericSpace',
'DefaultContinuousPanTiltVelocitySpace': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace',
'DefaultContinuousZoomVelocitySpace': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace',
'DefaultPTZSpeed': {
'PanTilt': {
'x': 1.0,
'y': 1.0,
'space': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace'
},
'Zoom': {
'x': 1.0,
'space': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace'
}
},
'DefaultPTZTimeout': datetime.timedelta(seconds=60),
'PanTiltLimits': {
'Range': {
'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
},
'YRange': {
'Min': -1.0,
'Max': 1.0
}
}
},
'ZoomLimits': {
'Range': {
'URI': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
},
'Extension': None,
'token': 'ptz0',
'_attr_1': {
}
}
PTZ configuration options:
{
'Spaces': {
'AbsolutePanTiltPositionSpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
},
'YRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'AbsoluteZoomPositionSpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'RelativePanTiltTranslationSpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
},
'YRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'RelativeZoomTranslationSpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/TranslationGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'ContinuousPanTiltVelocitySpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
},
'YRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'ContinuousZoomVelocitySpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'PanTiltSpeedSpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'ZoomSpeedSpace': [
{
'URI': 'http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace',
'XRange': {
'Min': -1.0,
'Max': 1.0
}
}
],
'Extension': None,
'_attr_1': None
},
'PTZTimeout': {
'Min': datetime.timedelta(seconds=1),
'Max': datetime.timedelta(seconds=60)
},
'_value_1': None,
'PTControlDirection': None,
'Extension': None,
'_attr_1': None
}
PTZ service capabilities:
Traceback (most recent call last):
File "/usr/local/lib/python3.11/dist-packages/onvif/client.py", line 23, in wrapped
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/onvif/client.py", line 153, in wrapped
return call(params, callback)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/onvif/client.py", line 140, in call
ret = func(**params)
^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/zeep/proxy.py", line 46, in call
return self._proxy._binding.send(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/zeep/wsdl/bindings/soap.py", line 135, in send
return self.process_reply(client, operation_obj, response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/zeep/wsdl/bindings/soap.py", line 229, in process_reply
return self.process_error(doc, operation)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/zeep/wsdl/bindings/soap.py", line 391, in process_error
raise Fault(
zeep.exceptions.Fault
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/sdansro/./fovtest.py", line 65, in
service_capabilities = ptz.GetServiceCapabilities(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/onvif/client.py", line 26, in wrapped
raise ONVIFError(err)
onvif.exceptions.ONVIFError: Unknown error:
Looks like your camera's ONVIF implementation doesn't support a the GetServiceCapabilities call.
Hanwha XNP-6400RW