Upgrade onvif-zeep dependency to use onvif-zeep-async (#15894)

* Upgrade to new dependency

* Start onvif work

* Update for async calls
This commit is contained in:
Nicolas Mowen 2025-01-08 07:40:37 -07:00
parent 0fc1a48230
commit fa652ca49d
3 changed files with 43 additions and 47 deletions

View File

@ -13,7 +13,7 @@ markupsafe == 2.1.*
python-multipart == 0.0.12 python-multipart == 0.0.12
# General # General
mypy == 1.6.1 mypy == 1.6.1
onvif_zeep == 0.2.12 onvif-zeep-async == 3.1.*
paho-mqtt == 2.1.* paho-mqtt == 2.1.*
pandas == 2.2.* pandas == 2.2.*
peewee == 3.17.* peewee == 3.17.*

View File

@ -29,6 +29,7 @@ class LoggerConfig(FrigateBaseModel):
logging.getLogger().setLevel(self.default.value.upper()) logging.getLogger().setLevel(self.default.value.upper())
log_levels = { log_levels = {
"httpx": LogLevel.error,
"werkzeug": LogLevel.error, "werkzeug": LogLevel.error,
"ws4py": LogLevel.error, "ws4py": LogLevel.error,
**self.logs, **self.logs,

View File

@ -1,15 +1,14 @@
"""Configure and control camera via onvif.""" """Configure and control camera via onvif."""
import asyncio
import logging import logging
from enum import Enum from enum import Enum
from importlib.util import find_spec from importlib.util import find_spec
from pathlib import Path from pathlib import Path
import numpy import numpy
import requests from onvif import ONVIFCamera, ONVIFError, ONVIFService
from onvif import ONVIFCamera, ONVIFError
from zeep.exceptions import Fault, TransportError from zeep.exceptions import Fault, TransportError
from zeep.transports import Transport
from frigate.camera import PTZMetrics from frigate.camera import PTZMetrics
from frigate.config import FrigateConfig, ZoomingModeEnum from frigate.config import FrigateConfig, ZoomingModeEnum
@ -49,11 +48,6 @@ class OnvifController:
if cam.onvif.host: if cam.onvif.host:
try: try:
session = requests.Session()
session.verify = not cam.onvif.tls_insecure
transport = Transport(
timeout=10, operation_timeout=10, session=session
)
self.cams[cam_name] = { self.cams[cam_name] = {
"onvif": ONVIFCamera( "onvif": ONVIFCamera(
cam.onvif.host, cam.onvif.host,
@ -64,7 +58,7 @@ class OnvifController:
Path(find_spec("onvif").origin).parent / "wsdl" Path(find_spec("onvif").origin).parent / "wsdl"
).replace("dist-packages/onvif", "site-packages"), ).replace("dist-packages/onvif", "site-packages"),
adjust_time=cam.onvif.ignore_time_mismatch, adjust_time=cam.onvif.ignore_time_mismatch,
transport=transport, encrypt=not cam.onvif.tls_insecure,
), ),
"init": False, "init": False,
"active": False, "active": False,
@ -74,11 +68,12 @@ class OnvifController:
except ONVIFError as e: except ONVIFError as e:
logger.error(f"Onvif connection to {cam.name} failed: {e}") logger.error(f"Onvif connection to {cam.name} failed: {e}")
def _init_onvif(self, camera_name: str) -> bool: async def _init_onvif(self, camera_name: str) -> bool:
onvif: ONVIFCamera = self.cams[camera_name]["onvif"] onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
await onvif.update_xaddrs()
# create init services # create init services
media = onvif.create_media_service() media: ONVIFService = await onvif.create_media_service()
logger.debug(f"Onvif media xaddr for {camera_name}: {media.xaddr}") logger.debug(f"Onvif media xaddr for {camera_name}: {media.xaddr}")
try: try:
@ -92,7 +87,7 @@ class OnvifController:
return False return False
try: try:
profiles = media.GetProfiles() profiles = await media.GetProfiles()
logger.debug(f"Onvif profiles for {camera_name}: {profiles}") logger.debug(f"Onvif profiles for {camera_name}: {profiles}")
except (ONVIFError, Fault, TransportError) as e: except (ONVIFError, Fault, TransportError) as e:
logger.error( logger.error(
@ -101,7 +96,7 @@ class OnvifController:
return False return False
profile = None profile = None
for key, onvif_profile in enumerate(profiles): for _, onvif_profile in enumerate(profiles):
if ( if (
onvif_profile.VideoEncoderConfiguration onvif_profile.VideoEncoderConfiguration
and onvif_profile.PTZConfiguration and onvif_profile.PTZConfiguration
@ -135,7 +130,8 @@ class OnvifController:
) )
return False return False
ptz = onvif.create_ptz_service() ptz: ONVIFService = await onvif.create_ptz_service()
self.cams[camera_name]["ptz"] = ptz
# setup continuous moving request # setup continuous moving request
move_request = ptz.create_type("ContinuousMove") move_request = ptz.create_type("ContinuousMove")
@ -246,7 +242,7 @@ class OnvifController:
# setup existing presets # setup existing presets
try: try:
presets: list[dict] = ptz.GetPresets({"ProfileToken": profile.token}) presets: list[dict] = await ptz.GetPresets({"ProfileToken": profile.token})
except ONVIFError as e: except ONVIFError as e:
logger.warning(f"Unable to get presets from camera: {camera_name}: {e}") logger.warning(f"Unable to get presets from camera: {camera_name}: {e}")
presets = [] presets = []
@ -325,19 +321,19 @@ class OnvifController:
) )
self.cams[camera_name]["features"] = supported_features self.cams[camera_name]["features"] = supported_features
self.cams[camera_name]["init"] = True self.cams[camera_name]["init"] = True
return True return True
def _stop(self, camera_name: str) -> None: def _stop(self, camera_name: str) -> None:
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["move_request"] move_request = self.cams[camera_name]["move_request"]
onvif.get_service("ptz").Stop( asyncio.run(
{ self.cams[camera_name]["ptz"].Stop(
"ProfileToken": move_request.ProfileToken, {
"PanTilt": True, "ProfileToken": move_request.ProfileToken,
"Zoom": True, "PanTilt": True,
} "Zoom": True,
}
)
) )
self.cams[camera_name]["active"] = False self.cams[camera_name]["active"] = False
@ -353,7 +349,6 @@ class OnvifController:
return return
self.cams[camera_name]["active"] = True self.cams[camera_name]["active"] = True
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["move_request"] move_request = self.cams[camera_name]["move_request"]
if command == OnvifCommandEnum.move_left: if command == OnvifCommandEnum.move_left:
@ -376,7 +371,7 @@ class OnvifController:
} }
try: try:
onvif.get_service("ptz").ContinuousMove(move_request) asyncio.run(self.cams[camera_name]["ptz"].ContinuousMove(move_request))
except ONVIFError as e: except ONVIFError as e:
logger.warning(f"Onvif sending move request to {camera_name} failed: {e}") logger.warning(f"Onvif sending move request to {camera_name} failed: {e}")
@ -404,7 +399,6 @@ class OnvifController:
camera_name camera_name
].frame_time.value ].frame_time.value
self.ptz_metrics[camera_name].stop_time.value = 0 self.ptz_metrics[camera_name].stop_time.value = 0
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["relative_move_request"] move_request = self.cams[camera_name]["relative_move_request"]
# function takes in -1 to 1 for pan and tilt, interpolate to the values of the camera. # function takes in -1 to 1 for pan and tilt, interpolate to the values of the camera.
@ -450,7 +444,7 @@ class OnvifController:
} }
move_request.Translation.Zoom.x = zoom move_request.Translation.Zoom.x = zoom
onvif.get_service("ptz").RelativeMove(move_request) asyncio.run(self.cams[camera_name]["ptz"].RelativeMove(move_request))
# reset after the move request # reset after the move request
move_request.Translation.PanTilt.x = 0 move_request.Translation.PanTilt.x = 0
@ -475,13 +469,14 @@ class OnvifController:
self.ptz_metrics[camera_name].start_time.value = 0 self.ptz_metrics[camera_name].start_time.value = 0
self.ptz_metrics[camera_name].stop_time.value = 0 self.ptz_metrics[camera_name].stop_time.value = 0
move_request = self.cams[camera_name]["move_request"] move_request = self.cams[camera_name]["move_request"]
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
preset_token = self.cams[camera_name]["presets"][preset] preset_token = self.cams[camera_name]["presets"][preset]
onvif.get_service("ptz").GotoPreset( asyncio.run(
{ self.cams[camera_name]["ptz"].GotoPreset(
"ProfileToken": move_request.ProfileToken, {
"PresetToken": preset_token, "ProfileToken": move_request.ProfileToken,
} "PresetToken": preset_token,
}
)
) )
self.cams[camera_name]["active"] = False self.cams[camera_name]["active"] = False
@ -498,7 +493,6 @@ class OnvifController:
return return
self.cams[camera_name]["active"] = True self.cams[camera_name]["active"] = True
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["move_request"] move_request = self.cams[camera_name]["move_request"]
if command == OnvifCommandEnum.zoom_in: if command == OnvifCommandEnum.zoom_in:
@ -506,7 +500,7 @@ class OnvifController:
elif command == OnvifCommandEnum.zoom_out: elif command == OnvifCommandEnum.zoom_out:
move_request.Velocity = {"Zoom": {"x": -0.5}} move_request.Velocity = {"Zoom": {"x": -0.5}}
onvif.get_service("ptz").ContinuousMove(move_request) asyncio.run(self.cams[camera_name]["ptz"].ContinuousMove(move_request))
def _zoom_absolute(self, camera_name: str, zoom, speed) -> None: def _zoom_absolute(self, camera_name: str, zoom, speed) -> None:
if "zoom-a" not in self.cams[camera_name]["features"]: if "zoom-a" not in self.cams[camera_name]["features"]:
@ -530,7 +524,6 @@ class OnvifController:
camera_name camera_name
].frame_time.value ].frame_time.value
self.ptz_metrics[camera_name].stop_time.value = 0 self.ptz_metrics[camera_name].stop_time.value = 0
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["absolute_move_request"] move_request = self.cams[camera_name]["absolute_move_request"]
# function takes in 0 to 1 for zoom, interpolate to the values of the camera. # function takes in 0 to 1 for zoom, interpolate to the values of the camera.
@ -548,7 +541,7 @@ class OnvifController:
logger.debug(f"{camera_name}: Absolute zoom: {zoom}") logger.debug(f"{camera_name}: Absolute zoom: {zoom}")
onvif.get_service("ptz").AbsoluteMove(move_request) asyncio.run(self.cams[camera_name]["ptz"].AbsoluteMove(move_request))
self.cams[camera_name]["active"] = False self.cams[camera_name]["active"] = False
@ -560,7 +553,7 @@ class OnvifController:
return return
if not self.cams[camera_name]["init"]: if not self.cams[camera_name]["init"]:
if not self._init_onvif(camera_name): if not asyncio.run(self._init_onvif(camera_name)):
return return
try: try:
@ -590,7 +583,7 @@ class OnvifController:
return {} return {}
if not self.cams[camera_name]["init"]: if not self.cams[camera_name]["init"]:
self._init_onvif(camera_name) asyncio.run(self._init_onvif(camera_name))
return { return {
"name": camera_name, "name": camera_name,
@ -604,15 +597,16 @@ class OnvifController:
return {} return {}
if not self.cams[camera_name]["init"]: if not self.cams[camera_name]["init"]:
self._init_onvif(camera_name) asyncio.run(self._init_onvif(camera_name))
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
service_capabilities_request = self.cams[camera_name][ service_capabilities_request = self.cams[camera_name][
"service_capabilities_request" "service_capabilities_request"
] ]
try: try:
service_capabilities = onvif.get_service("ptz").GetServiceCapabilities( service_capabilities = asyncio.run(
service_capabilities_request self.cams[camera_name]["ptz"].GetServiceCapabilities(
service_capabilities_request
)
) )
logger.debug( logger.debug(
@ -633,12 +627,13 @@ class OnvifController:
return {} return {}
if not self.cams[camera_name]["init"]: if not self.cams[camera_name]["init"]:
self._init_onvif(camera_name) asyncio.run(self._init_onvif(camera_name))
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
status_request = self.cams[camera_name]["status_request"] status_request = self.cams[camera_name]["status_request"]
try: try:
status = onvif.get_service("ptz").GetStatus(status_request) status = asyncio.run(
self.cams[camera_name]["ptz"].GetStatus(status_request)
)
except Exception: except Exception:
pass # We're unsupported, that'll be reported in the next check. pass # We're unsupported, that'll be reported in the next check.