Check webhook url is reachable in Reolink (#89585)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
starkillerOG 2023-03-28 22:46:59 +02:00 committed by GitHub
parent 24d0d15f38
commit e9925f6062
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 8 deletions

View File

@ -54,7 +54,9 @@ class ReolinkHost:
)
self.webhook_id: str | None = None
self._webhook_url: str | None = None
self._base_url: str = ""
self._webhook_url: str = ""
self._webhook_reachable: asyncio.Event = asyncio.Event()
self._lost_subscription: bool = False
@property
@ -138,6 +140,32 @@ class ReolinkHost:
await self.subscribe()
_LOGGER.debug(
"Waiting for initial ONVIF state on webhook '%s'", self._webhook_url
)
try:
await asyncio.wait_for(self._webhook_reachable.wait(), timeout=15)
except asyncio.TimeoutError:
_LOGGER.debug(
"Did not receive initial ONVIF state on webhook '%s' after 15 seconds",
self._webhook_url,
)
ir.async_create_issue(
self._hass,
DOMAIN,
"webhook_url",
is_fixable=False,
severity=ir.IssueSeverity.WARNING,
translation_key="webhook_url",
translation_placeholders={
"name": self._api.nvr_name,
"base_url": self._base_url,
"network_link": "https://my.home-assistant.io/redirect/network/",
},
)
else:
ir.async_delete_issue(self._hass, DOMAIN, "webhook_url")
if self._api.sw_version_update_required:
ir.async_create_issue(
self._hass,
@ -287,10 +315,10 @@ class ReolinkHost:
)
try:
base_url = get_url(self._hass, prefer_external=False)
self._base_url = get_url(self._hass, prefer_external=False)
except NoURLAvailableError:
try:
base_url = get_url(self._hass, prefer_external=True)
self._base_url = get_url(self._hass, prefer_external=True)
except NoURLAvailableError as err:
self.unregister_webhook()
raise ReolinkWebhookException(
@ -299,9 +327,9 @@ class ReolinkHost:
) from err
webhook_path = webhook.async_generate_path(event_id)
self._webhook_url = f"{base_url}{webhook_path}"
self._webhook_url = f"{self._base_url}{webhook_path}"
if base_url.startswith("https"):
if self._base_url.startswith("https"):
ir.async_create_issue(
self._hass,
DOMAIN,
@ -310,7 +338,7 @@ class ReolinkHost:
severity=ir.IssueSeverity.WARNING,
translation_key="https_webhook",
translation_placeholders={
"base_url": base_url,
"base_url": self._base_url,
"network_link": "https://my.home-assistant.io/redirect/network/",
},
)
@ -337,6 +365,8 @@ class ReolinkHost:
"""Handle incoming webhook from Reolink for inbound messages and calls."""
_LOGGER.debug("Webhook '%s' called", webhook_id)
if not self._webhook_reachable.is_set():
self._webhook_reachable.set()
if not request.body_exists:
_LOGGER.debug("Webhook '%s' triggered without payload", webhook_id)

View File

@ -41,7 +41,11 @@
"issues": {
"https_webhook": {
"title": "Reolink webhook URL uses HTTPS (SSL)",
"description": "Reolink products can not push motion events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`"
"description": "Reolink products can not push motion events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`, a valid address could, for example, be `http://192.168.1.10:8123` where `192.168.1.10` is the IP of the Home Assistant device"
},
"webhook_url": {
"title": "Reolink webhook URL unreachable",
"description": "Did not receive initial ONVIF state from {name}. Most likely, the Reolink camera can not reach the current (local) Home Assistant URL `{base_url}`, please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}) that points to Home Assistant. For example `http://192.168.1.10:8123` where `192.168.1.10` is the IP of the Home Assistant device. Also, make sure the Reolink camera can reach that URL."
},
"enable_port": {
"title": "Reolink port not enabled",

View File

@ -39,6 +39,8 @@ def reolink_connect(mock_get_source_ip: None) -> Generator[MagicMock, None, None
with patch(
"homeassistant.components.reolink.host.webhook.async_register",
return_value=True,
), patch(
"homeassistant.components.reolink.host.asyncio.Event.wait", AsyncMock()
), patch(
"homeassistant.components.reolink.host.Host", autospec=True
) as host_mock_class:

View File

@ -1,6 +1,7 @@
"""Test the Reolink init."""
import asyncio
from typing import Any
from unittest.mock import AsyncMock, MagicMock, Mock
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from reolink_aio.exceptions import ReolinkError
@ -99,6 +100,7 @@ async def test_no_repair_issue(
issue_registry = ir.async_get(hass)
assert (const.DOMAIN, "https_webhook") not in issue_registry.issues
assert (const.DOMAIN, "webhook_url") not in issue_registry.issues
assert (const.DOMAIN, "enable_port") not in issue_registry.issues
assert (const.DOMAIN, "firmware_update") not in issue_registry.issues
@ -138,6 +140,21 @@ async def test_port_repair_issue(
assert (const.DOMAIN, "enable_port") in issue_registry.issues
async def test_webhook_repair_issue(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> None:
"""Test repairs issue is raised when the webhook url is unreachable."""
with patch(
"homeassistant.components.reolink.host.asyncio.Event.wait",
AsyncMock(side_effect=asyncio.TimeoutError()),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
issue_registry = ir.async_get(hass)
assert (const.DOMAIN, "webhook_url") in issue_registry.issues
async def test_firmware_repair_issue(
hass: HomeAssistant, config_entry: MockConfigEntry, reolink_connect: MagicMock
) -> None: