Fix onvif binary sensors (#90202)

* Fix httpx client creating a new ssl context with each client

While working on https://github.com/home-assistant/core/issues/83524
it was discovered that each new httpx client creates a new ssl context

f1157dbc41/httpx/_transports/default.py (L261)

If an ssl context is passed in creating a new one is avoided here

f1157dbc41/httpx/_config.py (L110)

This change makes httpx ssl no-verify behavior match aiohttp ssl no-verify
behavior

6da04694fd/aiohttp/connector.py (L892)

aiohttp solved this by wrapping the code that generates the ssl context
in an lru_cache

* compact

* Fix onvif binary sensors

fixes #83524

needs https://github.com/hunterjm/python-onvif-zeep-async/pull/9 first
to avoid recreating the memory leak

* Fix memory leak in onvif

Work around until https://github.com/hunterjm/python-onvif-zeep-async/pull/9

followup to https://github.com/home-assistant/core/pull/83006

* move check

* onvif-zeep-async 1.2.2

* fix unloading
This commit is contained in:
J. Nick Koston 2023-03-25 17:28:38 -10:00 committed by GitHub
parent bd08d88812
commit 0b8fb36a7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -27,6 +27,13 @@ SUBSCRIPTION_ERRORS = (
) )
def _stringify_onvif_error(error: Exception) -> str:
"""Stringify ONVIF error."""
if isinstance(error, Fault):
return error.message or str(error) or "Device sent empty error"
return str(error)
class EventManager: class EventManager:
"""ONVIF Event Manager.""" """ONVIF Event Manager."""
@ -79,30 +86,30 @@ class EventManager:
async def async_start(self) -> bool: async def async_start(self) -> bool:
"""Start polling events.""" """Start polling events."""
if await self.device.create_pullpoint_subscription(): if not await self.device.create_pullpoint_subscription():
# Create subscription manager return False
self._subscription = self.device.create_subscription_service(
"PullPointSubscription"
)
# Renew immediately # Create subscription manager
await self.async_renew() self._subscription = self.device.create_subscription_service(
"PullPointSubscription"
)
# Initialize events # Renew immediately
pullpoint = self.device.create_pullpoint_service() await self.async_renew()
with suppress(*SUBSCRIPTION_ERRORS):
await pullpoint.SetSynchronizationPoint()
response = await pullpoint.PullMessages(
{"MessageLimit": 100, "Timeout": dt.timedelta(seconds=5)}
)
# Parse event initialization # Initialize events
await self.async_parse_messages(response.NotificationMessage) pullpoint = self.device.create_pullpoint_service()
with suppress(*SUBSCRIPTION_ERRORS):
await pullpoint.SetSynchronizationPoint()
response = await pullpoint.PullMessages(
{"MessageLimit": 100, "Timeout": dt.timedelta(seconds=5)}
)
self.started = True # Parse event initialization
return True await self.async_parse_messages(response.NotificationMessage)
return False self.started = True
return True
async def async_stop(self) -> None: async def async_stop(self) -> None:
"""Unsubscribe from events.""" """Unsubscribe from events."""
@ -112,7 +119,8 @@ class EventManager:
if not self._subscription: if not self._subscription:
return return
await self._subscription.Unsubscribe() with suppress(*SUBSCRIPTION_ERRORS):
await self._subscription.Unsubscribe()
self._subscription = None self._subscription = None
async def async_restart(self, _now: dt.datetime | None = None) -> None: async def async_restart(self, _now: dt.datetime | None = None) -> None:
@ -148,7 +156,7 @@ class EventManager:
"Retrying later: %s" "Retrying later: %s"
), ),
self.unique_id, self.unique_id,
err, _stringify_onvif_error(err),
) )
if not restarted: if not restarted:
@ -170,7 +178,11 @@ class EventManager:
.isoformat(timespec="seconds") .isoformat(timespec="seconds")
.replace("+00:00", "Z") .replace("+00:00", "Z")
) )
await self._subscription.Renew(termination_time) with suppress(*SUBSCRIPTION_ERRORS):
# The first time we renew, we may get a Fault error so we
# suppress it. The subscription will be restarted in
# async_restart later.
await self._subscription.Renew(termination_time)
def async_schedule_pull(self) -> None: def async_schedule_pull(self) -> None:
"""Schedule async_pull_messages to run.""" """Schedule async_pull_messages to run."""
@ -203,7 +215,7 @@ class EventManager:
" '%s': %s" " '%s': %s"
), ),
self.unique_id, self.unique_id,
err, _stringify_onvif_error(err),
) )
# Treat errors as if the camera restarted. Assume that the pullpoint # Treat errors as if the camera restarted. Assume that the pullpoint
# subscription is no longer valid. # subscription is no longer valid.