Skip to content

Commit

Permalink
Reolink ONVIF move read to primary callback (#91478)
Browse files Browse the repository at this point in the history
* Move read to primary callback

* fix styling

* Do not raise on ConnectionResetError

* Split request.text() to .read() and decode("utf-8")
  • Loading branch information
starkillerOG authored Apr 17, 2023
1 parent dd7de48 commit 88bde2a
Showing 1 changed file with 43 additions and 36 deletions.
79 changes: 43 additions & 36 deletions homeassistant/components/reolink/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,58 +362,65 @@ def unregister_webhook(self):
async def handle_webhook(
self, hass: HomeAssistant, webhook_id: str, request: Request
) -> None:
"""Shield the incoming webhook callback from cancellation."""
shielded_future = asyncio.shield(
self._handle_webhook(hass, webhook_id, request)
)
"""Read the incoming webhook from Reolink for inbound messages and schedule processing."""
_LOGGER.debug("Webhook '%s' called", webhook_id)
if not self._webhook_reachable.is_set():
self._webhook_reachable.set()
ir.async_delete_issue(self._hass, DOMAIN, "webhook_url")
await shielded_future

async def _handle_webhook(
self, hass: HomeAssistant, webhook_id: str, request: Request
) -> None:
"""Handle incoming webhook from Reolink for inbound messages and calls."""
data: bytes | None = None
try:
data = await request.text()
data = await request.read()
if not data:
_LOGGER.debug(
"Webhook '%s' triggered with unknown payload: %s", webhook_id, data
)
except ConnectionResetError:
# We lost the connection before reading the message, fallback to polling
# No need for a background task here as we already know the connection is lost
_LOGGER.debug(
"Webhook '%s' called, but lost connection before reading message, issuing poll",
"Webhook '%s' called, but lost connection before reading message "
"(ConnectionResetError), issuing poll",
webhook_id,
)
if not await self._api.get_motion_state_all_ch():
_LOGGER.error(
"Could not poll motion state after losing connection during receiving ONVIF event"
)
return
async_dispatcher_send(hass, f"{webhook_id}_all", {})
return

if not data:
except aiohttp.ClientResponseError:
_LOGGER.debug(
"Webhook '%s' triggered with unknown payload: %s", webhook_id, data
"Webhook '%s' called, but could not read the message, issuing poll",
webhook_id,
)
return

# We received the data but we want handle_webhook to return as soon as possible
# so we process the data in the background
hass.async_create_background_task(
self._process_webhook_data(hass, webhook_id, data),
"Process Reolink webhook",
)
except asyncio.CancelledError:
_LOGGER.debug(
"Webhook '%s' called, but lost connection before reading message "
"(CancelledError), issuing poll",
webhook_id,
)
raise
finally:
# We want handle_webhook to return as soon as possible
# so we process the data in the background, this also shields from cancellation
hass.async_create_background_task(
self._process_webhook_data(hass, webhook_id, data),
"Process Reolink webhook",
)

async def _process_webhook_data(
self, hass: HomeAssistant, webhook_id: str, data: str
self, hass: HomeAssistant, webhook_id: str, data: bytes | None
) -> None:
"""Process the data from the webhook."""
"""Process the data from the Reolink webhook."""
# This task is executed in the background so we need to catch exceptions
# and log them
if not self._webhook_reachable.is_set():
self._webhook_reachable.set()
ir.async_delete_issue(self._hass, DOMAIN, "webhook_url")

try:
channels = await self._api.ONVIF_event_callback(data)
if not data:
if not await self._api.get_motion_state_all_ch():
_LOGGER.error(
"Could not poll motion state after losing connection during receiving ONVIF event"
)
return
async_dispatcher_send(hass, f"{webhook_id}_all", {})
return

message = data.decode("utf-8")
channels = await self._api.ONVIF_event_callback(message)
except Exception as ex: # pylint: disable=broad-except
_LOGGER.exception(
"Error processing ONVIF event for Reolink %s: %s",
Expand Down

0 comments on commit 88bde2a

Please sign in to comment.