2023-04-24 13:20:37 +00:00
|
|
|
"""ONVIF util."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
2023-04-25 17:20:17 +00:00
|
|
|
from typing import Any
|
|
|
|
|
2023-04-24 13:20:37 +00:00
|
|
|
from zeep.exceptions import Fault
|
|
|
|
|
|
|
|
|
2023-04-25 17:20:17 +00:00
|
|
|
def extract_subcodes_as_strings(subcodes: Any) -> list[str]:
|
|
|
|
"""Stringify ONVIF subcodes."""
|
|
|
|
if isinstance(subcodes, list):
|
|
|
|
return [code.text if hasattr(code, "text") else str(code) for code in subcodes]
|
|
|
|
return [str(subcodes)]
|
|
|
|
|
|
|
|
|
2023-04-24 13:20:37 +00:00
|
|
|
def stringify_onvif_error(error: Exception) -> str:
|
|
|
|
"""Stringify ONVIF error."""
|
|
|
|
if isinstance(error, Fault):
|
2023-04-25 17:20:17 +00:00
|
|
|
message = error.message
|
2023-05-06 22:47:15 +00:00
|
|
|
if error.detail is not None: # checking true is deprecated
|
2023-04-29 19:33:25 +00:00
|
|
|
# Detail may be a bytes object, so we need to convert it to string
|
|
|
|
if isinstance(error.detail, bytes):
|
|
|
|
detail = error.detail.decode("utf-8", "replace")
|
|
|
|
else:
|
|
|
|
detail = str(error.detail)
|
|
|
|
message += ": " + detail
|
2023-05-06 22:47:15 +00:00
|
|
|
if error.code is not None: # checking true is deprecated
|
2023-04-25 17:20:17 +00:00
|
|
|
message += f" (code:{error.code})"
|
2023-05-06 22:47:15 +00:00
|
|
|
if error.subcodes is not None: # checking true is deprecated
|
2023-04-25 17:20:17 +00:00
|
|
|
message += (
|
|
|
|
f" (subcodes:{','.join(extract_subcodes_as_strings(error.subcodes))})"
|
|
|
|
)
|
|
|
|
if error.actor:
|
|
|
|
message += f" (actor:{error.actor})"
|
|
|
|
else:
|
|
|
|
message = str(error)
|
Improve reliability of ONVIF subscription renewals (#92551)
* Improve reliablity of onvif subscription renewals
upstream changelog: https://github.com/hunterjm/python-onvif-zeep-async/compare/v2.0.0...v2.1.0
* ```
Traceback (most recent call last):
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/onvif/client.py", line 75, in _async_wrap_connection_error_retry
return await func(*args, **kwargs)
File "/Users/bdraco/home-assistant/homeassistant/components/onvif/event.py", line 441, in _async_call_pullpoint_subscription_renew
await self._pullpoint_subscription.Renew(SUBSCRIPTION_RELATIVE_TIME)
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/zeep/proxy.py", line 64, in __call__
return await self._proxy._binding.send_async(
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/zeep/wsdl/bindings/soap.py", line 156, in send_async
response = await client.transport.post_xml(
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/zeep/transports.py", line 235, in post_xml
response = await self.post(address, message, headers)
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/zeep/transports.py", line 220, in post
response = await self.client.post(
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_client.py", line 1845, in post
return await self.request(
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_client.py", line 1530, in request
return await self.send(request, auth=auth, follow_redirects=follow_redirects)
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_client.py", line 1617, in send
response = await self._send_handling_auth(
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_client.py", line 1645, in _send_handling_auth
response = await self._send_handling_redirects(
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_client.py", line 1682, in _send_handling_redirects
response = await self._send_single_request(request)
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_client.py", line 1719, in _send_single_request
response = await transport.handle_async_request(request)
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 352, in handle_async_request
with map_httpcore_exceptions():
File "/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/contextlib.py", line 153, in __exit__
self.gen.throw(typ, value, traceback)
File "/Users/bdraco/home-assistant/venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 77, in map_httpcore_exceptions
raise mapped_exc(message) from exc
httpx.ReadTimeout
```
* adjust timeouts for slower tplink cameras
* tweak
* more debug
* tweak
* adjust message
* tweak
* Revert "tweak"
This reverts commit 10ee2a8de70e93dc5be85b1992ec4d30c2188344.
* give time in seconds
* revert
* revert
* Update homeassistant/components/onvif/event.py
* Update homeassistant/components/onvif/event.py
2023-05-05 18:26:58 +00:00
|
|
|
return message or f"Device sent empty error with type {type(error)}"
|
2023-04-25 17:20:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def is_auth_error(error: Exception) -> bool:
|
|
|
|
"""Return True if error is an authentication error.
|
|
|
|
|
|
|
|
Most of the tested cameras do not return a proper error code when
|
|
|
|
authentication fails, so we need to check the error message as well.
|
|
|
|
"""
|
|
|
|
if not isinstance(error, Fault):
|
|
|
|
return False
|
|
|
|
return (
|
|
|
|
any(
|
|
|
|
"NotAuthorized" in code
|
|
|
|
for code in extract_subcodes_as_strings(error.subcodes)
|
|
|
|
)
|
|
|
|
or "auth" in stringify_onvif_error(error).lower()
|
|
|
|
)
|