rroller / dahua

Dahua Camera and Doorbell Home Assistant Integration
MIT License
369 stars 71 forks source link

Error Handling HTTP Requests in Dahua Camera Integration #354

Open pswel1 opened 2 months ago

pswel1 commented 2 months ago

Encountering repeated ClientResponseError and HttpProcessingError with the Dahua camera integration in Home Assistant. Errors occur when attempting to fetch snapshots from the camera. The log indicates issues with parsing the HTTP response from the camera at /cgi-bin/snapshot.cgi. It appears the integration may not be handling certain HTTP response statuses or headers correctly. I'm a longtime fan, but out of my depth on potential fixes or updates needed in the integration code or camera settings.

Latest Dahua integration and HA 4/'24, using VTO2202F camera. Thanks for any help :)

2024-05-11 00:52:59.781 INFO (MainThread) [homeassistant.components.light] Setting up dahua.light
2024-05-11 00:52:59.782 WARNING (MainThread) [homeassistant.components.light] Entity None (<class 'custom_components.dahua.light.DahuaInfraredLight'>) is using deprecated supported features values which will be removed in HA Core 2025.1. Instead it should use <LightEntityFeature: 1> and color modes, please create a bug report at https://github.com/rroller/dahua/issues and reference https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation
2024-05-11 00:52:59.782 WARNING (MainThread) [homeassistant.components.light] None (<class 'custom_components.dahua.light.DahuaInfraredLight'>) does not set supported color modes, this will stop working in Home Assistant Core 2025.3, please create a bug report at https://github.com/rroller/dahua/issues

2024-05-11 00:55:30.896 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/aiohttp/client_proto.py", line 256, in data_received
    messages, upgraded, tail = self._parser.feed_data(data)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "aiohttp/_http_parser.pyx", line 557, in aiohttp._http_parser.HttpParser.feed_data
aiohttp.http_exceptions.BadHttpMessage: 400, message:
  Data after `Connection: close`:
    b'HTTP/1.1 500 Internal Server Error'
       ^
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/aiohttp/client_reqrep.py", line 976, in start
    message, payload = await protocol.read()  # type: ignore[union-attr]
                       ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/streams.py", line 640, in read
    await self._waiter
aiohttp.http_exceptions.HttpProcessingError: 0, message:
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/aiohttp/web_protocol.py", line 452, in _handle_request
    resp = await request_handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/web_app.py", line 543, in _handle
    resp = await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/web_middlewares.py", line 114, in impl
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/http/security_filter.py", line 92, in security_filter_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/http/forwarded.py", line 83, in forwarded_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/http/request_context.py", line 26, in request_context_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/http/ban.py", line 88, in ban_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp_session/__init__.py", line 199, in factory
    response = await handler(request)
               ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/http/auth.py", line 295, in auth_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/http/headers.py", line 32, in headers_middleware
    response = await handler(request)
               ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/http.py", line 73, in handle
    result = await handler(request, **request.match_info)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/camera/__init__.py", line 815, in get
    return await self.handle(request, camera)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/camera/__init__.py", line 833, in handle
    image = await _async_get_image(
            ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/camera/__init__.py", line 187, in _async_get_image
    else await camera.async_camera_image(width=width, height=height)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/dahua/camera.py", line 253, in async_camera_image
    return await self._coordinator.client.async_get_snapshot(self._channel_number)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/dahua/client.py", line 74, in async_get_snapshot
    return await self.get_bytes(url)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/dahua/client.py", line 758, in get_bytes
    response = await auth.request("GET", self._base + url)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/dahua/digest.py", line 46, in request
    response = await self.session.request(method, url, headers=headers, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/client.py", line 608, in _request
    await resp.start(conn)
  File "/usr/local/lib/python3.12/site-packages/aiohttp/client_reqrep.py", line 978, in start
    raise ClientResponseError(
aiohttp.client_exceptions.ClientResponseError: 0, message='', url=URL('http://192.168.xx.xx:80/cgi-bin/snapshot.cgi?channel=1')
pswel1 commented 2 months ago

I cobbled together a ghetto fix to camera.py, the source of the error, basically giving it a chance to miss a couple times and perhaps spacing out API requests more. I don't know if it's a "fix" or a "workaround" so I probably won't do a PR, unless it's wanted

async def async_camera_image(self, width: int | None = None, height: int | None = None):
    """Return a still image response from the camera."""
    retries = 3                         
    delay = 0.25                                 

    for attempt in range(retries):
        try:                                                                                                   
            # Try to fetch the snapshot    
            return await self._coordinator.client.async_get_snapshot(self._channel_number)
        except Exception as e:                       
            # Log the error with attempt count
            _LOGGER.warn(f"Attempt {attempt + 1} failed to get snapshot from channel {self._channel_number}: {str(e)}")
            # Wait before retrying                      
            if attempt < retries - 1:    
                await asyncio.sleep(delay)   
            else:                      
                _LOGGER.warn(f"All attempts failed; returning None")

    return None  # or perhaps a stock image could be used

clean logs for 24 hours so I'll call it "good enough" haha

patelreese87 commented 1 month ago

Just here to say Thank You for the above. I was in a similar boat with same error basically. Trying out your code now. Hopefully the ghetto fix works long term :)

Thank you again

mouldybread commented 3 weeks ago

With regards to your specific issue, the HTTP/1.1 500 Internal Server Error when fetching snapshots is a broader issue that commonly affects Dahua-based IPCs. This fix helped me to resolve the same issue. If you happen to be using an NVR then fixing the camera resolves the issue on the NVR. I also reduced the snapshot generation time to two seconds to reduce the chance of an incomplete snapshot. Snapshots are now reliably generated.

It would though be nicer if this and the error encountered in #365 were handled more gracefully.