From 636b484d9dc4992e35c9d3678605694fbb69f77c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 7 Jun 2025 13:39:59 -0500 Subject: [PATCH] Migrate onvif to use onvif-zeep-async 4.0.1 with aiohttp (#146297) --- homeassistant/components/onvif/__init__.py | 6 ++-- homeassistant/components/onvif/const.py | 11 +++++-- homeassistant/components/onvif/device.py | 6 ++-- homeassistant/components/onvif/event.py | 32 +++++++++++++++----- homeassistant/components/onvif/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 7 files changed, 43 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/onvif/__init__.py b/homeassistant/components/onvif/__init__.py index 09a4aba52bf..057993be181 100644 --- a/homeassistant/components/onvif/__init__.py +++ b/homeassistant/components/onvif/__init__.py @@ -5,7 +5,7 @@ from contextlib import suppress from http import HTTPStatus import logging -from httpx import RequestError +import aiohttp from onvif.exceptions import ONVIFError from onvif.util import is_auth_error, stringify_onvif_error from zeep.exceptions import Fault, TransportError @@ -49,7 +49,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await device.async_setup() if not entry.data.get(CONF_SNAPSHOT_AUTH): await async_populate_snapshot_auth(hass, device, entry) - except RequestError as err: + except (TimeoutError, aiohttp.ClientError) as err: await device.device.close() raise ConfigEntryNotReady( f"Could not connect to camera {device.device.host}:{device.device.port}: {err}" @@ -119,7 +119,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if device.capabilities.events and device.events.started: try: await device.events.async_stop() - except (ONVIFError, Fault, RequestError, TransportError): + except (TimeoutError, ONVIFError, Fault, aiohttp.ClientError, TransportError): LOGGER.warning("Error while stopping events: %s", device.name) return await hass.config_entries.async_unload_platforms(entry, device.platforms) diff --git a/homeassistant/components/onvif/const.py b/homeassistant/components/onvif/const.py index d191a1710d5..ec006a2db8d 100644 --- a/homeassistant/components/onvif/const.py +++ b/homeassistant/components/onvif/const.py @@ -1,8 +1,9 @@ """Constants for the onvif component.""" +import asyncio import logging -from httpx import RequestError +import aiohttp from onvif.exceptions import ONVIFError from zeep.exceptions import Fault, TransportError @@ -48,4 +49,10 @@ SERVICE_PTZ = "ptz" # Some cameras don't support the GetServiceCapabilities call # and will return a 404 error which is caught by TransportError -GET_CAPABILITIES_EXCEPTIONS = (ONVIFError, Fault, RequestError, TransportError) +GET_CAPABILITIES_EXCEPTIONS = ( + ONVIFError, + Fault, + aiohttp.ClientError, + asyncio.TimeoutError, + TransportError, +) diff --git a/homeassistant/components/onvif/device.py b/homeassistant/components/onvif/device.py index 3f37ba42397..9b4d0983682 100644 --- a/homeassistant/components/onvif/device.py +++ b/homeassistant/components/onvif/device.py @@ -9,7 +9,7 @@ import os import time from typing import Any -from httpx import RequestError +import aiohttp import onvif from onvif import ONVIFCamera from onvif.exceptions import ONVIFError @@ -235,7 +235,7 @@ class ONVIFDevice: LOGGER.debug("%s: Retrieving current device date/time", self.name) try: device_time = await device_mgmt.GetSystemDateAndTime() - except (RequestError, Fault) as err: + except (TimeoutError, aiohttp.ClientError, Fault) as err: LOGGER.warning( "Couldn't get device '%s' date/time. Error: %s", self.name, err ) @@ -303,7 +303,7 @@ class ONVIFDevice: # Set Date and Time ourselves if Date and Time is set manually in the camera. try: await self.async_manually_set_date_and_time() - except (RequestError, TransportError, IndexError, Fault): + except (TimeoutError, aiohttp.ClientError, TransportError, IndexError, Fault): LOGGER.warning("%s: Could not sync date/time on this camera", self.name) self._async_log_time_out_of_sync(cam_date_utc, system_date) diff --git a/homeassistant/components/onvif/event.py b/homeassistant/components/onvif/event.py index d1b93304ccc..86ec419f892 100644 --- a/homeassistant/components/onvif/event.py +++ b/homeassistant/components/onvif/event.py @@ -6,8 +6,8 @@ import asyncio from collections.abc import Callable import datetime as dt +import aiohttp from aiohttp.web import Request -from httpx import RemoteProtocolError, RequestError, TransportError from onvif import ONVIFCamera from onvif.client import ( NotificationManager, @@ -16,7 +16,7 @@ from onvif.client import ( ) from onvif.exceptions import ONVIFError from onvif.util import stringify_onvif_error -from zeep.exceptions import Fault, ValidationError, XMLParseError +from zeep.exceptions import Fault, TransportError, ValidationError, XMLParseError from homeassistant.components import webhook from homeassistant.config_entries import ConfigEntry @@ -34,10 +34,23 @@ from .parsers import PARSERS UNHANDLED_TOPICS: set[str] = {"tns1:MediaControl/VideoEncoderConfiguration"} SUBSCRIPTION_ERRORS = (Fault, TimeoutError, TransportError) -CREATE_ERRORS = (ONVIFError, Fault, RequestError, XMLParseError, ValidationError) +CREATE_ERRORS = ( + ONVIFError, + Fault, + aiohttp.ClientError, + asyncio.TimeoutError, + XMLParseError, + ValidationError, +) SET_SYNCHRONIZATION_POINT_ERRORS = (*SUBSCRIPTION_ERRORS, TypeError) UNSUBSCRIBE_ERRORS = (XMLParseError, *SUBSCRIPTION_ERRORS) -RENEW_ERRORS = (ONVIFError, RequestError, XMLParseError, *SUBSCRIPTION_ERRORS) +RENEW_ERRORS = ( + ONVIFError, + aiohttp.ClientError, + asyncio.TimeoutError, + XMLParseError, + *SUBSCRIPTION_ERRORS, +) # # We only keep the subscription alive for 10 minutes, and will keep # renewing it every 8 minutes. This is to avoid the camera @@ -372,13 +385,13 @@ class PullPointManager: "%s: PullPoint skipped because Home Assistant is not running yet", self._name, ) - except RemoteProtocolError as err: + except aiohttp.ServerDisconnectedError as err: # Either a shutdown event or the camera closed the connection. Because # http://datatracker.ietf.org/doc/html/rfc2616#section-8.1.4 allows the server # to close the connection at any time, we treat this as a normal. Some # cameras may close the connection if there are no messages to pull. LOGGER.debug( - "%s: PullPoint subscription encountered a remote protocol error " + "%s: PullPoint subscription encountered a server disconnected error " "(this is normal for some cameras): %s", self._name, stringify_onvif_error(err), @@ -394,7 +407,12 @@ class PullPointManager: # Treat errors as if the camera restarted. Assume that the pullpoint # subscription is no longer valid. self._pullpoint_manager.resume() - except (XMLParseError, RequestError, TimeoutError, TransportError) as err: + except ( + XMLParseError, + aiohttp.ClientError, + TimeoutError, + TransportError, + ) as err: LOGGER.debug( "%s: PullPoint subscription encountered an unexpected error and will be retried " "(this is normal for some cameras): %s", diff --git a/homeassistant/components/onvif/manifest.json b/homeassistant/components/onvif/manifest.json index 78df5130aed..63b7437be39 100644 --- a/homeassistant/components/onvif/manifest.json +++ b/homeassistant/components/onvif/manifest.json @@ -8,5 +8,5 @@ "documentation": "https://www.home-assistant.io/integrations/onvif", "iot_class": "local_push", "loggers": ["onvif", "wsdiscovery", "zeep"], - "requirements": ["onvif-zeep-async==3.2.5", "WSDiscovery==2.1.2"] + "requirements": ["onvif-zeep-async==4.0.1", "WSDiscovery==2.1.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index f8bd97ab258..c7351bf2559 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1584,7 +1584,7 @@ ondilo==0.5.0 onedrive-personal-sdk==0.0.14 # homeassistant.components.onvif -onvif-zeep-async==3.2.5 +onvif-zeep-async==4.0.1 # homeassistant.components.opengarage open-garage==0.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 29a5f6a20be..74b2097c5c1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1349,7 +1349,7 @@ ondilo==0.5.0 onedrive-personal-sdk==0.0.14 # homeassistant.components.onvif -onvif-zeep-async==3.2.5 +onvif-zeep-async==4.0.1 # homeassistant.components.opengarage open-garage==0.2.0